深度学习(十四):详解Matconvnet使用imagenet模型训练自己的数据集
来源:互联网 发布:淘宝淘金币兑换区 编辑:程序博客网 时间:2024/04/29 07:35
上节讨论过如何使一个简单的cnn网络训练mnist数据集,该节介绍复杂并且使用广泛的使用imagenet网络的预训练模型训练自己的数据集。
Ok首先是自己的数据集了。Matconvnet中训练imagenet的数据集的准备不像caffe这些工具箱弄得那么好,弄个train文件夹,test文件夹,以及两个txt索引就好了,感觉很不人性。后面我将会将其输入改为这种人性的类型输入格式。
这里是有一个网友准备的很小的图像数据库,原始链接
但是其类别索引是从0开始的,这在matlab中是不符合的,所以我将其改成从1开始的。同时添加了一个类class标签的txt,改完的
数据集下载地址
下载完打开这个文件夹看到:
其中train就是训练所用到的所有图片,test为测试所有图片,train_label为对应图片的名字以及跟随的类标签(从1开始),打开txt可以看到为:
这种格式的txt相信应该很容易从你自己的数据集中弄到。依次类推,test.txt中存放的是test文件夹所有图片的名字以及其类别。
Classind 就是每一类表示的分类的名字。
数据准备好了,放在哪呢?我们在Matconvnet的工具箱目录下新建一个文件夹为data,然后将这个数据集放进去,如下:
我们是在训练好的model上继续训练,所以需要一个model,再在这文件夹下建立一个models文件夹,然后把imagenet-vgg-f.mat放入到models里面。这里我们使用的是vgg-f的model,这个model在前两节说到了,自己去下载。
接着就是网络训练了。再建立一个文件夹train,可以编写函数了。
首先是主函数:
这里复制一下examples中的imagenet里面的一个主函数cnn_dicnn,然后修改一下里面的路径,程序为:
function [net, info] = cnn_dicnn(varargin)%CNN_DICNN Demonstrates fine-tuning a pre-trained CNN on imagenet datasetrun(fullfile(fileparts(mfilename('fullpath')), ... '..', 'matconvnet', 'matlab', 'vl_setupnn.m')) ;% 修改读入文件夹的路径opts.dataDir = fullfile('data','image') ;opts.expDir = fullfile('exp', 'image') ;% 导入预训练的modelopts.modelPath = fullfile('models','imagenet-vgg-f.mat');[opts, varargin] = vl_argparse(opts, varargin) ;opts.numFetchThreads = 12 ;opts.lite = false ;opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');opts.train = struct() ;opts.train.gpus = [];opts.train.batchSize = 8 ;opts.train.numSubBatches = 4 ;opts.train.learningRate = 1e-4 * [ones(1,10), 0.1*ones(1,5)];opts = vl_argparse(opts, varargin) ;if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;% -------------------------------------------------------------------------% Prepare model% -------------------------------------------------------------------------net = load(opts.modelPath);% 修改一下这个modelnet = prepareDINet(net,opts);% -------------------------------------------------------------------------% Prepare data% -------------------------------------------------------------------------% 准备数据格式if exist(opts.imdbPath,'file') imdb = load(opts.imdbPath) ;else imdb = cnn_image_setup_data('dataDir', opts.dataDir, 'lite', opts.lite) ; mkdir(opts.expDir) ; save(opts.imdbPath, '-struct', 'imdb') ;endimdb.images.set = imdb.images.sets;% Set the class names in the networknet.meta.classes.name = imdb.classes.name ;net.meta.classes.description = imdb.classes.name ;% % 求训练集的均值imageStatsPath = fullfile(opts.expDir, 'imageStats.mat') ;if exist(imageStatsPath) load(imageStatsPath, 'averageImage') ;else averageImage = getImageStats(opts, net.meta, imdb) ; save(imageStatsPath, 'averageImage') ;end% % 用新的均值改变均值net.meta.normalization.averageImage = averageImage;% -------------------------------------------------------------------------% Learn% -------------------------------------------------------------------------% 索引训练集==1 和测试集==3opts.train.train = find(imdb.images.set==1) ;opts.train.val = find(imdb.images.set==3) ;% 训练[net, info] = cnn_train_dag(net, imdb, getBatchFn(opts, net.meta), ... 'expDir', opts.expDir, ... opts.train) ;% -------------------------------------------------------------------------% Deploy% -------------------------------------------------------------------------% 保存训练完的网络net = cnn_imagenet_deploy(net) ;modelPath = fullfile(opts.expDir, 'net-deployed.mat');net_ = net.saveobj() ;save(modelPath, '-struct', 'net_') ;clear net_ ;% -------------------------------------------------------------------------function fn = getBatchFn(opts, meta)% -------------------------------------------------------------------------useGpu = numel(opts.train.gpus) > 0 ;bopts.numThreads = opts.numFetchThreads ;bopts.imageSize = meta.normalization.imageSize ;bopts.border = meta.normalization.border ;% bopts.averageImage = []; bopts.averageImage = meta.normalization.averageImage ;% bopts.rgbVariance = meta.augmentation.rgbVariance ;% bopts.transformation = meta.augmentation.transformation ;fn = @(x,y) getDagNNBatch(bopts,useGpu,x,y) ;% -------------------------------------------------------------------------function inputs = getDagNNBatch(opts, useGpu, imdb, batch)% -------------------------------------------------------------------------% 判断读入数据为训练还是测试for i = 1:length(batch) if imdb.images.set(batch(i)) == 1 %1为训练索引文件夹 images(i) = strcat([imdb.imageDir.train filesep] , imdb.images.name(batch(i))); else images(i) = strcat([imdb.imageDir.test filesep] , imdb.images.name(batch(i))); endend;isVal = ~isempty(batch) && imdb.images.set(batch(1)) ~= 1 ;if ~isVal % training im = cnn_imagenet_get_batch(images, opts, ... 'prefetch', nargout == 0) ;else % validation: disable data augmentation im = cnn_imagenet_get_batch(images, opts, ... 'prefetch', nargout == 0, ... 'transformation', 'none') ;endif nargout > 0 if useGpu im = gpuArray(im) ; end labels = imdb.images.label(batch) ; inputs = {'input', im, 'label', labels} ;end% 求训练样本的均值% -------------------------------------------------------------------------function averageImage = getImageStats(opts, meta, imdb)% -------------------------------------------------------------------------train = find(imdb.images.set == 1) ;batch = 1:length(train);fn = getBatchFn(opts, meta) ;train = train(1: 100: end);avg = {};for i = 1:length(train) temp = fn(imdb, batch(train(i):train(i)+99)) ; temp = temp{2}; avg{end+1} = mean(temp, 4) ;endaverageImage = mean(cat(4,avg{:}),4) ;% 将GPU格式的转化为cpu格式的保存起来(如果有用GPU)averageImage = gather(averageImage);
这里涉及到了几个函数需要自己写,其他的都有调用的。第一个函数就是数据格式的准备。Matconvnet训练是有自己的格式要求的,一般都是生成imdb结构体保存起来。下面是如何将我们这种格式的训练样本集生成要求的格式,建立一个m函数cnn_image_setup_data代码如下:
function imdb = cnn_image_setup_data(varargin)opts.dataDir = fullfile('data','image') ;opts.lite = false ;opts = vl_argparse(opts, varargin) ;% ------------------------------------------------------------------------% Load categories metadata% -------------------------------------------------------------------------metaPath = fullfile(opts.dataDir, 'classInd.txt') ;fprintf('using metadata %s\n', metaPath) ;tmp = importdata(metaPath);nCls = numel(tmp);% 判断类别与设定的是否一样 10为样本的类别总数(自己的数据集需要修改)if nCls ~= 10 error('Wrong meta file %s',metaPath);end% 将名字分离出来cats = cell(1,nCls);for i=1:numel(tmp) t = strsplit(tmp{i}); cats{i} = t{2};end% 数据集文件夹选择imdb.classes.name = cats ;imdb.imageDir.train = fullfile(opts.dataDir, 'train') ;imdb.imageDir.test = fullfile(opts.dataDir, 'test') ;%% -----------------------------------------------------------------% load image names and labels% -------------------------------------------------------------------------name = {};labels = {} ;imdb.images.sets = [] ;%%fprintf('searching training images ...\n') ;% 导入训练类别标签train_label_path = fullfile(opts.dataDir, 'train_label.txt') ;train_label_temp = importdata(train_label_path);temp_l = train_label_temp.data;for i=1:numel(temp_l) train_label{i} = temp_l(i);endif length(train_label) ~= length(dir(fullfile(imdb.imageDir.train, '*.jpg'))) error('training data is not equal to its label!!!');endi = 1;for d = dir(fullfile(imdb.imageDir.train, '*.jpg'))' name{end+1} = d.name; labels{end+1} = train_label{i} ; if mod(numel(name), 10) == 0, fprintf('.') ; end if mod(numel(name), 500) == 0, fprintf('\n') ; end imdb.images.sets(end+1) = 1;%train i = i+1;end%%fprintf('searching testing images ...\n') ;% 导入测试类别标签test_label_path = fullfile(opts.dataDir, 'test_label.txt') ;test_label_temp = importdata(test_label_path);temp_l = test_label_temp.data;for i=1:numel(temp_l) test_label{i} = temp_l(i);endif length(test_label) ~= length(dir(fullfile(imdb.imageDir.test, '*.jpg'))) error('testing data is not equal to its label!!!');endi = 1;for d = dir(fullfile(imdb.imageDir.test, '*.jpg'))' name{end+1} = d.name; labels{end+1} = test_label{i} ; if mod(numel(name), 10) == 0, fprintf('.') ; end if mod(numel(name), 500) == 0, fprintf('\n') ; end imdb.images.sets(end+1) = 3;%test i = i+1;end%%labels = horzcat(labels{:}) ;imdb.images.id = 1:numel(name) ;imdb.images.name = name ;imdb.images.label = labels ;
这个函数里面有几点需要注意的是,类别总数需要视自己的数据集修改。
之后是对导入的预训练model进行一点处理,建立一个函数
% -------------------------------------------------------------------------function net = prepareDINet(net,opts)% -------------------------------------------------------------------------% replace fc8fc8l = cellfun(@(a) strcmp(a.name, 'fc8'), net.layers)==1;%% note: 下面这个是类别数,一定要和自己的类别数吻合(这里为10类)nCls = 10;sizeW = size(net.layers{fc8l}.weights{1});% 将权重初始化if sizeW(4)~=nCls net.layers{fc8l}.weights = {zeros(sizeW(1),sizeW(2),sizeW(3),nCls,'single'), ... zeros(1, nCls, 'single')};end% change loss 添加一个loss层用于训练net.layers{end} = struct('name','loss', 'type','softmaxloss') ;% convert to dagnn dagnn网络,还需要添加下面这几层才能训练net = dagnn.DagNN.fromSimpleNN(net, 'canonicalNames', true) ;net.addLayer('top1err', dagnn.Loss('loss', 'classerror'), ... {'prediction','label'}, 'top1err') ;net.addLayer('top5err', dagnn.Loss('loss', 'topkerror', ... 'opts', {'topK',5}), ...{'prediction','label'}, 'top5err') ;
这里有一个重要的参数就是你的类别数nCls,还是是多少类就修改多少。
上面的几个函数都放在train文件夹下面就可以了,如下:
这里还有许多参数需要说明,一个重要的参数是迭代次数,以及是否选择使用gpu。
对于迭代次数,在cnn_dicnn函数中的训练函数cnn_train_dag
函数中,打开如下:
在训练的时候一般默认的迭代次数都会很多,还好每一代训练的结果都会保存,即使中断了,再运行的时候,程序会接着上一次的代数接着训练的。这样的一个好处是,假如你发现结果好的差不多了,又不想训练了,可以终止程序,把这个迭代次数改到目前的代数,那么下一次程序读入后发现到训练代数了。就不会在训练了,直接结束,执行下面的保存最终网络的程序过程,这一点操作还是很重要的很好用的。
对于是否需要选择gpu,这里有一个参数,同时最好也把主函数里面的gpu=[]也修改一样的,[]中填的是你的电脑gpu索引号码,比如gpu=[1],就是使用gpu 1来进行训练。
Ok做完这些操作后,就可以训练了,如果选择gpu出错了,先改成cpu试一试,要是cpu没有出错,那么就是gpu没装好。否则就是编译出错了。我实验的是我的gpu版本的matconvnet可以同时工作在cpu和GPU模式。
下面就是训练了。这个训练相对来说不算长,训练100代也就1个多小时,数据库小的缘故。正确训练的截图:
经过一段时间训练,到达指定代数后就会停止,想让它早点停止就按照上面一个方法。停止完后,会在\matconvnet_test\exp\image文件夹下面生成一个net-deployed.mat最终的model,像我让他训练了106代就强制停止了,然后把echo迭代次数从300改成106,再运行主函数,之后就可以得到net-deployed.mat了,这里把106之间的训练结果都删除后,如下:
有了这个model,我们就可以测试了,这个model就是我们训练的最终model。
下面我们来测试,同样对这个数据集中的测试样本进行测试:写一个test_accuracy脚本,添加如下代码:
clcclear% 导入modelnet1 = dagnn.DagNN.loadobj(load('D:\myself\matlab\matlab_documents\matconvnet_test\exp\image\net-deployed.mat')) ;net1.mode = 'test' ;% 导入准备数据imdb = load('D:\myself\matlab\matlab_documents\matconvnet_test\exp\image\imdb.mat') ;opts.dataDir = fullfile('data','image') ;opts.expDir = fullfile('exp', 'image') ;% 找到训练与测试集opts.train.train = find(imdb.images.sets==1) ;opts.train.val = find(imdb.images.sets==3) ;for i = 1:length(opts.train.val) i index = opts.train.val(i); label = imdb.images.label(index); % 读取测试的样本 im_ = imread(fullfile(imdb.imageDir.test,imdb.images.name{index})); im_ = single(im_); im_ = imresize(im_, net1.meta.normalization.imageSize(1:2)) ; im_ = bsxfun(@minus, im_, net1.meta.normalization.averageImage) ; % 测试 net1.eval({'input',im_}) ; scores = net1.vars(net1.getVarIndex('prob')).value ; scores = squeeze(gather(scores)) ; [bestScore, best] = max(scores) ; truth(i) = label; pre(i) = best; end% 计算准确率accurcy = length(find(pre==truth))/length(truth);disp(['accurcy = ',num2str(accurcy*100),'%']);
这样得到结果为accurcy = 89%。
如果大家看这个数据集会发现,这个简易的数据集还是很复杂的,图片都是原始搜集者在淘宝上找的,能有这样的准确率其实还算可以。
至此,使用现存的imagenet网络训练自己的数据库就到此结束了。剩下的只是修改准备自己的数据库即可了。特别需要注意的是,文件夹的名称一定要按我给的来,或者自己修改了要在程序里面去修改。其次是类别数一定记得改成自己的。注意这几点,那么使用大型的cnn训练自己的数据库就很容易了。
源码下载
- 深度学习(十四):详解Matconvnet使用imagenet模型训练自己的数据集
- Matconvnet 训练自己的数据(使用现有模型)
- 深度学习 9. MatConvNet 利用mnist的model来训练自己的data。MatConvNet 训练自己数据(一)。
- 依照ILSVRC2012在matconvnet-1.0-beta23 中用imagenet训练的情况,训练自己的数据
- 深度学习ssd检测模型训练自己的数据集
- 【深度学习】笔记6:使用caffe中的CIFAR10网络模型和自己的图片数据训练自己的模型(步骤详解)
- (3)如何在MatConvNet下训练自己的数据
- 深度学习-CAFFE利用CIFAR10网络模型训练自己的图像数据获得模型-1.制作自己的数据集
- 深度学习(三) YOLOv2训练自己的数据集
- matconvnet环境下训练自己的数据集及模型测试-mnist网络结构-cifar10部分数据集
- 深度学习 13. 能力提升, 一步一步的介绍如何自己构建网络和训练,利用MatConvNet(二),思路整理
- 深度学习12:能力提升, 一步一步的介绍如何自己构建网络和训练,利用MatConvNet
- 【神经网络与深度学习】Caffe使用step by step:使用自己数据对已经训练好的模型进行finetuning
- 使用faster rcnn训练imageNet上的部分数据集
- 训练深度模型的优化问题(十四)
- Matconvnet学习——利用mnist网络训练自己的数据分辨左右手
- 【whr的深度学习总结1】使用Matconvnet训练imbalance全连接网络
- 深度学习-CAFFE利用CIFAR10网络模型训练自己的图像数据获得模型-4应用生成模型进行预测
- 1058. 选择题(20)
- P1007 独木桥
- poj 3321 Apple Tree(dfs标号+树状数组)
- android开发加载assets下的html文件
- Android图片加载框架——UniversalImageLoader
- 深度学习(十四):详解Matconvnet使用imagenet模型训练自己的数据集
- 编码学习
- python 寻找list中最大元素对应的索引
- iOS - 关于环信集成的小bug
- 10.27NOIP 2016模拟赛 PA
- android开发fragment、Activity与Activity之间的值传递
- python socket(四)网络多路复用
- define之用处
- 利用Python的requests模块爬取当当网的书籍信息(待续)