Linux下使用Caffe自己制作数据、并训练手写数字识别模型(python版)

一:训练模型

第一步:下载MNIST数据集


在Linux的终端输入:

wget http://deeplearning.net/data/mnist/mnist.pkl.gz

原版mnist数据集下载比较慢,所以使用这个bengio组封装好的数据包。

 

第二步:处理数据集(显示数据 - 图片)


(1)使用python下载数据集并转换成图片

# encoding: utf-8

# os模块提供了多数操作系统的功能接口函数。当os模块被导入后,它会自适应于不同的操作系统平台,
# 根据不同的平台进行相应的操作,在python编程时,经常和文件、目录打交道,所以离不了os模块

import os
import pickle ,gzip  #pickle提供了一个简单的持久化功能。可以将对象以文件的形式存放在磁盘上。
import matplotlib   #绘图工具包
import matplotlib.pyplot

#从原始文件读入mnist数据
print ('Loading data from mnist.pkl.gzip ...')
with gzip.open('mnist.pkl.gz', 'rb') as f :
    train_set, vaild_set, test_set = pickle.load(f) #pickle.load(file)反序列化对象.将文件中的数据解析为一个Python对象。

#创建mnist文件夹

imgs_dir = 'mnist'
os.system('mkdir -p {}'.format(imgs_dir)) #python中format函数用于字符串的格式化
datasets = {'train': train_set, 'val': vaild_set, 'test': test_set}

#转换train,vai,test 数据集

for dataname, dataset in datasets.items():  #Python 字典 items() 方法以列表形式(并非直接的列表,若要返回列表值还需调用list函数)返回可遍历的(键, 值) 元组数组。
    print('Converting {} dataset ...'.format(dataname))
    data_dir = os.sep.join([imgs_dir, dataname])

    #在mninst文件夹下创建子文件夹
    os.system('mkdir -p {}'.format(data_dir))

    # i代表序号,用zip()函数读取对应位置的图片和标签
    for i, (img, label) in enumerate(zip(*dataset)):
        #格式化生成文件名,第一个字段是序号,第二个字段是数字值
        filename = '{:0>6d}_{}.jpg'.format(i,label)
        filepath = os.sep.join([data_dir, filename])

        #将展开的一维数组还原为二位数组
        img = img.reshape((28, 28))
        # 利用pyplot保存可以自动归一化生成像素值在0~255之间的灰度图片
        matplotlib.pyplot.imsave(filepath, img, cmap='gray')
        if (i+1)% 10000 == 0:
            print('{} images converted!'.format(i+1))

这样就处首先创建mnist文件夹(在pychram工程文件夹同一目录下)然后得到了train,test,val三个子文件夹分别保存对应的3个数据集转化后产生的图片(28 * 28像素)。命名规则是:第一个字段是序号,第二个字段是数字的值,保存为jpg格式。

Linux下使用Caffe自己制作数据、并训练手写数字识别模型(python版)_第1张图片

(2)利用CAFFE提供的工具制作LMDB数据

LMDB(Lightning Memery-Mapped Database,闪电般快速的内存映射型数据库)是caffe中最常用的一种数据库格式。

来自caffe提供了专门为图像分类任务将图片转换为的官方工具,路径是朱古力/建设/工具/ convert_imageset。

使用方法:

1.生成一个图片文件路径的列表。格式例如:mnist_images / val / 000000_3.jpg 3

                                                                       mnist_images / val / 000000_4.jpg 4

每一行是文件路径和其对应的标签,中间用Tab键分开。

下面是使用代码制作保存上述格式的TXT文件

# encoding: utf-8
import os
import sys

#输入路径,包含mnist图片的文件路径

input_path = sys.argv[1].rstrip(os.sep) # https://www.cnblogs.com/aland-1415/p/6613449.html

#输出的文件名,内容就是图片路径-标签的列表
output_path = sys.argv[2]

#列出路径下所有的文件名
filenames = os.listdir(input_path)
with open(output_path, 'w') as f:
    for filename in filenames:
        #完整的图片文件路径
        filepath = os.sep.join([input_path, filename])

        #第二个字段的值就是标签
        label = filename[:filename.rfind('.')].split('_')[1]

        #生成路径-标签的格式并写入文件
        line = '{} {}\n'.format(filepath, label)#注意两个大括号之间必须是一个空格,要加\n,否则无法制作lmdb数据
        f.write(line)


