深度学习(十四):详解Matconvnet使用imagenet模型训练自己的数据集

2023-11-16

上节讨论过如何使一个简单的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 dataset

run(fullfile(fileparts(mfilename('fullpath')), ...
  '..', 'matconvnet', 'matlab', 'vl_setupnn.m')) ;
% 修改读入文件夹的路径
opts.dataDir = fullfile('data','image') ;
opts.expDir  = fullfile('exp', 'image') ;
% 导入预训练的model
opts.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);
% 修改一下这个model
net = 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') ;
end

imdb.images.set = imdb.images.sets;

% Set the class names in the network
net.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  和测试集==3
opts.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)));
    end
end;
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') ;
end

if 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) ;
end

averageImage = 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);
end
if length(train_label) ~= length(dir(fullfile(imdb.imageDir.train, '*.jpg')))
    error('training data is not equal to its label!!!');
end

i = 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);
end
if length(test_label) ~= length(dir(fullfile(imdb.imageDir.test, '*.jpg')))
    error('testing data is not equal to its label!!!');
end
i = 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 fc8
fc8l = 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脚本,添加如下代码:

clc
clear
% 导入model
net1 = 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训练自己的数据库就很容易了。

源码下载

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度学习(十四):详解Matconvnet使用imagenet模型训练自己的数据集 的相关文章

  • 上传论文到arxiv教程

    由于文章需要引用或者虽然已经被期刊或者会议录用 但还未发表 可能需要先占坑 具体步骤如图 1 arxiv 注册 最好用 edu 邮箱 就不需要资格审查了 官网是 http arxiv org 注册好后直接登录 2 开始一个新的提交 以前记录
  • ADC学习系列(一):ADC基础概念

    本章主要是进行ADC的基础概念学习 从模拟和数字信号进行入手 分析各自的优缺点和应用场合 从而引出数模转换的重要性 紧接着提到了ADC部分最重要的奈奎斯特采样定理 了解采样频率和被测信号频率之间的关系 最后介绍了ADC的采样保持放大电路 作

