Caffe实战(九):MNIS(手写体数字识别)例程

 

MNlST数据集介绍

MNlST (MiXed Nationallnstìtute of Standards and Tecbnology )是一个大型的手写体数字数据库,广泛用于机器学习领域的训练和测试,由纽约大学Yann LeCun 教授整理。MNlST 包括60000个训练集和10000个测试集,每张图都已经进行尺寸归一化、数字居中处理,固定尺寸为28像素x28像素。如下图所示:
 
数据文件格式可以在mnist网站上查看(下载地址)
 
另外,将二进制的mnist数据库格式转化为相对简单的CSV数据文件格式,可以参考 https://pjreddie.com/projects/mnist-in-csv/
def convert(imgf, labelf, outf, n):
    f = open(imgf, "rb")
    o = open(outf, "w")
    l = open(labelf, "rb")
 
    f.read(16)
    l.read(8)
    images = []
 
    for i in range(n):
        image = [ord(l.read(1))]
        for j in range(28*28):
            image.append(ord(f.read(1)))
        images.append(image)
 
    for image in images:
        o.write(",".join(str(pix) for pix in image)+"\n")
    f.close()
    o.close()
    l.close()
 
convert("train-images-idx3-ubyte", "train-labels-idx1-ubyte",
        "mnist_train.csv", 60000)
convert("t10k-images-idx3-ubyte", "t10k-labels-idx1-ubyte",
        "mnist_test.csv", 10000)
 
CSV文件中,每一行为一幅图片信息,第一个值为label值,后面依次是图像的28*28个像素值(行排列),每个值之间用','区分开。
这个网站还提供了两个已将转换好的CSV文件:
 
利用matlab或者python很容易显示这些图像信息,例如
# load the mnist test data CSV fle into a list
test_data_file = open("mnist_test.csv",'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
 
# get the first test record
all_values = test_data_list[0].split(',')
print(all_values[0])
 
# asfarray()转换为浮点型的数组
image_array = numpy.asfarray(all_values[1:]).reshape((28,28))
matplotlib.pyplot.imshow(image_array, cmap='Greys', interpolation='None')
matplotlib.pyplot.show()
 
说明:
  • 执行data/mnist/get_mnist.sh脚本可以可得MNIST数据集
  • 图片文件中像素按照行组织,像素值0表示背景(白色),像素值255表示前景(黑色);与一般灰度图像的定义相反,在测试手写图像是一定要注意进行变换。
  • 文件格式中magic number(魔数)其实它就是一个校验数,用来判断这个文件是不是MNIST里面的image文件或者label文件;
  • 下载到的原始数据集为二进制文件,需要转换为LEVELDB或LMDB才能被Caffe识别;在caffe根目录下执行脚本./examples/minist/create_mnist.sh即可(源码为convert_mnist_data.cpp);
 
TIPS: Caffe 为什么采用LMDB、LEVELDB. 而不是直接读取原始数据?
 
一方面,数据类型多种多样(有二进制文件、文本文件、编码后的图像文件如JPEG 或PNG、网络爬取的数据等),不可能用一套代码实现所有类型的输入数据读取,转换为统一格式可以简I化数据读取层的实现:另一方面,使用LMDB 、LEVELDB 可以提高磁盘IO利用率。
 
【Bengio组封装好的数据包】--提供了将图片转会为lmdb的操作过错
    本小节参考《深度学习与计算机视觉:算法、框架应用与代码实现》
 
原版MNIST 下载很慢,也可以下载Bengio组封装好的数据包,下载地址
wget https://deeplearning.net/data/mnist/mnist.pkl.gz
 
mnist.pkl.gz压缩包是数据的训练集、验证集和测试集用pickle到处的文件被压缩为gzip格式,用python中的gzip模块读取文件中的数据。
 
文件中数据格式:每个数据集是一个元组,第一个元素存储的是手写数字图片,每张28*28=784一维浮点型numpy数组排列;(单通道灰度图片按行展开,1代表白,0代表黑);元组中第二个元素是图片对应的标签,是一个一维的整型numpy数组。
 
将以上数据集转换为图片的代码如下:
#!/usr/bin/env python
# -*-coding:UTF-8-*-
 
import os
import pickle
import gzip
from matplotlib import pyplot
 
# 从原始文件读取MNIST数据
print('Loading data from mnist.pkl.gz ...')
with gzip.open('mnist.pkl.gz', 'rb') as f:
    train_set, valid_set, test_set = pickle.load(f)
# 创建mnist文件夹
imgs_dir = 'mnist'
os.system('mkdir -p {}'.format(imgs_dir))
datasets = {'train': train_set, 'val': valid_set, 'test': test_set}
# 转换train\val和test数据集
for dataname, dataset in datasets.items():
    print('Converting {} dataset ...'.format(dataname))
    data_dir = os.sep.join([imgs_dir, dataname])
 
    # 在mnist文件夹下创建对应子文件夹
    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之间的灰度图片
        pyplot.imsave(filepath, img, cmap='gray')
        if (i+1) % 10000 == 0:
            print('{} images converted!'.format(i+1))
 
如果用caffe自带的工具convert_imageset工具将图片转会为LMDB,则需要包含图片路径和对应标签的文件。生成如下所是:
#!/usr/bin/env python
# -*-coding:UTF-8-*-
 
import os
import sys
 
# 输入路径,是包含mnist图片文件的路径
input_path = sys.argv[1].rstrip(os.sep)
# 输出的文件名,内容就是图片路径-标签的列表
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])
 
    # 第2个字段的值就是标签
    label = filename[:filename.rfind('.')].split('_')[1]
    # 生成路径-标签的格式并写入文件
    line = '{} {}\n'.format(filepath, label)
    f.write(line)

