一、CNN模型
CNN,又称卷积神经网络,它是一种前馈的神经网络,在图像识别领域有着巨大的应用。
二、如何理解卷积神经网络?
1 卷积
如图1所示,图中的X和O无论怎么旋转或者缩放,我们人眼其实还是很容易识别出X和0。
但是计算机不一样,它看到的其实是一个个的像素阵列,如图2。如何对像素的阵列进行特征的提取其实就是卷积神经网络要干的事情。
再看图3,我们发现X即使进行了旋转,但是绿、橙、紫框标记的区域在两张图中还是一致的,某种程度上,这其实就是X的特征。因此可以将这三个特征的区间提取出来,就形成了三个卷积核,如图4所示。
既然有了卷积核,那么卷积核是如何进行卷积操作的呢?
其实很简单,可以看一下图5,卷积核其实就是拿着这个矩阵在图片的矩阵上一点点的平移,就像扫地一样。每扫到一处地方就可以进行卷积的运算,计算方法很简单,如图5所示,左上角的卷积核扫到绿色框的位置,则卷积核矩阵的数字就和扫到的位置的矩阵的数字一一对应相乘然后相加,最后取一个均值,该值就是卷积核提取的特征,卷积核提取的所有的特征组成了一个长和宽变小的矩阵,这个矩阵又称为feature map,如图6。使用不同的卷积核也就能提取出不同的feature map。所以可以想象的是,如果不断的进行卷积操作,那么图片的矩阵会逐步地长宽减少,厚度增加。
可以看到卷积操作通过卷积核是可以分别提取到图片的特征的,但是如何提前知道卷积核呢?像图片中X的例子,我们其实很容易可以找到3个卷积核,但是假如是人脸识别这样成千上万个特征的图片,你就没办法提前知道什么是合适的卷积核。所以其实也没必要知道,因为选择什么样的卷积核,完全可以通过训练不断优化。初始时只需要随机设置一些卷积核,通过训练,模型其实自己可以学习到合适的卷积核,这也是卷积神经网络模型强大的地方。
2 池化(pooling)
池化,也叫下采样,本质上其实就是对数据进行一个缩小。因为我们知道,比如人脸识别,通过卷积操作得到成千上万个feature map,每个feature map也有很多的像素点,这些对于后续的运算的时间会变得很长。
池化其实就是对每个feature map进一步提炼的过程。如图7所示,原来4X4的feature map经过池化操作之后就变成了更小的2*2的矩阵。池化的方法包括max pooling,即取最大值,以及average pooling,即取平均值。
3. Normalization
这里的Normalization就是将矩阵中负数的值转成0,也就是使用一个称之为ReLu的激活函数进行负数变为0的操作。ReLu函数本质上就是max(0,x)。这一步其实也是为了方便运算。
4. 卷积神经网络理解
因此卷积、ReLu、pooling,不断重复其实也就基本上构成了卷积神经网络的框架,如图8。然后将最终得到的feaure map 排成一列(图8),接到全连接层,这样就形成了我们的卷积神经网络。值得注意的是,排成一列的数值,是有权重,而这些权重是通过训练、反向传播得到的,通过权重的计算,可以知道不同分类的概率是怎么样的。
三、理解baseline代码
baseline中使用的是resnet模型,这实际上是一种通过残差学习的CNN特征提取网络。为了方便,baseline中直接使用了这个预训练模型,当然这个肯定是可以改的。
forward其实就是一个变平的操作,通过feat.view(feat.shape[0], -1)实现
fc1~fc5是五个字符的输出
class SVHN_Model1(nn.Module):
def __init__(self):
super(SVHN_Model1, self).__init__()
model_conv = models.resnet18(pretrained=True)
model_conv.avgpool = nn.AdaptiveAvgPool2d(1)
model_conv = nn.Sequential(*list(model_conv.children())[:-1])
self.cnn = model_conv
self.fc1 = nn.Linear(512, 11)
self.fc2 = nn.Linear(512, 11)
self.fc3 = nn.Linear(512, 11)
self.fc4 = nn.Linear(512, 11)
self.fc5 = nn.Linear(512, 11)
def forward(self, img):
feat = self.cnn(img)
# print(feat.shape)
feat = feat.view(feat.shape[0], -1)
c1 = self.fc1(feat)
c2 = self.fc2(feat)
c3 = self.fc3(feat)
c4 = self.fc4(feat)
c5 = self.fc5(feat)
return c1, c2, c3, c4, c5