随机推荐

  • Python全栈开发工程师 第一模块作业

    1 编程题 100分 第一模块作业 1 学习计算机运行原理 绘制一张python代码从输入电脑 到处理 到输出结果的整个流程图 2 构建自己的python编程环境 整个过程做笔记 把截图放到word里提交 3 写下自己学习编程的目标 建议按
  • 【IntelliJ IDEA】升级之后又要激活的解决方法

    用了几个月的idea 在它升级之后 又不听话了 启动时候需要重新激活 解决方法 1 找到C Windows System32 drivers etc 下的hosts文件 在文件中添加如下 0 0 0 0 account jetbrains
  • VC++ CComboBox自绘

    头文件定义 CSWComboBox h pragma once class CSWComboBox public CComboBox DECLARE DYNAMIC CSWComboBox public CSWComboBox CSWCom
  • 传奇GOM引擎合区工具源码

    传奇GOM引擎合区工具源码 自己写的一点小项目 所以在此给大家分享 包含MFC框架 Windows消息机制原理 WIN32API调用 还有找了半天的后台组合按键消息 合区功能基本都有 是学习MFC的一个好的小项目 VS2013 unicod
  • python中dataframe将一列中的数值拆分成多个列

    起初的数据是这样的 想将page no这一列拆分成多个列 然后将其中的值都作为列名 想要做成的结果如下图 也就是统计每个id下各个page no出现的次数 实现的思路是先对page no这一列进行one hot编码 将一列变为多列 然后再用
  • mybatis-plus3.5.2分页插件无效解决方案

    解决问题思路 测试分页插件的时候发现sql语句没有添加limit并返回的是所有数据 排查得知mybatis plus版本为3 5 2 搜索资料得知旧版本的PaginationInterceptor已经过时 官方推荐使用PaginationI
  • Python基本语法,python入门到精通

    python你不去认识它 可能没什么 一旦你认识了它 你就会爱上它 Python基本语法 1 定义变量 代码正文 x 1 y 2 z x y Python定义变量的方式呢很简单 就是上面这段代码 相信只要稍微懂点数学的人都能看懂这段代码的含
  • 微信终于支持 H5 跳转 App &小程序

    继小程序灰度测试分享朋友圈刷屏后 滴滴滴 这 闲着也闲着 顺藤摸瓜点了进去 好家伙 产品小姐姐写这个文案还是太含蓄了 我猜有可能是老干妈还没吃够 根据刀哥多年写代码要看文档的经验来看 证实了这次更新不仅支持了打开小程序 连app也顺带支持了
  • 第17节-PhotoShop基础课程-画笔修复工具

    文章目录 前言 1 画笔工具 1 基本操作 2 工具选项 1 不透明度 2 流量 3 平滑 2 画笔大小工具栏大小设置 4 笔刷 2 铅笔工具 3 颜色替换工具 批量替换颜色 4 混合器画笔工具 人像精修 前言 画笔工具的使用 1 画笔工具
  • BottomSheetDialogFragment圆角

    自己使用BottomSheetDialogFragment时 想实现上方圆角 布局设置了圆角的背景后 需要给dialog的北京设置为透明 才能有圆角的效果 网上其他的文章都是这么实现的 dialog getWindow findViewBy
  • 小程序发布上线全流程(包含小程序怎么通过审核)

    小程序在开发完成后 需要上传代码 设为体验版本 功能测试 提交审核 发布上线这几个基本步骤 接下来用自己的亲身经历一一详细介绍 小程序发布上线全流程 1 上传代码 在微信开发者工具的右上角上传处上传全部代码 如下图 如果小程序中涉及到一些r
  • 程序kill后仍占用GPU

    sc yolov5 zqchen gpurtx02 ultralytics gpustat gpurtx02 Thu Aug 24 09 18 31 2023 470 74 0 Quadro RTX 6000 41 C 0 0 24220
  • 2-需求分析

    一 需求收集 1 需求概念 以下常见三种情形 提问题 目的不明确 明确困境 提目的 目的明确 解决方案不明确 提方案 目的明确 方案明确 概念 本质是用户的预期和现状之间的差异产生的需求 在提出需求时 往往会基于目的描述问题 想法或建议 往
  • python代码~考研祝福

    完整代码如下所示 from turtle import speed 2 Turtle screen delay 0 def go to x y up goto x y down def ring a b c d for i in range
  • STM32F103小容量、中容量和大容量单片机介绍

    一 小容量 中容量和大容量表示的型号 STM32F103x4和STM32F103x6被归为小容量产品 闪存小于等于32K STM32F103x8和STM32F103xB被归为中等容量产品 闪存小于等于128K STM32F103xC STM
  • diskgenius创建efi分区_怎么创建efi系统分区?efi系统分区创建教程

    文章导读 近两年出来的的电脑不管是新台式机还是笔记本电脑 绝大多数是uefi主板 要采用对应的硬盘分区是gpt格式的 所以我们一定要记得采用efi引导对应的分区类型一定是gpt分区 EFI分区是GPT磁盘分区表里面的一个必要分区 是独立于系
  • win10上安装python3.9.0+robotframework

    win10上安装python3 9 robotframework python3 9 0下载安装 robotframework安装 wxpython安装 ride安装 python3 9 0下载安装 下载地址 python3 9 0下载地址
  • 【机器实战学习】朴素贝叶斯 python代码实现

    朴素贝叶斯 输入数据创造词汇表 代码实现 coding UTF 8 def loadDataSet 创建了一下实验样本 return 词条且分的文档集合 类别标签的集合 自动检测侮辱性的语言 postingList my dog has f
  • 23种设计模式:适配器模式(最强解析!!!)

    适配器模式是23种设计模式之一 适配器模式作用 适配器模式的作用 在于将一个类的接口变换为客户端所期待的另一种接口 使得原本因为接口不匹配而无法一起工作的两个类能在一起工作 也就是说 适配器模式解决的是接口兼容性问题 适配器模式实现方式 适
  • 深度学习(十四):详解Matconvnet使用imagenet模型训练自己的数据集

    上节讨论过如何使一个简单的cnn网络训练mnist数据集 该节介绍复杂并且使用广泛的使用imagenet网络的预训练模型训练自己的数据集 Ok首先是自己的数据集了 Matconvnet中训练imagenet的数据集的准备不像caffe这些工