深度学习与计算机视觉 实践学习(4)最简单的图片分类——手写数字识别(基于MXNet)

目标:在MXNet上实现手写数字识别,进而了解用MXNet进行图片分类任务的基本步骤。

1. 制作Image Recordio数据

和Caffe中用LMDB保存大量数据相对应,MXNet对于大量数据IO的实现,采用的是Image Recordio,这是DMLC自己研发的一种高效易于分布式访问的数据存储方式,和LMDB一样也是基于内存映射(Memory Map)。因为在硬盘上的存储编码可以是JPG等压缩格式,所以空间占用上和Caffe中用LMDB类存储cv::Mat的方式比起来优势很大,并且转换时的速度也不慢,这一点又胜过Caffe中直接读取图片的ImageDataLayer。和Caffe类似,要制作这种格式,第一步也是要制作一个文件路径和标签的列表,格式如下:

0    5    mnist/train/000000_5.jpg
1    0    mnist/train/000001_0.jpg
2    4    mnist/train/000002_4.jpg
3    1    mnist/train/000003_1.jpg
...

每一行都是用制表符分隔开的3个字段,第一个字段是整数编号,第二个字段是标签,第三个字段是文件名或文件路径。注意虽然例子中的编号是按顺序的,其实不重要。

从产生的MNIST图片生成列表的代码如下:

import os
import sys
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 i, filename in enumerate(filenames):
         filepath = os.sep.join([input_path, filename]):
         label = filename[:filename.rfind('.')].split('_')[1]
         line = '{}\t{}\t{}\n'.format(i, label, filepath)
         f.write(line)

把这段code保存为gen_mxnet_imglist.py,然后依次执行下面命令:

>> python gen_mxnet_imglist.py mnist/train train.lst
>> python gen_mxnet_imglist.py mnist/val val.lst
>> python gen_mxnet_imglist.py mnist/test test.lst

接下来第二步就可以利用MXNet的官方工具mxnet/bin/im2rec进行数据转换了,执行下面命令:

>> /path/to/mxnet/bin/im2rec train.lst ./ train.rec color=0
>> /path/to/mxnet/bin/im2rec val.lst ./ val.rec color=0
>> /path/to/mxnet/bin/im2rec test.lst ./ test.rec color=0

需要提一下的是,列表文件中,第二个字段可以是多个标签的,如果是这个情况,就需要再执行imrec时指定label_width参数为标签的个数。更多参数的含义可以参考htt://mxnet.io/zh/api/python/io.html。

0.9版的MXNet已经推出了image模块,提供了python实现的更灵活的接口ImageIter,可以在MXNet的github主页进行了解。

2.用Module模块训练LeNet-5

MXNet中接口最简单的Model模块的使用,可以参考深度学习与计算机视觉 实践学习(1)——用MXNet实现一个神经网络_Fan0920的专栏-CSDN博客(介绍了MXNet基本使用和使用MXNet实现一个神经网络)。

下面进一步了解更灵活的Module模块。按照Caffe中LeNet-5版本的结构,定义网络并用Module模块封装的代码如下:

import mxnet as mx
data = mx.symbol.Variable('data')
conv1= mx.symbol.Convolution(data=data, kernel = (5,5), num_filter=20)
pool1 = mx.symbol.Pooling(data=conv1, pool_type = "max", kernel=(2,2), stride=(2,2))
conv2 = mx.symbol.Convolution(data=pool1, kernel = (5,5), num_filter=50)
pool2 = mx.symbol.Pooling(data=conv2, pool_type = "max", kernel=(2,2), stride=(2,2))
flatten = mx.symbol.Flatten(data=pool2)
fc1 = mx.symbol.FullyConnected(data=flatten, num_hidden=500)
relu1 = mx.symbol.Activation(data=fc1, act_type="relu")
fc2 = mx.symbol.FullyConnected(data=relu1, num_hidden=10)
lenet5 = mx.symbol.SoftmaxOutput(data=fc2, name='softmax')
mod = mx.mod.Module(lenet5, context=mx.gpu(0))

