%%=========================================================================
%函數名稱: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
複制
量化投資與機器學習
知識、能力、深度、專業
勤奮、天賦、耐得住寂寞