1.如何做分类输出的
在这的y尖代表真实值,y一撇代表预测的值,然后真实值使用的是独热编码(One-hot),注意最大值的下标就是他的类别
2.图片分类
2.1 输出
图片分类也是和回归任务一样,要输入数据取模型当中,但是这里的图片必须是要相同的大小的
回归任务和分类的区别:回归是输出一个数然后和真实值做loss,而分类是输出的不是一个值虽和真实值做loss但是两个任务的loss的求法不同,回归的是mseloss而分类的是交叉相乘的loss(Cross entropy)
2.2 输入
首先如果用全连接linear的话参数会非常的的大(3*224*224*1000+1000)会导致模型过拟合,所以这图片分类任务当中我们使用的卷积神经网络来实现的,这里的3是因为图片是3通道的RGB
举例:使用第二图的方法判断是否有,并且数值越大,图越像
当看一张图片的时候:我们的目标就是如何卷积核变得像我们想要的 最后得到一个有意义的特征图,这个过程就是通过训练得到的
1.可以在原来的卷积核旁边加一圈0
2.用更大的或者更多的卷积核层数来实现
2.2手算卷积神经网络
这的卷积核大小为2就是2*2的 padding为1就是补一圈0 为2就是补两圈0
其实就是这样计算的(1 2行列的和卷积核相乘得到第一个数字)
第三个先加一个padding的话原本的4*4就变为了6*6所以再来乘3*3的就变为4*4
第四个是三个三个的卷积然后到98的时候 98 99 100可以卷但是到99的话就不可以了因为只有99和100,所以卷积出来就是98*98 第五个类似
所以得出公式:卷出来的大小=原有的特征图大小 - 卷积核大小 + 1
第三个开始就有了厚度3 所以卷积核也必须是3通道的然后再说大小 padding为1后就变为了3 6 6 然后卷出来时 1 4 4 所以大小是为3 3 然后通道为3 所以这一套卷积核大小为 3 3 3 然后参数量就为3*3*3=27个
第四个 卷积核数量为7 但是我们知道每一个卷积核都可以卷出来一张特征图 所以这里是7 4 4
五和四类似
第六个的卷积核参数量为128个64*3*3这么多
2.3 使特征图变小
扩大步长
卷积尺寸计算公式
池化pooling
Pooling(2)的意思是两个格子变为一个 而 AdaptiveAvgPool(7)是无论怎么变最后都变为7
手搓卷积神经网络
卷积到全连接
分类的loss
3.一个基本的分类神经网络
3.1经典的图片数据集
3.2 经典的模型(Sota模型)
3.3 AlexNet
归一化:
import torchvision.models as models
# mymodel = models.alexnet()
#
# print(mymodel)
import torch.nn as nn
class myAlexNet(nn.Module):
def __init__(self, out_dim):
super(myAlexNet, self).__init__()
#初始的时候是3*224*224
#参数依次是输入特征图数量、输出特征图数量、卷积核大小、步长、padding
self.conv1 = nn.Conv2d(3,64,11,4,2) #后变为64*55*55 (224-11+2*2)/ 4 + 1 = 55 向下取整
#Pool(3,2)的意思是9个数据一起算然后计算下一个的时候是移动2个
self.pool1 = nn.MaxPool2d(3, 2) #变为64*27*27
self.conv2 = nn.Conv2d(64,192,5,1,2) #变为192*27*27
self.pool2 = nn.MaxPool2d(3, 2) #变为192*13*13
self.conv3 = nn.Conv2d(192,384,3,1,1) #变为384*13*13
self.conv4 = nn.Conv2d(384, 256, 3, 1, 1)#变为256*13*13
self.conv5 = nn.Conv2d(256, 256, 3, 1, 1)#变为256*13*13
self.pool3 = nn.MaxPool2d(3, 2) #变为256*6*6
self.pool4 = nn.AdaptiveAvgPool2d(6) #256*6*6 AdaptiveAvgPool2d(6)的意思是无论怎么变都变为6*6
self.fc1 = nn.Linear(9216, 4096) #这里的9216是自己算的 是256*6*6得来的
self.fc2 = nn.Linear(4096, 4096)
self.fc3 = nn.Linear(4096, out_dim) #最后全连接变为自己的类别数
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)
x = x.view(x.size()[0], -1) #拉直。 batch
x = self.fc1(x)
x = self.fc2(x)
x = self.fc3(x)
return x
model = myAlexNet(1000)
import torch
# a = torch.ones((4,3,224,224)) #batch, 3, 224,224
# pred = model(a)
def get_parameter_number(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}
print(get_parameter_number(model.pool1))
3.4 VggNet
import torchvision.models as models
import torch.nn as nn
vgg = models.vgg13()
print(vgg)
#vgg模型 两个一pool
class vggLayer(nn.Module):
def __init__(self,in_cha, mid_cha, out_cha):
super(vggLayer, self).__init__()
self.relu = nn.ReLU()
self.conv1 = nn.Conv2d(in_cha, mid_cha, 3, 1, 1)
self.conv2 = nn.Conv2d(mid_cha, out_cha, 3, 1, 1)
self.pool = nn.MaxPool2d(2)
def forward(self,x):
x = self.conv1(x)
x= self.relu(x)
x = self.conv2(x)
x = self.relu(x)
x = self.pool(x)
return x
class MyVgg(nn.Module):
def __init__(self):
super(MyVgg, self).__init__()
self.layer1 = vggLayer(3, 64, 64)
self.layer2 = vggLayer(64, 128, 128)
self.layer3 = vggLayer(128, 256, 256)
self.layer4 = vggLayer(256, 512, 512)
self.layer5 = vggLayer(512, 512, 512)
self.adapool = nn.AdaptiveAvgPool2d(7)
self.relu = nn.ReLU()
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.adapool(x)
x= self.adapool(x)
x = x.view(x.size()[0], -1)
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.relu(x)
x = self.fc3(x)
x = self.relu(x)
return x
import torch
myVgg = MyVgg()
#
img = torch.zeros((1, 3, 224,224))
out = myVgg(img)
print(out.size())
def get_parameter_number(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}
print(get_parameter_number(myVgg))
print(get_parameter_number(myVgg.layer1))
print(get_parameter_number(myVgg.layer1.conv1))
#
# print(get_parameter_number(vgg))
3.5 ResNet
1*1的目的是为了降维
为什么 ResNet 那么深?
ResNet(Residual Network)之所以能够设计得非常深(例如 ResNet-152 有 152 层),主要归功于其引入的 残差连接(Residual Connection) 或 跳跃连接(Skip Connection)。残差连接解决了深度神经网络训练中的 梯度消失 和 梯度爆炸 问题,使得非常深的网络能够有效地训练。
1. 梯度消失和梯度爆炸问题
-
在深度神经网络中,随着网络层数的增加,梯度在反向传播过程中可能会逐渐消失(接近于 0)或爆炸(变得非常大),导致网络难以训练。
-
这种问题在传统的深度卷积网络(如 VGGNet)中尤为明显,限制了网络的深度。
2. 残差连接的引入
-
残差连接 的核心思想是:如果一个网络层难以学习到复杂的特征,那么它可以学习一个恒等映射(Identity Mapping),即将输入直接传递到输出。
-
具体来说,残差连接将输入 x 直接加到输出 H(x) 上,形成 F(x)=H(x)−x。这样,网络只需要学习残差函数 F(x),而不是直接学习 H(x)。
-
残差连接的公式为:
y=F(x)+x其中 F(x) 是残差函数,x 是输入,y 是输出。
3. 残差连接的优势
-
缓解梯度消失问题:残差连接提供了一条梯度直接传播的路径,使得梯度能够有效地反向传播到较浅的层。
-
更容易优化:残差连接使得网络更容易学习到恒等映射,从而避免了网络退化的问题。
-
允许更深的网络:由于残差连接的存在,ResNet 可以设计得非常深(如 152 层),而不会出现训练困难的问题。
VGGNet 和 ResNet 的区别
1. 网络结构
-
VGGNet:
-
VGGNet 使用了非常均匀的结构,主要由多个卷积层(3x3 卷积核)和池化层(2x2 最大池化)组成。
-
VGGNet 的卷积层之间没有跳跃连接。
-
VGGNet 的深度相对较小,例如 VGG-16 有 16 层,VGG-19 有 19 层。
-
-
ResNet:
-
ResNet 引入了残差连接,形成了残差块(Residual Block)。
-
每个残差块包含两个或多个卷积层,输入通过残差连接直接加到输出。
-
ResNet 的深度可以非常大,例如 ResNet-50 有 50 层,ResNet-101 有 101 层,ResNet-152 有 152 层。
-
2. 残差连接
-
VGGNet:没有残差连接,网络层之间是顺序连接的。
-
ResNet:每个残差块都有残差连接,输入直接加到输出,形成跳跃连接。
3. 训练深度
-
VGGNet:由于没有残差连接,VGGNet 的深度有限,通常在 16 到 19 层之间。
-
ResNet:由于残差连接的存在,ResNet 可以设计得非常深,达到 152 层甚至更深。
4. 参数数量
-
VGGNet:VGGNet 的参数数量较多,尤其是全连接层部分。例如,VGG-16 有约 1.38 亿个参数。
-
ResNet:ResNet 的参数数量相对较少,尤其是较深的版本。例如,ResNet-50 有约 2560 万个参数,ResNet-152 有约 6020 万个参数。
5. 性能
-
VGGNet:VGGNet 在 ImageNet 数据集上取得了较好的性能,但随着网络深度的增加,训练变得困难。
-
ResNet:ResNet 由于引入了残差连接,能够在更深的网络中保持较好的性能,显著提高了分类准确率。
总结
-
VGGNet 是一种经典的深度卷积神经网络,结构简单但深度有限,适合较小规模的任务。
-
ResNet 通过引入残差连接,解决了深度网络训练中的梯度消失问题,能够设计得非常深,从而在大规模任务中表现出色。
-
ResNet 的残差连接是其核心创新,使得网络能够有效地训练和优化,成为深度学习领域的重要里程碑。