ZFNet是ILSVRC2013的冠军,用于图像识别部分的网络就是在Alex Net的基础上,将kernel_size和stride减小。
ZFNet可视化部分的网络,使用解卷积将CNN可视化,探究网络各个中间层的作用和分类器的操作,揭示输入是如何刺激各层的特征映射,展示训练过程中特征的演变、革新以及诊断网络建在的问题,目的在于解决为什么CNN表现良好和CNN应当如何改进的问题。
用解卷积网络,将激活后的特征数值映射会输入的像素空间,揭示输入模式会如何导致特征映射的激活。
1.在convnet中,max_pooling 操作是不可逆的
2.在deconvnet中,记录各max_pooling区域最大初始值的位置,保存有效刺激的结构,做pooling的近似逆操作
3.进行Unpooling操作时,在有记录的位置设置对应的记录值,其余设置为0.
1.在convnet中,使用ReLU激活函数,使激活值永远为正
2.在deconvnet中,同样使用ReLU,保证激活值为正
1.将convent的卷积核转置
每个特征单独投影到像素空间揭露了不同的结构都能刺激一个给定的特征图,因此展示了它对于变形的输入内在的不变性。由上图可以看到第二层应对角落和其他边缘或者颜色的结合;第三层有更加复杂的不变性,捕捉到了相似的纹理;第四层显示了特定类间显著的差异性;第五层显示了有显著构成变化的整个物体。个人认为,随着层的深入,感受野在变大,提取的特征越多越复杂,覆盖范围变广(从局部到整体),包含信息变多。
1.迭代过程中有sudden jump
2.低层收敛较快
3.高层收敛较慢
4.有必要等整个网络都收敛之后,再去看最终的训练结果
图5按行顺序分别为对5类图像进行不同程度的垂直方向上的平移、尺度变换、旋转对输出结果影响的分析图。按列顺序分别为原始变换图像,第一层中原始图片和变换后的图片的欧氏距离,第7层中原始图片和变换后的图片的欧氏距离,变换后图片被正确分类的概率图。
1.小的变化对于模型的第一层都有非常大的影响,但对于最高层的影响却几乎没有。
2.网络的输出对于平移和尺度变化是稳定的,
3.网络不具有旋转不变性,除非目标图像是旋转对称的。
通过可视化可以得出Alex Net的缺点有:
1.第一层convent集合了高频和低频信息,而中频信息几乎没有拟合
2.较大的卷积步长(stride=4)导致特征混叠
ZFNet的改进为:
3.基于此,ZFNet将第一层的卷积核减小为7*7, 并减小步长,使stride=2。同时,renormalize each filter in the convlayers whose RMS( Root Mean Squre) value exceeds a fixed radius of 1 0 − 1 10^{-1} 10−1 to the fixed radius(归一化kernel的数值)。
4.可以看到相比前面有更多的独特的特征以及更少的无意义的特征,且特征更清晰,最终得到的结果明显优于Alex Net。
1.探究的问题:模型是否真的知道对象在图片中的位置在哪里,还是仅仅依靠周围内容判断
2.方法:用灰色方块遮挡图像的不同位置,检测输出
3.结果表明:如果图像中的目标被遮挡,那么被正确分类的概率会显著降低
1.遮挡五张图像中对象的相应特征位置的一小部分,取多个位置进行遮挡
2.计算遮挡图像与原始图像在第 l l l层feature vector 的差值 ϵ i = x i l − x 0 l \epsilon_i=x^l_i-x_0^l ϵi=xil−x0l
3.计算每两张遮挡图片 ϵ l \epsilon^l ϵl的海明距离之和,来度量一致性:
Δ l = ∑ i , j = 1 , i ≠ j 5 H ( s i g n ( ϵ i l ) , s i g n ( ϵ j l ) ) \Delta_l = \sum^5_{i,j=1,i\not=j} H(sign(\epsilon^l_i),sign( \epsilon^l_j)) Δl=i,j=1,i=j∑5H(sign(ϵil),sign(ϵjl))
4.所得值越小,一致性越强。
5.个人认为,这个所谓的一致性描述了不同局部特征(比如说眼睛,鼻子)对于结果的影响程度。
下表是第五层和第七层的一致性 Δ \Delta Δscore
1.在第5层随机遮挡的情况比其他眼睛鼻子被遮挡的情况一致性分析较差。
2.而第7层中,四类得分差不多,那是因为底层判别的是一类物体共有的部分,而高层判别的是类别中不同的品种这种更高维的部分了。
(就是改了AlexNet的几个参数)
from torch import nn
class ZFNet(nn.Module):
def __init__(self, num_classes=2):
super(ZFNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), 256 * 6 * 6)
x = self.classifier(x)
return x