深度学习(deep learning)之一步一步实现编写深度神经网络(DNN)

本文将以matlab为工具介绍下如何实现深度神经网络,以方便初学者,神经元激活函数为 ReLU。
ReLU函数的数学公式很简单ReLU(x)=max(x,0),但其对DNN的贡献是巨大的。
若DNN用于数据分类,则可以简单的认为其主要由两个部分组成:多隐层网络+分类器。分类器以softmax为例。
第一步:准备数据
1)将你需要分类的样本数据以每列的形式保存于矩阵中;->TrainData
2)将每个样本的类别标记按数据顺序存为一行向量,类别为1,2,3,…,n;->TrainLabel
第二步:网络配置、参数初始化和转换

inputsize=size(TrainData ,1);%获取数据的维度
datanum=size(TrainData ,2);%获取数据的数量
netsize=[inputsize,50,50,50];%可以简单地用一个向量来定义网络的深度,以及每层神经元数目。这表示一个三隐藏层的DNN,神经元数都为50。
classnum=2;%类别数目

DNN中的可训练参数主要为每层的权值W,偏置b,以及softmax 分类器的的参数SoftmaxTheta;

lastsize=netsize(end)+1;%网络最后一层神经元数数目,再考虑一个偏置。
stack = initializeNet(netsize);%初始化网络参数,以结构体的形式保存。
[stackTheta, netconfig] = stack2params(stack);%在训练时,往往需要将参数转成一列向量,提供给损失函数。stack ->stackTheta,netconfig保存一些结构参数
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
Theta=[ SoftmaxTheta ; stackTheta ];%最终网络需要的参数
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
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

当Theta传递进入损失函数内部时,还需要从Theta抽出stackTheta,再转成stack
代码如下,都是Andrew Ng的教程里面的提供的。
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

第三步:编写损失函数(最为核心的内容)
假设损失函数的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)];

%开始求解损失

% groundTruth = full(sparse(trainLabels, 1:datanum, 1));%这是Andrew NG教程原版的语句,但其在应用小批量样本训练时会出错,下一行是另一种实现方式
groundTruth=bsxfun(@eq,repmat(trainLabels’,numClasses,1),(1:1:numClasses)’);

M = softmaxTheta*a2;
h = exp(M);
h = bsxfun(@rdivide, h, sum(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

function re = relu(x)
re = max(x,0)-1;
end
function dre= drelu(x)
dre=zeros(size(x));
dre(x>0)=1;
dre(x==0)=0.5;%这句可以不要
end
第四步:训练
检查梯度计算无错误后,就可以利用NG教程序里面介绍的优化工具箱minFunc来进行优化,也可以利用随机梯度下降算法SGD来训练网络(搞深度学习还是首选后者)。SGD每次迭代只需要小批量的样本。
工具箱:
options.Method = ‘lbfgs’;
options.maxIter = 2000;
options.MaxFunEvals=10000;
options.display = ‘on’;
lambda = 1e-4;
[OptTheta, cost] = minFunc( @(p)ReLUDNNCost(p,classnum,lastsize,netconfig,lambda, TrainData,TrainLabel),Theta, options);



最简单的SGD:

batchsize=10;%每次训练的小批量样本数
batchnum=floor(size(TrainData,2)/batchsize);
DataNum=size(TrainData,2);
alpha=1e-1;这是学习率,一般随着网络的悬念都需要不断的减小
while(1)

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;

end

epoch=epoch+1;

end
第五步:测试
测试的代码主要为损失函数ReLUDNNCost中的前馈部分,求得h后,类别为
[val,PREDCLASS]=max(h);

你可能感兴趣的:(算法)