前面提到的回归可以理解为一个填空题,要求模型预测出一个值,而分类就相当于一个选择题,从已有的选项中选择一个答案
分类任务的输出
对于分类任务的输出,一般是输出一个维度为类别个数的向量,比如 猫 狗 人 那么就应该输出一个三维向量,对于标签值可以采用one-hot编码来实现。此例中:猫(1 0 0) 狗(0 1 0) 人(0 0 1),对于模型来说,输出的三维向量应该是三个不确定大小的值,因此我们需要把值映射到0-1的概率,如图:
将模型的输出经过softmax转为概率,因此最后的y撇表示,有0的概率是dog,有0.953的概率是cat,有0.047的概率是tree,当然在设计模型的时候最后不需要手动写softmax,因为softmax封装在了多分类任务的损失函数(nn.CrossEntroyloss)中,
即 标签*log(概率),至此模型的输出讲完,介绍模型的输入
分类任务的输入
一个图片一般都是3*224*224,若直接把图片展平了送到线性全连接层中,参数量巨大,因此引入了卷积。卷积就是,一边卷动一边求积,用一个卷积核把一个原始图片进行卷积操作,得到图片的特征图
流程
首先先用卷积核来提取图片的特征,为了解决图片边缘特征丢失的问题,采用padding后再利用卷积核进行卷积
将6*6通过padding1变成8*8,这样子经过3*3的卷积核后就会使特征图的大小和原图保持一致
第二步,进行最大池化,意义是使得特征图的大小进一步缩小,以缩小参数量,为将来展平放入线性全连接层做准备,如图:
第三步,扁平化处理,池化后特征图变小,参数变少看,可以展平放入线性全连接层
最终流程如下:
补充知识:
原始图片经过多少个卷积核,就生成多少个特征图
批量归一化:
代码:
AlexNet代码:
import torch
import torch.nn as nn
# 获得模型参数量
def get_model_parameters(model):
total_num = sum(p.numel() for p in model.parameters())
trainable_num = sum(p.numel() for p in model.parameters() if p.requires_grad)
return {'Total':total_num,'Trainable':trainable_num}
class MyModel(nn.Module):
# class_num 最后分类的类别数量
def __init__(self,class_num):
super().__init__()
'''
输入特征图数量 输出特征图数量 卷积核大小 步长 padding
'''
self.conv1 = nn.Conv2d(3,64,11,4,2)
self.pool1 = nn.MaxPool2d(3,2)
self.conv2 = nn.Conv2d(64,192,5,1,2)
self.pool2 = nn.MaxPool2d(3,2)
self.conv3 = nn.Conv2d(192,384,3,1,1)
self.conv4 = nn.Conv2d(384,256,3,1,1)
self.conv5 = nn.Conv2d(256,256,3,1,1)
self.pool3 = nn.MaxPool2d(3,2)
self.pool4 = nn.AdaptiveAvgPool2d(6)
# 三层全连接 256*6*6=9216
self.fc1 = nn.Linear(9216,4096)
self.fc2 = nn.Linear(4096,4096)
self.fc3 = nn.Linear(4096,class_num)
def forward(self,x):
x = self.conv1(x)
x = self.pool1(x)
x = self.conv2(x)
x = self.pool2(x)
x = self.conv3(x)
x = self.conv4(x)
x = self.conv5(x)
x = self.pool3(x)
x = self.pool4(x) #batch*256*6*6
# 将x展平
x = x.view(x.size()[0],-1) # -1表示全部展到第二维上
# 展平后送入线性层
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
return x
if __name__ == '__main__':
class_num = 1000
data = torch.ones((4,3,224,224)) # 4个 3*224*224 的样本
model = MyModel(class_num)
pred = model(data)
print(get_model_parameters(model))
vggNet代码:
import torch
import torch.nn as nn
# vgg模型层
class VggLayer(nn.Module):
def __init__(self,in_feature,out_feature):
super().__init__()
self.conv1 = nn.Conv2d(in_feature,out_feature,3,1,1)
self.conv2 = nn.Conv2d(out_feature,out_feature,3,1,1)
self.pool = nn.MaxPool2d(2)
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x)
x = self.pool(x)
return x
#vgg模型
class myModel(nn.Module):
def __init__(self):
super().__init__()
# 调用vgg模型层
self.layer1 = VggLayer(3,64)
self.layer2 = VggLayer(64,128)
self.layer3 = VggLayer(128,256)
self.layer4 = VggLayer(256,512)
self.layer5 = VggLayer(512,512)
self.pool = nn.AdaptiveAvgPool2d(7)
self.fc1 = nn.Linear(25088,4096)
self.fc2 = nn.Linear(4096,4096)
self.fc3 = nn.Linear(4096,1000)
def forward(self,x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.layer5(x)
x = self.pool(x)
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
return x