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