前言: 本次是練習(xí)2個隱含層的網(wǎng)絡(luò)的訓(xùn)練方法,每個網(wǎng)絡(luò)層都是用的sparse autoencoder思想,利用兩個隱含層的網(wǎng)絡(luò)來提取出輸入數(shù)據(jù)的特征。本次實(shí)驗(yàn)驗(yàn)要完成的任務(wù)是對MINST進(jìn)行手寫數(shù)字識別,實(shí)驗(yàn)內(nèi)容及步驟參考網(wǎng)頁教程Exercise: Implement deep networks for digit classification。當(dāng)提取出手寫數(shù)字圖片的特征后,就用softmax進(jìn)行對其進(jìn)行分類。關(guān)于MINST的介紹可以參考網(wǎng)頁:MNIST Dataset。本文的理論介紹也可以參考前面的博文:Deep learning:十六(deep networks)。
實(shí)驗(yàn)基礎(chǔ): 進(jìn)行deep network的訓(xùn)練方法大致如下: 1. 用原始輸入數(shù)據(jù)作為輸入,訓(xùn)練出(利用sparse autoencoder方法)第一個隱含層結(jié)構(gòu)的網(wǎng)絡(luò)參數(shù),并將用訓(xùn)練好的參數(shù)算出第1個隱含層的輸出。 2. 把步驟1的輸出作為第2個網(wǎng)絡(luò)的輸入,用同樣的方法訓(xùn)練第2個隱含層網(wǎng)絡(luò)的參數(shù)。 3. 用步驟2 的輸出作為多分類器softmax的輸入,然后利用原始數(shù)據(jù)的標(biāo)簽來訓(xùn)練出softmax分類器的網(wǎng)絡(luò)參數(shù)。 4. 計(jì)算2個隱含層加softmax分類器整個網(wǎng)絡(luò)一起的損失函數(shù),以及整個網(wǎng)絡(luò)對每個參數(shù)的偏導(dǎo)函數(shù)值。 5. 用步驟1,2和3的網(wǎng)絡(luò)參數(shù)作為整個深度網(wǎng)絡(luò)(2個隱含層,1個softmax輸出層)參數(shù)初始化的值,然后用lbfs算法迭代求出上面損失函數(shù)最小值附近處的參數(shù)值,并作為整個網(wǎng)絡(luò)最后的最優(yōu)參數(shù)值。 上面的訓(xùn)練過程是針對使用softmax分類器進(jìn)行的,而softmax分類器的損失函數(shù)等是有公式進(jìn)行計(jì)算的。所以在進(jìn)行參數(shù)校正時(shí),可以對把所有網(wǎng)絡(luò)看做是一個整體,然后計(jì)算整個網(wǎng)絡(luò)的損失函數(shù)和其偏導(dǎo),這樣的話當(dāng)我們有了標(biāo)注好了的數(shù)據(jù)后,就可以用前面訓(xùn)練好了的參數(shù)作為初始參數(shù),然后用優(yōu)化算法求得整個網(wǎng)絡(luò)的參數(shù)了。但如果我們后面的分類器不是用的softmax分類器,而是用的其它的,比如svm,隨機(jī)森林等,這個時(shí)候前面特征提取的網(wǎng)絡(luò)參數(shù)已經(jīng)預(yù)訓(xùn)練好了,用該參數(shù)是可以初始化前面的網(wǎng)絡(luò),但是此時(shí)該怎么微調(diào)呢?因?yàn)榇藭r(shí)標(biāo)注的數(shù)值只能在后面的分類器中才用得到,所以沒法計(jì)算系統(tǒng)的損失函數(shù)等。難道又要將前面n層網(wǎng)絡(luò)的最終輸出等價(jià)于第一層網(wǎng)絡(luò)的輸入(也就是多網(wǎng)絡(luò)的sparse autoencoder)?本人暫時(shí)還沒弄清楚,日后應(yīng)該會想明白的。 關(guān)于深度網(wǎng)絡(luò)的學(xué)習(xí)幾個需要注意的小點(diǎn)(假設(shè)隱含層為2層):
另外在實(shí)際的訓(xùn)練過程中可以看到,訓(xùn)練第一個隱含層所用的時(shí)間較長,應(yīng)該需要訓(xùn)練的參數(shù)矩陣為200*784(沒包括b參數(shù)),訓(xùn)練第二個隱含層的時(shí)間較第一個隱含層要短些,主要原因是此時(shí)只需學(xué)習(xí)到200*200的參數(shù)矩陣,其參數(shù)個數(shù)大大減小。而訓(xùn)練softmax的時(shí)間更短,那是因?yàn)樗膮?shù)個數(shù)更少,且損失函數(shù)和偏導(dǎo)的計(jì)算公式也沒有前面兩層的復(fù)雜。最后對整個網(wǎng)絡(luò)的微調(diào)所用的時(shí)間和第二個隱含層的訓(xùn)練時(shí)間長短差不多。 程序中部分函數(shù): [params, netconfig] = stack2params(stack) 是將stack層次的網(wǎng)絡(luò)參數(shù)(可能是多個參數(shù))轉(zhuǎn)換成一個向量params,這樣有利用使用各種優(yōu)化算法來進(jìn)行優(yōu)化操作。Netconfig中保存的是該網(wǎng)絡(luò)的相關(guān)信息,其中netconfig.inputsize表示的是網(wǎng)絡(luò)的輸入層節(jié)點(diǎn)的個數(shù)。netconfig.layersizes中的元素分別表示每一個隱含層對應(yīng)節(jié)點(diǎn)的個數(shù)。 [ cost, grad ] = stackedAECost(theta, inputSize, hiddenSize, numClasses, netconfig,lambda, data, labels) 該函數(shù)內(nèi)部實(shí)現(xiàn)整個網(wǎng)絡(luò)損失函數(shù)和損失函數(shù)對每個參數(shù)偏導(dǎo)的計(jì)算。其中損失函數(shù)是個實(shí)數(shù)值,當(dāng)然就只有1個了,其計(jì)算方法是根據(jù)sofmax分類器來計(jì)算的,只需知道標(biāo)簽值和softmax輸出層的值即可。而損失函數(shù)對所有參數(shù)的偏導(dǎo)卻有很多個,因此每個參數(shù)處應(yīng)該就有一個偏導(dǎo)值,這些參數(shù)不僅包括了多個隱含層的,而且還包括了softmax那個網(wǎng)絡(luò)層的。其中softmax那部分的偏導(dǎo)是根據(jù)其公式直接獲得,而深度網(wǎng)絡(luò)層那部分這通過BP算法方向推理得到(即先計(jì)算每一層的誤差值,然后利用該誤差值計(jì)算參數(shù)w和b)。 stack = params2stack(params, netconfig) 和上面的函數(shù)功能相反,是吧一個向量參數(shù)按照深度網(wǎng)絡(luò)的結(jié)構(gòu)依次展開。 [pred] = stackedAEPredict(theta, inputSize, hiddenSize, numClasses, netconfig, data) 這個函數(shù)其實(shí)就是對輸入的data數(shù)據(jù)進(jìn)行預(yù)測,看該data對應(yīng)的輸出類別是多少。其中theta為整個網(wǎng)絡(luò)的參數(shù)(包括了分類器部分的網(wǎng)絡(luò)),numClasses為所需分類的類別,netconfig為網(wǎng)絡(luò)的結(jié)構(gòu)參數(shù)。 [h, array] = display_network(A, opt_normalize, opt_graycolor, cols, opt_colmajor) 該函數(shù)是用來顯示矩陣A的,此時(shí)要求A中的每一列為一個權(quán)值,并且A是完全平方數(shù)。函數(shù)運(yùn)行后會將A中每一列顯示為一個小的patch圖像,具體的有多少個patch和patch之間該怎么擺設(shè)是程序內(nèi)部自動決定的。 matlab內(nèi)嵌函數(shù): struct: s = sturct;表示創(chuàng)建一個結(jié)構(gòu)數(shù)組s。 nargout: 表示函數(shù)輸出參數(shù)的個數(shù)。 save: 比如函數(shù)save('saves/step2.mat', 'sae1OptTheta');則要求當(dāng)前目錄下有saves這個目錄,否則該語句會調(diào)用失敗的。
實(shí)驗(yàn)結(jié)果: 第一個隱含層的特征值如下所示:
第二個隱含層的特征值顯示不知道該怎么弄,因?yàn)榈诙€隱含層每個節(jié)點(diǎn)都是對應(yīng)的200維,用display_network這個函數(shù)去顯示的話是不行的,它只能顯示維數(shù)能夠開平方的那些特征,所以不知道是該將200弄成20*10,還是弄成16*25好,很好奇關(guān)于deep learning那么多文章中第二層網(wǎng)絡(luò)是怎么顯示的,將200分解后的顯示哪個具有代表性呢?待定。所以這里暫且不顯示,因?yàn)榻厝?00前面的196位用display_network來顯示的話,什么都看不出來:
沒有經(jīng)過網(wǎng)絡(luò)參數(shù)微調(diào)時(shí)的識別準(zhǔn)去率為: Before Finetuning Test Accuracy: 92.190% 經(jīng)過了網(wǎng)絡(luò)參數(shù)微調(diào)后的識別準(zhǔn)確率為: After Finetuning Test Accuracy: 97.670%
實(shí)驗(yàn)主要部分代碼及注釋: stackedAEExercise.m:
%% CS294A/CS294W Stacked Autoencoder Exercise % Instructions % ------------ % % This file contains code that helps you get started on the % sstacked autoencoder exercise. You will need to complete code in % stackedAECost.m % You will also need to have implemented sparseAutoencoderCost.m and % softmaxCost.m from previous exercises. You will need the initializeParameters.m % loadMNISTImages.m, and loadMNISTLabels.m files from previous exercises. % % For the purpose of completing the assignment, you do not need to % change the code in this file. % %%====================================================================== %% STEP 0: Here we provide the relevant parameters values that will % allow your sparse autoencoder to get good filters; you do not need to % change the parameters below. DISPLAY = true; inputSize = 28 * 28; numClasses = 10; hiddenSizeL1 = 200; % Layer 1 Hidden Size hiddenSizeL2 = 200; % Layer 2 Hidden Size sparsityParam = 0.1; % desired average activation of the hidden units. % (This was denoted by the Greek alphabet rho, which looks like a lower-case "p", % in the lecture notes). lambda = 3e-3; % weight decay parameter beta = 3; % weight of sparsity penalty term %%====================================================================== %% STEP 1: Load data from the MNIST database % % This loads our training data from the MNIST database files. % Load MNIST database files trainData = loadMNISTImages('train-images.idx3-ubyte'); trainLabels = loadMNISTLabels('train-labels.idx1-ubyte'); trainLabels(trainLabels == 0) = 10; % Remap 0 to 10 since our labels need to start from 1 %%====================================================================== %% STEP 2: Train the first sparse autoencoder % This trains the first sparse autoencoder on the unlabelled STL training % images. % If you've correctly implemented sparseAutoencoderCost.m, you don't need % to change anything here. % Randomly initialize the parameters sae1Theta = initializeParameters(hiddenSizeL1, inputSize); %% ---------------------- YOUR CODE HERE --------------------------------- % Instructions: Train the first layer sparse autoencoder, this layer has % an hidden size of "hiddenSizeL1" % You should store the optimal parameters in sae1OptTheta addpath minFunc/; options = struct; options.Method = 'lbfgs'; options.maxIter = 400; options.display = 'on'; [sae1OptTheta, cost] = minFunc(@(p)sparseAutoencoderCost(p,... inputSize,hiddenSizeL1,lambda,sparsityParam,beta,trainData),sae1Theta,options);%訓(xùn)練出第一層網(wǎng)絡(luò)的參數(shù) save('saves/step2.mat', 'sae1OptTheta'); if DISPLAY W1 = reshape(sae1OptTheta(1:hiddenSizeL1 * inputSize), hiddenSizeL1, inputSize); display_network(W1'); end % ------------------------------------------------------------------------- %%====================================================================== %% STEP 2: Train the second sparse autoencoder % This trains the second sparse autoencoder on the first autoencoder % featurse. % If you've correctly implemented sparseAutoencoderCost.m, you don't need % to change anything here. [sae1Features] = feedForwardAutoencoder(sae1OptTheta, hiddenSizeL1, ... inputSize, trainData); % Randomly initialize the parameters sae2Theta = initializeParameters(hiddenSizeL2, hiddenSizeL1); %% ---------------------- YOUR CODE HERE --------------------------------- % Instructions: Train the second layer sparse autoencoder, this layer has % an hidden size of "hiddenSizeL2" and an inputsize of % "hiddenSizeL1" % % You should store the optimal parameters in sae2OptTheta [sae2OptTheta, cost] = minFunc(@(p)sparseAutoencoderCost(p,... hiddenSizeL1,hiddenSizeL2,lambda,sparsityParam,beta,sae1Features),sae2Theta,options);%訓(xùn)練出第一層網(wǎng)絡(luò)的參數(shù) save('saves/step3.mat', 'sae2OptTheta'); figure; if DISPLAY W11 = reshape(sae1OptTheta(1:hiddenSizeL1 * inputSize), hiddenSizeL1, inputSize); W12 = reshape(sae2OptTheta(1:hiddenSizeL2 * hiddenSizeL1), hiddenSizeL2, hiddenSizeL1); % TODO(zellyn): figure out how to display a 2-level network % display_network(log(W11' ./ (1-W11')) * W12'); % W12_temp = W12(1:196,1:196); % display_network(W12_temp'); % figure; % display_network(W12_temp'); end % ------------------------------------------------------------------------- %%====================================================================== %% STEP 3: Train the softmax classifier % This trains the sparse autoencoder on the second autoencoder features. % If you've correctly implemented softmaxCost.m, you don't need % to change anything here. [sae2Features] = feedForwardAutoencoder(sae2OptTheta, hiddenSizeL2, ... hiddenSizeL1, sae1Features); % Randomly initialize the parameters saeSoftmaxTheta = 0.005 * randn(hiddenSizeL2 * numClasses, 1); %% ---------------------- YOUR CODE HERE --------------------------------- % Instructions: Train the softmax classifier, the classifier takes in % input of dimension "hiddenSizeL2" corresponding to the % hidden layer size of the 2nd layer. % % You should store the optimal parameters in saeSoftmaxOptTheta % % NOTE: If you used softmaxTrain to complete this part of the exercise, % set saeSoftmaxOptTheta = softmaxModel.optTheta(:); softmaxLambda = 1e-4; numClasses = 10; softoptions = struct; softoptions.maxIter = 400; softmaxModel = softmaxTrain(hiddenSizeL2,numClasses,softmaxLambda,... sae2Features,trainLabels,softoptions); saeSoftmaxOptTheta = softmaxModel.optTheta(:); save('saves/step4.mat', 'saeSoftmaxOptTheta'); % ------------------------------------------------------------------------- %%====================================================================== %% STEP 5: Finetune softmax model % Implement the stackedAECost to give the combined cost of the whole model % then run this cell. % Initialize the stack using the parameters learned stack = cell(2,1); %其中的saelOptTheta和sae1ptTheta都是包含了sparse autoencoder的重建層網(wǎng)絡(luò)權(quán)值的 stack{1}.w = reshape(sae1OptTheta(1:hiddenSizeL1*inputSize), ... hiddenSizeL1, inputSize); stack{1}.b = sae1OptTheta(2*hiddenSizeL1*inputSize 1:2*hiddenSizeL1*inputSize hiddenSizeL1); stack{2}.w = reshape(sae2OptTheta(1:hiddenSizeL2*hiddenSizeL1), ... hiddenSizeL2, hiddenSizeL1); stack{2}.b = sae2OptTheta(2*hiddenSizeL2*hiddenSizeL1 1:2*hiddenSizeL2*hiddenSizeL1 hiddenSizeL2); % Initialize the parameters for the deep model [stackparams, netconfig] = stack2params(stack); stackedAETheta = [ saeSoftmaxOptTheta ; stackparams ];%stackedAETheta是個向量,為整個網(wǎng)絡(luò)的參數(shù),包括分類器那部分,且分類器那部分的參數(shù)放前面 %% ---------------------- YOUR CODE HERE --------------------------------- % Instructions: Train the deep network, hidden size here refers to the ' % dimension of the input to the classifier, which corresponds % to "hiddenSizeL2". % % [stackedAEOptTheta, cost] = minFunc(@(p)stackedAECost(p,inputSize,hiddenSizeL2,... numClasses, netconfig,lambda, trainData, trainLabels),... stackedAETheta,options);%訓(xùn)練出第一層網(wǎng)絡(luò)的參數(shù) save('saves/step5.mat', 'stackedAEOptTheta'); figure; if DISPLAY optStack = params2stack(stackedAEOptTheta(hiddenSizeL2*numClasses 1:end), netconfig); W11 = optStack{1}.w; W12 = optStack{2}.w; % TODO(zellyn): figure out how to display a 2-level network % display_network(log(1 ./ (1-W11')) * W12'); end % ------------------------------------------------------------------------- %%====================================================================== %% STEP 6: Test % Instructions: You will need to complete the code in stackedAEPredict.m % before running this part of the code % % Get labelled test images % Note that we apply the same kind of preprocessing as the training set testData = loadMNISTImages('t10k-images.idx3-ubyte'); testLabels = loadMNISTLabels('t10k-labels.idx1-ubyte'); testLabels(testLabels == 0) = 10; % Remap 0 to 10 [pred] = stackedAEPredict(stackedAETheta, inputSize, hiddenSizeL2, ... numClasses, netconfig, testData); acc = mean(testLabels(:) == pred(:)); fprintf('Before Finetuning Test Accuracy: %0.3f%%\n', acc * 100); [pred] = stackedAEPredict(stackedAEOptTheta, inputSize, hiddenSizeL2, ... numClasses, netconfig, testData); acc = mean(testLabels(:) == pred(:)); fprintf('After Finetuning Test Accuracy: %0.3f%%\n', acc * 100); % Accuracy is the proportion of correctly classified images % The results for our implementation were: % % Before Finetuning Test Accuracy: 87.7% % After Finetuning Test Accuracy: 97.6% % % If your values are too low (accuracy less than 95%), you should check % your code for errors, and make sure you are training on the % entire data set of 60000 28x28 training images % (unless you modified the loading code, this should be the case)
stackedAECost.m:
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 %% Unroll softmaxTheta parameter % 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)); for d = 1:numel(stack) stackgradnx55hf5.w = zeros(size(stackp5rrvl5.w)); stackgradnlnltvt.b = zeros(size(stack15dxhf5.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)); %% --------------------------- YOUR CODE HERE ----------------------------- % Instructions: Compute the cost function and gradient vector for % the stacked autoencoder. % % You are given a stack variable which is a cell-array of % the weights and biases for every layer. In particular, you % can refer to the weights of Layer d, using stackj55h5vv.w and % the biases using stackz1r5v5f.b . To get the total number of % layers, you can use numel(stack). % % The last layer of the network is connected to the softmax % classification layer, softmaxTheta. % % You should compute the gradients for the softmaxTheta, % storing that in softmaxThetaGrad. Similarly, you should % compute the gradients for each layer in the stack, storing % the gradients in stackgradzdlffh5.w and stackgrad5djb55x.b % Note that the size of the matrices in stackgrad should % match exactly that of the size of the matrices in stack. % depth = numel(stack); z = cell(depth 1,1); a = cell(depth 1, 1); a{1} = data; for layer = (1:depth) z{layer 1} = stack{layer}.w * a{layer} repmat(stack{layer}.b, [1, size(a{layer},2)]); a{layer 1} = sigmoid(z{layer 1}); end M = softmaxTheta * a{depth 1}; M = bsxfun(@minus, M, max(M)); p = bsxfun(@rdivide, exp(M), sum(exp(M))); cost = -1/numClasses * groundTruth(:)' * log(p(:)) lambda/2 * sum(softmaxTheta(:) .^ 2); softmaxThetaGrad = -1/numClasses * (groundTruth - p) * a{depth 1}' lambda * softmaxTheta; d = cell(depth 1); d{depth 1} = -(softmaxTheta' * (groundTruth - p)) .* a{depth 1} .* (1-a{depth 1}); for layer = (depth:-1:2) d{layer} = (stack{layer}.w' * d{layer 1}) .* a{layer} .* (1-a{layer}); end for layer = (depth:-1:1) stackgrad{layer}.w = (1/numClasses) * d{layer 1} * a{layer}'; stackgrad{layer}.b = (1/numClasses) * sum(d{layer 1}, 2); end % ------------------------------------------------------------------------- %% Roll gradient vector grad = [softmaxThetaGrad(:) ; stack2params(stackgrad)]; end % You might find this useful function sigm = sigmoid(x) sigm = 1 ./ (1 exp(-x)); end
stackedAEPredict.m:
function [pred] = stackedAEPredict(theta, inputSize, hiddenSize, numClasses, netconfig, data) % stackedAEPredict: Takes a trained theta and a test data set, % and returns the predicted labels for each example. % 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 % data: Our matrix containing the training data as columns. So, data(:,i) is the i-th training example. % Your code should produce the prediction matrix % pred, where pred(i) is argmax_c P(y(c) | x(i)). %% Unroll theta parameter % 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); %% ---------- YOUR CODE HERE -------------------------------------- % Instructions: Compute pred using theta assuming that the labels start % from 1. depth = numel(stack); z = cell(depth 1,1); a = cell(depth 1, 1); a{1} = data; for layer = (1:depth) z{layer 1} = stack{layer}.w * a{layer} repmat(stack{layer}.b, [1, size(a{layer},2)]); a{layer 1} = sigmoid(z{layer 1}); end [~, pred] = max(softmaxTheta * a{depth 1});%閫夋鐜囨渶澶х殑閭d釜杈撳嚭鍊? % ----------------------------------------------------------- end % You might find this useful function sigm = sigmoid(x) sigm = 1 ./ (1 exp(-x)); end
參考資料: Exercise: Implement deep networks for digit classification Deep learning:十六(deep networks)
|
|