這個exercise須要完成cnn中的forward pass,cost,error和gradient的計算。須要弄清楚每一層的以上四個步驟的原理,而且要充分利用matlab的矩陣運算。大概把過程總結了一下以下圖所示:html
STEP 1:Implement CNN Objectivegit
STEP 1a: Forward Propagationgithub
Forward Propagation主要是爲了計算輸入圖片通過神經網絡後的輸出,這個網絡有三層:convolution->pooling->softmax(dense connected),卷積層對於每一個圖像用全部的模板進行卷積;pooling層對卷積層輸出的feature map採樣;softmax層根據pooling層輸出的feature預測圖像的分類結果。其中convolution和pooling操做在以前就實現過了。具體的過程能夠參見上圖中Forward Pass中每層的具體操做。代碼以下:算法
%%% YOUR CODE HERE %%% %調用以前已經實現的函數 activations = cnnConvolve(filterDim, numFilters, images, Wc, bc);%sigmoid(wx+b) activationsPooled = cnnPool(poolDim, activations); % Reshape activations into 2-d matrix, hiddenSize x numImages, % for Softmax layer %將activationsPooled從outDim*outDim*numFilters*numImages拼接成hiddenSize*numImages的大矩陣 activationsPooled = reshape(activationsPooled,[],numImages); %% Softmax Layer % Forward propagate the pooled activations calculated above into a % standard softmax layer. For your convenience we have reshaped % activationPooled into a hiddenSize x numImages matrix. Store the % results in probs. % numClasses x numImages for storing probability that each image belongs to % each class. probs = zeros(numClasses,numImages); %%% YOUR CODE HERE %%% h = exp(bsxfun(@plus,Wd * activationsPooled,bd)); probs = bsxfun(@rdivide,h,sum(h,1));
STEP 1b: Calculate Cost網絡
計算梯度降低要優化的目標函數,主要分爲兩部分,一部分是因爲分類器輸出結果和真實結果的差別引發的偏差函數,另外一部分是對權重w的正則約束。第一部分能夠參考softmax regression中對損失函數的計算,第二部分就是對Wc和Wd的全部項求平方和。相似下面的公式,不過第一項中的J是softmax的cross entropy損失函數。最後要對第一項除以圖像的總數,這是十分重要的,一開始我沒有除,最後獲得的算法是發散的,緣由多是第一項數值過大,直接把正則項的影響給忽略了。ide
代碼:函數
%%% YOUR CODE HERE %%% logp = log(probs); index = sub2ind(size(logp),labels',1:size(probs,2)); ceCost = -sum(logp(index)); wCost = lambda/2 * (sum(Wd(:).^2)+sum(Wc(:).^2)); cost = ceCost/numImages + wCost;
STEP 1c: Backpropagation優化
BP算法首先要計算各層的對最終偏差的貢獻delta。spa
softmax層:這一層的偏差最容易計算,只要用ground truth減去神經網絡的輸出probs就能夠了:.net
output = zeros(size(probs)); output(index) = 1; DeltaSoftmax = probs - output;
pool層:這一層首先根據公式δl = Wδl+1 * f'(zl)(pool層沒有f'(zl)這一項)計算該層的error,此時獲得一個hiddenSize*numImages的矩陣,首先利用reshape函數把error還原成一個convDim*convDim*numFilters*numImages的矩陣,在pooling操做時,pooling層一個節點的輸入是conv層2*2個節點的輸出(假設poolDim=2)以下圖所示:
因此pooling層的這個節點要將本身的error在這2*2個節點中平均分(由於使用的是mean pooling),UFLDL上面提示了能夠用kron這個函數來實現,這樣如上圖所示,就能夠經過pooling層一個2*2的filter對應的error計算獲得convolution層一個4*4的filter對應的error了。代碼以下:
DeltaPool = reshape(Wd' * DeltaSoftmax,outputDim,outputDim,numFilters,numImages); DeltaUnpool = zeros(convDim,convDim,numFilters,numImages); for imNum = 1:numImages for FilterNum = 1:numFilters unpool = DeltaPool(:,:,FilterNum,imNum); DeltaUnpool(:,:,FilterNum,imNum) = kron(unpool,ones(poolDim))./(poolDim ^ 2); end end
convolution層:仍是根據公式δl = Wδl+1 * f'(zl)來計算:
DeltaConv = DeltaUnpool .* activations .* (1 - activations);
STEP 1d: Gradient Calculation
整個cnn一共有三層:convolution->pooling->softmax(dense connected),只有convolution和softmax層有權重,分別爲Wc,bc,Wd,bd。那麼就要計算目標函數J對他們的倒數以便在梯度降低中更新W和b。
Wd和bd的梯度計算:
根據下面兩個公式:
其中al-1對應pooling層的激勵(輸出)activitonsPooled,δl就是這一層的偏差DeltaSoftmax,代碼以下:
Wd_grad = (1./numImages) .* DeltaSoftmax*activationsPooled'+lambda*Wd; bd_grad = (1./numImages) .* sum(DeltaSoftmax,2);
Wc和bc的梯度計算:
仍是根據上面兩個計算梯度的公式,不過麻煩就麻煩在l-1層實際上是輸入的圖像,因此al-1對應的是輸入的圖像,那麼就得用for循環逐個便利圖像並利用UFLDL上提供的公式計算對應梯度:
這裏爲了方便,先對全部DeltaConv進行旋轉,而後再用for循環依次求出梯度:
%%% YOUR CODE HERE %%% Wd_grad = (1./numImages) .* DeltaSoftmax*activationsPooled'+lambda*Wd; bd_grad = (1./numImages) .* sum(DeltaSoftmax,2); bc_grad = zeros(size(bc)); Wc_grad = zeros(filterDim,filterDim,numFilters); for filterNum = 1:numFilters error = DeltaConv(:,:,filterNum,:); bc_grad(filterNum) = (1./numImages) .* sum(error(:)); end %旋轉全部DealtaConv for filterNum = 1:numFilters for imNum = 1:numImages error = DeltaConv(:,:,filterNum,imNum); DeltaConv(:,:,filterNum,imNum) = rot90(error,2); end end for filterNum = 1:numFilters for imNum = 1:numImages Wc_grad(:,:,filterNum) = Wc_grad(:,:,filterNum) + conv2(images(:,:,imNum),DeltaConv(:,:,filterNum,imNum),'valid'); end end Wc_grad = (1./numImages) .* Wc_grad + lambda*Wc;
當時明明個人梯度降低無法收斂,這一步竟然經過了=。=
這步比較簡單,根據UFLDL對隨機梯度降低的解釋,在minFuncSGD中加上衝量的影響就能夠了:
%%% YOUR CODE HERE %%% velocity = mom*velocity+alpha*grad; theta = theta - velocity;
運行cnnTrain,最後準確率能夠達到97%+
以上就可UFLDL上cnn的實現,最重要的是弄清楚每一層在每個過程當中須要進行的操做,我都總結在文章開頭的表格裏面了~matlab給我一個很大的感覺就是矩陣的demension match,有時候知道公式是什麼樣的,可是實現起來要考慮矩陣的維度,兩個維度match的矩陣才能相乘或者相加,不過好處就是再不知道怎麼寫代碼的時候能夠結果維度match來寫代碼。並且cnn debug起來真的好睏難,徹底不知道是哪裏出了問題=。=
完整的代碼在個人github上。
參考:
【1】http://ufldl.stanford.edu/tutorial/supervised/ExerciseConvolutionalNeuralNetwork/
【2】http://blog.csdn.net/lingerlanlan/article/details/41390443