摘要
网络结构:mnist的LeNet结构
数据集:cifar10中的batch1和batch2
准确率:测试集测试51.37%
我将下载好的cifar-10数据可视化为图片后,这样就可以假装是自己的数据集了,毕竟以后任何的图片数据集我都会操作了。将batch1和batch2做成和mnist的mat数据结构一样的mat结构(其实就是依样画葫芦,mnist怎么存储的我就怎么存储的,保持所有格式一致),我用batch1的10000张图片做训练集,用batch2的10000张图片做验证集。然后修改mnist网络的数据参数(conv、pooling层的参数要自己计算),这样就训练出自己数据的模型了,然后写程序测试模型的准确率。
制作数据集
cifar10可视化,之前说过matlab环境下的cifar10数据可视化
参照mnist的数据格式:mnist的imdb.mat文件包含一个images和一个meta
data中为28*28*1*70000的数据集,其中60000为训练集,10000为验证集
sets中是三个标签,set = 1为训练集,set = 2为测试集,set = 3为验证集
class中是10个类别
前面说过cifar10中共20000张图片,训练验证各一半,每张大小为32*32*3,制作数据集的时候会在第四维上进行联结,所以我们的data大小为32*32*3*20000,其它不变
制作数据集代码如下:
%将cifar10的图像构建成和mnist相似的数据体格式
% 读取一个文件夹的多幅图片,如果为单通道图片则为I(:,:,k)
imgpath1 = ('E:\2-科研\2-实验\cifar-10-batches-mat\image\data_batch_1\');
di1 = dir(fullfile(imgpath1,'*.png'));
for k= 1:length(di1)
I(:,:,:,k) = imread([imgpath1,di1(k).name]);
end
%读一张图片验证
%I1 = imread(fullfile(imgpath,'batch_label_0_116.png'));
%将其转为double型
data1 = im2double(I);
for k= 1:length(di1)
label1(1,k) = str2num(di1(k).name(13:13));
end
% ---------------------------------------------------------------------
imgpath2 = ('E:\2-科研\2-实验\cifar-10-batches-mat\image\data_batch_2\');
di2 = dir(fullfile(imgpath1,'*.png'));
for k= 1:length(di2)
I2(:,:,:,k) = imread([imgpath1,di2(k).name]);
end
data2 = im2double(I2);
for k= 1:length(di2)
label2(1,k) = str2num(di2(k).name(13:13));
end
% ----------------------------------------------------------------------------
% imdb结构体:
% 1. 这是用于cnn_train中的结构体,也就是实际训练的部分。
% 2. 该结构体内共有4个部分,由data,label,set,class组成。
% data:包含了train data和test data。
% label:包含了train label和test label。
% set:set的个数个label的个数是相等的,set=1表示这个数据是train data,set=3则表示这个数据是test data。 以此方法用于计算机自己判断的标准。
% class:于数据中的class完全一样。
% 3. imdb构造时遵循train在上层,test在下层的顺序。
% 4. 相关的data需要进行泛化处理。
% --------------------------------------------------------------------
%计算出训练数据集中所有图像的均值
%set = 1 对应训练;set = 3 对应的是测试
set = [ones(1,numel(label1)) 3*ones(1,numel(label2))];%numel返回元素的总数
data = single(cat(4,data1,data2)); %将x1的训练数据集和x2的测试数据集的第三个维度进行拼接组成新的数据集,并且转为single型减少内存
dataMean = mean(data1(:,:,:,:), 4);%求出训练数据集中所有的图像的均值
dataMean = single(dataMean);
data = bsxfun(@minus, data, dataMean) ; %利用bsxfun函数将数据集中的每个元素逐个减去均值
% ---------------------------------------------------------------------
%将数据存入imdb结构中
mycifar_batch1_2_imdb.images.data = data ;%data的大小为[32 32 3 20000]前10000训练,后10000测试
mycifar_batch1_2_imdb.images.data_mean = dataMean; %dataMean的大小为[32 32 3]
mycifar_batch1_2_imdb.images.labels = cat(2, label1, label2) ; %拼接训练数据集和测试数据集的标签,拼接后的大小为[1 20000]
mycifar_batch1_2_imdb.images.set = set ; %set的大小为[1 20000],unique(set) = [1 3]
mycifar_batch1_2_imdb.meta.sets = {'train', 'val', 'test'} ; %imdb.meta.sets=1用于训练,imdb.meta.sets=2用于验证,imdb.meta.sets=3用于测试
%读入类别
mycifar_batch1_2_imdb.meta.class = {'airplane' 'automobile' 'bird' 'cat' 'deer' 'dog' 'frog' 'horse' 'ship' 'truck'};
%imdb.meta.classes = arrayfun(@(x)sprintf('%d',x),0:9,'uniformoutput',false) ;%arrayfun函数通过应用sprintf函数得到array中从0到9的元素并且将其数字标签转化为char文字型
训练模型
- 三个函数说明:
- cnn_mnist_experiments_cifar_my:主函数,主要调用下一个函数,并将结果画图显示
- cnn_mnist_cifar_my:最主要的函数,包括各种函数及训练验证的过程
- cnn_mnist_init_cifar_my:初始化网络结构的函数,主要是网络参数的设定
1.cnn_mnist_experiments_cifar_my
% Experiment with the mycnn_mnist_fc_bnorm
[net_bn, info_bn] = cnn_mnist_cifar_my(...
'expDir', 'data/mnist-bnorm', 'batchNormalization', true);
[net_fc, info_fc] = cnn_mnist_cifar_my(...
'expDir', 'data/mnist-baseline', 'batchNormalization', false);
figure(1) ; clf ;
subplot(1,2,1) ;
semilogy([info_fc.val.objective]', 'o-') ; hold all ;
semilogy([info_bn.val.objective]', '+--') ;
xlabel('Training samples [x 10^3]'); ylabel('energy') ;
grid on ;
h=legend('BSLN', 'BNORM') ;
set(h,'color','none');
title('objective') ;
subplot(1,2,2) ;
plot([info_fc.val.top1err]', 'o-') ; hold all ;
plot([info_fc.val.top5err]', '*-') ;
plot([info_bn.val.top1err]', '+--') ;
plot([info_bn.val.top5err]', 'x--') ;
h=legend('BSLN-val','BSLN-val-5','BNORM-val','BNORM-val-5') ;
grid on ;
xlabel('Training samples [x 10^3]'); ylabel('error') ;
set(h,'color','none') ;
title('error') ;
drawnow ;
2.cnn_mnist_experiments_cifar_my
function [net, info] = cnn_mnist_cifar_my(varargin)
run('E:\2-科研\MatConvNet\matconvnet-1.0-beta25\matconvnet-1.0-beta25\matlab\vl_setupnn.m') ;
opts.batchNormalization = false ; %选择batchNormalization的真假
opts.network = [] ; %初始化一个网络
opts.networkType = 'simplenn' ; %选择网络结构 %%% simplenn %%% dagnn
[opts, varargin] = vl_argparse(opts, varargin) ; %调用vl_argparse函数
sfx = opts.networkType ; %sfx=simplenn
if opts.batchNormalization, sfx = [sfx '-bnorm'] ; end %这里条件为假
opts.expDir = fullfile(vl_rootnn,'my','1025','data',['cifar10-' sfx]) ; %选择数据存放的路径:data\cifar-baseline-simplenn
[opts, varargin] = vl_argparse(opts, varargin) ; %调用vl_argparse函数
opts.dataDir = fullfile(vl_rootnn,'my','1025','data', 'cifar10') ;
opts.mycifar_batch1_2_imdbPath = fullfile(opts.expDir, 'mycifar_batch1_2_imdb.mat'); %选择mycifar_batch1_2_imdb结构体的路径:data\data\cifar-baseline-simplenn\mycifar_batch1_2_imdb
%------------------------------新增加的两句
opts.whitenData = true ;
opts.contrastNormalization = true ;
%------------------------------------------
opts.train = struct() ; %选择训练集返回为struct型
opts = vl_argparse(opts, varargin) ; %调用vl_argparse函数
% --------------------------------------------------------------------
% Prepare data
% --------------------------------------------------------------------
if isempty(opts.network) %如果原网络为空:
net = cnn_mnist_init_cifar_my('batchNormalization', opts.batchNormalization, ... % 则调用cnn_cifat_init网络结构
'networkType', opts.networkType) ;
else %否则:
net = opts.network ; % 使用上面选择的数值带入现有网络
opts.network = [] ;
end
if exist(opts.mycifar_batch1_2_imdbPath, 'file')%如果cifar中存在mycifar_batch1_2_imdb的结构体:
mycifar_batch1_2_imdb = load(opts.mycifar_batch1_2_imdbPath) ; % 载入mycifar_batch1_2_imdb
else %否则:
mycifar_batch1_2_imdb = getMnistmycifar_batch1_2_imdb(opts) ; % 调用getMnistmycifar_batch1_2_imdb函数得到mycifar_batch1_2_imdb并保存
mkdir(opts.expDir) ;
save(opts.mycifar_batch1_2_imdbPath, '-struct', 'mycifar_batch1_2_imdb') ;
end
%arrayfun函数通过应用sprintf函数得到array中从1到10的元素并且将其数字标签转化为char文字型
net.meta.classes.name = arrayfun(@(x)sprintf('%d',x),1:10,'UniformOutput',false) ;
% --------------------------------------------------------------------
% Train
% --------------------------------------------------------------------
switch opts.networkType %选择网络类型:
case 'simplenn', trainfn = @cnn_train ; % 1.simplenn
case 'dagnn', trainfn = @cnn_train_dag ; % 2.dagnn
end
%调用训练函数,开始训练:find(mycifar_batch1_2_imdb.images.set == 3)为验证集的样本
[net, info] = trainfn(net, mycifar_batch1_2_imdb, getBatch(opts), ...
'expDir', opts.expDir, ...
net.meta.trainOpts, ...
opts.train, ...
'val', find(mycifar_batch1_2_imdb.images.set == 3)) ;
% --------------------------------------------------------------------
function fn = getBatch(opts)
%% --------------------------------------------------------------
% 函数名:getBatch
% 功能: 1.由opts返回函数
% 2.从imdb结构体取出数据
% 相对于正常数据集,如果Batch_Size过小,训练数据就会非常难收敛,从而导致underfitting。
% 增大Batch_Size,相对处理速度加快。
% 增大Batch_Size,所需内存容量增加(epoch的次数需要增加以达到最好结果)。
% 这里我们发现上面两个矛盾的问题,因为当epoch增加以后同样也会导致耗时增加从而速度下降。因此我们需要寻找最好的batch_size。
% 再次重申:batchsize 的正确选择是为了在内存效率和内存容量之间寻找最佳平衡。
%%--------------------------------------------------------------------
switch lower(opts.networkType) %根据网络类型使用不同的getBatcch
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(mycifar_batch1_2_imdb, batch)
%% --------------------------------------------------------------
% 函数名:getSimpleNNBatch
% 功能: 1.由SimpleNN网络的批得到函数
% 2.batch为样本的索引值
% ------------------------------------------------------------------------
images = mycifar_batch1_2_imdb.images.data(:,:,:,batch) ;
labels = mycifar_batch1_2_imdb.images.labels(1,batch) ;
% --------------------------------------------------------------------
function inputs = getDagNNBatch(opts, mycifar_batch1_2_imdb, batch)
%% --------------------------------------------------------------------
images = mycifar_batch1_2_imdb.images.data(:,:,:,batch) ;
labels = mycifar_batch1_2_imdb.images.labels(1,batch) ;
if opts.numGpus > 0
images = gpuArray(images) ;
end
inputs = {'input', images, 'label', labels} ;
function mycifar_batch1_2_imdb = getMnistmycifar_batch1_2_imdb(opts)
%% --------------------------------------------------------------
% 函数名:getMnistmycifar_batch1_2_imdb
% 功能: 1.从mnist数据集中获取data
% 2.将得到的数据减去mean值
% 3.将处理后的数据存放如mycifar_batch1_2_imdb结构中
% ------------------------------------------------------------------------
% Preapre the mycifar_batch1_2_imdb structure, returns image data with mean image subtracted
load('E:\2-科研\2-实验\mycifar_batch1_2_imdb.mat');
3.cnn_mnist_init_cifar_my
%cifar数据集batch1和batch2 运用mnist的网络来训练
function net = cnn_mnist_init_cifar_my(varargin)
% CNN_MNIST_LENET Initialize a CNN similar for MNIST
opts.batchNormalization = true ;
opts.networkType = 'simplenn' ;
opts = vl_argparse(opts, varargin) ;
rng('default');%设置随机数发生器,重现每次运行结果?
rng(0) ;
% 开始构建网络结构,这里是LeNet5
f=1/100 ;
net.layers = {} ;
net.layers{end+1} = struct('type', 'conv', ...
'weights', {{f*randn(5,5,3,32, 'single'), zeros(1, 32, '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(5,5,32,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(5,5,50,500, 'single'), zeros(1,500,'single')}}, ...
'stride', 1, ...
'pad', 0) ;
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) ;
net.layers{end+1} = struct('type', 'softmaxloss') ;
% optionally switch to batch normalization
if opts.batchNormalization %如果opts.batchNormalization为真:
net = insertBnorm(net, 1) ; %在原网络第一层后添加Bnorm
net = insertBnorm(net, 4) ; %在原网络第四层后添加Bnorm
net = insertBnorm(net, 7) ;%在原网络第七层后添加Bnorm
end
% Meta parameters结构元参数
net.meta.inputSize = [32 32 3] ; %大小为28*28*1的input data
net.meta.trainOpts.learningRate = 0.001 ; %学习率为0.001
net.meta.trainOpts.numEpochs = 2 ; %Epoch为20
net.meta.trainOpts.batchSize = 100 ;%批的大小为100
% Fill in defaul values
net = vl_simplenn_tidy(net) ;%添加默认的属性值
% Switch to DagNN if requested
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
% --------------------------------------------------------------------
function net = insertBnorm(net, l)
% --------------------------------------------------------------------
assert(isfield(net.layers{l}, 'weights')); %断言以确保第l层有权重项
ndim = size(net.layers{l}.weights{1}, 4); %第l层的神经元的个数(卷积核个数)
layer = struct('type', 'bnorm', ...
'weights', {{ones(ndim, 1, 'single'), zeros(ndim, 1, 'single')}}, ...
'learningRate', [1 1 0.05], ...
'weightDecay', [0 0]) ;%Bnorm层的权值=上一层的神经元个数
net.layers{l}.weights{2} = [] ; % eliminate bias in previous conv layer
net.layers = horzcat(net.layers(1:l), layer, net.layers(l+1:end)) ;
4.测试模型的准确率
- 这里用的是验证集的图片,accurcy = 51.37%
%% 测试所有图片
run('E:\2-科研\MatConvNet\matconvnet-1.0-beta25\matconvnet-1.0-beta25\matlab\vl_setupnn.m') ;
load('E:\2-科研\MatConvNet\matconvnet-1.0-beta25\matconvnet-1.0-beta25\my\1025\data\mnist-bnorm1\net-epoch-20.mat') ;
mycifar_batch1_2_imdb = load('E:\2-科研\MatConvNet\matconvnet-1.0-beta25\matconvnet-1.0-beta25\my\1025\data\mnist-bnorm\mycifar_batch1_2_imdb.mat');
test_index = find(mycifar_batch1_2_imdb.images.set==3);
test_data = mycifar_batch1_2_imdb.images.data(:,:,:,test_index);
test_label =mycifar_batch1_2_imdb.images.labels(test_index);
net = vl_simplenn_tidy(net) ;
net.layers{1,end}.type = 'softmax';
for i = 1:length(test_label)
i
im_ = test_data(:,:,:,i);
im_ = im_ - mycifar_batch1_2_imdb.images.data_mean;
res = vl_simplenn(net, im_,[], [], ...
'accumulate', 0, ...
'mode', 'test', ...
'backPropDepth', Inf, ...
'sync', 0, ...
'cudnn', 1);
scores = squeeze(gather(res(end).x)) ;
[bestScore, best] = max(scores) ;
pre(i) = best;
end
% 计算准确率
accurcy = length(find(pre==test_label))/length(test_label);
disp(['accurcy = ',num2str(accurcy*100),'%']);
致谢
在这一整个网络的折腾中其实参考了很多的博客,但尤其是这两个博客的帮助最大:
深度学习12:能力提升, 一步一步的介绍如何自己构建网络和训练,利用MatConvNet
MatConvNet框架下mnist数据集测试
想说
这个系列折腾了两天多,终于出成果了,虽说最后结果是51%但十分类的问题如果盲猜的准确率才10%,作为第一个自己训练的网络已经很满足很开心了!接下去会再继续努力的。
接下去想探究三个问题:1.数据集的图片不是正方形该如何处理,在哪块进行改动;2.在数据集没有那么大的情况下是否能支持;3.探究其它网络结构