採用ReLU作為啟用函式的簡單深度神經網路matlab程式碼設計
本文介紹下如何實現神經元啟用函式為ReLU的深度神經網路。ReLU函式的數學公式很簡單ReLU(x)=max(x,0)。若DNN用於資料分類,則可以簡單的認為其主要由兩個部分組成:多隱層網路+分類器。分類器採用softmax。
第一步:準備資料
1)將你需要分類的樣本資料以每列的形式保存於矩陣中;->TrainData 2)將每個樣本的類別標記按資料順序存為一行向量,類別為1,2,3,…,n;->TrainLabel
並將資料儲存入MyData.mat資料檔案中。
採用以下程式實現資料的生成。
x=1:10
y=1:8:80
rt=x.*x-50*x+40*y-y.^2;
TrainData=[x;y];
for k=1:10
v_rt_k=rt(k)
if rt(k)<=0
TrainLabel(k)=1;
else
TrainLabel(k)=2;
end
end
save('MyData.mat','TrainData','TrainLabel')
第二步:網路配置、引數初始化和轉換
將第一步中準備好的資料載入記憶體中,並採用以下程式執行資料。
1. Main_function without minFunc
clear all
clc
close all
load MyData.mat
inputsize=size(TrainData ,1);%獲取資料的維度
datanum=size(TrainData ,2);%獲取資料的數量
% netsize=[inputsize,50,50,50];
%可以簡單地用一個向量來定義網路的深度,以及每層神經元數目。這表示一個三隱藏層的DNN,神經元數都為50。
netsize=[inputsize,4,3];
classnum=2;%類別數目
lastsize=netsize(end)+1;%網路最後一層神經元數數目,再考慮一個偏置。
stack = initializeNet(netsize);%初始化網路引數,以結構體的形式儲存。
v_stack=stack
v_stack_1=stack{1}
v_stack_1_w=stack{1}.w
v_stack_1_b=stack{1}.b
v_stack_2=stack{2}
v_stack_2_w=stack{2}.w
v_stack_2_b=stack{2}.b
%在訓練時,往往需要將引數轉成一列向量,提供給損失函式。stack ->stackTheta,netconfig儲存一些結構引數
[stackTheta, netconfig] = stack2params(stack);
v_stackTheta=stackTheta
v_netconfig=netconfig
v_netconfig_layersizes=netconfig.layersizes
v_lastsize=lastsize
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
v_SoftmaxTheta=SoftmaxTheta
Theta=[ SoftmaxTheta ; stackTheta ];%最終網路需要的引數
% the following part is for the traing epoch.
batchsize=10;
% batchsize=5;
%%每次訓練的小批量樣本數</span>
batchnum=floor(size(TrainData,2)/batchsize);
DataNum=size(TrainData,2);
alpha=1e-2;
%這是學習率,一般隨著網路的懸念都需要不斷的減小
lambda = 1e-4; % Weight decay parameter
for epoch=1:16000
v_epoch=epoch
idx=randperm(DataNum);
for t=1:batchnum
subdata=TrainData(:,idx((t-1)*batchsize+1:(t)*batchsize));
sublabel=TrainLabel(idx((t-1)*batchsize+1:(t)*batchsize));
[cost,grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);
Theta=Theta-alpha*grad;
v_grad=grad
end
end
% Note: 當Theta傳遞進入損失函式內部時,還需要從Theta抽出stackTheta,再轉成stack程式碼如下,都是Andrew Ng的教程裡面的提供的。
2. Main_function with minFunc of UFLDL
clear all
clc
close all
load MyData.mat
inputsize=size(TrainData ,1);%獲取資料的維度
datanum=size(TrainData ,2);%獲取資料的數量
% netsize=[inputsize,50,50,50];
%可以簡單地用一個向量來定義網路的深度,以及每層神經元數目。這表示一個三隱藏層的DNN,神經元數都為50。
netsize=[inputsize,4,3];
classnum=2;%類別數目
lastsize=netsize(end)+1;%網路最後一層神經元數數目,再考慮一個偏置。
% v_lastsize=lastsize
stack = initializeNet(netsize);%初始化網路引數,以結構體的形式儲存。
v_stack=stack
v_stack_1=stack{1}
v_stack_1_w=stack{1}.w
v_stack_1_b=stack{1}.b
v_stack_2=stack{2}
v_stack_2_w=stack{2}.w
v_stack_2_b=stack{2}.b
%在訓練時,往往需要將引數轉成一列向量,提供給損失函式。stack ->stackTheta,netconfig儲存一些結構引數
[stackTheta, netconfig] = stack2params(stack);
v_stackTheta=stackTheta
v_netconfig=netconfig
v_netconfig_layersizes=netconfig.layersizes
v_lastsize=lastsize
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
v_SoftmaxTheta=SoftmaxTheta
Theta=[ SoftmaxTheta ; stackTheta ];%最終網路需要的引數
% the following part is for the minFunc in the UFLDL tutorial
options.Method = 'lbfgs';
options.maxIter = 20000;
options.MaxFunEvals=1000000;
options.display = 'on';
lambda = 1e-4;
[OptTheta, cost] = minFunc( @(p)ReLUDNNCost(p,classnum,lastsize,netconfig,lambda, TrainData,TrainLabel),Theta, options);
v_TrainLabel=TrainLabel
save('weights_matrix_minFunc.mat','OptTheta')
% % the following part is for the SGD traing epoch.
% batchsize=10;
% % batchsize=5;
% %%每次訓練的小批量樣本數</span>
% batchnum=floor(size(TrainData,2)/batchsize);
% DataNum=size(TrainData,2);
% alpha=1e-2;
% %這是學習率,一般隨著網路的懸念都需要不斷的減小
% lambda = 1e-4; % Weight decay parameter
% for epoch=1:16000
% v_epoch=epoch
% idx=randperm(DataNum);
% for t=1:batchnum
% subdata=TrainData(:,idx((t-1)*batchsize+1:(t)*batchsize));
% sublabel=TrainLabel(idx((t-1)*batchsize+1:(t)*batchsize));
% [cost,grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);
% Theta=Theta-alpha*grad;
% v_grad=grad
% end
% end
% save('weights_matrix_minFunc.mat','Theta')
3. Main_function2 with minFunc of UFLDL
clear all
clc
close all
load MyData.mat
inputsize=size(TrainData ,1);%獲取資料的維度
datanum=size(TrainData ,2);%獲取資料的數量
% netsize=[inputsize,50,50,50];
%可以簡單地用一個向量來定義網路的深度,以及每層神經元數目。這表示一個三隱藏層的DNN,神經元數都為50。
netsize=[inputsize,4,3];
classnum=2;%類別數目
lastsize=netsize(end)+1;%網路最後一層神經元數數目,再考慮一個偏置。
% v_lastsize=lastsize
stack = initializeNet(netsize);%初始化網路引數,以結構體的形式儲存。
v_stack=stack
v_stack_1=stack{1}
v_stack_1_w=stack{1}.w
v_stack_1_b=stack{1}.b
v_stack_2=stack{2}
v_stack_2_w=stack{2}.w
v_stack_2_b=stack{2}.b
%在訓練時,往往需要將引數轉成一列向量,提供給損失函式。stack ->stackTheta,netconfig儲存一些結構引數
[stackTheta, netconfig] = stack2params(stack);
v_stackTheta=stackTheta
v_netconfig=netconfig
v_netconfig_layersizes=netconfig.layersizes
v_lastsize=lastsize
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
v_SoftmaxTheta=SoftmaxTheta
Theta=[ SoftmaxTheta ; stackTheta ];%最終網路需要的引數
% the following part is for the traing epoch.
batchsize=10;
% batchsize=5;
%%每次訓練的小批量樣本數</span>
batchnum=floor(size(TrainData,2)/batchsize);
DataNum=size(TrainData,2);
alpha=1e-2;
%這是學習率,一般隨著網路的懸念都需要不斷的減小
lambda = 1e-4; % Weight decay parameter
for epoch=1:16000
v_epoch=epoch
idx=randperm(DataNum);
for t=1:batchnum
subdata=TrainData(:,idx((t-1)*batchsize+1:(t)*batchsize));
sublabel=TrainLabel(idx((t-1)*batchsize+1:(t)*batchsize));
[cost,grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);
Theta=Theta-alpha*grad;
v_grad=grad
end
v_trainLabels=sublabel
end
save('weights_matrix2.mat','Theta')
4. ReLUDNNCost
function [cost,grad] = ReLUDNNCost(theta,numClasses,lasthiddenSize, netconfig,lambda, trainData,trainLabels)
%引數獲取的一些操作
softmaxTheta = reshape(theta(1:lasthiddenSize*numClasses), numClasses, lasthiddenSize);
stack = params2stack(theta(lasthiddenSize*numClasses+1:end), netconfig);
%從theta向量中抽取網路權值引數並轉化
stackgrad = cell(size(stack));
PARA=cell(numel(stack),1);%這裡儲存在應用BP演算法求梯度時需要的資料
datanum=size(trainData,2);%傳進來的樣本數
%開始前饋,網路雖然多層,但只是重複而已
data=trainData;
for d = 1:numel(stack)
PARA{d}.a=data;
z2=(stack{d}.w*data)+stack{d}.b*ones(1,datanum);
a2=relu(z2);%RelU函式
data=a2;
PARA{d}.daz=drelu(z2);%RelU函式的導函式
end
a2=[a2;ones(1,datanum)];
%開始求解損失
% v_trainLabels=trainLabels
% v_datanum=datanum
groundTruth = full(sparse(trainLabels, 1:datanum, 1));
% %這是Andrew NG教程原版的語句,但其在應用小批量樣本訓練時會出錯,下一行是另一種實現方式
% v_trainLabels=trainLabels
% v_numClasses=numClasses
% v_element1=repmat(trainLabels,numClasses,1)
% v_element2=(1:1:numClasses)'
% groundTruth=bsxfun(@eq,repmat(trainLabels,numClasses,1),(1:1:numClasses)');
% v_groundTruth=groundTruth
% pause
M = softmaxTheta*a2;
h = exp(M);
h = bsxfun(@rdivide, h, sum(h));
% v_size_groundTruth=size(groundTruth)
% v_log_h=size(log(h))
cost = -1/datanum*sum(sum(groundTruth.*log(h)))+lambda/2*sum(sum(softmaxTheta.^2));
%softmax 損失函式,沒啥好說的
softmaxThetaGrad = -1/datanum*((groundTruth-h)*a2')+lambda*softmaxTheta;
%softmax 目標函式對softmaxTheta 的導數,
predelta=-softmaxTheta'*(groundTruth-h);
%想理解這裡,還有後面的梯度是如何計算出的,建議看那本關於矩陣的工具書《The Matrix Cookbook》
predelta=predelta(1:end-1,:);
for d = numel(stack):-1:1
delta=predelta.*PARA{d}.daz;
stackgrad{d}.w=delta*PARA{d}.a'/datanum;%.*PARA{d}.idx
stackgrad{d}.b=sum(delta,2)/datanum;
predelta=stack{d}.w'*delta;
end
grad = [softmaxThetaGrad(:) ; stack2params(stackgrad)];
end
5. relu
function re = relu(x)
re = max(x,0)-1;
end
6. drelu
function dre= drelu(x)
dre=zeros(size(x));
dre(x>0)=1;
dre(x==0)=0.5;%這句可以不要
end
7. initializeNet
function stack = initializeNet(netsize)
layersize=length(netsize(:));
stack = cell(layersize-1,1);
for l=1:layersize-1
hiddenSize=netsize(l+1);
visibleSize=netsize(l);
r =sqrt(6) / sqrt(hiddenSize+visibleSize+1);
stack{l}.w= rand(hiddenSize, visibleSize) * 2 * r - r; stack{l}.b= zeros(hiddenSize, 1);
end
end
8. params2stack
function stack = params2stack(params, netconfig)
depth = numel(netconfig.layersizes);
stack = cell(depth,1);
prevLayerSize = netconfig.inputsize; % the size of the previous layer
curPos = double(1); % mark current position in parameter vector
for d = 1:depth
% Create layer d
stack{d} = struct;
% Extract weights
wlen = double(netconfig.layersizes{d} * prevLayerSize);
stack{d}.w = reshape(params(curPos:curPos+wlen-1), netconfig.layersizes{d}, prevLayerSize);
curPos = curPos+wlen;
% Extract bias
blen = double(netconfig.layersizes{d});
stack{d}.b = reshape(params(curPos:curPos+blen-1), netconfig.layersizes{d}, 1);
curPos = curPos+blen;
% Set previous layer size
prevLayerSize = netconfig.layersizes{d};
end
end
9. stack2params
function [params, netconfig] = stack2params(stack)
params = [];
for d = 1:numel(stack)
params = [params ; stack{d}.w(:) ;
stack{d}.b(:) ];
end
if nargout > 1
if numel(stack) == 0
netconfig.inputsize = 0;
netconfig.layersizes = {};
else
netconfig.inputsize = size(stack{1}.w, 2);
netconfig.layersizes = {};
for d = 1:numel(stack)
netconfig.layersizes = [netconfig.layersizes ; size(stack{d}.w,1)];
end
end
end
end
第三步,採用已訓練的深度網路對輸入資料進行測試
1. Main_test_function
clear all
clc
close all
load MyData.mat
load weights_matrix.mat
inputsize=size(TrainData ,1);%獲取資料的維度
datanum=size(TrainData ,2);%獲取資料的數量
% netsize=[inputsize,50,50,50];
%可以簡單地用一個向量來定義網路的深度,以及每層神經元數目。這表示一個三隱藏層的DNN,神經元數都為50。
netsize=[inputsize,4,3];
classnum=2;%類別數目
netconfig2.inputsize=netsize(1)
netconfig2.layersizes{1}=netsize(2)
netconfig2.layersizes{2}=netsize(3)
netconfig2.layersizes=netconfig2.layersizes'
lastsize=netsize(end)+1;%網路最後一層神經元數數目,再考慮一個偏置。
v_result = forward_computation(Theta,classnum,lastsize,netconfig2,TrainData)
v_TrainLabel=TrainLabel
2. forward_computation
function v_result = forward_computation(theta,numClasses,lasthiddenSize, netconfig,trainData,trainLabels)
%引數獲取的一些操作
softmaxTheta = reshape(theta(1:lasthiddenSize*numClasses), numClasses, lasthiddenSize);
stack = params2stack(theta(lasthiddenSize*numClasses+1:end), netconfig);
%從theta向量中抽取網路權值引數並轉化
stackgrad = cell(size(stack));
PARA=cell(numel(stack),1);%這裡儲存在應用BP演算法求梯度時需要的資料
datanum=size(trainData,2);%傳進來的樣本數
%開始前饋,網路雖然多層,但只是重複而已
data=trainData;
for d = 1:numel(stack)
PARA{d}.a=data;
z2=(stack{d}.w*data)+stack{d}.b*ones(1,datanum);
a2=relu(z2);%RelU函式
data=a2;
PARA{d}.daz=drelu(z2);%RelU函式的導函式
end
a2=[a2;ones(1,datanum)];
M = softmaxTheta*a2;
h = exp(M);
h = bsxfun(@rdivide, h, sum(h));
v_result=h
end