有了模型,接下来定义数据。在MXNet中,数据迭代器比Caffe的Data Layer强大不少,提供了更丰富的数据扰动方式,这里用随机裁剪和随机旋转。需要注意的是,MXNet内置的随机裁剪是正方形裁剪,随机旋转是原大小旋转,也就是说会出现没有图像的区域,我们用黑色来填充,相应code如下,

train_dataiter = mx.io.ImageRecordIter(
    path_imgrec ="../data/train.rec",
    data_shape = (1, 28, 28),
    batch_size = 50,
    mean_r = 128,
    scale = 0.00390625,
    rand_crop = True,
    min_crop_size = 24,
    max_crop_size = 28,
    max_rotate_angle = 15,
    fill_value = 0
)
val_dataiter = mx.io.ImageReocordIter(
    path_imgrec = "../data/val.rec",
    data_shape = (1, 28, 28),
    data_size = 100,
    mean_r = 128,
    scale = 0.00390625,
)

模型和数据两大要素已经备齐,下面可以开始训练模型:

import logging
logging.getLogger().setLevel(logging.DEBUG)
fh = logging.FileHandler('train_mnist_lenet.log')
logging.getLogger().addHandler(fh)
lr_scheduler = mx.lr_scheduler.FactorScheduler(1000,factor=0.95)
optimizer_params = {
    'learning_rate': 0.01
    'momentum': 0.9,
    'wd': 0.0005,
    'lr_scheduler': lr_scheduler
    }
checkpoint = mx.callback.do_checkpoint('mnist_lenet', period=5)
mod.fit(train_dataiter,
        eval_data=val_dataiter,
        optimizer_params=optimizer_params,
        num_epoch=36,
        epoch_end_callback=checkpoint)

把上面所有代码合在一起,保存在train_lenet5.py中,然后执行,

>> python train_lenet5.py

这样就开始训练了,训练完毕会输出一个mnist_lenet-symbol.json文件,这个是模型结构的描述文件。按照设定,每迭代5次保存一次的模型参数存档,命名形式为mnist_lenet-[训练代数].params。当然还有输出的log,例子如下,主要包含训练的精度、验证集的精度、学习率的变化和保存模型的信息。

MXNet中没有Caffe里专门画训练曲线的工具,但是在自带例子中有个mxnet/example/kaggle-ndsb1/training_curves.py对只输出准确率的训练log文件都管用:

>> python /path/to/mxnet/example/kaggle-ndsb1/training_curves.py --log-file=train_mnist_lenet.log

运行程序得到如图所示的可视化结果:

3. 测试与评估

测试模型准确率:MXNet在测试集上评估非常简单,把训练好的模型读取到一个Module中,把测试数据装载到一个ImageRecordIter中,然后调用Module的score()函数就可以。

import mxnet as mx
test_dataiter = mx.io.ImageRecordIter(
        path_imgrec="../data/test.rec"
        data_shape=(1,28,28),
        batch_size=100,
        mean_r=128,
        scale=0.00390625,
)
mod = mx.mod.Module.load('mnist_lenet', 35, context=mx.gpu(0))
mod.fit(..., begin_epoch=35)
mod.bind(
        data_shapes=test_dataiter.provide_data,
        label_shapes=test_dataiter.provide_label,
        for_traning=False)
metric = mx.metric.create('acc')
mod.score(test_dataiter, metric)
for name, val in metric.get_name_value():
    print('{}={:.2f}%'.format(name, val*1000))

把上面代码保存并执行,就能得到在测试机上的评估结果。

评估模型性能:这里用一个比较粗略的方法来评估前向计算性能,就是用python的time模块,迭代一定次数计算总时间,然后求得每次前向计算的消耗时间,

猜你喜欢

转载自blog.csdn.net/Fan0920/article/details/107716910