卷积神经网络Step by Step(五)
来到卷积神经网络详解的最后一章,在前面四篇博客里,我们首先对CNN的整体框架进行讲解,然后结合代码对卷积层、池化处理、全连接网络层以及计算cost的前向传播,计算梯度值的反向传播都分别进行讲解。这一章,我们要把这些全部捏合起来,构建一个CNN神经网络,并在MNIST数据集上进行分类训练和测试。
在那之前,我们还有两个在工程中很实用的概念没有提到:
- Stochastic Gradient Descent,以下简称SGD
- Momentum
随机梯度下降(SGD)
在标准的梯度下降算法中,我们对目标函数
从上式可以看到,传统的GD算法中的cost和gradient是将整个训练集的数据考虑进去,再进行更新的。
而SGD的思路是,每次进行参数学习更新时,只考虑一个训练样本或者一部分训练样本的贡献。那每次迭代的参数更新变为:
从上式可以看到,SGD算法的参数更新的每一次迭代只考虑一个batch的训练数据,一般来说,最小的batch size取值为256。
SGD的优点:
- 减小在参数更新中的方差,让训练的收敛更平稳;
- 可以利用高度并行化的设计来提升算法的效率。
冲量(Momentum)
基于冲量的参数更新:
每一步梯度下降的大小和方向,我们也参考上一步的v,要是方向一致,就大步往前走;要是忽左忽右,就快步走出这个区域。加入冲量的思想可以很好解决局部最优,加快网络的收敛。
% 冲量
mom = 0.5;
momIncrease = 20;
velocity = zeros(size(theta));
%%======================================================================
%% 随机梯度下降
it = 0;
for e = 1:epochs
% 随机打乱训练样本的顺序
rp = randperm(m);
for s=1:minibatch:(m-minibatch+1)
it = it + 1;
% 在每一次it达到momIncrease时,更新冲量
if it == momIncrease
mom = options.momentum;
end;
% 按照minibatch获取训练数据
mb_data = data(:,:,rp(s:s+minibatch-1));
mb_labels = labels(rp(s:s+minibatch-1));
% 计算目标函数值
[cost grad] = funObj(theta,mb_data,mb_labels);
% 更新theta,ufldl教程Optimization: Stochastic Gradient Descent
velocity = mom*velocity+alpha*grad;
theta = theta-velocity;
fprintf('Epoch %d: Cost on iteration %d is %f\n',e,it,cost);
end;
% 每次迭代后衰减learning rate
alpha = alpha/2.0;
end;
在我们的实现代码中,冲量的初始取值为0.5,当迭代次数达到momIncrease后,冲量取值变为0.9。
cnnTrain.m
cnnTrain.m是整个CNN网络训练分类MNIST数据集的主函数,对每部分的代码分别进行介绍。
参数初始化
这部分的代码是为网络训练做准备,包括载入MNIST训练集,参数初始化等。
%% 第一步:初始化参数,载入训练数据
% 参数配置
% MNIST数据库图片的大小为28×28
imageDim = 28;
% 要分类的类别数
numClasses = 10;
%卷积层的特征提取模块的维数(滤波器维数)
filterDim = 9;
% 特征提取滤波器的个数
numFilters = 20;
% 池化的维数,应该整除imageDim-filterDim+1
poolDim = 2;
% 载入MNIST数据库的训练数据
addpath function/;
images = loadMNISTImages('../MNIST/train-images-idx3-ubyte');
images = reshape(images,imageDim,imageDim,[]);
labels = loadMNISTLabels('../MNIST/train-labels-idx1-ubyte');
% 将分类标签0重新映射到10
labels(labels==0) = 10;
% 初始化参数
theta = cnnInitParams(imageDim,filterDim,numFilters,poolDim,numClasses);
CNN训练
minibatch大小设置为256,冲量取值为0.95,总的迭代次数是3。
%% 训练CNN网络
options.epochs = 3;
options.minibatch = 256;
options.alpha = 1e-1;
options.momentum = .95;
opttheta = minFuncSGD(@(x,y,z) cnnCost(x,y,z,numClasses,filterDim,...
numFilters,poolDim),theta,images,labels,options);
测试CNN网络
% 载入MNIST数据库的测试集
testImages = loadMNISTImages('../MNIST/t10k-images-idx3-ubyte');
testImages = reshape(testImages,imageDim,imageDim,[]);
testLabels = loadMNISTLabels('../MNIST/t10k-labels-idx1-ubyte');
testLabels(testLabels==0) = 10;
[~,cost,preds]=cnnCost(opttheta,testImages,testLabels,numClasses,...
filterDim,numFilters,poolDim,true);
acc = sum(preds==testLabels)/length(preds);
% 打印出测试集的分类准备率
fprintf('Accuracy is %f\n',acc);
在完成3次迭代后,整个CNN网络收敛,在测试数据集上的预测准确率达到97.1%。结果如下:
.
.
.
Epoch 3: Cost on iteration 687 is 0.119978
Epoch 3: Cost on iteration 688 is 0.126712
Epoch 3: Cost on iteration 689 is 0.087837
Epoch 3: Cost on iteration 690 is 0.100424
Epoch 3: Cost on iteration 691 is 0.112769
Epoch 3: Cost on iteration 692 is 0.062723
Epoch 3: Cost on iteration 693 is 0.120102
Epoch 3: Cost on iteration 694 is 0.144303
Epoch 3: Cost on iteration 695 is 0.126082
Epoch 3: Cost on iteration 696 is 0.140024
Epoch 3: Cost on iteration 697 is 0.074275
Epoch 3: Cost on iteration 698 is 0.123467
Epoch 3: Cost on iteration 699 is 0.154408
Epoch 3: Cost on iteration 700 is 0.095232
Epoch 3: Cost on iteration 701 is 0.098181
Epoch 3: Cost on iteration 702 is 0.111052
Accuracy is 0.971000
代码地址
整个工程代码我已经上传到github上,欢迎下载运行,有很详细的注释帮助理解卷积神经网络的每一步。
后续我还会用pytorch来实现这个cnn网络,对比效果,同时也练练手。请关注代码的github地址:
https://github.com/wblgers/stanford_dl_cnn
参考资料
http://ufldl.stanford.edu/tutorial/supervised/OptimizationStochasticGradientDescent/
http://blog.csdn.net/nuoman_cheng/article/details/50426943