自動編碼器模型和程式碼解釋
CNN演算法與程式研究
1) 深度學習基本理論方法
http://wenku.baidu.com/view/2e630ddfc5da50e2524d7ff3
特徵多,給出的資訊多,識別準確性會提升。
但是,計算複雜度增加,搜尋的空間大,可以用來訓練的資料在每個特徵上就會稀疏。
採用層次網路結構,
BP一層隱層節點的淺層模型,帶有一層隱層節點(如SVM、Boosting)
對複雜函式的表示能力和泛化能力有限
深度學習指的是:多隱層的人工神經網路具有優異的特徵學習能力,學習得到的特徵對資料有更本質的刻畫,從而有利於視覺化或分類;深度神經網路在訓練上的難度,可以通過“逐層初始化”(layer-wise pre-training)來有效克服,逐層初始化可通過無監督學習實現的。
與淺層學習區別:
1)強調了模型結構的深度,通常有5-10多層的隱層節點;
2)明確突出了特徵學習的重要性,通過逐層特徵變換,將樣本在原空間的特徵表示變換到一個新特徵空間,從而使分類或預測更加容易。與人工規則構造特徵的方法相比,利用大資料來學習特徵,更能夠刻畫資料的豐富內在資訊。
可通過學習一種深層非線性網路結構,實現複雜函式逼近,表徵輸入資料分散式表示
不同點:
神經網路:採用BP演算法調整引數,即採用迭代演算法來訓練整個網路。隨機設定初值,計算當前網路的輸出,然後根據當前輸出和樣本真實標籤之間的差去改變前面各層的引數,直到收斂;
深度學習:採用逐層訓練機制。採用該機制的原因在於如果採用BP機制,對於一個deep network(7層以上),殘差傳播到最前面的層將變得很小,出現所謂的gradient diffusion(梯度擴散)----【梯度擴散問題。因為當網路層次比較深時,在計算損失函式的偏導時一般需要使用BP演算法,但是這些梯度值隨著深度慢慢靠前而顯著下降,這樣導致前面的網路對最終的損失函式的貢獻很小。這樣的話前面的權值更新速度就非常非常慢了】
不採用BP演算法的原因
(1)反饋調整時,梯度越來越稀疏,從頂層越往下,誤差校正訊號越來越小;
(2)收斂易至區域性最小,由於是採用隨機值初始化,當初值是遠離最優區域時易導致這一情況;
(3)BP演算法需要有標籤資料來訓練,但大部分資料是無標籤的
深度學習訓練過程
第一步:採用自下而上的無監督學習
1)逐層構建單層神經元。
2)每層採用wake-sleep演算法進行調優。每次僅調整一層,逐層調整。
這個過程可以看作是一個featurelearning的過程,是和傳統神經網路區別最大的部分。
wake-sleep演算法:
1)wake階段:
認知過程,通過下層的輸入特徵(Input)和向上的認知(Encoder)權重產生每一層的抽象表示(Code),再通過當前的生成(Decoder)權重產生一個重建資訊(Reconstruction),計算輸入特徵和重建資訊殘差,使用梯度下降修改層間的下行生成(Decoder)權重。也就是“如果現實跟我想象的不一樣,改變我的生成權重使得我想象的東西變得與現實一樣”。
2)sleep階段:
生成過程,通過上層概念(Code)和向下的生成(Decoder)權重,生成下層的狀態,再利用認知(Encoder)權重產生一個抽象景象。利用初始上層概念和新建抽象景象的殘差,利用梯度下降修改層間向上的認知(Encoder)權重。也就是“如果夢中的景象不是我腦中的相應概念,改變我的認知權重使得這種景象在我看來就是這個概念”。
第二步:自頂向下的監督學習
這一步是在第一步學習獲得各層引數進的基礎上,在最頂的編碼層新增一個分類器(例如羅傑斯特迴歸、SVM等),而後通過帶標籤資料的監督學習,利用梯度下降法去微調整個網路引數。
深度學習的第一步實質上是一個網路引數初始化過程。區別於傳統神經網路初值隨機初始化,深度學習模型是通過無監督學習輸入資料的結構得到的,因而這個初值更接近全域性最優,從而能夠取得更好的效果。
自動編碼器( AutoEncoder )
稀疏自動編碼器
稀疏自動編碼器(SparseAutoEncoder)
1)Training階段:給定一系列的樣本圖片[x1, x 2, …],我們需要學習得到一組基[Φ1, Φ2, …],也就是字典。
可使用K-SVD方法交替迭代調整a [k],Φ [k],直至收斂,從而可以獲得一組可以良好表示這一系列x的字典。
2)Coding階段:給定一個新的圖片x,由上面得到的字典,利用OMP演算法求解一個LASSO問題得到稀疏向量a。這個稀疏向量就是這個輸入向量x的一個稀疏表達。
降噪自動編碼器(Denoising AutoEncoders)
在自動編碼器的基礎上,對訓練資料加入噪聲,自動編碼器必須學習去去除這種噪聲而獲得真正的沒有被噪聲汙染過的輸入。因此,這就迫使編碼器去學習輸入訊號的更加魯棒的表達,這也是它的泛化能力比一般編碼器強的原因
限制波爾茲曼機(RestrictedBoltzmann Machine)
假設有一個二部圖,同層節點之間沒有連結,一層是可視層,即輸入資料層(v),一層是隱藏層(h),如果假設所有的節點都是隨機二值( 0,1值)變數節點,同時假設全概率分佈p(v,h)滿足Boltzmann 分佈,我們稱這個模型是Restricted BoltzmannMachine (RBM)。
4.主函式程式碼:test_sae.m
clear all; closeall; clc;
%% //匯入資料
loadmnist_uint8;
train_x =double(train_x)/255;
test_x =double(test_x)/255;
train_y =double(train_y);
test_y =double(test_y);
%%一:採用autoencoder進行預訓練
rng(0);%高版本的matlab可以使用這個語句,低版本會出錯
sae =saesetup([784 200 100]);
sae.ae{1}.activation_function ='sigm';
sae.ae{1}.learningRate =1;
sae.ae{1}.inputZeroMaskedFraction =0.;
sae.ae{2}.activation_function ='sigm';
sae.ae{2}.learningRate =1;
sae.ae{2}.inputZeroMaskedFraction =0.;
opts.numepochs= 1;
opts.batchsize =100;
visualize(sae.ae{1}.W{1}(:,2:end)')
%二:fine_tuning過程
% Use the SDAEto initialize a FFNN
nn =nnsetup([784 200 100 10]);
nn.activation_function ='sigm';
nn.learningRate =1;
%add pretrained weights
nn.W{1} =sae.ae{1}.W{1};
nn.W{2} =sae.ae{2}.W{1};
% Train the FFNN
opts.numepochs= 1;
opts.batchsize =100;
nn = nntrain(nn,train_x, train_y, opts);
[er, bad] =nntest(nn, test_x, test_y);
str =sprintf('testing error rate is: %f',er);
disp(str)
三:pre_training階段程式碼詳解
3.1:網路結構建立
Sae網路就是堆疊多個autoencoder網路,所以此網路結構由2個autoencoder構成,分別是:v—h1—v和h1—h2—h1.
此處主要利用saesetup函式來實現,
3.1.1 Saesetup函式說明
輸入引數:size為構建網路的節點向量,由於此處pre_training階段的網路為784-200-100;所以size=[784 200 100]
輸出引數:sae元包矩陣,每一個元包矩陣對應一個autoencoder網路;例如sae.ae{1}中“存放”的就是v——h1——v這個autoencoder;
函式體說明:通過for迴圈2次,呼叫nnsetup函式兩次,生成兩個autoencoder
function sae=saesetup(size)
for u = 2:numel(size)
sae.ae{u-1}= nnsetup([size(u-1) size(u) size(u-1)]);
end
end
autoencoder構建:我們知道autoencoder就是一個v——h——v 的3層神經網路;所以主要通過nnsetup來構建。
3.1.2 nnsetup函式說明
輸入引數:nnsetup(architecture),architecture,顧名思義,就是構建網路的結構,也就是每層網路的神經元節點個數,例如本題為第一個autoencoder結構為v—h1—v [784 200 784]
輸出引數nn:一個元包矩陣,元包矩陣中存放著一個autoencoder網路的各種配置引數
函式體說明:
Encoder過程:y=f(xw'+b)
啟用函式nn.activation_function:f(),一般有sigmoid和tanh兩種;
學習率 nn.learningRate:控制學習的速度,
動量項 nn.momentum:調節在應用minibatch方法訓練網路時,每次新權值更新值和上一次權值更行值的相對比例;
權值懲罰項 nn.weightPenaltyL2:防止權值過大,發生過擬合現象;
稀疏目標 nn.sparsityTarget:通過控制隱層啟用值的平均值,來控制隱層啟用值的稀疏性
Decoder過程:x=g(wy+b)
解碼啟用函式設定:sigmoid和tanh兩種
其他引數:例如dae中的加噪比例引數,dropout中的隱層節點的忽略比例等
權值W初始化:
W結構初始化:權值矩陣W的行列數,例如若使用:h=f(x*w'+b),本例中第一個autoencoder中,權值矩陣w1為200*785 ,(785=784+1,1為偏置項b,後面介紹)
W元素初始化:W的元素初始值為一個0均值的隨機矩陣,權值係數很小,這樣可以防止過擬合
nn.W{i - 1} =(a*b);
a=rand(nn.size(i),nn.size(i - 1)+1) - 0.5)%生產一個0均值,範圍元素值在[-0.5 0.5]範圍內的矩陣
b=2 * 4 * sqrt(6 /(nn.size(i) + nn.size(i - 1))%縮減矩陣元素值到一個更小的範圍
3.2:網路訓練階段
此階段主要呼叫saetrain函式來訓練網路;
3.2.1 Seatrain函式說明
輸入引數:saetrain(sae, x, opts)
Sae為step1階段設定好的網路結構;x為輸入資料;opts中有兩個引數;其中opts.numepochs為訓練次數,所有樣本一共訓練多少次;opts.batchsize引數,為在對所有樣本進行minibatch訓練時,每個batch的容量,即每個batch的樣本個數。
輸出引數:訓練好的元包矩陣sae
函式體說明:通過for迴圈,呼叫nntrain函式,依次訓練每個autoencoder。
3.2.2 Nntrain函式說明:
輸入引數:nntrain(nn, train_x, train_y, opts, val_x, val_y)
其中nn為一個元包矩陣,nn中存放的是網路的配置引數;train_x,train_y就是輸入資料,和目標資料;在autoencoder網路中,都是輸入資料x,opts引數,已經說過;至於val_x和val_y做什麼的不清楚,訓練中暫時用不到。
輸出引數:[nn, L],已經訓練好的網路引數,主要訓練W和b,L為損失函式值,在autoencoder中是重構誤差。
函式體說明:
1.minibatch部分
所謂的minibatch訓練方法就是把所有訓練樣本分成多個batch,然後依次訓練每個batch中的樣本。
將訓練集事先分成包含幾十或幾百個樣本的小批量資料進行計算將更高效,這主要是可以利用圖形處理器Gpu和matlab中矩陣之間相乘運算的優勢。
m = size(train_x,1);%提取樣本總數
batchsize =opts.batchsize; %每個batch中樣本個數
numepochs =opts.numepochs; %所有樣本訓練次數
numbatches = m /batchsize; %所有樣本分成多少個batch
2.訓練過程
for i = 1 : numepochs%所有樣本訓練次數
kk=randperm(m); %形成樣本編號的隨機向量
for l = 1 : numbatches%每次提取batchsize個樣本,一共提取numbatches次
batch_x =train_x(kk((l - 1) * batchsize + 1 : l * batchsize), :);
batch_y =train_y(kk((l - 1) * batchsize + 1 : l * batchsize), :);
nn =nnff(nn, batch_x, batch_y); %前饋計算網路
nn =nnbp(nn);%計算誤差和權值梯度
nn =nnapplygrads(nn);%引數更新
L(n) =nn.L;
n = n + 1;
end
end
在nntrain末尾還可以縮減學習率,使更新步長逐漸變小。
nn.learningRate =nn.learningRate *nn.scaling_learningRate;
3.2.3 前饋函式nnff說明
輸入引數:nnff(nn, batch_x, batch_y),這個就不說了,和上面一樣
輸出引數:元包矩陣nn,主要計算了隱層的啟用函式值。
函式體說明:
1 新增偏置項
x = [ones(m,1)x];
nn.a{1} = x;
一般前饋計算過程為,h=f(xw’+b);其中引數b即為偏置項;此時在計算過程中,需要“複製”b維數,是的複製後的b可以和wx乘積結果可以相加(詳見SparseAutoEncoder稀疏編碼詳解));這樣每次複製b,會增大計算量;本程式碼中,把偏置項,直接新增到輸入資料x中,在x前新增一列0元素,作為偏置項b;這是樣輸入資料矩陣,就變成了60000*785,這也就和前面為什麼要把權值矩陣w1定義為為200*785,而不是200*784了。
所以最後權值矩陣W1為200*785;W2為100*201 ;W3為10*101。
這樣前饋計算過程,由h=f(xw’+b),變成了h=f(x*w’)
2 encoder和decoder過程
Encoder階段:
for i = 2 : n-1
switchnn.activation_function
case 'sigm'
% Calculate theunit's outputs (including the bias term)
nn.a{i}= sigm(nn.a{i - 1} * nn.W{i - 1}');
case 'tanh_opt'
nn.a{i}= tanh_opt(nn.a{i - 1} * nn.W{i - 1}');
end
nn.a{i} =[ones(m,1) nn.a{i}];%給下一個網路的輸入新增偏置項
end
decoder階段:
根據選擇的decoder函式,來計算;由於此處是autoencoder,通過 nn.a{n} = sigm(nn.a{n - 1} * nn.W{n - 1}');來生成輸入資料的近似估計,以便用來求重構誤差。
switch nn.output
case 'sigm'
nn.a{n}= sigm(nn.a{n - 1} * nn.W{n - 1}');
case 'linear'
nn.a{n}= nn.a{n - 1} * nn.W{n - 1}';
case 'softmax'
nn.a{n}= nn.a{n - 1} * nn.W{n - 1}';
nn.a{n}= exp(bsxfun(@minus, nn.a{n}, max(nn.a{n},[],2)));
nn.a{n}= bsxfun(@rdivide, nn.a{n}, sum(nn.a{n}, 2));
end
3.計算損失函式
nn.e = y -nn.a{n};
switch nn.output
case {'sigm', 'linear'}
nn.L =1/2 * sum(sum(nn.e .^ 2)) / m; %均方誤差
case 'softmax'
nn.L =-sum(sum(y .* log(nn.a{n}))) / m;
end
3.2.4反向函式nnbp說明:
輸入,輸出引數:nn = nnbp(nn),都是元胞矩陣nn
這裡首先要複習一下,bp演算法的誤差傳播方法。
誤差反向傳播:
最後一層誤差:nl為最後一層的層數,z為最後一層的輸入,上一層的輸出值
Sigmoid函式的導數為f’(a)=a(1-a)
輸出層誤差程式碼:
switch nn.output
case 'sigm'
d{n} =- nn.e .* (nn.a{n} .* (1 - nn.a{n}));
case {'softmax','linear'}
d{n} =- nn.e;
end
中間層誤差:本層和下一層的連線權值,乘以,上一層誤差,在乘以,本層輸入的導數。
for i = (n -1) : -1 :2
% 各層啟用函式求導
switchnn.activation_function
case 'sigm'
d_act= nn.a{i} .* (1 - nn.a{i});
case 'tanh_opt'
d_act= 1.7159 * 2/3 * (1 - 1/(1.7159)^2 * nn.a{i}.^2);
end
% 計算誤差
if i+1==n
d{i} =(d{i + 1} * nn.W{i} + sparsityError) .* d_act;
%倒數第二層的殘殺d{nl-1},是由最後一層殘差d{nl}傳播得到的,而最後一層的殘差沒有新增偏置項,也就是輸出層節點是10,而不是11;所以誤差可以直接通過連線的權值矩陣向前傳遞。
else
d{i} =(d{i + 1}(:,2:end) * nn.W{i} + sparsityError) .* d_act;
倒數第二層以前的殘差d{i}是由上一層殘差d{i+1}傳遞過來的;而中間層再向前傳遞的時候每層都加了偏置項,而這裡我們要計算的是連線權值矩陣W的殘差,所以應該把新增的偏置項去掉,所以去挑d{i+1}的第一列,使用d{i + 1}(:,2:end),而不是整個d{i+1}。
end
end
權值更新量計算:
l層的權值更新量delta_W=下一層網路的誤差 * 本層輸入
l層偏置項更新量delta_b=殘差
為了避免在小批量資料的樣本容量發生改變時,學習率也必須做相應的修改;通常的做法是在引數的更新過程中昬使用引數的平均梯度;即總梯度除以資料容量。
for i = 1 : (n- 1)
if i+1==n
nn.dW{i}= (d{i + 1}' * nn.a{i}) / size(d{i + 1}, 1);
else
nn.dW{i}= (d{i + 1}(:,2:end)' * nn.a{i}) / size(d{i + 1},1);
end
end
權值更新:
基本的梯度下降更新:無動量項,無權值懲罰項
w=w-alpha*delta_W
for i = 1 : (nn.n - 1)
if(nn.weightPenaltyL2>0)
dW =nn.dW{i} + nn.weightPenaltyL2 * [zeros(size(nn.W{i},1),1) nn.W{i}(:,2:end)];
else
dW =nn.dW{i};
end
dW =nn.learningRate * dW;
if(nn.momentum>0)
nn.vW{i}= nn.momentum*nn.vW{i} + dW;
dW =nn.vW{i};
end
nn.W{i} =nn.W{i} - dW;
end
四、fine_tuning階段
4.1初始化網路結構
1.首先通過nnsetup(784,200,100,10)函式來構建網路;此時不是通過saesetup函式來構建;
2.設定網路啟用函式,學習率等引數
3.把Pre_training階段,學習到的權值矩陣,作為網路權值的初始值;
nn.W{1} =sae.ae{1}.W{1};
nn.W{2}= sae.ae{2}.W{1};
4.2 fine_tuning
利用nntrain函式,使用真實的標籤資料train_y來微調整個網路
nn= nntrain(nn, train_x, train_y, opts);
4.3:預測階段 (此部分程式碼詳解見博文:Denosing Autoencoder訓練過程程式碼詳解)
[er,bad] =nntest(nn, test_x, test_y);
7.普通deep autoencoder訓練過程
本文主要參考Deeplearn toolbox中程式碼
matlab的Deep Learning toolbox,見:https://github.com/rasmusbergpalm/DeepLearnToolbox
一:載入資料
二:pre_training階段
2.1初始化DAE網路框架
sae = saesetup([784 100 100]);%建立一個3層網路
在函式saesetup函式內部,迴圈呼叫nnsetup函式,此處2次呼叫nnsetup函式
最終saesetup([784 100 100]);建立一個2個autoencoder網路
分別是:784 ——100——784;100 ——100——100
2.1.1 nnsetup函式說明;
nn.size =architecture; nn.n=numel(nn.size)=3;
初始化啟用函式型別,學習率,動量項,稀疏項等引數。
初始化網路結構:
for i= 2 : nn.n=3 %第一次迴圈呼叫:輸入nnsetup([784 100 784])
經過2次迴圈,初始化一個784 ——100——784 的autoencoder網路。
2.2初始化SAE網路的訓練引數
初始化啟用函式型別(此處預設為tanh_opt函式),學習率,噪聲比例等引數。
2.3開始訓練SAE網路
sae = saetrain(sae, train_x,opts);%輸入網路結構,樣本資料,批處理的個數
saetrain內部呼叫nntrain函式來訓練sae的每個autoencoder子網路.
sae.ae{i} = nntrain(sae.ae{i},x, x, opts);%迴圈兩次
2.3.1 nntrain函式說明
輸入引數:nntrain(nn, train_x, train_y, opts,val_x, val_y)
由於是autoencoder網路,此處的train_y是輸入資料train_x
整個資料迴圈訓練numepochs次,每次將整個資料分成numbatches個組,進行minibatch訓練。
每次minibatch訓練過程:
從整個資料中,“隨機提取”minibatch個batch_x和batch_y資料;
呼叫nnff函式前饋計算 損失函式nn.L,和誤差向量 nn.e
呼叫nnbp函式,反向計算誤差dw{i}
呼叫nnapplygrads函式,來更新權值
三:fine_tuning 階段
3.1初始化前饋網路
nn = nnsetup([784 100 100 10]);
把pre_training階段訓練的權值矩陣賦值給前饋網路。
nn.W{1} = sae.ae{1}.W{1};
nn.W{2} = sae.ae{2}.W{1};
3.2標籤資料來fine_tuning整個網路
nn = nntrain(nn, train_x,train_y, opts);
四:測試資料
[er, bad] = nntest(nn, test_x,test_y);
4.1 預測樣本分類
labels = nnpredict(nn, x);
nnpredict函式首先實現一個網路的前饋計算,計算樣本屬於每個分類的“概率”
nn = nnff(nn, x,zeros(size(x,1), nn.size(end)));
根據最大化原則,確定樣本的分類,並提取類別標籤
[~, i] = max(nn.a{end},[],2);%max(a,[],2)提取矩陣a每行的最大值
labels = i;
4.2計算錯誤率
[~, expected] = max(y,[],2);
bad = find(labels ~= expected);
er = numel(bad) / size(x, 1);
Denosing Autoencoder訓練過程
Denoising autoencode主要通過在nntrain函式中對輸入資料加入噪聲,其他訓練部分相同
一:Pre_training階段
資料加噪:
sae.ae{1}.inputZeroMaskedFraction = 0.5;
sae.ae{2}.inputZeroMaskedFraction = 0.5;
由於是使用saetrain函式來分別訓練每個autoencoder網路,所以這裡對每個網路的輸入資料都加入50%的隨機噪聲。
加噪程式碼:
if(nn.inputZeroMaskedFraction~= 0)
batch_x=batch_x.*(rand(size(batch_x))>nn.inputZeroMaskedFraction);
end
加入50%隨機噪聲後,原始資料
二:fine_tuning階段
由於此處直接使用的是nntrain,微調整個網路;所以只在在呼叫nntrain函式時,把原始輸入資料train_x加入噪聲
程式碼的一些困惑:
在pre_training階段,在nnsetup中,初始化每個autoencoder的前饋計算的encoder階段使用的是tanh函式,decoder階段使用的是sigmoid函式,這兩個階段為什麼不一樣呢?其次就是最後fine_tuning過程,前饋計算過程都使用的是sigmoid函式,這個也和pre_training過程的不一樣。這個木有想明白