做業文件:git
在以前的練習中,咱們已經實現了神經網絡的前反饋傳播算法,而且使用這個算法經過做業給的參數值預測了手寫體數字。這個練習中,咱們將實現反響傳播算法來學習神經網絡的參數。數組
這一節的代碼將會加載數據,而且以二維的格式展示出來。運行代碼會將訓練集加載到變量X與y中。網絡
load('ex4data1.mat'); m = size(X, 1); % Randomly select 100 data points to display sel = randperm(size(X, 1)); sel = sel(1:100); displayData(X(sel, :));
在ex4data1.mat中有5000個訓練樣本,每一個樣本是有關數字的20*20的像素的灰度圖像。每一個像素表明灰度相應位置的灰度強度。這20*20的灰度被展開成1*400的向量。每一個訓練樣本變成了單獨的一行,在咱們的X矩陣中。因此訓練集爲一個5000*400的矩陣,其中每一行表示一個手寫體數字圖。app
訓練集的第二部分是一個5000*1的向量y,表明這個訓練集的標籤。爲了更適應MATLAB沒有0號的索引的狀況,咱們用標籤10表明手寫體數字0。 標籤1-9表示手寫體數字1-9。dom
圖爾表示咱們的神經網絡。包含三層,一個輸入層,一個隱藏層和一個輸出層。咱們的輸入爲手寫體數字圖的像素,由於圖的大小爲20*20,因此輸入層有400個單元,(不包括額外的偏置單元)koa
做業已經提供了訓練好的網絡的參數,theta1,theta2。存儲在 ex4weights.mat中,運行下面的代碼加載theta1與theta2。參數維度的大小與每層的神經元的個數有關。函數
% Load the weights into variables Theta1 and Theta2 load('ex4weights.mat');
咱們如今將會實現神經網絡的代價函數與梯度。首先,在nnCostFunction.m文件中完成代碼返回代價值。回憶神經網絡的代價函數(沒有正則化)是:學習
的計算如圖二所示,K=10表示可能的標籤的數目。注意是第K個輸出單元的激活值(輸出值)。想起原始標籤(在變量y中)是1-10。爲了訓練神經網絡,咱們須要將y設置只包含0或1的狀況,像這樣。測試
舉個例子,若是xi是數字5的圖像,所以對應的yi(咱們使用代價函數計算的值)應該爲一個10維的向量,其中y5=1其餘的元素值爲0。咱們應該實現前反饋傳而後計算每一個樣本i的,而且將全部的代價值加在一塊兒。咱們的代碼應該適應任意大小的數據集,有任意數量的標籤(咱們能夠假定至少有三個以上的標籤。
實現說明:矩陣X包含的每一個樣本在每一行中。好比(X(i,:))是第i個訓練樣本。當咱們在nnCostFunction.m中實現代碼的時候。咱們應該爲X添加一列1。神經網絡的每一個參數以行的形式在Theta1與Theta2中表示。具體來講,Theta1的第一行表示第二層的第一個隱藏單元。咱們可使用循環遍歷每一個樣原本計算代價。推薦第一次實現前反饋傳播不要使用正則化。這樣會容易調試。只有咱們將會實現正則化代價函數。
若是咱們完成了代碼,調用 nnCostFunction使用已經給好的Theta1與Theta2咱們應該看到代價值應該爲 0.287629
nnCostFunction.m文件中填寫代碼:
X = [ones(m,1),X];
for i = 1:m x = X(i,:); for j = 1:num_labels y1 = y(i)==j; z2 = x*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [1,a2]; z3 = a2*Theta2(j,:)'; a3 = 1./(1+exp(-z3)); J = J+(1/m)*(-y1*log(a3)-(1-y1)*log(1-a3)); end end
疑問,第一次填寫的代碼爲下面部分,本地測試沒問題,提交做業提示 c(i,:) = (1:10)維度問題不能賦值。
X = [ones(m,1),X]; z2 = X*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [ones(m,1),a2]; z3 = a2*Theta2'; a3 = 1./(1+exp(-z3)); c = zeros(size(a3)); for i = 1:m c(i,:) = (1:10); c(i,:) = c(i,:)==y(i); end J = (-c.*log(a3)-(1-c).*log(1-a3)); J = (1/m)*sum(J(:));
運行下面代碼,結果輸出 0.287629
input_layer_size = 400; % 20x20 Input Images of Digits hidden_layer_size = 25; % 25 hidden units num_labels = 10; % 10 labels, from 1 to 10 (note that we have mapped "0" to label 10) % Unroll parameters nn_params = [Theta1(:) ; Theta2(:)]; % Weight regularization parameter (we set this to 0 here). lambda = 0; J = nnCostFunction(nn_params, input_layer_size, hidden_layer_size, num_labels, X, y, lambda); fprintf('Cost at parameters (loaded from ex4weights): %f', J);
神經網絡的正則化的代價函數爲:
咱們能夠假定神經網絡只有三層,一個輸入層,一個隱藏層,和一個輸出層。然而咱們的代碼應該對於任意的輸入單元,隱藏單元,和輸出單元都適應。雖然咱們已經具體指明瞭Theta1與Theta2。
注意咱們不該該正則化偏執單元參數。對於矩陣Theta1和Theta2來講。不該該正則化這兩個矩陣的第一列。咱們如今應該爲咱們的代價函數添加正則化。咱們已經在 nnCostFunction.m完成了非正則化的代價函數,所以能夠在此文件中直接添加正則化項。咱們完成後,運行下面代碼,使用已經給定的參數Thtea1與Theta2,與lambda=1.咱們應該看待代價值爲0.383770
添加正則化項後,nnCostFunction.m中的代碼爲:
for i = 1:m x = X(i,:); for j = 1:num_labels y1 = y(i)==j; z2 = x*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [1,a2]; z3 = a2*Theta2(j,:)'; a3 = 1./(1+exp(-z3)); J = J+(1/m)*(-y1*log(a3)-(1-y1)*log(1-a3)); end end J1 = Theta1(:,2:end).^2; J2 = Theta2(:,2:end).^2; J = J+lambda/(2*m)*(sum(J1(:))+sum(J2(:)));
運行下面代碼咱們看到結果爲 0.383770
% Weight regularization parameter (we set this to 1 here). lambda = 1; J = nnCostFunction(nn_params, input_layer_size, hidden_layer_size, num_labels, X, y, lambda); fprintf('Cost at parameters (loaded from ex4weights): %f', J);
在這一節咱們將會實現經過反向傳播算法來計算神經網絡的代價函數的梯度。咱們應該在 nnCostFunction.m文件中完成代碼,返回梯度的正確值。一旦咱們完成梯度。咱們就能夠高級優化器如fmicg來最小化代價函數訓練神經網絡。
咱們首先實現反向傳播算法計算未正則化的神經網絡的參數的梯度。在驗證咱們計算的未正則化的梯度是正確的以後,咱們將會實現正則化的神經網絡的梯度。
爲了幫助我方開始本節的計算,咱們將會首先實現sigmoid梯度函數,sigmoid函數的梯度能夠經過下面式子計算。
其中
當咱們完成代碼後,試着經過調用sigmoidGradient(z)來測試一些數。對於大的數(不管是正的仍是負的),梯度的值應該接近0,當z=0的時候,梯度值應該是0.25。咱們的代碼應該對不管是矩陣仍是向量,都應該起做用。對於矩陣,咱們的函數應該對每個元素都執行。
sigmoidGradient.m中填寫代碼
g = sigmoid(z).*(1-sigmoid(z));
當咱們訓練神經網絡的時候,進行隨機初始化來來打破對稱性是很是有必要的。
爲何要打破對稱性,能夠參考知乎用戶回答:
爲何神經網絡在考慮梯度降低的時候,網絡參數的初始值不能設定爲全0,而是要採用隨機初始化思想? - koala tree的回答 - 知乎 https://www.zhihu.com/question/36068411/answer/95670563
還有個回答是:
設想你在登山,但身處直線形的山谷中,兩邊是對稱的山峯。
因爲對稱性,你所在之處的梯度只能沿着山谷的方向,不會指向山峯;你走了一步以後,狀況依然不變。
結果就是你只能收斂到山谷中的一個極大值,而走不到山峯上去。
這個回答暫時不理解先存疑。
進行隨機初始化參數一個頗有效的方法是隨機選擇theta的值均勻分佈在中。咱們應該使用。這個範圍的值保證了參數值是小的,而且是學習更有效率。
咱們的任務是完成randInitializeWeights.m文件,來能夠初始化權重theta。將下面代碼粘貼到文件中。
% Randomly initialize the weights to small values epsilon_init = 0.12; W = rand(L out, 1 + L in) * 2 * epsilon_init - epsilon init;
當咱們完成時,運行下面代碼來調用randInitialWeights來初始化神經網絡參數。
initial_Theta1 = randInitializeWeights(input_layer_size, hidden_layer_size); initial_Theta2 = randInitializeWeights(hidden_layer_size, num_labels); % Unroll parameters initial_nn_params = [initial_Theta1(:) ; initial_Theta2(:)];
一個有效的策略選擇是基於網絡神經元的個數。的一個好的選擇是
其中,是與相鄰的層的神經元的個數。
如今咱們將會實現反向傳播算法。想起反向傳播算法的只管理解以下。給定一個訓練樣本,咱們首先須要執行向前傳播來計算神經網絡的全部激活值。包括假設函數的輸出值。以後對於每一層l的每一個節點j。咱們通常計算一個「偏差項」,來衡量一個節點值對輸出的全部錯誤的影響程度。
對於輸出節點,咱們能夠直接計算網絡的激活值與真實目標值的差值。而且定義爲,由於第3層就是輸出層。對於隱藏層,咱們計算的是第l+1層的節點偏差項的加權平均值。
具體來講,下面是反向傳播算法(也如圖3所示)。咱們應該在一個循環中實現第1-4步來一次出處理一個樣本。具體來講,咱們應該實現對於一個循環 for t = 1:m,步驟1-4在帶這個循環內。對於第i次迭代執行的是處理第i個訓練樣本(xi,yi)。第5步是用m除累加的梯度值,來得到此神經網絡價值函數的梯度值。
對於隱藏層l=2,令
對每一個樣本累加梯度值經過下面這個式子:,注意咱們應該跳過或者移除。在MATLAB中移除對應delta_2 = delta_2(2:end)。
或者神經網絡代價函數(未正則化)梯度值經過,用m除累加的梯度。
當實現反向傳播算法時候,若是出現維度不匹配狀況,使用size函數來打印矢量大小是很是有幫助的。
當咱們實現反向傳播算法,下一節的代碼將會執行梯度檢測,梯度檢測會使咱們對本身寫的梯度代碼有信心。
在nnCostFunction.m文件中添加梯度後:
X = [ones(m,1),X]; Delta1 = zeros(size(Theta1)); Delta2 = zeros(size(Theta2)); for i = 1:m x = X(i,:); for j = 1:num_labels y1 = y(i)==j; z2 = x*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [1,a2]; z3 = a2*Theta2(j,:)'; a3 = 1./(1+exp(-z3)); J = J+(1/m)*(-y1*log(a3)-(1-y1)*log(1-a3)); end end J1 = Theta1(:,2:end).^2; J2 = Theta2(:,2:end).^2; J = J+lambda/(2*m)*(sum(J1(:))+sum(J2(:))); for i = 1:m x = X(i,:); for j = 1:num_labels y1 = y(i)==j; z2 = x*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [1,a2]; z3 = a2*Theta2(j,:)'; a3 = 1./(1+exp(-z3)); delta3(j) = a3-y1; end delta2 = (delta3*Theta2(:,2:end)).*sigmoidGradient(z2); Delta1 = Delta1+delta2'*x; Delta2 = Delta2+delta3'*a2; end Theta1_grad = 1/m*Delta1; Theta2_grad = 1/m*Delta2;
在咱們的神經網絡中,咱們最小化代價函數爲了執行梯度檢測,咱們想象能夠把參數Theta1與Theta2展開成一個長向量。經過這樣作。咱們能夠認爲代價函數是,並使用它來執行梯度檢測。
加入咱們有一個函數據稱能夠計算,咱們想驗證是否輸出了正確的偏導數。
咱們能夠驗證函數的正確性,對於每個i
咱們已經在computeNumericalGradient.m實現了這個方法來計算數值梯度。咱們不須要修改這個文件,可是做業鼓勵咱們看看這個代碼,來了解他是怎麼工做的。
運行下面代碼,將會建立一個小型的神經網絡和數據集,來檢測咱們的梯度。若是咱們的梯度值是正確的咱們應該會看到相對差距應該小於
當執行梯度檢測的時候,使用有數量相對少的輸入單元與隱藏單元的神經網絡是更有效率的。由於有更少的參數。Theta的每一個維度都須要計算兩次代價函數,成本很好。在checkNNGradients,方法中,咱們的代碼建立了一個小的隨機模型和數據集,在computeNumericalGradient中來進行梯度檢測。當咱們肯定咱們的梯度計算是正確的以後,咱們應該在學習算法中關掉梯度檢測。
梯度檢測對任意的咱們計算的代價與梯度都使用。具體來講咱們可使用相同的computeNumericalGradient.m函數來檢測是否咱們在其餘章節實現的梯度是否是正確的。
在咱們完成反向傳播算法後,咱們將會爲咱們的梯度加上正則化。能夠證實咱們能夠爲使用反向傳播計算梯度的時候咱們能夠加上額外項。
需注意咱們不行該正則化的第一列。如今修改 nnCostFunction.m中的代碼,考慮正則化。運行下面代碼。若是咱們的代碼正確。咱們應該看到相對差距應該小於1e-9
nnCostFunction.m中的代碼:
X = [ones(m,1),X]; Delta1 = zeros(size(Theta1)); Delta2 = zeros(size(Theta2)); for i = 1:m x = X(i,:); for j = 1:num_labels y1 = y(i)==j; z2 = x*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [1,a2]; z3 = a2*Theta2(j,:)'; a3 = 1./(1+exp(-z3)); J = J+(1/m)*(-y1*log(a3)-(1-y1)*log(1-a3)); end end J1 = Theta1(:,2:end).^2; J2 = Theta2(:,2:end).^2; J = J+lambda/(2*m)*(sum(J1(:))+sum(J2(:))); for i = 1:m x = X(i,:); for j = 1:num_labels y1 = y(i)==j; z2 = x*Theta1'; a2 = 1./(1+exp(-z2)); a2 = [1,a2]; z3 = a2*Theta2(j,:)'; a3 = 1./(1+exp(-z3)); delta3(j) = a3-y1; end delta2 = (delta3*Theta2(:,2:end)).*sigmoidGradient(z2); Delta1 = Delta1+delta2'*x; Delta2 = Delta2+delta3'*a2; end Theta1_grad = 1/m*Delta1; Theta2_grad = 1/m*Delta2; Theta1_grad(:,2:end) = Theta1_grad(:,2:end)+lambda/m*Theta1(:,2:end); Theta2_grad(:,2:end) = Theta2_grad(:,2:end)+lambda/m*Theta2(:,2:end);
上面任務都完成後,運行下面代碼使用fmincg來學習一組好的參數。當訓練完成後。代碼將會告訴咱們分類器的精度。若是咱們以前作的都正確的話,咱們應該看到訓練精度應該爲95.3%。可能由於隨機初始化的緣由變化1%。若是迭代更屢次的話,可能會得到更高的精度。
options = optimset('MaxIter', 50); lambda = 1; % Create "short hand" for the cost function to be minimized costFunction = @(p) nnCostFunction(p, input_layer_size, hidden_layer_size, num_labels, X, y, lambda); % Now, costFunction is a function that takes in only one argument (the % neural network parameters) [nn_params, ~] = fmincg(costFunction, initial_nn_params, options); % Obtain Theta1 and Theta2 back from nn_params Theta1 = reshape(nn_params(1:hidden_layer_size * (input_layer_size + 1)), hidden_layer_size, (input_layer_size + 1)); Theta2 = reshape(nn_params((1 + (hidden_layer_size * (input_layer_size + 1))):end), num_labels, (hidden_layer_size + 1)); pred = predict(Theta1, Theta2, X); fprintf('\nTraining Set Accuracy: %f\n', mean(double(pred == y)) * 100);
理解咱們神經網絡學習的內容一種方法是可視化咱們的隱藏層捕獲到了什麼。通常來講。給定一個隱藏層單元,可視化其計算內容的一種方法是找到將致使其激活的輸入x。(也就是說計算的激活值接近1)。對於咱們訓練到的神經網絡來講。注意的第i行是一個401維的向量,表明第i個隱藏層的參數。若是咱們不考慮偏置項,咱們能夠獲得一個1*400的向量,每一個維度值表明輸入樣本的每一個像素的權重。
所以,可視化隱藏單元捕獲的「特徵」的一種方法是將這個400維向量從新整形爲20 x 20圖像並顯示它
下面的代碼將會使用displayData函數,而且使用25個單元爲咱們展現圖像。每個都對應網絡的隱藏層的單元。咱們訓練的網絡中。咱們應該會發現每一個隱藏層的單元粗略的表示輸入層中相應筆畫,和其餘圖案的探測器。
% Visualize Weights displayData(Theta1(:, 2:end));
因此就能夠證實,至關於經過必定約束,在輸入層找到一個輸入,爲隱藏層提供一個最高的激活值。
在這一部分的練習。咱們嘗試爲咱們的神經網絡設置不一樣學習參數(正則化參數,訓練次數),來看咱們的神經網絡的表現的變化。神經網絡是很強大的模型能夠造成很是複雜的決策邊界。沒有正則化神經網絡極可能會過分擬合,對於給定的訓練集得到100%精度。可是預測的話不會有很好的表現。如今咱們能夠設置正則化參數,與迭代次數。來觀察識別精度。
% Change lambda and MaxIter to see how it affects the result lambda = 3; MaxIter = 25; options = optimset('MaxIter', MaxIter); % Create "short hand" for the cost function to be minimized costFunction = @(p) nnCostFunction(p,input_layer_size, hidden_layer_size, num_labels, X, y, lambda); % Now, costFunction is a function that takes in only one argument (the neural network parameters) [nn_params, ~] = fmincg(costFunction, initial_nn_params, options); % Obtain Theta1 and Theta2 back from nn_params Theta1 = reshape(nn_params(1:hidden_layer_size * (input_layer_size + 1)), hidden_layer_size, (input_layer_size + 1)); Theta2 = reshape(nn_params((1 + (hidden_layer_size * (input_layer_size + 1))):end), num_labels, (hidden_layer_size + 1)); pred = predict(Theta1, Theta2, X); fprintf('\nTraining Set Accuracy: %f\n', mean(double(pred == y)) * 100); % Visualize Weights displayData(Theta1(:, 2:end));