歡迎來到實力至上主義的CS231n教室(二)
Hello,大家好,這裡是糖葫蘆喵喵~!
ようこそ実力至上主義のCS231n教室へ
已經進入了糖葫蘆販賣的季節了呢^ ^!北方的同學們已經享受上了暖氣,南方的同學們請抱好你們的1080Ti來取暖(雖然我們在本次作業中用不到GPU
那麼,第二篇CS231n 2017 Spring Assignment2 就要開始了~!
喵喵的代碼實現:Observerspy/CS231n,歡迎watch和star!
Observerspy/CS231n
視頻講解CS231n Assignment2:
Assignment 2-AI慕課學院Part 1 Fully-connected Neural Network
上一次的作業中我們實現了一個兩層的神經網路,這裡主要是對代碼進行模塊化,把每一層都抽象出來,分別實現每一層的前向和反向部分。
1 Affine layer(線性層)
前向:wx+b,很簡單,np.dot()就行了
反向:db就是上游傳回來的值dout直接sum起來,dw是x和dout相乘(就是線性wx+b對w求導嘛),dx是w和dout相乘,請注意dw和dx的形狀,到底是誰和誰(或是誰的轉置)乘,一定要搞清楚!dw和w形狀一致,dx和x形狀一致,所以算一下形狀就很好確定啦!
2 ReLU
前向:ReLU不過就是和0比較,輸出大的那個,np.maximum()搞定
反向:反向只向後傳遞在前向時輸出大於0的位置(梯度為1)的dout,其他的由於小於0所以梯度為0
dx = (x > 0) * doutn
3 affine_relu
就是把上面兩個連在一起,已經為你實現好了,讀一下吧!
4 loss
loss上次我們也做過了,忘記了的可以回去看一看。
5 Two-layer network
結構:affine - relu - affine - softmax
基本的模塊上面已經做好了,下面組合在一起就好:
參數初始化===>前向 + Loss===>反向
注意w初始化np.random.normal()的三個參數的意義!尤其是形狀一定要把握好。
跑一下你的模型,10epoch可以在validation上得到50%左右的結果。
6 Multilayer network
結構:{affine - [batch norm] - relu - [dropout]} x (L - 1) - affine - softmax
無非就是套層循環,在循環時要想一想哪些是可以循環的,哪些是不行的(最後一層)
循環時一定要注意用的參數的名字,一定要注意i和你的參數名字的關係!
反向小技巧,逆序循環:
for i in range(self.num_layers - 1, 0, -1):n
跑一下模型,你可以調一調參數,20epoch就過擬合了
7 SGD+Momentum
官方文檔給了很多梯度更新的說明,很詳細喵喵就不多說了。
Momentum:mu是超參,多用0.9,交叉驗證可以用[0.5, 0.9, 0.95, 0.99]
# Momentum updatenv = mu * v - learning_rate * dx # integrate velocitynx += v # integrate position n
8 RMSProp and Adam
RMSProp:decay_rate是超參,多用[0.9, 0.99, 0.999],eps是平滑項,範圍為1e-4 ~ 1e-8
# RMSProp updatencache = decay_rate * cache + (1 - decay_rate) * dx**2nx += - learning_rate * dx / (np.sqrt(cache) + eps)n
Adam:推薦超參數eps = 1e-8, beta1 = 0.9,beta2 = 0.999
# Adam updatenm = beta1*m + (1-beta1)*dxnv = beta2*v + (1-beta2)*(dx**2)nx += - learning_rate * m / (np.sqrt(v) + eps)n
最終調一調參數,可以在validation上得到55%左右的結果。
Part 2 Batch Normalization
這是我們接觸到的第一個黑魔法!
這個黑魔法用在卷積層/全連接層後,激勵函數前。
這個黑魔法可以有效減緩過擬合,減小不好的初始化影響以及你可以用大一點的學習率了!
前向:
(訓練)非常經典的一張圖,其中 和 是需要學習的參數, 是一個常數
簡單說前向就是計算minibatch的均值和方差,然後對minibatch做normalize和scale、shift
(測試)測試的時候沒有minibatch啊,怎麼來計算均值和方差呢?這裡我們的解決方案是通過使用基於momentum的指數衰減,從而估計出均值和方差:
running_mean = momentum * running_mean + (1 - momentum) * sample_meannrunning_var = momentum * running_var + (1 - momentum) * sample_varn
momentum超參,一般是0.9
通過這樣的方式在訓練時不斷更新預測的均值(running_mean)和方差(running_var),測試時直接用這兩個數計算normalize和scale、shift,和訓練時的計算方式一樣。
反向:
我們需要求dx,dgamma和dbeta,前向時我們分了四步計算出了輸出(對照圖中公式,分別為mean,var,xhat和輸出y),這裡我們要反向去計算每個部分。
第四步:線性,因此 dxhat = dout * gamma;dgamma = sum(dout * dxhat);dbeta = sum(dout)(具體形狀一定要注意,為什麼要sum呢,因為這兩個學習的參數是一維的,類似神經元里的bias,注意axis參數的設置)
解決了gamma和beta,我們來分析dx是哪裡來的。
根據公式,x和min,var,xhat都是相關的,因此,dx要由這三部分構成,分別求導
第三步:
第二步:
第一步:
m是什麼,minibatch的大小,x的shape[0]。
那麼我們把求dx轉化為求上面三部分,dxhat已經有了,剩下要求dmean和dvar(同理,var和mean也都是一維的,所以要sum)
var只有第三步有貢獻,直接求導 :
mean第二步第三步都有貢獻,為兩部分之和:
這樣就終於通過公式則把dx求出來了,然後輕鬆愉快實現代碼。其實作業要求的部分是通過鏈式法則直接計算,選作的部分是公式計算,不過公式計算的運算速度快一些,有興趣的可以去試試。
最後把黑魔法放到網路里,看看變化吧!
Part 3 (inverted) Dropout
這又是一個有用的黑魔法!
這個黑魔法用在激勵函數後。
這個黑魔法可以有效緩解過擬合。
其實原理很簡單,就是以一定的概率選擇使用一部分神經元。
inverted dropout通過多除一個p保證了訓練預測分布的統一。
前向:
(訓練)用mask選擇神經元,有了mask元素乘一下就好了。
mask = (np.random.rand(x.shape[0], x.shape[1]) < p) / pn
(測試)當然是什麼也不做了!
反向:
類似ReLU,不參與更新的部分都用mask去和dout相乘(元素乘)。
在剛才的網路里加上dropout黑魔法吧,你可以對比加與不加的效果。
休息一下!因為後面就是手擼CNN大boss啦!
Part 4 Convolutional Networks
古人云:學習CNN最好的方式就是手寫一個CNN
是非常有道里的話!
那麼就讓我們開始挑戰吧!(強烈推薦deeplearning.ai第四課CNN,第一周作業詳細講了怎麼手擼CNN,不過那個作業給的循環太多,而本節作業是向量化的)
1 卷積核
(前向)
首先明確輸入x和卷積核的形狀(數量,通道,高,寬):
N, C, H, W = x.shapenF, C, HH, WW = w.shapen
(1) 確定輸出的形狀
pad參數: P,決定了你要在四周pad多少
stride參數:S,決定了卷積核每次移動多少
所以根據公式,可以直接計算輸出out的形狀(N,F,Hnew,Wnew),記得用0初始化
(2) zero padding
np.pad()就是實現這個功能的,請仔細閱讀函數接受的參數意義。pad_width接受每個維度上前後pad的大小,當某一維度使用(pad,)表示前後都是pad大小(等於(pad, pad))
(3) 卷積操作
這裡的卷積和通信原理里的卷積還是稍有區別的,在這裡其實只是卷積核和相應的區域進行元素乘,然後求和,課程官網給的說明十分形象生動:
也就是每個卷積核分別在每個通道上和對應區域進行元素乘,然後求和,對應圖中:
(-3(通道1元素乘後求和) + -1(通道2元素乘後求和) + 0 (通道3元素乘後求和))(三個通道求和) + 1(bias_0) = -3(out的第一個格子里的值)
所以,關鍵問題就是根據步長如何確定x對應區域,這裡需要對Hnew(下標i)和Wnew(下標j)進行雙循環:
x_mask = [:, :, i*stride:i*stride+HH, j*stride:j*stride+WW]n
選好區域直接和每個卷積(下標k)核作元素乘就行了,注意sum的時候我們其實是在(C, H, W)上作的,因此axis=(1, 2, 3)。這時候一個輸出out[:, k , i, j]就計算好了。
所以上述一共套了i, j ,k三層循環,循環完畢後out再加上bias就行了。注意b的形狀(F,),因此要先把b擴展成和out一樣的形狀:b[None, :, None, None](None相當於np.newaxis)
至此,前向計算完畢!
(反向)
首先明確我們要求什麼。dx,dw和db。
(1) db
db最好求啊,直接就是sum(dout),還要和b的形狀(F,)一致,所以axis=(0, 2, 3)
那麼dx,dw到底是什麼呢。其實dx和dw是另外兩個卷積。只不過這次是換在dout的對應區域進行卷積操作了。這篇博客寫的很詳細,這裡借用其幾張圖片:
(2) dx
dx對應區域 = dout對應區域和卷積核w進行卷積:
這個圖中的是通過對dout進行padding然後和旋轉180度的卷積核進行卷積計算的,實際上可以不這麼麻煩。用dout中的每一個值和原卷積核進行元素乘,然後對dx的對應區域進行疊加就可以了。數學上是等價的:
如圖所示,4*4的矩陣中每一個顏色對應dout中的一個值(共9種),粗體對應原卷積核。每一個藍框對應一個dout值和w的元素乘操作。把重疊區域加起來後和上圖結果一致。
卷積我們已經會了,剩下就是確定對應區域到底是什麼了。
dx的形狀是(N, C, H, W)
w的形狀是(F, C, HH, WW)
dout的形狀是(N,F,Hnew,Wnew)
對於每個樣本(即對N循環,n為下標):
n,i,j將確定一個dout,確定後dout的形狀變為(F,),因此和前向bias同理需要維度擴展為
[:, None, None, None],以保證和w的形狀一致
dx的對應區域由n和x_mask(就是前向中的x對應區域)共同確定:
dx_mask[n, :, i*stride:i*stride+HH, j*stride:j*stride+WW]n
三層循環i,j,n完成卷積後,需要對pad過的dx進行解pad:
dx = dx_pad[:,:,pad:-pad,pad:-pad]n
(3) dw
dw對應區域 = dout和x對應區域進行卷積:
對於每個卷積(即對F循環,k為下標):
x對應區域為(向前時已經給出了,就是那個x_mask)
k,i,j將確定一個dout,此時dout的形狀為(N,),為了何x_mask一致同樣需要擴展為
[:, None, None, None]
然後就可以三層循環i,j,k做卷積了,計算結果就是dw[k]。
OK,至此反向計算完畢!大功告成!
剩下一個簡單的操作,pooling。
2 pooling
2.1 max pooling
(前向)
pooling同樣有步長,確定輸出形狀的公式:
其中HH,WW是pooling的大小。
max pooling顧名思義就是取這個pooling大小區域內的max值。注意axis=(2, 3)。
(反向)
反向和ReLU、DropOut是類似的,也就是說只有剛才前向通過的才允許繼續傳遞梯度。
那麼我們需要記錄max在區域里的位置,用temp_binary_mask表示:
x_padded_mask = x[:, :, i*stride:i*stride+pool_height, j*stride:j*stride+pool_width] #(:, :, HH, WW)nmax_mask = np.max(x_padded_mask, axis=(2,3)) ntemp_binary_mask = (x_padded_mask == (max_mask)[:,:,None,None])n
max_mask形狀是(HH, WW),為了和x_padded_mask形狀對應也要擴展。
然後dout和這個temp_binary_mask元素乘即可。同樣注意dout是由i,j確定的,因此形狀需要擴展。
2.2 average pooling
附送一個average pooling,也很簡單。
前向就是在pooling大小區域內求平均值。
反向就是把dout平均在pooling區域內。
3 CNN
結構:conv - relu - 2x2 max pool - affine - relu - affine - softmax
快把剛才完成的結構組合在一起吧!1epoch可以得到40%的準確率。
4 Spatial batch normalization
以前我們做的BN形狀是(N, D),這裡不過是將(N, C, H, W)reshape為(N*H*W, C)。因為spatial batch normalization computes a mean and variance for each of the C
feature channels by computing statistics over both the minibatch dimension N
and the spatial dimensions H
and W
.
需要用到np.transpose(),現將(N, C, H, W)變為(N, H, W, C),再reshape。反向也是同理。
至此,手擼CNN全部結束!
完結撒花!(並沒有完結!
Part 5 Tensorflow on CIFAR-10
歡迎來到TensorFlow的世界,從此你可以告別複雜的反向傳播推導了!
tensorflow入門資料回顧,在part D哦。
在這個世界中我們只需要考慮怎麼樣設計網路和loss就行了。
層,激勵函數,Loss:https://www.tensorflow.org/api_guides/python/nn
優化器:https://www.tensorflow.org/api_guides/python/train#Optimizers
BN:https://www.tensorflow.org/api_docs/python/tf/layers/batch_normalization
這裡強調一點,使用BN黑魔法時請務必注意:
A 在你的優化器上套上這兩句:
# batch normalization in tensorflow requires this extra dependencynextra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)nwith tf.control_dependencies(extra_update_ops):n train_step = optimizer.minimize(mean_loss)n
B 注意tf.layers.batch_normalization()中的is_training(是一個tf.placeholder)在訓練和測試時的設置!如果你要使用dropout也是類似的。
你可以嘗試各種各樣的網路。這裡喵喵10epoch在test達到了76.2%。相信你可以做得更好。
恭喜你達到了第二章的BOSS,下一次讓我們來做一些有趣的事情吧!
じゃ、おやすみ~!
下期預告:Assignment3作業詳解
推薦閱讀:
※(二)計算機視覺四大基本任務(分類、定位、檢測、分割)
※初見相關濾波與OTB
※[DL-醫療-綜述] 002 綜合指南及實例(中)
※何愷明大神的「Focal Loss」,如何更好地理解?
※【小林的OpenCV基礎課 番外】Spyder下配置OpenCV
TAG:深度学习DeepLearning | 计算机视觉 | TensorFlow |