cs231n assignments學習心得
cs231n是斯坦福的一門以計算機視覺為載體的深度學習課程,由李飛飛和她的幾個博士生上課。這門課親測好評。下面是我完成這些assignment的一些疑問點和解決辦法。
首先先貼代碼:https://github.com/oubindo/cs231n-cnn
歡迎star和fork。感謝支持!
Assignment1:KNN,SVM,Softmax,Neuron Network
總體來說,這個assignment難度適中,但是對于numpy的要求還挺高的,要比較純熟的使用才能完成一些諸如矢量化一樣的操作。比較困難的地方在于梯度的計算。作為初學者的我一開始是非常懵逼的,(現在好一點了也還有點懵逼)。看了官方給出的一些說明,還有慕課學院講解課以后才理解了一些。現在嘗試對于一些問題給出自己的理解。圖片部分出自上面內容
1.KNN
KNN主要的考察點就是兩重循環,一重循環和全向量化。
先介紹一下背景,給出n維的測試點和訓練點,要求出它們之間的距離。使用兩重循環的話就是通過索引到這兩個數據再處理。
for i in xrange(num_test):
for j in xrange(num_train):
distances = np.sqrt(np.sum(np.square(self.X_train[j] - X[i])))
dists[i,j]=distances
使用一重循環是借助了numpy ndarry之間的相減功能,單獨的算出所有訓練點到一個測試點的距離,再一次便利即可。
for i in xrange(num_test):
distances = np.sqrt(np.sum(np.square(self.X_train - X[i]),axis = 1))
dists[i, :] = distances
使用全向量化就比較有技術了,這里通過(X-Y)2=X2-2XY+Y^2來計算。
num_test = X.shape[0]
num_train = self.X_train.shape[0]
dists = np.zeros((num_test, num_train))
a = -2 * np.dot(X, self.X_train.T)
b = np.sum(np.square(self.X_train), axis = 1)
c = np.transpose([np.sum(np.square(X), axis=1)])
dists = np.sqrt(a + b + c)
2.SVM
SVM這里我想介紹一下背景知識。首先介紹一下SVM的loss計算。
這里的1是margin。SVM使用的是hinge loss。hinge loss圖形如下:
我們之前學習到SVM的代價函數是這個樣子
調轉一下約束項的位置,就成了e >= 1 - ywx了。可以看出來SVM損失函數可以看作是L2-norm和Hinge Loss之和。
在這里我們只需要計算hinge loss就行了。
num_train = X.shape[0]
num_classes = W.shape[1]
scores = X.dot(W)
correct_class_scores = scores[range(num_train), list(y)].reshape(-1,1) #(N, 1)
margins = np.maximum(0, scores - correct_class_scores + 1)
margins[range(num_train), list(y)] = 0
loss = np.sum(margins) / num_train + 0.5 * reg * np.sum(W * W)
至于gradient,我們需要對這個loss進行w求導:
注意上面的計算l(*)只有在符合相應條件的時候才進行。
for i in xrange(num_train):
scores = X[i].dot(W)
correct_class_score = scores[y[i]]
for j in xrange(num_classes):
if j == y[i]:
continue
margin = scores[j] - correct_class_score + 1 # note delta = 1
if margin > 0:
loss += margin
dW[:,j] += X[i].T
dW[:,y[i]] += -X[i].T
loss /= num_train
dW /= num_train
# vectorized操作
coeff_mat = np.zeros((num_train, num_classes))
coeff_mat[margins > 0] = 1
coeff_mat[range(num_train), list(y)] = 0
coeff_mat[range(num_train), list(y)] = -np.sum(coeff_mat, axis=1)
dW = (X.T).dot(coeff_mat)
dW = dW/num_train + reg*W
3.Softmax
Softmax也是常見的non-linearity函數。下面是Softmax的定義
單個測試數據的損失就是這樣計算,最后求總和要加起來所有的才行。
num_classes = W.shape[1]
num_train = X.shape[0]
scores = X.dot(W)
softmax_output = np.exp(scores)/np.sum(np.exp(scores), axis = 1).reshape(-1,1)
loss = -np.sum(np.log(softmax_output[range(num_train), list(y)]))
loss /= num_train
loss += 0.5* reg * np.sum(W * W)
再求gradient。求導很重要的一點就是要分清求導對象
dS = softmax_output.copy()
dS[range(num_train), list(y)] += -1
dW = (X.T).dot(dS)
dW = dW/num_train + reg* W
4.Two-layer NN
從題目可以知道這里的結構是Input--FC--ReLU--FC--Softmax+loss的結構。由于我們引入了ReLU層,將輸入中所有小于0的項都給去掉了。所以反向將gradient傳回來的時候,這些小于0的位是沒有貢獻的。
下面是殘差分布,這里對于后向傳播的gradient計算做了一些解釋。梯度計算與反向傳播對梯度計算給出了一個很好的實例。
dscores = softmax_output.copy() # how this come from please see http://cs231n.github.io/neural-networks-case-study/
dscores[range(N), list(y)] -= 1
dscores /= N
grads['W2'] = h_output.T.dot(dscores) + reg * W2
# 以上通過Softmax章節的w求導就可以得到
grads['b2'] = np.sum(dscores, axis = 0)
dh = dscores.dot(W2.T)
dh_ReLu = (h_output > 0) * dh
grads['W1'] = X.T.dot(dh_ReLu) + reg * W1
grads['b1'] = np.sum(dh_ReLu, axis = 0)
5.feature
這個涉及到圖片的直方圖之類的,感覺用處不大,懶得看了
Assignment2: FC-NN, BatchNormalization, Dropout, cnn, Pytorch
Assignment2相對Assignment1來說知識程度更深了,但是因為有了Assignment1中對梯度和backpropagate的學習,所以相對來說都能觸類旁通。唯一比較復雜的就只有卷積層梯度的求解了。所以這部分我先總結一下自己所學到的東西,然后針對題目中的相關問題給出一些講解。
1.Fully-connected Neural Network
這一部分介紹了幾種常見的層的forward/backward,并對這些行為的實現加以封裝。
1.Affine Layer仿射層。其實也就是fully-connected layer. Affine Layer其實就是y=wx+b的實現。這一層的backward梯度也比較好求
2.ReLU層。這一層運用了ReLU函數,對于前面傳來的小于0的輸入都置零,大于0的輸入照常輸出。引入這種非線性激勵函數的作用是避免線性情況下輸出永遠都是輸入的線性組合,從而與沒有隱藏層效果相當。在求backward梯度時要注意,只有輸出為正數的才有梯度,輸出的梯度應該等于dout*x。
除了講解上面的層級,還引入了模塊化編程的概念,使用Solver來控制整個訓練過程,將模型常用的參數都傳給Solver,然后Solver內部進行訓練。斯坦福大學學生編程能力真的強。
然后給出了幾種更新規則的區別,SGD+momentum,RMSProp,Adam等,這些算法只要知道個原理,都不是很難。
2.BatchNormalization
這一部分難點主要在于
1.test模式下BN的操作:由于我們在訓練時候已經得到了running_mean和running_var,這兩個值將用在test模式下替換原有的sample_mean和sample_var。再帶入公式即可。
2.backward梯度的計算:這里有一篇非常好的文章Understanding the backward pass through Batch Normalization Layer。簡單來說就是當我們沒辦法一下子看出梯度來時,畫出計算圖,逐層遞推。這和cs231n課程講到的也是一個意思。最后得到梯度后直接計算,可以比逐層遞推有更高的效率。
具體怎么搞就去看代碼吧。
3.Dropout
Dropout相對比較簡單,但是要注意訓練模式和測試模式下的不同。測試模式下我們可以使用Dropout,但是測試模式下為了避免隨機性不能使用Dropout。為了實現高效率,我們直接對訓練時除以p即可。具體的原因請看上面的參考文章:深度學習筆記二。在這里,我們并不是簡單的去除以p,而是除以1-p。因為這樣可以避免后續的normalize操作。并且這里要把Dropout的mask記住,然后在backward的時候需要。這是和BN一樣的原理。
4.Convolutional Network
最難的應該是這部分了。
首先,第一個難點就是backward梯度的推導。這里我推導了一次。好難過啊,不知道怎么顯示latex。這里的推導大家如果看不懂就去慕課學院講解課這里看吧。
假設我們有一個原來的圖片。用3*3的簡化
$$ \begin{Bmatrix} a_{11} & a_{12} & a_{12} \ a_{21} & a_{22} & a_{23}\ a_{31} & a_{32} & a_{33} \end{Bmatrix} $$
我們的filter是這樣的:
$$ \begin{Bmatrix}
w_{11} & w_{12}\
w_{21} & w_{22}
\end{Bmatrix} $$
最后的結果是:
$$ \begin{Bmatrix}
z_{11} & z_{12}\
z_{21} & z_{22}
\end{Bmatrix} $$
容易得到:
$ z_{11}=a_{11}w_{11}+a_{12}w_{12}+a_{21}w_{21}+a_{22}w_{22} $
$ z_{12}=a_{12}w_{11}+a_{13}w_{12}+a_{22}w_{21}+a_{23}w_{22} $
$ z_{21}=a_{21}w_{11}+a_{22}w_{12}+a_{31}w_{21}+a_{32}w_{22} $
$ z_{22}=a_{22}w_{11}+a_{23}w_{12}+a_{32}w_{21}+a_{33}w_{22} $
又因為我們可以計算出{z}的gradient。也就是backpropagate時候從后面傳來的上游gradient。
$$ \begin{Bmatrix}
\delta_{11} & \delta_{12}\
\delta_{21} & \delta_{22}
\end{Bmatrix} $$
這樣當我們對$ a_{ij} $求導的時候,由于同一個$ a_{ij} $可能參與了多個$ z_{ij} $的計算,所以求導的時候要加起來。例如:
$ \triangledown a_{11} = \delta_{11}w_{11} $
$ \triangledown a_{12} = \delta_{11}w_{12} + \delta_{12}w_{11} $
...
$ \triangledown a_{33} = \delta_{22}w_{22} $
然后我們進行一下排列組合。
第二個難點是在fast_layer的時候,會出現col2im_6d_cython isn't defined的問題,這時候需要刪除cs231n文件夾下面除im2col_cython.pyx以外所有以im2col_cython開頭的文件,然后重新編譯。
第三個難點是在Spatial Batch Normalization處理圖片時,這里的輸入是(N,C,H,W),我們需要先轉換為(N,H,W,C)再reshape成(N*H*W, C),最后再轉換回來,這樣才能保留住channel進行Spatial BN操作。
然后我們就可以愉快的組裝layer成一個完整的convolutional network了。
5.Pytorch和TensorFlow
這里就沒啥好講的了。
Assignment3: RNN, Network visualization, style transfer, GAN
1.RNN
RNN是種十分強大的網絡,尤其是改進版LSTM,更是讓人嘆為觀止。這個作業寫了一個文本標注的例子,只要注意到了rnn的模型架構,一般不會有問題。我放在這里來。
特別注意LSTM的模型中,$c_t$的梯度來源有兩個,dc_t和tanh。所以要把兩個相加。
Network visualization
Style transfer
GAN
這幾個專題感覺都是偏應用型的,代碼沒什么難度,而且我的代碼注釋比較詳細。直接跟著代碼看就行了。