cs231n assignments學(xué)習(xí)心得
cs231n是斯坦福的一門以計(jì)算機(jī)視覺(jué)為載體的深度學(xué)習(xí)課程,由李飛飛和她的幾個(gè)博士生上課。這門課親測(cè)好評(píng)。下面是我完成這些assignment的一些疑問(wèn)點(diǎn)和解決辦法。
首先先貼代碼:https://github.com/oubindo/cs231n-cnn
歡迎star和fork。感謝支持!
Assignment1:KNN,SVM,Softmax,Neuron Network
總體來(lái)說(shuō),這個(gè)assignment難度適中,但是對(duì)于numpy的要求還挺高的,要比較純熟的使用才能完成一些諸如矢量化一樣的操作。比較困難的地方在于梯度的計(jì)算。作為初學(xué)者的我一開(kāi)始是非常懵逼的,(現(xiàn)在好一點(diǎn)了也還有點(diǎn)懵逼)??戳斯俜浇o出的一些說(shuō)明,還有慕課學(xué)院講解課以后才理解了一些?,F(xiàn)在嘗試對(duì)于一些問(wèn)題給出自己的理解。圖片部分出自上面內(nèi)容
1.KNN
KNN主要的考察點(diǎn)就是兩重循環(huán),一重循環(huán)和全向量化。
先介紹一下背景,給出n維的測(cè)試點(diǎn)和訓(xùn)練點(diǎn),要求出它們之間的距離。使用兩重循環(huán)的話就是通過(guò)索引到這兩個(gè)數(shù)據(jù)再處理。
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
使用一重循環(huán)是借助了numpy ndarry之間的相減功能,單獨(dú)的算出所有訓(xùn)練點(diǎn)到一個(gè)測(cè)試點(diǎn)的距離,再一次便利即可。
for i in xrange(num_test):
distances = np.sqrt(np.sum(np.square(self.X_train - X[i]),axis = 1))
dists[i, :] = distances
使用全向量化就比較有技術(shù)了,這里通過(guò)(X-Y)2=X2-2XY+Y^2來(lái)計(jì)算。
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這里我想介紹一下背景知識(shí)。首先介紹一下SVM的loss計(jì)算。

這里的1是margin。SVM使用的是hinge loss。hinge loss圖形如下:

我們之前學(xué)習(xí)到SVM的代價(jià)函數(shù)是這個(gè)樣子

調(diào)轉(zhuǎn)一下約束項(xiàng)的位置,就成了e >= 1 - ywx了。可以看出來(lái)SVM損失函數(shù)可以看作是L2-norm和Hinge Loss之和。
在這里我們只需要計(jì)算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,我們需要對(duì)這個(gè)loss進(jìn)行w求導(dǎo):

注意上面的計(jì)算l(*)只有在符合相應(yīng)條件的時(shí)候才進(jìn)行。
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也是常見(jiàn)的non-linearity函數(shù)。下面是Softmax的定義