之后在终端输入:

python get_lmdb.py minst/train train.txt
python get_lmdb.py minst/val val.txt
python get_lmdb.py minst/test test.txt

过了好久.....之后就会制作好三个带地址和标签的txt文件。PS:不要用gedit中打开,会很卡。

PS:第一次制作数据是出现了不少错误,1.上面python代码中路径出现错误,导致无法找到minst文件夹,上面代码是在主文件夹下运行的。2.生成txt文件时,内容格式错误,里面的内容格式一定要和下图一样,路径和标签之间只有一个空格,每个路径标签占用一行。否则在调用caffe生成lmdb数据工具时会出现错误。

Linux下使用Caffe自己制作数据、并训练手写数字识别模型(python版)_第2张图片

2.调用convert_imageset制作lmdb

终端输入:

/home/lizhen/caffe/build/tools/convert_imageset ./ train.txt train_lmdb --gray --shuffle
/home/lizhen/caffe/build/tools/convert_imageset ./ test.txt test_lmdb --gray --shuffle
/home/lizhen/caffe/build/tools/convert_imageset ./ val.txt val_lmdb --gray --shuffle

其中--gray是单通道读取灰度图的选项, - shuffle是常选祥不用管,作用是打乱文件列表的顺序完成之后生成三个文件夹                                                                                          

这样就把我们需要的数据制作完成了。

 

第三步:训练LeNet -5模型


神经网络结构和Caffe官方例子相同,不同的是把数据换成了我们自己制作的LMDB数据集。

描述数据源和网络结构的lenet_train_val.prototxt的位置在caffe /examples/ mnist/ lenet_train_val.prototxt。

