MatConvNet为vlFeat作者写的matlab下的卷积神经网络工具包,可以使用GPU。
主页:http://www.vlfeat.org/matconvnet/
教程:http://www.robots.ox.ac.uk/~vgg/practicals/cnn/index.html
注意:需要下载新版的MatConvNet替换掉教程中工具包中的matconvnet:http://www.vlfeat.org/matconvnet/download/matconvnet-1.0-beta12.tar.gz
本文程序和excise3效果一致,但使用了MatConvNet的examples中类似的网络结果进行定义,可以使用cnn_train函数进行训练。
内容:
- 演示了自定义网络层、自定义误差函数的方法
- 演示了自定义errorFunction的方法
- 演示了对于不同参数分别设置learning rate和weight decay 的方法
不多说,上代码,注释很详细:
function LC_Ex3_usingNetStructure
clc
% -------------------------------------------------------------------------
% Part 3: Learning a simple CNN 改为了使用网络定义
% -------------------------------------------------------------------------
% 改写http://www.robots.ox.ac.uk/~vgg/practicals/cnn/index.htm 的 exercise 3为一个网络
% 注意:MatConvNet工具包已经替换为新版:http://www.vlfeat.org/matconvnet/download/matconvnet-1.0-beta12.tar.gz
setup ;
%%
doSmooth=false;
subMean=false;
doSmooth=true;
subMean=true;
wSize=9;
% -------------------------------------------------------------------------
% Part 3.1: Load an example image and generate its labels
% -------------------------------------------------------------------------
%%
% Load an image
im = rgb2gray(im2single(imread('data/dots.jpg'))) ;
% Compute the location of black blobs in the image
[pos,neg] = extractBlackBlobs(im) ;
%%
% -------------------------------------------------------------------------
% Part 3.2: Image preprocessing
% -------------------------------------------------------------------------
% Pre-smooth the image
if(doSmooth)
im = vl_imsmooth(im,3) ;
end
%%
% Subtract median value
if(subMean)
im = im - median(im(:)) ;
end
%% Create pixel-level labels to compute the loss
y = zeros(size(pos),'single') ;
y(pos) = +1 ;
y(neg) = -1 ;
%% 转换为imdb格式
imdb.images.data = {im} ;
imdb.images.labels = {y} ; %labels的数据结构完全由自己决定,与getBatch函数以及误差层的backward函数兼容即可(在backward函数中以layer.class形式出现,layer.class等于getBatch函数的返回值labels);对于分类问题,一般为[1,1,2,2]等类别信息
imdb.images.set = [1];% 样本类型,1为训练样本,2为validation样本,3为测试样本,,但是cnn_train函数只会对训练样本和测试样本进行计算
imdb.meta.sets = {'train', 'val', 'test'} ;%集合名称,即1代表训练集,2代表val集(validation)
imdb.meta.classes ={'1'}; %类别名称
%% -------------------------------------------------------------------------
% Part 3.3: Learning with stochastic gradient descent
% -------------------------------------------------------------------------
trainOpts.batchSize = 1 ;
trainOpts.numEpochs = 500 ; %运行多少轮
trainOpts.continue = false ;%如果trainOpts.expDir已经有对应epoch的网络,是否直接载入
trainOpts.gpus=[]; %gpu下标,不使用GPU则设为[],,如果有两个GPU可以设为 [1 2]
trainOpts.learningRate =.5 ;
trainOpts.expDir = 'data/ex3-experiment' ; %输出的网络结果、实验结果图存在哪儿
trainOpts.momentum=0.9;
trainOpts.weightDecay = 0.0001 ;
trainOpts.errorFunction=@error_ErrPixels; %用于显示误差的自定义函数
trainOpts.errorLabels ={'错误率'};
net=LC_CNN_Ex3('wSize',wSize); %将pos,neg均传给网络,记录到输出层的结构中,便于根据之前的公式计算误差
trainOpts.val= imdb.images.set==1; %验证集下标,,必须有验证集,否则报错。。。只好把训练集当成验证集了
[net, info] = cnn_train(net, imdb, @getBatch, ...
trainOpts) ;
end
%只有一幅图片,直接返回就行了
function [im, labels]=getBatch(imdb,batch)
im= imdb.images.data{1};
im=reshape(im,[size(im) 1]);
labels=imdb.images.labels{1};
end
%绘图用,用于检测不同epoch下网络的性能,这里返回的指标为分类错误率
function err = error_ErrPixels(opts, labels, res)
% -------------------------------------------------------------------------
resX=res(end-1).x;
y=labels;
fp = resX >0 & y < 0 ;
fn = resX < 1 & y > 0 ;
tn = resX <= 0 & y < 0 ;
tp = resX >= 1 & y > 0 ;
% err=[fp;fn;tn;tp]; %可以设置多个指标,对应的trainOpts.errorLabels 也要改为多个标签
err=100*(1-sum(sum(tp|tn))/sum(sum(fp|fn|tp|tn)));
end
function net=LC_CNN_Ex3(varargin)
% 改写http://www.robots.ox.ac.uk/~vgg/practicals/cnn/index.html 的 exercise 3为一个网络
opts.wSize=3;
opts=vl_argparse(opts,varargin);
net.layers = {} ;
lr=ones(1,10);
weightDecay=[1 0];
w = 10 * randn(opts.wSize,opts.wSize, 1) ; %最后一个表示输出只有一个通道
w = single(w - mean(w(:))) ;
b = single(0) ;
pad1 = ([size(w,1) size(w,1) size(w,2) size(w,2)] - 1) / 2 ;
net.layers{end+1}= struct('type', 'conv', ...
'weights', {{w, b}}, ...
'learningRate', [1 2], ... %自定义learning rate相对于全局的learning rate 的比率,[1 2] 表示w的学习率与全局学习率一致,而偏置b的学习率则为全局学习率的两倍
'weightDecay',weightDecay,...;%自定义weightDecay相对于全局的weightDecay的比率,[1 0]表示只对w矩阵进行weight decay,而不对偏置b进行weight decay
'stride', 1, ...
'pad', pad1) ;
rho2 = 3 ;
pad2 = (rho2 - 1) / 2 ;
net.layers{end+1}= struct('type', 'pool', ...
'method', 'max', ...
'pool', rho2, ...
'stride', 1, ...
'pad', pad2) ;
%% 自定义的误差函数层
net.layers{end+1}= struct('type', 'custom', ...
'forward', @forwardFunc , ... %
'backward', @backwardFunc) ;
end
function resIp1= forwardFunc(layer, resI, resIp1) % 计算数据项误差
pos=layer.class==1;
neg=layer.class==-1;
resIp1.x= mean(max(0, 1 - resI.x(pos))) + ...
mean(max(0, resI.x(neg)));
end
%后向传播,,求导;;;注意layer.class为该batch内图片的labels。
function resI = backwardFunc(layer, resI, resIp1) %计算导数
pos=layer.class==1;
neg=layer.class==-1;
resI.dzdx=- single(resI.x < 1 & pos) / sum(pos(:)) + ...
+ single(resI.x > 0 & neg) / sum(neg(:)) ;
end