接上一篇文章,說到模型定義:
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個值,哪個值最大就是哪一個類別。