UFLDL深度學習筆記 (四)用於分類的深度網絡
1. 主要思路
本文要討論的“UFLDL 建立分類用深度網絡”基本原理基於前2節的softmax回歸和 無監督特征學習,區別在於使用更“深”的神經網絡,也即網絡中包含更多的隱藏層,我們知道前一篇“無監督特征學習”只有一層隱藏層。原文深度網絡概覽不僅給出了深度網絡優勢的一種解釋,還總結了幾點訓練深度網絡的困難之處,並解釋了逐層貪婪訓練方法的過程。關於深度網絡優勢的表述非常好,貼在這里。
使用深度網絡最主要的優勢在於,它能以更加緊湊簡潔的方式來表達比淺層網絡大得多的函數集合。正式點說,我們可以找到一些函數,這些函數可以用\(k\)層網絡簡潔地表達出來(這里的簡潔是指隱層單元的數目只需與輸入單元數目呈多項式關系)。但是對於一個只有\(k-1\)層的網絡而言,除非它使用與輸入單元數目呈指數關系的隱層單元數目,否則不能簡潔表達這些函數。
逐層訓練法的思路表述如下:
逐層貪婪算法的主要思路是每次只訓練網絡中的一層,即我們首先訓練一個只含一個隱藏層的網絡,僅當這層網絡訓練結束之后才開始訓練一個有兩個隱藏層的網絡,以此類推。在每一步中,我們把已經訓練好的前\(k-1\) 層固定,然后增加第\(k\)層(也就是將我們已經訓練好的前\(k-1\) 的輸出作為輸入)。每一層的訓練可以是有監督的(例如,將每一步的分類誤差作為目標函數),但更通常使用無監督方法(例如自動編碼器,我們會在后邊的章節中給出細節)。這些各層單獨訓練所得到的權重被用來初始化最終(或者說全部)的深度網絡的權重,然后對整個網絡進行“微調”(即把所有層放在一起來優化有標簽訓練集上的訓練誤差).
深度網絡相比於前一篇“無監督特征學習”增加了隱藏層數,帶來局部極值
梯度彌散
問題,解決的辦法就是將網絡作為一個整體用有監督學習對權重參數進行微調:fine-tune
。值得注意的是,開始微調時,兩隱藏層與softmax分類輸出層的權重$W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}; \theta $不是用隨機參數賦值的,而是用稀疏自編碼學習獲得的,和 無監督特征學習的做法相同。

