欢迎来到专栏《2小时玩转开源框架系列》,这是我们第10篇,前面已经说过了caffe,tensorflow,pytorch,mxnet,keras,paddlepaddle,cntk,chainer,deeplearning4j。
今天说MatConvnet,本文所用到的数据,代码请参考我们官方git
https://github.com/longpeng2008/LongPeng_ML_Course
作者&编辑 | 言有三
1 MatConvnet是什么
不同于各类深度学习框架广泛使用的语言Python,MatConvnet是用matlab作为接口语言的开源深度学习库,底层语言是cuda。
官网地址为:http://www.vlfeat.org/matconvnet/
github地址为:https://github.com/vlfeat/matconvnet
因为是在matlab下面,所以debug的过程非常的方便,而且本身就有很多的研究者一直都使用matlab语言,所以其实该语言的群体非常大。
在用python之前,我也是用matlab的,那个经典的deep-learning-toolbox的代码其实也非常值得研读,说起来,matlab还是非常做图像处理的。
2 MatConvnet训练准备
2.1 安装
以linux系统为例,首先要安装好matlab,这个大家自己搞定吧。然后,在matlab环境下进行安装,几行代码就可以。
mex -setup ##设置好编译器
untar('http://www.vlfeat.org/matconvnet/download/matconvnet-1.0-beta25.tar.gz') ;
cd matconvnet-1.0-beta25
run matlab/vl_compilenn ;
没有报错的话就完成了,完成后为了确保没有问题,先用官方的例子确认一下。
%下载预训练模型
urlwrite(... 'http://www.vlfeat.org/matconvnet/models/imagenet-vgg-f.mat', ... 'imagenet-vgg-f.mat')
%设置环境
run matlab/vl_setupnn
%载入模型
net = load('imagenet-vgg-f.mat') ;
net = vl_simplenn_tidy(net)
%读取图像并预处理
im = imread('peppers.png') ;
im_ = single(im) ; % note: 255 range
im_ = imresize(im_, net.meta.normalization.imageSize(1:2)) ;
im_ = im_ - net.meta.normalization.averageImage ;
%得到结果
res = vl_simplenn(net, im_) ;
result.scores = squeeze(gather(res(end).x)) ;
[bestScore, best] = max(scores) ;
figure(1) ; clf ; imagesc(im) ;
title(sprintf('%s (%d), score %.3f',...
net.meta.classes.description{best}, best, bestScore)) ;
成功的话结果如下:
更复杂的还可以用DagNN wrapper的API,不过这不是本文的主要目标,因此不再讲述。
当然,我们是要用GPU的,所以还要完成GPU编译,按照这里来:
http://www.vlfeat.org/matconvnet/install/
因为版本不一定完全匹配,所以用nvcc编译。
vl_compilenn('enableGpu', true, 'cudaRoot', '/usr/local/cuda','cudaMethod', 'nvcc')
2.2 数据准备
前面讲了官方的例子,接下来就是要用我们自己的例子了,第一步还是老规矩,准备数据,完整的代码如下。
function imdb = mydataset(datadir)
inputSize =[48,48,1];
subdir=dir(datadir);
imdb.images.data=[];
imdb.images.labels=[];
imdb.images.set = [] ;
imdb.meta.sets = {'train', 'val', 'test'} ;
image_counter=0;
trainratio=0.8;
subdir
for i=3:length(subdir)
imgfiles=dir(fullfile(datadir,subdir(i).name));
imgpercategory_count=length(imgfiles)-2;
disp([i-2 imgpercategory_count]);
image_counter=image_counter+imgpercategory_count;
for j=3:length(imgfiles)
img=imread(fullfile(datadir,subdir(i).name,imgfiles(j).name));
img=imresize(img, inputSize(1:2));
img=single(img);
% [~,~,d]=size(img);
% if d==3
% img=rgb2gray(img);
% continue;
% end
imdb.images.data(:,:,:,end+1)=single(img);
imdb.images.labels(end+1)= i-2;
if j-2<imgpercategory_count*trainratio
imdb.images.set(end+1)=1;
else
imdb.images.set(end+1)=3;
end
end
end
dataMean=mean(imdb.images.data,4);
imdb.images.data = single(bsxfun(@minus,imdb.images.data, dataMean)) ;
imdb.images.data_mean = dataMean;
end
如果使用过Matlab的同学,应该一下就看懂了,实际上就是3个步骤:
1)使用fullfile函数遍历图像。
2)预处理,包括缩放,类型转换等。
3)生成IMDB格式数据集。
2.3 网络定义
还是跟以前一样,定义一个3层的卷积神经网络,非常简单,不做过多注释了噢。
function net =simpleconv3()
rng('default');
rng(0) ;
f=1/100 ;
usebatchNormalization = true ;
net.layers = {};
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(3,3,3,12, 'single'), zeros(1, 12, 'single')}}, ...
'stride', 1, ...
'pad', 1) ;
net.layers{end+1} = struct('type', 'pool', ...
'method', 'max', ...
'pool', [2 2], ...
'stride', 2, ...
'pad', 0) ;
net.layers{end+1} = struct('type', 'relu') ;
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(3,3,12,24, 'single'),zeros(1,24,'single')}}, ...
'stride', 1, ...
'pad', 1) ;
net.layers{end+1} = struct('type', 'pool', ...
'method', 'max', ...
'pool', [2 2], ...
'stride', 2, ...
'pad', 0) ;
net.layers{end+1} = struct('type', 'relu') ;
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(3,3,24,48, 'single'),zeros(1,48,'single')}}, ...
'stride', 1, ...
'pad', 1) ;
net.layers{end+1} = struct('type', 'pool', ...
'method', 'max', ...
'pool', [2 2], ...
'stride', 2, ...
'pad', 0) ;
net.layers{end+1} = struct('type', 'relu') ;
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(6,6,48,2, 'single'),zeros(1,2,'single')}}, ...
'stride', 1, ...
'pad', 0) ;
net.layers{end+1} = struct('type', 'softmaxloss') ;
net = insertBnorm(net, 1) ;
net = insertBnorm(net, 5) ;
net = insertBnorm(net, 9) ;
% Meta parameters
net.meta.inputSize = [48 48 3] ;
net.meta.trainOpts.learningRate = logspace(-2, -5, 100);
net.meta.trainOpts.numEpochs = 50 ;
net.meta.trainOpts.batchSize = 16 ;
% Fill in defaul values
net = vl_simplenn_tidy(net) ;
end
% --------------------------------------------------------------------
3 模型训练
完整代码如下。
function [net, info] = trainconv3()
global datadir;
run matlab/vl_setupnn ; %初始化
datadir='/home/longpeng/project/LongPeng_ML_Course/projects/classification/matconvnet/conv3/mouth';
opts.expDir = fullfile('/home/longpeng/project/LongPeng_ML_Course/projects/classification/matconvnet/conv3/','imdb') ;
opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');
if exist(opts.imdbPath,'file')
imdb=load(opts.imdbPath);
else
imdb=mydataset(datadir);
mkdir(opts.expDir) ;
save(opts.imdbPath, '-struct', 'imdb') ;
end
net=simpleconv3();
net.meta.normalization.averageImage =imdb.images.data_mean ;
opts.train.gpus=1;
[net, info] = cnn_train(net, imdb, getBatch(opts), ...
'expDir', opts.expDir, ...
net.meta.trainOpts, ...
opts.train, ...
'val', find(imdb.images.set == 3)) ;
function fn = getBatch(opts)
% --------------------------------------------------------------------
fn = @(x,y) getSimpleNNBatch(x,y) ;
end
function [images, labels] = getSimpleNNBatch(imdb, batch)
images = imdb.images.data(:,:,:,batch) ;
labels = imdb.images.labels(1,batch) ;
if opts.train.gpus > 0
images = gpuArray(images) ;
end
end
end
总共就这么几个步骤:
1)初始化环境,run matlab/vl_setupnn 。
2)定义网络:net=simpleconv3()。
3)调用训练接口:[net, info] = cnn_train(net, imdb, getBatch(opts)。
4 可视化
工具已经给封装好了可视化,直接运行代码就会跳出来,可以看出收敛正常,略有过拟合,展示的分别是softmax损失和错误率。
5 测试
%netpath=[opts.expDir '/net-epoch-50.mat'];
netpath='/home/longpeng/project/LongPeng_ML_Course/projects/classification/matconvnet/conv3/imdb/net-epoch-50.mat';
class=1;index=1;
datadir='/home/longpeng/project/LongPeng_ML_Course/projects/classification/matconvnet/conv3/mouth';
subdir=dir(datadir);
imgfiles=dir(fullfile(datadir,subdir(class+2).name));
img=imread(fullfile(datadir,subdir(class+2).name,imgfiles(index+2).name));
imshow(img);
net=load(netpath);
net=net.net;
im_=single(img);
im_=imresize(im_,net.meta.inputSize(1:2));
im_=im_ - net.meta.normalization.averageImage;
opts.batchNormalization = false ;
net.layers{end}.type = 'softmax';
res=vl_simplenn(net,im_);
scores=squeeze(gather(res(end).x));
[bestScore,best]=max(scores);
str=[subdir(best+2).name ':' num2str(bestScore)];
title(str);
disp(str);
从上面可以看出,就是载入模型,完成正确的预处理,然后进行分类。
一个样本的结果如下,0:0.99968,表示分类为类别0的概率是0.99968,可知结果正确,0代表的类别就是中性表情。
总结
有很多的优秀代码仍然使用matconvnet,而且它的社区所包含的预训练模型也非常多,非常适合训练过程中进行调试,建议大家有Matlab环境和多余精力的可以学习一下,学习成本很低,技多不压身嘛。