卷积神经网络实战(2)
接上一篇文章,说到模型定义:
class CNN(nn.Module):def __init__(self, activation="relu"):super(CNN, self).__init__()self.activation = F.relu if activation == "relu" else F.selu#输入通道数,图片是灰度图,所以是1,图片是彩色图,就是3,输出通道数,就是卷积核的个数(32,1,28,28)#输入x(32,1,28,28) 输出x(32,32,28,28)self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1)#输入x(32,32,28,28) 输出x(32,32,28,28)self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=1)self.pool = nn.MaxPool2d(2, 2) #池化不能够改变通道数,池化核大小为2(2*2),步长为2 (28-2)//2+1=14self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)self.flatten = nn.Flatten()# input shape is (28, 28, 1) so the fc1 layer in_features is 128 * 3 * 3self.fc1 = nn.Linear(128 * 3 * 3, 128)self.fc2 = nn.Linear(128, 10) #输出尺寸(32,10)self.init_weights()def init_weights(self):"""使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W"""for m in self.modules():if isinstance(m, (nn.Linear, nn.Conv2d)):nn.init.xavier_uniform_(m.weight)nn.init.zeros_(m.bias)def forward(self, x):act = self.activationx = self.pool(act(self.conv2(act(self.conv1(x))))) # 1 * 28 * 28 -> 32 * 14 * 14# print(x.shape)x = self.pool(act(self.conv4(act(self.conv3(x))))) # 32 * 14 * 14 -> 64 * 7 * 7# print(x.shape)x = self.pool(act(self.conv6(act(self.conv5(x))))) # 64 * 7 * 7 -> 128 * 3 * 3# print(x.shape)x = self.flatten(x) # 128 * 3 * 3 ->1152x = act(self.fc1(x)) # 1152 -> 128x = self.fc2(x) # 128 -> 10return xfor idx, (key, value) in enumerate(CNN().named_parameters()):print(f"{key}\tparamerters num: {np.prod(value.shape)}") # 打印模型的参数信息
一开始conv1,第一层卷积层,去运算的时候,以前全连接的时候直接展平,现在是三维的,通道数是1(黑白照片),卷积的时候卷积核也是(1,3,3),但现在卷积核的尺寸实际上是立方体,虽然写的是2d,这是接口的这么一个设计,之所以是2d是因为在两个维度上进行移动(上下左右),不像以前的全连接是一维的必须展平。
in_channels=1就是通道数,必须和图像的通道数是一样的也就是x(32,1,28,28)的第二个数。
kernel_size=3就代表3*3的大小,其实也可以写成一个元组(3,3)。
out_channels就是输出,输出是32就是有32个卷积核。网络初始化时,每个卷积核的权重矩阵会通过特定分布(如Xavier均匀分布)独立随机初始化。
padding=1就是补一圈0的意思。
tips:如果一张图片不是方形,一开始就会使用图片增强技术把图片变成方形。
对于第二层卷积层,卷积核是(32,3,3),这层的输入和输出的大小一样,作用是提取高层特征。
每层卷积后一次激活函数,两层卷积后做一层池化。
池化层:nn.MaxPool2d(2,2),前面一个2代表池化核大小是2*2,后面的2代表步长。池化一次把图像尺寸减半。尺寸计算公式:。
到后面图像尺寸变为128*3*3时,图像尺寸已经很小了,根据视野域,这你每一个像素就相当于把整个图看了一遍,这个时候展平,有1152个像素,相当于从这么多角度学习了图像信息,这个时候对它进行分类然后降为128再降为10最后是十分类,这样每一个batchsize就得到10个值,哪个值最大就是哪一个类别。