單個(gè)測(cè)試數(shù)據(jù)的損失就是這樣計(jì)算,最后求總和要加起來(lái)所有的才行。
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。求導(dǎo)很重要的一點(diǎn)就是要分清求導(dǎo)對(duì)象

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
從題目可以知道這里的結(jié)構(gòu)是Input--FC--ReLU--FC--Softmax+loss的結(jié)構(gòu)。由于我們引入了ReLU層,將輸入中所有小于0的項(xiàng)都給去掉了。所以反向?qū)radient傳回來(lái)的時(shí)候,這些小于0的位是沒(méi)有貢獻(xiàn)的。
下面是殘差分布,這里對(duì)于后向傳播的gradient計(jì)算做了一些解釋。梯度計(jì)算與反向傳播對(duì)梯度計(jì)算給出了一個(gè)很好的實(shí)例。

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
# 以上通過(guò)Softmax章節(jié)的w求導(dǎo)就可以得到
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
這個(gè)涉及到圖片的直方圖之類的,感覺(jué)用處不大,懶得看了
Assignment2: FC-NN, BatchNormalization, Dropout, cnn, Pytorch
Assignment2相對(duì)Assignment1來(lái)說(shuō)知識(shí)程度更深了,但是因?yàn)橛辛薃ssignment1中對(duì)梯度和backpropagate的學(xué)習(xí),所以相對(duì)來(lái)說(shuō)都能觸類旁通。唯一比較復(fù)雜的就只有卷積層梯度的求解了。所以這部分我先總結(jié)一下自己所學(xué)到的東西,然后針對(duì)題目中的相關(guān)問(wèn)題給出一些講解。
1.Fully-connected Neural Network
這一部分介紹了幾種常見(jiàn)的層的forward/backward,并對(duì)這些行為的實(shí)現(xiàn)加以封裝。
1.Affine Layer仿射層。其實(shí)也就是fully-connected layer. Affine Layer其實(shí)就是y=wx+b的實(shí)現(xiàn)。這一層的backward梯度也比較好求
2.ReLU層。這一層運(yùn)用了ReLU函數(shù),對(duì)于前面?zhèn)鱽?lái)的小于0的輸入都置零,大于0的輸入照常輸出。引入這種非線性激勵(lì)函數(shù)的作用是避免線性情況下輸出永遠(yuǎn)都是輸入的線性組合,從而與沒(méi)有隱藏層效果相當(dāng)。在求backward梯度時(shí)要注意,只有輸出為正數(shù)的才有梯度,輸出的梯度應(yīng)該等于dout*x。
除了講解上面的層級(jí),還引入了模塊化編程的概念,使用Solver來(lái)控制整個(gè)訓(xùn)練過(guò)程,將模型常用的參數(shù)都傳給Solver,然后Solver內(nèi)部進(jìn)行訓(xùn)練。斯坦福大學(xué)學(xué)生編程能力真的強(qiáng)。
然后給出了幾種更新規(guī)則的區(qū)別,SGD+momentum,RMSProp,Adam等,這些算法只要知道個(gè)原理,都不是很難。
2.BatchNormalization
這一部分難點(diǎn)主要在于
1.test模式下BN的操作:由于我們?cè)谟?xùn)練時(shí)候已經(jīng)得到了running_mean和running_var,這兩個(gè)值將用在test模式下替換原有的sample_mean和sample_var。再帶入公式即可。
2.backward梯度的計(jì)算:這里有一篇非常好的文章Understanding the backward pass through Batch Normalization Layer。簡(jiǎn)單來(lái)說(shuō)就是當(dāng)我們沒(méi)辦法一下子看出梯度來(lái)時(shí),畫出計(jì)算圖,逐層遞推。這和cs231n課程講到的也是一個(gè)意思。最后得到梯度后直接計(jì)算,可以比逐層遞推有更高的效率。
具體怎么搞就去看代碼吧。
3.Dropout
Dropout相對(duì)比較簡(jiǎn)單,但是要注意訓(xùn)練模式和測(cè)試模式下的不同。測(cè)試模式下我們可以使用Dropout,但是測(cè)試模式下為了避免隨機(jī)性不能使用Dropout。為了實(shí)現(xiàn)高效率,我們直接對(duì)訓(xùn)練時(shí)除以p即可。具體的原因請(qǐng)看上面的參考文章:深度學(xué)習(xí)筆記二。在這里,我們并不是簡(jiǎn)單的去除以p,而是除以1-p。因?yàn)檫@樣可以避免后續(xù)的normalize操作。并且這里要把Dropout的mask記住,然后在backward的時(shí)候需要。這是和BN一樣的原理。
4.Convolutional Network
最難的應(yīng)該是這部分了。
首先,第一個(gè)難點(diǎn)就是backward梯度的推導(dǎo)。這里我推導(dǎo)了一次。好難過(guò)啊,不知道怎么顯示latex。這里的推導(dǎo)大家如果看不懂就去慕課學(xué)院講解課這里看吧。
假設(shè)我們有一個(gè)原來(lái)的圖片。用3*3的簡(jiǎn)化
$$ \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} $$
最后的結(jié)果是:
$$ \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} $
又因?yàn)槲覀兛梢杂?jì)算出{z}的gradient。也就是backpropagate時(shí)候從后面?zhèn)鱽?lái)的上游gradient。
$$ \begin{Bmatrix}
\delta_{11} & \delta_{12}\
\delta_{21} & \delta_{22}
\end{Bmatrix} $$
這樣當(dāng)我們對(duì)$ a_{ij} $求導(dǎo)的時(shí)候,由于同一個(gè)$ a_{ij} $可能參與了多個(gè)$ z_{ij} $的計(jì)算,所以求導(dǎo)的時(shí)候要加起來(lái)。例如:
$ \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} $
然后我們進(jìn)行一下排列組合。

第二個(gè)難點(diǎn)是在fast_layer的時(shí)候,會(huì)出現(xiàn)col2im_6d_cython isn't defined的問(wèn)題,這時(shí)候需要?jiǎng)h除cs231n文件夾下面除im2col_cython.pyx以外所有以im2col_cython開(kāi)頭的文件,然后重新編譯。
第三個(gè)難點(diǎn)是在Spatial Batch Normalization處理圖片時(shí),這里的輸入是(N,C,H,W),我們需要先轉(zhuǎn)換為(N,H,W,C)再reshape成(N*H*W, C),最后再轉(zhuǎn)換回來(lái),這樣才能保留住channel進(jìn)行Spatial BN操作。
然后我們就可以愉快的組裝layer成一個(gè)完整的convolutional network了。
5.Pytorch和TensorFlow
這里就沒(méi)啥好講的了。
Assignment3: RNN, Network visualization, style transfer, GAN
1.RNN
RNN是種十分強(qiáng)大的網(wǎng)絡(luò),尤其是改進(jìn)版LSTM,更是讓人嘆為觀止。這個(gè)作業(yè)寫了一個(gè)文本標(biāo)注的例子,只要注意到了rnn的模型架構(gòu),一般不會(huì)有問(wèn)題。我放在這里來(lái)。

特別注意LSTM的模型中,$c_t$的梯度來(lái)源有兩個(gè),dc_t和tanh。所以要把兩個(gè)相加。
Network visualization
Style transfer
GAN
這幾個(gè)專題感覺(jué)都是偏應(yīng)用型的,代碼沒(méi)什么難度,而且我的代碼注釋比較詳細(xì)。直接跟著代碼看就行了。