【UFLDL】Exercise: Convolutional Neural Network

這個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層:這一層首先根據公式δ= 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層:仍是根據公式δ= 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;

Step 2: Gradient Check

當時明明個人梯度降低無法收斂,這一步竟然經過了=。=

Step 3: Learn Parameters

這步比較簡單,根據UFLDL對隨機梯度降低的解釋,在minFuncSGD中加上衝量的影響就能夠了:

%%% YOUR CODE HERE %%%
        velocity = mom*velocity+alpha*grad;
        theta = theta - velocity;

Step 4: Test

運行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

相關文章
相關標籤/搜索