簡介
這次的作業相較於第一次作業又更深入了一些,這次作業要依序實作 Fully Connected Network、Batch Normalization、Dropout、Convolutional Neural Network 等方法,並將裡頭的步驟模組化。
Fully Connected Network
Modular network
首先要將 FCN 的 forward pass 以及 backward 模組化,包括 affine layer 與 ReLU activation。 在 forward pass 時使用cache
將所需變數儲存,以便 backward pass 的時候使用。
1 | def affine_forward(x, w, b): |
1 | def affine_backward(dout, cache): |
1 | def relu_forward(x): |
1 | def relu_backward(dout, cache): |
利用剛剛完成的模組來建構TwoLayerNet
以及可以自訂 size 的FullyConnectedNet
。
1 | class TwoLayerNet(object): |
1 | class FullyConnectedNet(object): |
SGD + Momentum
原始的 Stochastic Gradient Descent:
\(x_{t+1}=x_t-\alpha\nabla f(x_t)\)
SGD + Momumtum:
\(v_{t+1}=\rho v_t-\alpha\nabla f(x_t) \\ x_{t+1}=x_t+v_{t+1}\)
\(v\) 代表目前的方向速度,初始值為 0,如果負梯度與目前方向相同,則速度會越來越快,參數的更新幅度就會變大;反之則越來越慢,參數的更新幅度會變小。
至於 \(\rho\) 則是一個 hyperparameter,通常設在 0.9 左右。
使用 SGD + Momentum 通常比 Vanilla SGD 能夠更快收斂。
1 | def sgd_momentum(w, dw, config=None): |
RMSProp
\(v_t=\rho v_{t-1}+(1-\rho) \times (\nabla f(x_t))^2\)
\(\Delta x_t=-\dfrac{\alpha}{\sqrt{v_t+\epsilon}} \times \nabla f(x_t)\)
\(x_{t+1}=x_t+\Delta x_t\)
\(\rho\) 為 decay rate,通常設為 0.9、0.99、0.999。
\(\epsilon\) 是一個很小的值,為了避免除以 0 的情況產生。
1 | def rmsprop(w, dw, config=None): |
Adam
1 | def adam(w, dw, config=None): |
比較不同 optimizer 的表現:
Batch Normalization
實作過程參考以下論文:
Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
batch normalization 的目的是為了讓每一層的輸出都保持高斯分佈,主要的目的是為了避免 gradient vanishing。做法是將 fordward pass 時用來訓練的批次資料計算 mean 以及 variance,利用 mini-batch 的 mean 及 vairance 來更新整體的 mean 及 variance。
Forward Pass
論文中具體的實作方法如下:
1 | def batchnorm_forward(x, gamma, beta, bn_param): |
Backward Pass
論文中對於計算 BN 的反向傳播也有一些描述:
真的是滿複雜的,最好還是自己畫過一次計算圖之後再試著去計算 backward pass,這部分的話這篇文章寫得滿不錯的,可以參考一下。
1 | def batchnorm_backward(dout, cache): |
簡化版:
1 | def batchnorm_backward_alt(dout, cache): |
Layer Normalization
batch normalization 使得類神經網路的訓練更有效率,但是對於複雜的網路結構來說,在 batch size 不夠大的時候效果可能不會太好。因此另一個方法是對 feature 進行 normalize,參考論文:Layer Normalization。
1 | def layernorm_forward(x, gamma, beta, ln_param): |
1 | def layernorm_backward(dout, cache): |
Dropout
Dropout: A Simple Way to Prevent Neural Networks from Overfitting
drouput 是一種正規化的方法,在 forward pass 時隨機將某些 neuron 的值丟掉,跟 L1, L2 regularization 一樣,目的都是為了避免 overfitting。
實作方法是在 training 時根據一個機率 p 來隨機產生一個 mask (值為 True or False),將 x 乘上 mask 就可以將部分 neuron 的值設為 0, predicting 的時候就直接將 x 乘上 p。
但與其在 predicting 時乘上 p,其實我們可以在 training 的時候就除以 p,這樣就可以減少 predicting 的計算量,因為我們通常比較在意 predicting 時的效率,這個技巧稱為 inverted dropout。
1 | def dropout_forward(x, dropout_param): |
1 | def dropout_backward(dout, cache): |
Convolutional Neural Network
Convolution Layer Forward Pass
實作 CNN 的 forward pass,輸入 \(x\) 的大小為 \((N,C,H,W)\),以及 \(F\) 個 filter,合起來成為一個 \((F,C,HH,WW)\) 的矩陣,經過 convolution 的計算後,輸出一個 \((N,F,H^\prime,W^\prime)\) 的矩陣。
1 | def conv_forward_naive(x, w, b, conv_param): |
Convolution Layer Backward Pass
計算 convolution layer 的 backpropagation 可以參考這篇文章。因為 forward 時的計算也算是 \(x\) 乘上 \(w\),因此 backward 時計算 \(dx\) 就是用 \(dout\) 與 \(w\) 做計算;計算 \(dw\) 時則是用 \(dout\) 與 \(x\) 做計算,雖然概念上不難理解,但是要透過numpy
實作的話對維度要有一定的掌握才行。
1 | def conv_backward_naive(dout, cache): |
Max Pooling Forward Pass
1 | def max_pool_forward_naive(x, pool_param): |
Max Pooling Backward Pass
1 | def max_pool_backward_naive(dout, cache): |
最後還有實作 Spatial Batch Normalization 以及 Group Normalization,但這部分不是很熟所以略過。