name: "LeNet"   //网络名字是LeNet
layer {         //定义一个层(Layer)
  name: "mnist" //层的名字是mnist
  type: "Data"  //层的类型是数据层
  top: "data"   //层的输出blob有两个 data、label,blob是caffe中的数据块
  top: "label"  //
  include {
    phase: TRAIN//该层仅在训练阶段有效
  }
  transform_param {
    scale: 0.00390625   //数据变换使用的数据缩放因子
  }
  data_param {       //数据层参数
    source: "examples/mnist/mnist_train_lmdb"   //制作的LMDB数据的路径
    batch_size: 64   //批量数目,一次读64张图
    backend: LMDB    //数据格式LMDB
  }
}
layer {//一个新的数据层,只在分类(验证)阶段有效
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "examples/mnist/mnist_test_lmdb"
    batch_size: 100
    backend: LMDB
  }
}
layer {   //定义一个新的卷积层,conv1,输入为data 输出为conv1
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1   //权值学习速率倍乘因子,1表示保持与全局一致
  }
  param {
    lr_mult: 2   //bias学习速率倍乘因子,是全局参数的2倍
  }
  convolution_param {  //卷积参数
    num_output: 20     //输出特征feature map数目是20
    kernel_size: 5     //卷积核尺寸5*5
    stride: 1          //步长1
    weight_filler {    //权值使用xavier填充器(这是啥??)
      type: "xavier"
    }
    bias_filler {      //bias使用常数填充,默认0
      type: "constant"
    }
  }
}
layer {               //定义下采样层,输入bolb为covn1输出pool1
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {   //使用最大值下采样方法
    pool: MAX
    kernel_size: 2  //下采样窗口尺寸2*2
    stride: 2       //步长2
  }
}
layer {            //新的卷积层covn2
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {        //下采样层
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {        //全连接层
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500            //输出元素个数500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {      //非线性层,激活函数
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}qi
layer {              //新的全连接层
  name: "ip2"
 q    type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {                //分类准确层,test阶段
  name: "accuracy"
  type: "Accuracy"
  bottom: "ip2"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {                 //损失层,
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip2"
  bottom: "label"
  top: "loss"
}

我们在训练时,将数据的路径改成我们自己的就可以了。

下面就是lenet_solver.prototxt,主要用来设置训练的基本参数,如迭代次数,训练方式(gpu or cpu)等。

# The train/test net protocol buffer definition
net: "examples/mnist/lenet_train_test.prototxt" //网络地址
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 100  //
# Carry out testing every 500 training iterations.
test_interval: 500   //每训练500次执行一次test
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "inv"  //采用inv的学习策略 ,lr=base_lr*(1+gamma*iter)^(-power)
gamma: 0.0001
power: 0.75
# Display every 100 iterations
display: 100        //每迭代100次显示当前训练信息,主要是loss和学习率
# The maximum number of iterations
max_iter: 10000  //最大迭代次数
# snapshot intermediate results
snapshot: 5000   //每迭代5000次保存一次模型的参数和训练状态
snapshot_prefix: "examples/mnist/lenet"
# solver mode: CPU or GPU
solver_mode: GPU

最后开始训练:

在开始训练时会出现一点问题,我是把训练用的数据、描述网络结构、描述参数的prototxt文件放在了 caffe文件夹下自建的mymnist下,所以训练是终端输入会有点区别,如果路径不对的话也是会出现错误的。

我的命令是:

cd caffe
/home/lizhen/caffe/build/tools/caffe train -solver mymnist/lenet_solver.prototxt -gpu all -log_dir ./mymnist

其中-gpu all 表示使用所有gpu训练,如果是多卡的话可以选择0,1,2选择相应的gpu。-log_dir  ./mnist,指定日志输出位置。caffe提供了可视化log文件工具,位置在caffe/tools/extra文件夹下的plot_training_log.py.example,使用时把extra整个文件夹拷贝一份到你需要的地方,去掉.example,就可以运行这个python程序了。

具体命令是:

$ cd caffe/mymnist/extra
$ python plot_training_log.py 0 test_acc_vs_iters.png mnist_train.log

mnist_train.log,是我们生成log文件改的名字,之前的名字太长了。

这个脚本的输入参数分别是 图的类型、生成图片的路径、log路径。

图类型的输入对应是:0:测试准确率vs迭代次数

                                    1:测试准确率vs训练时间

                                    2:测试loss vs 迭代次数

                                    3:测试loss vs 训练时间

                                    4:学习率vs迭代次数

                                    5:学习率vs训练时间

                                    6:训练loss vs 迭代次数

                                    7:训练loss vs训练时间

附上一张log图:

Linux下使用Caffe自己制作数据、并训练手写数字识别模型(python版)_第3张图片

差不多到这里就完事了。

笔者的电脑是i7-4700MQ+Nvidia GTX850M,在ubuntu16.4下使用Nvidia CUDA 9.2、CuDNN 7.1加速库,训练36000次用时6分16秒。

二:对模型进行评估


这里记录了训练的模型的测试和评估情况。

终端输入:

build / tools / caffe test -model mymnist / lenet_test.prototxt -weights examples / mnist / lenet_iter_36000.caffemodel -gpu all -iterations 100

lenet_test.prototxt,和之前的lenet_train_txt.prototxt,中网络模型相同。只不过把数据输入的位置改成我们的测试数据。

-weights后面的参数是之前在训练时caffe自动保存的模型。

(2)评估模型:

评估模型一般是评估模型的速度和内存占用。在caffe中只要有描述模型网络结构的prototxt文件就可以了,因为支持评估计算性能,所以不需要参数。可以直接使用caffe / examples / mnist下的lenet 。.prototxt作为评估的结构在终端输入:

build/tools/caffe time -model examples/mnist/lenet.prototxt -gpu 0

运行结果:

Linux下使用Caffe自己制作数据、并训练手写数字识别模型(python版)_第4张图片

使用CPU:

 build/tools/caffe time -model examples/mnist/lenet.prototxt

Linux下使用Caffe自己制作数据、并训练手写数字识别模型(python版)_第5张图片

三:识别手写


有了训练好的模型,就可以用来识别手写数字了。我们测试用的是test数据集的图片,和之前生辰的列表,基于python接口实现。


后记:这个文章主要是机械工业出版社《深度学习与计算机视觉》这本书第八章内容的亲手实验,代码也是书上的但是其中还是出现了各种莫名奇妙的问题。真是心累啊!!不过解决问题之后的喜悦也冲淡了身体和心理上的疲惫。。。

                                                                          纸上得来终觉浅,绝知此事要躬行

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Cffe框架学习,DeepLearning)