深度學習Matlab工具箱代碼註釋——cnnbp.m

%%=========================================================================
%函數名稱:cnnbp()
%輸入參數:net,呆訓練的神經網絡;y,訓練樣本的標籤,即期望輸出
%輸出參數:net,經過BP算法訓練得到的神經網絡
%主要功能:通過BP算法訓練神經網絡參數
%實現步驟:1)將輸出的殘差擴展成與最後一層的特徵map相同的尺寸形式
%         2)如果是卷積層,則進行上採樣
%         3)如果是下采樣層,則進行下采樣
%         4)採用誤差傳遞公式對靈敏度進行反向傳遞
%注意事項:1)從最後一層的error倒推回來deltas,和神經網絡的BP十分相似,可以參考“UFLDL的反向傳導算法”的說明
%         2)在fvd裏面保存的是所有樣本的特徵向量(在cnnff.m函數中用特徵map拉成的),所以這裏需要重新換回來特徵map的形式,
%            d保存的是delta,也就是靈敏度或者殘差
%         3)net.o .* (1 - net.o))代表輸出層附加的非線性函數的導數,即sigm函數的導數
%%=========================================================================
function net = cnnbp(net, y)
n     = numel(net.layers);                         %網絡層數
net.e = net.o - y;                                 %實際輸出與期望輸出之間的誤差
net.L = 1/2* sum(net.e(:) .^ 2) / size(net.e, 2);  %代價函數,採用均方誤差函數作爲代價函數
net.od = net.e .* (net.o .* (1 - net.o));          %輸出層的靈敏度或者殘差,(net.o .* (1 - net.o))代表輸出層的激活函數的導數
net.fvd = (net.ffW' * net.od);                     %殘差反向傳播回前一層,net.fvd保存的是殘差
if strcmp(net.layers{n}.type, 'c')                 %只有卷積層採用sigm函數
    net.fvd = net.fvd .* (net.fv .* (1 - net.fv)); %net.fv是前一層的輸出(未經過simg函數),作爲輸出層的輸入
end

%%%%%%%%%%%%%%%%%%%%將輸出的殘差擴展成與最後一層的特徵map相同的尺寸形式%%%%%%%%%%%%%%%%%%%%
sa    = size(net.layers{n}.a{1});           %最後一層特徵map的大小。這裏的最後一層都是指輸出層的前一層
fvnum = sa(1) * sa(2);                      %因爲是將最後一層特徵map拉成一條向量,所以對於一個樣本來說,特徵維數是這樣
for j = 1 : numel(net.layers{n}.a)          %最後一層的特徵map的個數
    net.layers{n}.d{j} = reshape(net.fvd(((j - 1) * fvnum + 1) : j * fvnum, :), sa(1), sa(2), sa(3));
end

for l = (n - 1) : -1 : 1                    %對於輸出層前面的層(與輸出層計算殘差的方式不同)
    if strcmp(net.layers{l}.type, 'c')      %如果是卷積層,則進行上採樣
        for j = 1 : numel(net.layers{l}.a)  %該層特徵map的個數
            %%=========================================================================
            %主要功能:卷積層的靈敏度誤差傳遞
            %注意事項:1)net.layers{l}.d{j} 保存的是 第l層 的 第j個 map 的 靈敏度map。 也就是每個神經元節點的delta的值
            %            expand的操作相當於對l+1層的靈敏度map進行上採樣。然後前面的操作相當於對該層的輸入a進行sigmoid求導
            %            這條公式請參考 Notes on Convolutional Neural Networks
            %%=========================================================================
            net.layers{l}.d{j} = net.layers{l}.a{j} .* (1 - net.layers{l}.a{j}) .* (expand(net.layers{l + 1}.d{j}, [net.layers{l + 1}.scale net.layers{l + 1}.scale 1]) / net.layers{l + 1}.scale ^ 2);
        end

    elseif strcmp(net.layers{l}.type, 's')            %如果是下采樣層,則進行下采樣
        %%=========================================================================
        %主要功能:下采樣層的靈敏度誤差傳遞
        %注意事項:1)這條公式請參考 Notes on Convolutional Neural Networks
        %%=========================================================================
        for i = 1 : numel(net.layers{l}.a)            %第i層特徵map的個數
            z = zeros(size(net.layers{l}.a{1}));
            for j = 1 : numel(net.layers{l + 1}.a)    %第l+1層特徵map的個數
                z = z + convn(net.layers{l + 1}.d{j}, rot180(net.layers{l + 1}.k{i}{j}), 'full');
            end
            net.layers{l}.d{i} = z;
        end
    end
end

%%=========================================================================
%主要功能:計算梯度
%實現步驟:
%注意事項:1)這裏與Notes on Convolutional Neural Networks中不同,這裏的子採樣層沒有參數,也沒有
%            激活函數,所以在子採樣層是沒有需要求解的參數的
%%=========================================================================
for l = 2 : n
    if strcmp(net.layers{l}.type, 'c')
        for j = 1 : numel(net.layers{l}.a)
            for i = 1 : numel(net.layers{l - 1}.a)

                %%%%%%%%%%%%%%%%%%%%dk保存的是誤差對卷積核的導數%%%%%%%%%%%%%%%%%%%%
                net.layers{l}.dk{i}{j} = convn(flipall(net.layers{l - 1}.a{i}), net.layers{l}.d{j}, 'valid') / size(net.layers{l}.d{j}, 3);
            end

            %%%%%%%%%%%%%%%%%%%%db保存的是誤差對於bias基的導數%%%%%%%%%%%%%%%%%%%%
            net.layers{l}.db{j} = sum(net.layers{l}.d{j}(:)) / size(net.layers{l}.d{j}, 3);
        end
    end
end

%%%%%%%%%%%%%%%%%%%%最後一層perceptron的gradient的計算%%%%%%%%%%%%%%%%%%%%
net.dffW = net.od * (net.fv)' / size(net.od, 2);
net.dffb = mean(net.od, 2);

    function X = rot180(X)
        X = flipdim(flipdim(X, 1), 2);
    end
end

原文鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章