深度學習經典網路回顧:AlexNet+VGG
一.AlexNet:
雖然深度學習元年是2006年,但是真正使深度學習火起來的是2012年的ImageNet上的AlexNet,由於本人本科畢設有用到該網路模型,雖然後面更優秀的網路模型層出不窮本人再沒有使用過該網路,但是該網路的設計對後序網路的設計還是又不可磨滅的影響。首先該網路出自論文:《ImageNet Classification with Deep ConvolutionalNeural Networks》。其中主要貢獻有如下幾點:
1.從實踐中證明了深度神經網路的可行性,確立了多GPU+深度神經網路解決大規模非結構化資料(CV、NLP、語音)問題的工作正規化,它得益於現代硬體尤其是GPU的成熟發展,卻也反過來激勵與AI相關硬體的更高速的發展。
2.啟用函式單元創新:引入了ReLU非線性啟用函式,對tanh和sigmoid會引起的梯度消失問題有一個很好的解決。
3.引入了Dropout,對過擬合問題有一個很好的解決。
4.引入了正則化層:區域性響應正則化(LRN),不過後來被證明並不很work,被各大網路捨棄。btw,後面BN層橫空出世。
下面從上述幾點分別闡述:
1.網路結構如圖所示:
更直觀的觀察:
為什麼第一層有96個卷積核,論文原圖上畫了兩個48,後面幾層也是這樣??因為當時是在兩個gpu上跑的。
2.ReLU啟用函式:
長這樣:簡單點就是max(0,x)
優點如下:
(1).不會像sigmoid一樣梯度消失。
(2).計算很快。
(3).收斂迅速,比sigmoid/tanh快6倍。
(4).更合理的生物性解釋。
簡單點為什麼sigmoid會梯度消失它不會?ReLU不會梯度消失直觀看來梯度為常數,所以當然不會啦。那sigmoid為什麼會:因為sigmoid的導數f'(x)=f(x)(1-f(x))值域為(0,1/4),在網路層數過深時經反向穿鼻即(1/4)^n約等於0,因此肯定會梯度消失啦。
3.Dropout:
原理為:Dropout以p的概率將隱層神經元的輸出置為0,這些神經元不參與前向傳播與反向傳播。這樣可以減少神經元之間的依賴性,因為從生物的角度上來說,一個神經元不會依賴於其他神經元而存在。藉助Dropout,網路可以學到更具有泛化能力的特徵。測試時,將所有神經元的輸出乘0.5來產生對Dropout的近似,該模型將Dropout機制用於全連線層中。
個人理解:該機制多用於全連線網路,可以將Dropout看作是整合學習的一種,本質解決的是過擬合問題。
溫馨提示:實踐中多采用“Inverted dropout”,即訓練時輸出處以概率值p,測試時不變。
4.LRN區域性相應歸一化,作為被歷史捨棄的產物不想再贅述,後來被batch-normal、instance-norm、group-nrom拍死在沙灘上。
最後貼一下pytorch官方實現的alexNet程式碼(看起來很簡單,捨棄掉了LRN層):
class AlexNet(nn.Module):
def __init__(self, num_classes=1000):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), 256 * 6 * 6)
x = self.classifier(x)
return x
二:VGG:
VGG的思想可以精簡為:Small filter,Deeper networks。個人覺得它主要貢獻為確立了卷積塊單元(block)設計的正規化,即網路設計結構的規範化。因為VGG網路看起來就是一個非常結構規範的網路:同一個block內特徵圖不變,相鄰的block之間通過stride為2的max-pooling操作使特徵圖尺寸減半,其最大的亮點為全網路的卷積核都是3*3的卷積,stride為1,padding為1。網路結構如下圖所示:conv3-64表示3*3的卷積核,channel數為64,圖中省去了ReLU單元,btw當時batch-norm還沒有問世,在batch-norm出現後又出現了好多vgg加bn的版本,其效果要好過原vgg結構。。
為什麼全是3*3的卷積核可以work?
從感受野的角度上說,理論上兩個3*3的卷積核堆疊在一起的感受野等同於一個5*5的卷積核,且使用更少的引數量(3*3*2<5*5),同時兩層比一層非線性抽象能力更強,擬合能力更強。
最後上程式碼:
class VGG(nn.Module):
def __init__(self, features, num_classes=1000, init_weights=True):
super(VGG, self).__init__()
self.features = features
self.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes),
)
if init_weights:
self._initialize_weights()
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
if m.bias is not None:
m.bias.data.zero_()
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
elif isinstance(m, nn.Linear):
m.weight.data.normal_(0, 0.01)
m.bias.data.zero_()
def make_layers(cfg, batch_norm=False):
layers = []
in_channels = 3
for v in cfg:
if v == 'M':
layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
else:
conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
if batch_norm:
layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
else:
layers += [conv2d, nn.ReLU(inplace=True)]
in_channels = v
return nn.Sequential(*layers)
cfg = {
'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}
以上即兩個網路的基本結構。
下面來說下對網路的思考和麵試中可能會遇到的與本文相關問題:
首先幾個面試中的問題:
1.ReLU相比sigmoid的優點,sigmoid為什麼會梯度消失?答案如上文所說。
2.深度學習中常用的防止過擬合的手段?
加正則、dropout、資料增強、batchnorm、early stopping、模型整合、加噪聲。。
當然以上屬於常規操作。面試中也碰到一些讓我印象深刻的深層次問題:
春招騰訊實習面試二面:
問:深度學習網路演化規律?答:越來越深,卷積核越來越小。
問:還記得AlexNet的網路結構嗎?第一層卷積核多大?
答:9*9。
問:為什麼後面卷積核越來越小?
答:按上述感受野和引數量的角度說了下。
問:那以前人們不知道這個道理嗎?為什麼以前的卷積核設計的那麼大?
我:喵喵喵?內心OS:可能以前人們真不知道。憋了半天不會。
面試官說了答案:之前的影象資料清晰度不高,資訊量不豐富,比較稀疏。適用於大卷積核,小卷積核反而效果不阿紅,現在影象都是高清,資訊量大。
我:好吧,這也可以,你贏了。
當時二面被懟的懷疑人生回去心情沮喪,不過最後竟然給過了也是amazing,後來順利拿到了實習的offer,還是很開心。
秋招面陌陌時碰到的問題:
1.pooling的反向傳播時候是怎麼實現的?
2.caffe和tensorflow中的dropout的實現方法不一樣,分別是怎麼實現的?
喵喵喵?不會不好意思。。後來知道caffe產生一個貝努力分佈的vector,用輸出乘以這個vector。
還有面試中我記得好幾次被問到對batchnorm的理解,可見batchnorm很重要。
對網路結構的思考:之前的網路最後都會連幾層很寬的全連線層eg:4096。這樣做的不好的地方?(全連線層佔了大多數的引數量,造成引數冗餘)。ResNet和googleNet捨棄了全連線層,這樣做的好處?(輸入影象任意尺寸)。
加BN和不加BN對網路輸入的要求有何不同?
不加BN層輸入影象必須進行歸一化,加BN之後可以不進行歸一化。
最後說一句:實踐出真知,注意細節。Devils in the details。