一、问题总结
在深度学习模型中,为什么每次训练的测试结果不同?
二、代码练习
1、MobileNetV1
#深度可分离卷积
class Block(nn.Module):
def __init__(self, in_planes, out_planes, stride=1):
super(Block, self).__init__()
# Depthwise 卷积,3*3 的卷积核,分为 in_planes,即各层单独进行卷积
self.conv1 = nn.Conv2d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=in_planes, bias=False)
self.bn1 = nn.BatchNorm2d(in_planes)
# Pointwise 卷积,1*1 的卷积核
self.conv2 = nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False)
self.bn2 = nn.BatchNorm2d(out_planes)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
return out
#定义模型
class MobileNetV1(nn.Module):
# (128,2) means conv planes=128, stride=2
cfg = [(64,1), (128,2), (128,1), (256,2), (256,1), (512,2), (512,1),
(1024,2), (1024,1)]
def __init__(self, num_classes=10):
super(MobileNetV1, self).__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(32)
self.layers = self._make_layers(in_planes=32)
self.linear = nn.Linear(1024, num_classes)
def _make_layers(self, in_planes):
layers = []
for x in self.cfg:
out_planes = x[0]
stride = x[1]
layers.append(Block(in_planes, out_planes, stride))
in_planes = out_planes
return nn.Sequential(*layers)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layers(out)
out = F.avg_pool2d(out, 2)
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
第一次训练的测试结果:
Accuracy of the network on the 10000 test images: 78.30 %
第二次训练的测试结果:
Accuracy of the network on the 10000 test images: 77.08 %
第三次训练的测试结果:
Accuracy of the network on the 10000 test images: 78.27 %
2、MobileNetV2
主要改进点:
(1)引入残差结构,先升维再降维,增强梯度的传播,显著减少推理期间所需的内存占用;
(2)去掉Narrow layer后的ReLU,保留特征多样性,增强网络的表达能力
(3)网络为全卷积的,使得模型可以适应不同尺寸的图像;使用ReLU6为激活函数。使得模型在低精度计算下具有更强的鲁棒性
#expand + Depthwise + Pointwise
class Block(nn.Module):
def __init__(self, in_planes, out_planes, expansion, stride):
super(Block, self).__init__()
self.stride = stride
planes = expansion * in_planes
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, stride=1, padding=0, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, groups=planes, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = nn.Conv2d(planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False)
self.bn3 = nn.BatchNorm2d(out_planes)
if stride == 1 and in_planes != out_planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(out_planes))
if stride == 1 and in_planes == out_planes:
self.shortcut = nn.Sequential()
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
if self.stride == 1:
return out + self.shortcut(x)
else:
return out
#创建MobileNetV2网络
class MobileNetV2(nn.Module):
#(expansion,out_planes,num_blocks,stride)
cfg = [(1,16,1,1),(6,24,2,1),(6,32,3,2),(6,64,4,2),(6,96,3,1),(6,160,3,2),(6,320,1,1)]
def __init__(self,num_classes=10):
super(MobileNetV2,self).__init__()
self.conv1 = nn.Conv2d(3,32,kernel_size=3,stride=1,padding=1,bias=False)
self.bn1 = nn.BatchNorm2d(32)
self.layers = self._make_layers(in_planes=32)
self.conv2 = nn.Conv2d(320,1280,kernel_size=1,stride=1,padding=0,bias=False)
self.bn2 = nn.BatchNorm2d(1280)
self.linear = nn.Linear(1280,num_classes)
def _make_layers(self,in_planes):
layers = []
for expansion,out_planes,num_blocks,stride in self.cfg:
strides = [stride] + [1]*(num_blocks-1)
for stride in strides:
layers.append(Block(in_planes,out_planes,expansion,stride))
in_planes = out_planes
return nn.Sequential(*layers)
def forward(self,x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layers(out)
out = F.relu(self.bn2(self.conv2(out)))
out = F.avg_pool2d(out,4)
out = out.view(out.size(0),-1)
out = self.linear(out)
return out
第一次训练的测试结果:Accuracy of the network on the 10000 test images: 79.83 %
第二次训练的测试结果:Accuracy of the network on the 10000 test images: 81.87 %
第三次训练的测试结果:Accuracy of the network on the 10000 test images: 80.53 %
3、HybridSN
3D卷积和2D卷积的区别:应用于一个图像的2D卷积将输出一个图像,施加在多个图像上的2D卷积也输出一个图像,因此,2D ConvNets在每次卷积运算后就会丢失输入信号的时间信息。只有3D卷积才能保留输入信号的时间信息,从而产生输出卷积。
三、论文阅读
1、《Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising》
本文提出了一个前馈去噪卷积神经网络(DnCNN)用于图像去噪,使用了更深的结构、残差学习算法、正则化和批量归一化等方法提高去噪性能。可以处理未知噪声水平的高斯去噪。可以扩展到一般的图像处理中。
残差学习:在DnCNN中使用单个残差单元来预测残差图像。
批量归一化:通过在每层中的非线性之前引入归一化步骤和缩放和移位步骤来减轻内部协变量偏移。对于批量归一化,每次激活仅添加两个参数,并且可以使用反向传播更新它们。批量归一化具有几个优点。例如,加快训练速度,更好的性能和对初始化的低灵敏度。
残差学习和批量归一化的整合可以带来快速稳定的训练和更好的去噪性能。
一方面,残差学习从批量归一化中受益。批量归一化为CNN减轻了内部协变量偏移问题。
另一方面,批量归一化从残差学习中受益。通过残差学习,DnCNN隐含地使用隐藏层中的操作移除潜在的干净图像。这使得每层的输入都是高斯分布的,相关性较低,并且与图像内容的相关性较小。
DnCNN的感受野大小设置为35*35,相应的深度为17,。对于一般图像的去噪任务,采用更大的感受野并将深度设置为20。
论文最大的贡献:第一次在图像降噪领域使用深度学习。
2、《Squeeze-and-Excitation Networks》
SENet block并不是一个完整的网络结构,而是一个子结构,可以嵌入到其他分类或检测模型中。
SENet的核心思想在于通过网络根据loss去学习特征权重,使得有效的feature map权重大,无效或效果小的feature map权重小的方式训练模型达到更好的效果。
对U先做一个Global Average Pooling,输出的1x1xC数据再经过两级全连接,最后用sigmoid限制到[0,1]的范围,把这个值作为scale乘到U的C个通道上,作为下一级的输入数据。
这种结构的原理是想通过控制scale的大小,把重要的特征增强,不重要的特征减弱,从而让提取的特征指向性更强。
3、《Deep Supervised Cross-modal Retrieval》
解决问题:在不同模态下,样本生成可以直接比较的公共表示空间。不仅保证了公共空间各模态数据与标签的相似性,而且保证了语义区分和公共空间模态的不变性,可以有效地学习异构数据的公共表示。
主要思想:VGG-19提取图像特征,Word2Vec,CNN提取文本特征,创建线性分类器对样本生成标签,在标签空间和公共表示空间下最小化区别损失,同时,最小化两种模态在公共表示空间中所有样本的识别损失来学习多模态不变特征,为了消除跨模态数据差异,将所有图像到文本对之间的表示距离最小化。
创新点:(1)提出了一种基于深度监督的跨模态学习结构,以弥补不同模式间的异质性。通过端到端方式,同时保证语义区分和模态不变性,可以有效地学习异构数据的公共表示。(2)为了研究图像和文本模态之间的跨模态相关性,在最后一层建立了两个具有权值共享约束的子网络。将模态不变性损失直接表示为目标函数,消除了模态间的差异。(3)利用线性分类器对公共表示空间中的样本进行分类。这样,DSCMR最大限度地减少了标签空间和公共表示空间的区分损失,使得学习的公共表示具有显著的区分性。