本文要討論的「UFLDL 創建分類用深度網絡」基本原理基於前2節的softmax迴歸和 無監督特徵學習,區別在於使用更「深」的神經網絡,也即網絡中包含更多的隱藏層,咱們知道前一篇「無監督特徵學習」只有一層隱藏層。原文深度網絡概覽不只給出了深度網絡優點的一種解釋,還總結了幾點訓練深度網絡的困難之處,並解釋了逐層貪婪訓練方法的過程。關於深度網絡優點的表述很是好,貼在這裏。php
使用深度網絡最主要的優點在於,它能以更加緊湊簡潔的方式來表達比淺層網絡大得多的函數集合。正式點說,咱們能夠找到一些函數,這些函數能夠用\(k\)層網絡簡潔地表達出來(這裏的簡潔是指隱層單元的數目只需與輸入單元數目呈多項式關係)。可是對於一個只有\(k-1\)層的網絡而言,除非它使用與輸入單元數目呈指數關係的隱層單元數目,不然不能簡潔表達這些函數。html
逐層訓練法的思路表述以下:git
逐層貪婪算法的主要思路是每次只訓練網絡中的一層,即咱們首先訓練一個只含一個隱藏層的網絡,僅當這層網絡訓練結束以後纔開始訓練一個有兩個隱藏層的網絡,以此類推。在每一步中,咱們把已經訓練好的前\(k-1\) 層固定,而後增長第\(k\)層(也就是將咱們已經訓練好的前\(k-1\) 的輸出做爲輸入)。每一層的訓練能夠是有監督的(例如,將每一步的分類偏差做爲目標函數),但更一般使用無監督方法(例如自動編碼器,咱們會在後邊的章節中給出細節)。這些各層單獨訓練所獲得的權重被用來初始化最終(或者說所有)的深度網絡的權重,而後對整個網絡進行「微調」(即把全部層放在一塊兒來優化有標籤訓練集上的訓練偏差).github
深度網絡相比於前一篇「無監督特徵學習」增長了隱藏層數,帶來局部極值
梯度彌散
問題,解決的辦法就是將網絡做爲一個總體用有監督學習對權重參數進行微調:fine-tune
。值得注意的是,開始微調時,兩隱藏層與softmax分類輸出層的權重$W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}; \theta $不是用隨機參數賦值的,而是用稀疏自編碼學習得到的,和 無監督特徵學習的作法相同。算法
能夠看出須要使用新公式的地方在於第4步,深度網絡的代價函數的梯度,這裏仍然運用最基礎的梯度後向傳播原理,從softmax迴歸推導中咱們知道輸出層權重\(\theta\)梯度爲數據庫
\[\begin{align} \frac {\nabla J} {\nabla \theta_j} &= -\frac 1 m\sum_{i=1}^m x^{(i)}\left [ 1\{y^{(i)}=j\} - p(y^{(i)}=j|x^{(i)};\theta) \right] +\lambda\theta_j \end{align}\]網絡
矩陣化表達爲:函數
\[ \begin{align} \frac {\nabla J} {\nabla \theta} &=-\frac 1 m (G_{k \times m}-P_{k\times m}) *X_{(n+1) \times m}^T +\lambda\theta \end{align} \]學習
使用稀疏自編碼 中相同的方法,推導殘差後向傳導形式,便可獲得代價函數對\(W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}\)的梯度,測試
因爲softma輸出並無用\(sigmoid\)函數,則激活值對輸出值的偏導爲1,輸出層\(n_l=4\)
\[\begin{align} \delta_i^{(n_l)} &= -(y_i-a_i^{(n_l)})*f'(z_i^{(n_l)}) \\ &= -y_i-a_i^{(n_l)} \\ vectorize \\ \delta^{(n_l)} &= -(G_{k \times m}-P_{k\times m}) \end{align}\]
運用後向傳導原理,第三層(第二隱藏層)的殘差爲
\[ \begin{align} \delta^{(3)} &= \theta^T*\delta^{(n_l)} .* f'(z_i^{(3)}) \\ &= \theta^T*\delta^{(n_l)} .*(a^{(3)}.*(1-a^{(3)})) \end{align} \]
根據梯度與殘差矩陣的關係可得:
\[\begin{align} \frac {\nabla J} {\nabla W^{(2)}} & =\frac 1 m \delta^{(3)}*a^{(2)} \\ \frac {\nabla J} {\nabla b^{(2)}} &=\frac 1 m\delta^{(3)} \end{align} \]
同理可求出
\[\begin{align} \frac {\nabla J} {\nabla W^{(1)}} & = \frac 1 m\delta^{(2)}*a^{(1)} \\ \frac {\nabla J} {\nabla b^{(1)}} &=\frac 1 m\delta^{(2)} \end{align} \]
這樣咱們就獲得了代價函數對\(W^{(1)}, b^{(1)}; W^{(2)}, b^{(2)}; \theta\)的梯度矩陣。能夠看到softmax是個特例外,多層隱藏層形式統一,這樣便於代碼循環實現,這裏對兩層隱藏層的推導只是爲了便於理解。
根據前面的步驟描述,複用原來的係數自編碼模塊外,咱們要增長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
數據集仍然來自Yann Lecun的筆跡數據庫。
設定與練習說明相同的參數,輸入層包含784個節點,第1、第二隱藏層都是196個節點,輸出層10個節點。運行代碼主文件stackAEExercise.m 能夠看到預測準確率達到97.77%。知足練習的標準結果。
咱們來比較一下微調先後隱藏層學習到的特徵有什麼變化。
逐層貪心訓練 | 微調後 | |
---|---|---|
第一隱層 | ||
第二隱層 | ||
softmax輸出層 |
相似稀疏自編碼對邊緣的學習,上圖的第一隱藏層特徵可理解爲筆記鉤旋弧線特徵,第二隱藏層就難以理解爲直觀的含義了,深層網絡不必定每一層都能對應到人腦對事物的一層理解上,此外微調後彷佛是增長了干擾,也期待大牛們能解釋一下這些變化!