2. 訓練步驟與公式推導
-
- 把有標簽數據分為兩部分\(X_{train},X_{test}\),先對一份原始數據\(X_{train}\)做無監督的稀疏自編碼訓練,獲得輸入層到第一隱藏層的最優化權值參數\(W^{(1)}, b^{(1)}\)
-
- 將\(X_{train}\)前向傳播通過第一隱藏層得到\(feature1\), 以此為輸入訓練第二隱藏層,得到最優化權值參數\(W^{(2)}, b^{(2)}\);
-
- 將\(feature1\)前向傳播通過第二隱藏層得到\(feature2\), 以此為輸入訓練softmax輸出層,得到最優化權值參數\(\theta\);
-
- 用\(W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}; \theta\)作為初始化參數,以\(X_{train}\)為輸入,用后向傳播原理給出整個網絡的代價函數與梯度,在已知分類標簽情況下微調權重參數,得到最優化參數\(W_{optim}^{(1)}, b_{optim}^{(1)}; W_{optim}^{(2)}, b_{optim}^{(2)}; \theta_{optim}\)。
-
- 用上述參數對測試集\(X_{test}\)進行分類,計算出分類准確率。
可以看出需要使用新公式的地方在於第4步,深度網絡的代價函數的梯度,這里仍然運用最基礎的梯度后向傳播原理,從softmax回歸推導中我們知道輸出層權重\(\theta\)梯度為
矩陣化表達為:
使用稀疏自編碼 中相同的方法,推導殘差后向傳導形式,即可得到代價函數對\(W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}\)的梯度,
由於softma輸出並沒有用\(sigmoid\)函數,則激活值對輸出值的偏導為1,輸出層\(n_l=4\)
運用后向傳導原理,第三層(第二隱藏層)的殘差為
根據梯度與殘差矩陣的關系可得:
同理可求出
這樣我們就得到了代價函數對\(W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}; \theta\)的梯度矩陣。可以看到softmax是個特例外,多層隱藏層形式統一,這樣便於代碼循環實現,這里對兩層隱藏層的推導只是為了便於理解。
3. 代碼實現
根據前面的步驟描述,復用原來的系數自編碼模塊外,我們要增加fine tune的全局代價函數對權重的梯度,實現代碼為stackedAECost.m
,詳見https://github.com/codgeek/deeplearning
function [ cost, grad ] = stackedAECost(theta, inputSize, hiddenSize, ...
numClasses, netconfig, ...
lambda, data, labels,~)
% stackedAECost: Takes a trained softmaxTheta and a training data set with labels,
% and returns cost and gradient using a stacked autoencoder model. Used for
% finetuning.
% theta: trained weights from the autoencoder
% visibleSize: the number of input units
% hiddenSize: the number of hidden units *at the 2nd layer*
% numClasses: the number of categories
% netconfig: the network configuration of the stack
% lambda: the weight regularization penalty
% data: Our matrix containing the training data as columns. So, data(:,i) is the i-th training example.
% labels: A vector containing labels, where labels(i) is the label for the
% i-th training example
% We first extract the part which compute the softmax gradient
softmaxTheta = reshape(theta(1:hiddenSize*numClasses), numClasses, hiddenSize);
% Extract out the "stack"
stack = params2stack(theta(hiddenSize*numClasses+1:end), netconfig);
% You will need to compute the following gradients
softmaxThetaGrad = zeros(size(softmaxTheta));
stackgrad = cell(size(stack));
numStack = numel(stack);
for d = 1:numStack
stackgrad{d}.w = zeros(size(stack{d}.w));
stackgrad{d}.b = zeros(size(stack{d}.b));
end
cost = 0; % You need to compute this
% You might find these variables useful
M = size(data, 2);
groundTruth = full(sparse(labels, 1:M, 1));
% forward propagation
activeStack = cell(numStack+1, 1);% first element is input data
activeStack{1} = data;
for d = 2:numStack+1
activeStack{d} = sigmoid((stack{d-1}.w)*activeStack{d-1} + repmat(stack{d-1}.b, 1, M));
end
z = softmaxTheta*activeStack{numStack+1};% softmaxTheta:numClasse×hiddenSize. Z:numClasses×numCases
z = z - max(max(z)); % avoid overflow while keep p unchanged.
za = exp(z); % matrix product: numClasses×numCases
p = za./repmat(sum(za,1),numClasses,1); % normalize the probbility aganist numClasses. numClasses×numCases
cost = -mean(sum(groundTruth.*log(p), 1)) + sum(sum(softmaxTheta.*softmaxTheta)).*(lambda/2);
% back propagation
softmaxThetaGrad = -(groundTruth - p)*(activeStack{numStack+1}')./M + softmaxTheta.*lambda; % numClasses×inputSize
lastLayerDelta = -(groundTruth - p);%各層殘差delta定義是J對各層z的偏導數,不是激活值a, 輸出層殘差delta是▽J/▽z,沒有1/a(i,j) 這個系數
lastLayerDelta = (softmaxTheta')*lastLayerDelta.*(activeStack{numStack+1}.*(1-activeStack{numStack+1})); % res of softmax input layer
for d = numel(stack):-1:1
stackgrad{d}.w = (activeStack{d}*lastLayerDelta')'./M;
stackgrad{d}.b = mean(lastLayerDelta, 2);
lastLayerDelta = ((stack{d}.w)')*lastLayerDelta.*(activeStack{d}.*(1-activeStack{d}));
end
%% Roll gradient vector
grad = [softmaxThetaGrad(:) ; stack2params(stackgrad)];
end
function sigm = sigmoid(x)
sigm = 1 ./ (1 + exp(-x));
end
4.圖示與結果
數據集仍然來自Yann Lecun的筆跡數據庫。
設定與練習說明相同的參數,輸入層包含784個節點,第一、第二隱藏層都是196個節點,輸出層10個節點。運行代碼主文件stackAEExercise.m 可以看到預測准確率達到97.77%。滿足練習的標准結果。
我們來比較一下微調前后隱藏層學習到的特征有什么變化。
逐層貪心訓練 | 微調后 | |
---|---|---|
第一隱層 | ![]() |
![]() |
第二隱層 | ![]() |
![]() |
softmax輸出層 | ![]() |
![]() |
類似稀疏自編碼對邊緣的學習,上圖的第一隱藏層特征可理解為筆記鈎旋弧線特征,第二隱藏層就難以理解為直觀的含義了,深層網絡不一定每一層都能對應到人腦對事物的一層理解上,此外微調后似乎是增加了干擾,也期待大牛們能解釋一下這些變化!