原文连接:https://blog.csdn.net/u012273127/article/details/76206977
感谢这位博主的笔记
cnn_mnist_init.m文件代码
function net = cnn_mnist_init(varargin)
% 初始化CNN网络结构
% CNN_MNIST_LENET Initialize a CNN similar for MNIST
%默认参数设置
%开启批量归一化操作选项
%是否开启批量归一化的操作
opts.batchNormalization = true ;
%默认使用simpleNN网络封装器
opts.networkType = 'simplenn' ;
%根据外部参数修改参数默认值
opts = vl_argparse(opts, varargin) ;
%设置随机数发生器
rng('default');
rng(0) ;
% 设置网络结构
% f用于缩小随机初始化的conv层权值参数
f=1/100 ;
net.layers = {} ;
%卷积层参数:
% type -- 该层的类型
% weights -- 权重和偏置参数
% stride -- 步长参数
% pad -- 卷积图像边缘填充参数
% pad = 0 --图像块上的扩展宽度为0,即是不做任何扩展
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(5,5,1,20, 'single'), zeros(1, 20, 'single')}}, ...
'stride', 1, ...
'pad', 0) ;
%池化层参数:
% type -- 该层的类型
% method -- 池化的类型,这里为最大池化
% pool -- 为池化模板的大小
% stride -- 步长参数
% pad -- 图像边缘填充参数
net.layers{end+1} = struct('type', 'pool', ...
'method', 'max', ...
'pool', [2 2], ...
'stride', 2, ...
'pad', 0) ;
% 卷积层
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(5,5,20,50, 'single'),zeros(1,50,'single')}}, ...
'stride', 1, ...
'pad', 0) ;
%最大池化层
net.layers{end+1} = struct('type', 'pool', ...
'method', 'max', ...
'pool', [2 2], ...
'stride', 2, ...
'pad', 0) ;
% 卷积层
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(4,4,50,500, 'single'), zeros(1,500,'single')}}, ...
'stride', 1, ...
'pad', 0) ;
% relu激活层,即使用relu为激活函数激活层
net.layers{end+1} = struct('type', 'relu') ;
%卷积层
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(1,1,500,10, 'single'), zeros(1,10,'single')}}, ...
'stride', 1, ...
'pad', 0) ;
% softmax层
net.layers{end+1} = struct('type', 'softmaxloss') ;
% optionally switch to batch normalization
% 根据是否需要归一化在网络中插入相应的层
% 如果需要批量归一化则需要在相应的位置插入层
if opts.batchNormalization
net = insertBnorm(net, 1) ;
net = insertBnorm(net, 4) ;
net = insertBnorm(net, 7) ;
end
% Meta parameters
% 设置网络的一些参数
% 设置输入图像的尺寸, 输入尺寸为28*28
net.meta.inputSize = [28 28 1] ;
% 设置学习率
net.meta.trainOpts.learningRate = 0.001 ;
% 训练回合次数
net.meta.trainOpts.numEpochs = 20 ;
% 一个批次传递的样本数量
% 即每次积累多少样本的误差进行一次梯度下降更新参数
net.meta.trainOpts.batchSize = 100 ;
% Fill in defaul values
% 填充一些默认的值
% 在网络中添加一些默认的属性值
net = vl_simplenn_tidy(net) ;
% Switch to DagNN if requested
% 如果网络是simplenn类型不做任何处理
% 如果网络是dagnn,需要做一些设置
switch lower(opts.networkType)
case 'simplenn'
% done
case 'dagnn'
net = dagnn.DagNN.fromSimpleNN(net, 'canonicalNames', true) ;
net.addLayer('top1err', dagnn.Loss('loss', 'classerror'), ...
{'prediction', 'label'}, 'error') ;
net.addLayer('top5err', dagnn.Loss('loss', 'topkerror', ...
'opts', {'topk', 5}), {'prediction', 'label'}, 'top5err') ;
otherwise
assert(false) ;
end
% --------------------------------------------------------------------
% 在net的第L和L+1层中间插入Bnorm层的实现函数,用于需要批量归一化的时候
function net = insertBnorm(net, l)
% --------------------------------------------------------------------
assert(isfield(net.layers{l}, 'weights'));
ndim = size(net.layers{l}.weights{1}, 4);
layer = struct('type', 'bnorm', ...
'weights', {{ones(ndim, 1, 'single'), zeros(ndim, 1, 'single')}}, ...
'learningRate', [1 1 0.05], ...
'weightDecay', [0 0]) ;
net.layers{l}.weights{2} = [] ; % eliminate bias in previous conv layer
net.layers = horzcat(net.layers(1:l), layer, net.layers(l+1:end)) ;
cnn_mnist.m文件代码
function [net, info] = cnn_mnist(varargin)
%CNN_MNIST Demonstrates MatConvNet on MNIST
%将..\\..\\matlab\\vl_setupnn.m文件导入到当前workspace中
% 运行 vl_setupnn.m 文件
% vl_setupnn.m文件功能:将matconvnet框架工具箱添加到MATLAB的path中
run(fullfile(fileparts(mfilename('fullpath')),...
'..', '..', 'matlab', 'vl_setupnn.m')) ;
%opt为可选参数,所有的参数
opts.batchNormalization = false ; %是否批量处理,批量归一化
opts.network = [] ;
opts.networkType = 'simplenn' ; %网络的模型,只有两种sinplenn或者dagnn
[opts, varargin] = vl_argparse(opts, varargin) ; %根据外部参数修改参数默认值
%整个代码段构建实验路径
sfx = opts.networkType ;
if opts.batchNormalization, sfx = [sfx '-bnorm'] ; end
%配置实验结果存放路径: data\mnist-baseline-simplenn文件夹
%vl_rootnn为找到框架根目录
opts.expDir = fullfile(vl_rootnn, 'data', ['mnist-baseline-' sfx]) ;
[opts, varargin] = vl_argparse(opts, varargin) ;
%mnist原始数据所在的文件夹路径: matconvnet-1.0-beta24\data\mnist
opts.dataDir = fullfile(vl_rootnn, 'data', 'mnist') ;
%配置可以直接用的数据的路径
%不会直接用原始数据进行跑,会进行预处理,去除均值等
% imdb.mat中存储的就是可以直接用的数据,h*w*c*n这种形式的数据
% 设置网络可用数据的存储路径
%imdb结构体存放的路径: data\mnist-baseline-simplenn\imdb.mat
opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');
opts.train = struct() ;
%解析参数,将传进来的参数赋值给网络参数
opts = vl_argparse(opts, varargin) ;
%默认使用cpu跑程序
%是否使用GPU编译程序
if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;
% --------------------------------------------------------------------
% Prepare data
% --------------------------------------------------------------------
%判断网络持否已经初始化好
if isempty(opts.network)
%如果没有初始化好一个网络,程序自己初始化一个网络
% cnn_mnist_init函数 -- 初始化一个cnn网络
% 根据前面的一些参数初始化一个网路
net = cnn_mnist_init('batchNormalization', opts.batchNormalization, ...
'networkType', opts.networkType) ;
else
%已经初始化好一个网络
net = opts.network ;
opts.network = [] ;
end
%读取网络可使用的数据imdb
if exist(opts.imdbPath, 'file')
%加载数据
imdb = load(opts.imdbPath) ;
else
% 从minst数据集中获取数据,减去图像均值,存放到Imdb结构体中
% 即将原始数据转化成网络可以使用的数据格式,同时做一些预处理
% 并将可以直接用的数据存储起来
imdb = getMnistImdb(opts) ;
mkdir(opts.expDir) ;
save(opts.imdbPath, '-struct', 'imdb') ;
end
%数据集的lable类别
% 数据的类别, 1到10
net.meta.classes.name = arrayfun(@(x)sprintf('%d',x),1:10,'UniformOutput',false) ;
% --------------------------------------------------------------------
% Train
% --------------------------------------------------------------------
%根据网络类型选择不同的训练函数
% @为函数句柄
switch opts.networkType
case 'simplenn', trainfn = @cnn_train ;
case 'dagnn', trainfn = @cnn_train_dag ;
end
%训练网络
% getBatch每次获取一个batch的图输入
[net, info] = trainfn(net, imdb, getBatch(opts), ...
'expDir', opts.expDir, ...
net.meta.trainOpts, ...
opts.train, ...
'val', find(imdb.images.set == 3)) ; %选择验证机标签
%%每次获取一个batch的输入数据
% --------------------------------------------------------------------
function fn = getBatch(opts)
% --------------------------------------------------------------------
switch lower(opts.networkType)
case 'simplenn'
fn = @(x,y) getSimpleNNBatch(x,y) ;
case 'dagnn'
bopts = struct('numGpus', numel(opts.train.gpus)) ;
fn = @(x,y) getDagNNBatch(bopts,x,y) ;
end
% --------------------------------------------------------------------
function [images, labels] = getSimpleNNBatch(imdb, batch)
% --------------------------------------------------------------------
images = imdb.images.data(:,:,:,batch) ;
labels = imdb.images.labels(1,batch) ;
% --------------------------------------------------------------------
function inputs = getDagNNBatch(opts, imdb, batch)
% --------------------------------------------------------------------
images = imdb.images.data(:,:,:,batch) ;
labels = imdb.images.labels(1,batch) ;
if opts.numGpus > 0
images = gpuArray(images) ;
end
inputs = {'input', images, 'label', labels} ;
% --------------------------------------------------------------------
function imdb = getMnistImdb(opts)
% --------------------------------------------------------------------
% 从原始数据中生成和获取我们网络可以使用的imdb输入数据文件
% Preapre the imdb structure, returns image data with mean image subtracted
%原始输入数据
files = {'train-images-idx3-ubyte', ...
'train-labels-idx1-ubyte', ...
't10k-images-idx3-ubyte', ...
't10k-labels-idx1-ubyte'} ;
if ~exist(opts.dataDir, 'dir')
mkdir(opts.dataDir) ;
end
for i=1:4
% 读取files中的第一个文件,训练图像包含6万张尺寸为28*28的图像
if ~exist(fullfile(opts.dataDir, files{i}), 'file')
url = sprintf('http://yann.lecun.com/exdb/mnist/%s.gz',files{i}) ;
fprintf('downloading %s\n', url) ;
gunzip(url, opts.dataDir) ;
end
end
%读取files中的第一个文件,训练图像集包含6万张尺寸为28*28的图像
f=fopen(fullfile(opts.dataDir, 'train-images-idx3-ubyte'),'r') ;
x1=fread(f,inf,'uint8');
fclose(f) ;
%调整成矩阵,60000张图像调整成一个矩阵
x1=permute(reshape(x1(17:end),28,28,60e3),[2 1 3]) ;
%读取files中的第二个文件
f=fopen(fullfile(opts.dataDir, 't10k-images-idx3-ubyte'),'r') ;
x2=fread(f,inf,'uint8');
fclose(f) ;
x2=permute(reshape(x2(17:end),28,28,10e3),[2 1 3]) ;
%读取files中的第三个文件--训练图像的标签
f=fopen(fullfile(opts.dataDir, 'train-labels-idx1-ubyte'),'r') ;
y1=fread(f,inf,'uint8');
fclose(f) ;
y1=double(y1(9:end)')+1 ;
%读取files中的第四个文件 -- 测试图像集的标签
f=fopen(fullfile(opts.dataDir, 't10k-labels-idx1-ubyte'),'r') ;
y2=fread(f,inf,'uint8');
fclose(f) ;
y2=double(y2(9:end)')+1 ;
% 用于表示样本集合中训练集和测试集的集和
% set中元素==1为训练集,==3为测试集
set = [ones(1,numel(y1)) 3*ones(1,numel(y2))];
% 将x1的训练集和x2的测试集在第三维拼接起来,构成完成的数据集,从uint8转成single
data = single(reshape(cat(3, x1, x2),28,28,1,[]));
%求出所有的训练集中所有图像的均值图像
dataMean = mean(data(:,:,:,set == 1), 4);
%bsxfun函数将minus算子应用到data的每个元素上,对所有的图像减去均值
data = bsxfun(@minus, data, dataMean) ;
%生成网络可以直接使用的输入数据类型
% 填充imdb结构体构造数据集
imdb.images.data = data ; %存储所有的数据
imdb.images.data_mean = dataMean; %存储所有训练图像的均值图像
imdb.images.labels = cat(2, y1, y2) ; %将训练集和测试集标签的第二维拼接起来
imdb.images.set = set ;
imdb.meta.sets = {'train', 'val', 'test'} ; %set == 训练;==2验证;==3测试
imdb.meta.classes = arrayfun(@(x)sprintf('%d',x),0:9,'uniformoutput',false) ;