本節參考:《DIVE INTO DEEP LEARNING》
AlexNet和VGG對LeNet的改進主要在於如何對卷積層和全連接層模塊加寬(增加通道數)和加深。而NiN提出了另外一個思路,即串聯多個由卷積層和“全連接”層構成的小網絡來構建一個深層網絡。
NiN塊
卷積層中可以把通道當作特徵,高和寬上的每個元素相當於樣本。因此,NiN使用 卷積層來替代全連接層,從而使空間信息能夠自然傳遞到後面的層中去(可以實現多個feature map的線性組合,實現跨通道的信息整合的功效)。
NiN塊是NiN中的基礎塊。它由一個卷積層加兩個充當全連接層的 卷積層串聯而成。其中第一個卷積層的超參數可以自行設置,而第二和第三個卷積層的超參數一般是固定的。
def nin_block(in_channels, out_channels, kernel_size, strides, padding):
blk = []
blk += [nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1),
nn.ReLU(inplace=True)]
return blk
NiN模型
NiN是在AlexNet問世不久後提出的。它們的卷積層設定有類似之處。NiN使用卷積窗口形狀分別爲 、 和 的卷積層,相應的輸出通道數也與AlexNet中的一致。每個NiN塊後接一個步幅爲2、窗口形狀爲 的最大池化層。
NiN去掉了AlexNet最後的3個全連接層,取而代之地,NiN使用了輸出通道數等於標籤類別數的NiN塊,然後使用全局平均池化層(Global Average Pooling, GAP) 對每個通道中所有元素求平均並直接用於分類。
# 以ImageNet的3*224*224圖像爲例,類別數爲10
class NiN(nn.Module):
def __init__(self, num_classes=1000):
super(NiN, self).__init__()
layers = []
channels = [3, 96, 256, 384]
kernel_size = [11, 5, 3]
strides = [4, 1, 1]
paddings = [0, 2, 1]
for i in range(3):
layers += nin_block(channels[i], channels[i + 1], kernel_size[i], strides[i], paddings[i])
layers.append(nn.MaxPool2d(3, 2))
layers.append(nn.Dropout(0.5))
self.features = nn.Sequential(*layers)
layers = []
layers += nin_block(384, 10, kernel_size=3, strides=1, padding=1)
layers.append(nn.AdaptiveAvgPool2d(1))
self.classifier = nn.Sequential(*layers)
def forward(self, x):
x = self.features(x)
x = self.classifier(x)
x = x.squeeze()
return x
爲什麼NiN塊中要有兩個1x1卷積層?
截取《DIVE INTO DEEP LEARNING》討論區裏的回答:
每個卷積層都可以視爲多了一層非線性變換,多加了這種非線性變換網絡的性能應該也能變好