AlexNet
一些前置知識
top-1 和top-5錯誤率
top-1錯誤率指的是在最後的n哥預測結果中,只有預測機率最大對應的類別是正確答案才算預測正確。
top-5錯誤率指的是在最後的n個預測結果中,只要預測機率最大的前五個中含有正確答案就算預測正確。
max-pooling層
最大池化又叫做subsampling,其主要作用是減少影像的高度和長度而深度(寬度)則不會改變。下面是一個列子:
fully-connect層
在全連線層中,其每個神經元都與前一層的所有神經元相連線,每個連線都有一個權重用於調節資訊傳遞的強度,並且每個神經元還有一個偏置項。
1000-way softmax
它其實也屬於全連線層,這個層原本包含1000個未歸一化的輸出,而softmax將這個向量轉換為機率分佈。計算方式如下:
\[P(y_i) = \frac{e^{z_i}}{\sum_{j = 1}^{1000}e^{z_j}}
\]
non-saturating neurons
非飽和神經元是深度學習中一種設計神經元的理念,目的是避免神經元在訓練過程中出現飽和現象。飽和現象會導致梯度消失,進而使得模型難以訓練。下面是一些常見的非飽和啟用函式:
- ReLU
- Leaky ReLU
- ELU
- SELU
dropout
在訓練時以一定的機率將輸入置0,輸出時接受所有神經元的輸出,但要乘以機率(1-p)。使得模型在每次前向和反向傳播時都使用不同的子網路進行訓練,從而提高模型的泛化能力。這種方法有效地減少了神經元之間的共適應性(co-adaptation),迫使網路的每個神經元在更具魯棒性的特徵上進行學習。
缺點:收斂速度可能變慢。
網路結構
由於這篇文章在提出時沒有很好的GPU,估計視訊記憶體不夠?所有采用了雙GPU訓練的方法。具體來說上下兩塊GPU分別負責一般的引數,但是這其中也有資訊的融合,比如第3、6,7層。其次這裡輸出的影像維度應該有誤,應更正為2252253
演算法實現
import torch.nn as nn
import torch
class AlexNet(nn.Module):
def __init__(self, num_classes=1000, init_weights=False):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]
nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]
nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]
)
self.classifier = nn.Sequential(
nn.Dropout(p=0.5),
nn.Linear(128 * 6 * 6, 2048),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(2048, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes),
)
if init_weights:
self._initialize_weights()
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1)
x = self.classifier(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)