# 调用方 python gen_caffe_imglist.py mnist/train train.txt python gen_caffe_imglist.py mnist/val val.txt python gen_caffe_imglist.py mnist/test test.txt
 
convert_imageset调用方式:
build/tools/convert_imageset 输入图片路径 图片+标签列表文件 输出lmdb路径
 
convert_imageset ./ train.txt train_lmdb
 
--gray是单通道读取灰度图像的选项;
--shuffle作用是打乱文件列表顺序。
 

LeNet-5模型

 
LeNet-5是一个经典的深度学习模型,该模型是Yann LeCun 最早提出的,并应用到邮政编码识别中。
 
大话CNN经典模型:LeNet https://my.oschina.net/u/876354/blog/1632862
 
关于LeNet-5模型的描述文件可以参考:examples/mnist/lenet_train_test.prototxt
 
数据源mnist负责从预处理得到的lmdb数据库中读取图像数据data和标签数据label图像数据送入后续CNN结构中进行处理。CNN结构包括一组由卷积层conv(l,2)+下采样层pool(l ,2)交替形成的特征层,以及两个全连接层ipl 和ip2 (类似于多层感知器结构〉。对ip2的输出进一步同标签数据label对比,可计算分类准确率accuracy和损失值loss。LeNet 的设计蕴涵了CNN的精髓,理解该模型对设计更大模型(如ImageNet数据集上的AlexNet 、oogleNet 、VGG 等)有参考价值。
 

训练超参数过程

 
mnist数据训练测试操作指令
 
【windous下】
mnist数据转换为lmdb
convert_mnist_data.exe ..\\..\\..\\data\mnist\train-images.idx3-ubyte ..\\\..\\..\\data\\mnist\\train-labels.idx1-ubyte ..\\..\\..\\examples\\mnist\\mnist_train_lmdb
convert_mnist_data.exe ..\\..\..\data\t10k-images.idx3-ubyte ..\\..\\..\\data\\mnist\\t10k-labels.idx1-ubyte ..\\..\\..\\examples\\mnist\\mnist_test_lmdb
模型训练
caffe.exe train --solver=..\\..\\..\\examples\\mnist\\lenet_solver.prototxt
模型测试
caffe.exe test -model ..\\..\\..\\examples\\mnist\\lenet_train_test.prototxt -weights ..\\..\\..\\examples\\mnist\\lenet_iter_10000.caffemodel -iterations 100
 
Linux下直接调用相应的脚本
mnist数据转换为lmdb  create_mnist.sh
模型训练             train_lenet.sh
模型测试             caffe.bin test -model ..\\..\\examples\\mnist\\lenet_train_test.prototxt -weights ..\\..\\examples\\mnist\\lenet_iter_10000.caffemodel -iterations

 

最终训练好的模型权值保存在examples/mnist/lenet_iter_10000.caffemodel文件中,训练状态(当前迭代次数,网络描述文件,权值历史信息等,包含从上次停止点回复训练模型所需要的信息)保存在examples/mnist/lenet_iter_10000.solverstate文件中。
 
特别注意相对路径问题:
  • 执行脚本和可执行程序在同一目录下:传入数据参数的路径是相对本目录的;因此从当前目录出发去寻找指定的位置;
  • 执行脚本和可执行程序不在同一目录下:传入数据参数的路径依然是相对本目录的,而不是相对exe所在的路径。这个地方一定要特别注意。
  • 不管可执行程序在哪个目录下,也不管数据在哪个目录下,只要记住所有的路径都是相对于当前路径寻找的。
例如上面的模型训练:
# caffe\\Build\\x64\\Release\\caffe.exe
# 可执行脚本和可执行程序在同一目录下,脚本内容如下
caffe.exe train --solver=..\\..\\..\\examples\\mnist\\lenet_solver.prototxt
 
# 可执行脚本和可执行程序不在同一目录下,例如
# 可执行程序在caffe\\Build\\x64\\Release\\caffe.exe
# 可执行脚本在caffe\\examples\\mnist\\
# lenet_solver.prototxt也在caffe\\examples\\mnist\\
..\\..\\Build\\x64\\Release\\caffe.exe --solver=lenet_solver.prototxt

 

【ubuntu平台】
caffe.bin用法介绍
$ . / buìld/tools/caffe.þin
caffe.bin: command line brew
usage: caffe <command><args>
commands:
train 训练或微调一个模型
test 对一个模型打分
del1 i ce_query 显示GPU诊断信息
time 评估模型执行时间
 
 
Flags from tools/caffe.cpp:
-gpu (可选参数,给定时运行在GPU模式,' -gpu all'则表示运行在所有可用GPU设备上,此时真正训练批量大小是N*B,N为指定的GPU设备数目)
-iterations (循环迭代次数,默认为50)
-model (指定模型定义文本文件名,*.prototxt)
-sighup_effect (可收到SIGHUP信号时要采取的动作,可选项:snapshot,stop或none,默认为snapshot,即打快照)
-sigint_effect (当收到SIGINT 信号时要采取的动作,可选项同上,默认stop>
-snapshot (恢复训练时需要指定上次中止的快照,*.solverstate)
-solver (指定求解器文本文件名,*.prototxt)
-weights (指定用于微调的预训练权值,*.caffemodel,不可与snapshot同时出现)
 
在linux上,caffe团队给出非常详细的例子,而且各个路径已经配置好,直接运行相应的脚本即可。
# 切换到caffe目录
 
# 下载数据
sh ./data/mnist/get_mnist.sh
 
# 转换为lmdb
sh ./examples/mnist/create_mnist.sh
 
# 训练网络模型
sh ./examaples/mnist/train_lenet.sh
 
出现的问题汇总:
 
(1)找不到库:在执行create_mnist.sh进行转换时,由于需要调用./build/examples/mnist/convert_mnist.bin,提示找不到libglog.so.o文件,这是由于本人将所有的依赖库安装到了/home/gd/local_install/,导致LD加载器没有找到相应的库。最简单的是在~/.bashrc文件中添加:
export LD_LIBRARY_PATH=/home/gd/local_install/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/home/gd/local_install/cuda-10.2/lib64:$LD_LIBRARY_PATH
 
或者将其添加到/etc/ld.so.conf文件中,并执行ldconfig指令立即生效。
 
(2)在windous下无论是在工程中,还是调用convert_mnist_data.exe,只要是相对路径或者绝对路径,都报出读取文件失败的错误;但是把数据和exe放在同一目录下,则正常运行。原因????
 
原因:
1)查看文件名是否正确。书中训数据集的名称诸如train-images-idx3-ubyte形式,而实际下载的数据集的名称诸如train-images.idx3-ubyte形式;一定要以实际的名称为准。
2)在windows下编写cmd或bat脚本时,不要用自带的记事本编辑,会在开头加入BOM标记,导致很多时候不能识别中文路径或者其它未知的错误。因此,最后用notepad++或者ultraEdit转换为UTF-8编码;或者干脆用这两个记事本采用UTF-8编码形式编写脚本内容。
 
特别注意:在中文Windows系统中,如果一个文本文件是UTF-8编码的,那么在CMD.exe命令行窗口(所谓的DOS窗口)中不能正确显示文件中的内容。在默认情况下,命令行窗口中使用的代码页是中文或者美国的,即编码是中文字符集或者西文字符集。如果想要正确显示有两种方法:
方法一:修改cmd窗口默认使用的代码页,即改为支持UTF-8编码;
方法二:编写文档时采用GB2132等中文集(在notepad++中可以选择编码的字符集),或者转换为中文集。
 
 

posted on 2021-08-10 17:57  悬崖边上打坐  阅读(574)  评论(0编辑  收藏  举报

导航