在看完卜居老师的《21天实战Caffe》第六章后收获颇丰,对之前在Caffe官网看的Tutorial,超入门的examples mnist的训练与识别终于有了一个比较清晰的认识。
果然还是对英语写的指导不太敏感呢(T_T)...感谢卜居大大写的书~~
如果说“Hello World”是所有编程语言的第一个入门练习的话,那mnist就是深度学习方面的“Hello World”了。它是Mixed National Institute of Standards and Technology的缩写,是一个大型的手写体数字数据库,广泛用于机器学习领域的训练和测试,是NYU的Yann LeCun教授整理,具体信息在这里http://yann.lecun.com/exdb/mnist/,这里有个教程也不错MNIST机器学习入门 。
在这一章中,卜居老师详细介绍了caffe是如何转换mnist数据格式并进行训练和测试的,最后留下的习题是让自己写个程序,将自己的手写体数字图片送入自己训练好的LeNet进行测试,评估效果。这个题刚看到还是觉得很难的,并没有什么思路,于是果断打开收藏的卜老师的习题答案,【深度学习:21 天实战 Caffe】课后习题参考答案:思路:用 Windows 画图软件绘制手写体数字,缩放到 28 x 28,另存为图片(jpg 或 png 均可),用 MATLAB 将图片转换为 MNIST 格式数据,替换掉 test 数据集,之后再创建 test_lmdb,修改 lenet_train_val.prototxt 使用该数据集。利用训练好的模型和 lenet_train_val.prototxt 进行 caffe test,检查正确率。
于是按这个思路,搜索了相关的实现,一开始还看到有人用C++实现,我觉得一方面实在工作量比较大,另一方面既然都用C++将数据转换为mnist格式,再运行create_mnist.sh脚本调用Caffe里的convert_mnist_data.cpp将图片转换成普通的数组格式,不是浪费时间吗,可能也只有练习C++的作用了,以后用C++的场合应该就直接修改Caffe源码来进行相关练习和测试吧。那么关于matlab的话主要参考这篇文章,图像转化为手写体mnist格式的数据,然后自己做了点微小的工作。
1.先使用PS故意画了个很挫的6,保存为28*28像素的png文件;
2.使用matlab打开它可以看到是28*28*3的矩阵,因为我使用的RGB标准,所以这里既可以在PS里直接把它改为灰度图,也可以在matlab里取三个分量的平均值或取单个通道的值也可以(因为我画的时候纯白底纯黑画笔画的);
3.将这个原始图像以mnist格式写入一个文件,类似mnist官方下载到的t10k-images.idx3-ubyte和t10k-labels.idx1-ubyte
代码如下:
clc;
clear;
imgOrigin = imread('6.png');
imgBinaryImg = imgOrigin(:,:,1); %只取原图像一个分量,转换为灰度图像
imgMnist = fopen('test_img_ubyte', 'wb'); %w:写,如果没有自动创建
magic = 2051;
numImages = 1;
numRows = 28;
numCols = 28;
fwrite(imgMnist, magic, 'int32', 0, 'ieee-be'); %ieee-be:大端存储
fwrite(imgMnist, numImages, 'int32', 0, 'ieee-be');%0貌似表是读完这个都下个之前的skip,0就是连着读,不跳
fwrite(imgMnist, numRows, 'int32', 0, 'ieee-be');
fwrite(imgMnist, numCols, 'int32', 0, 'ieee-be');
imgBinaryImg = permute(imgBinaryImg, [2 1]); %matlab以列为主进行运算
imgBinaryImg = reshape(imgBinaryImg, 1, numCols* numRows* numImages); %reshape按列转换
fwrite(imgMnist, imgBinaryImg, 'unsigned char');
fclose(imgMnist);
%将label转换为二进制
flabel = fopen('test_lable_ubyte', 'wb');
magic = 2049;
numLabels = 1;
labels = 6;
fwrite(flabel, magic, 'int32', 0, 'ieee-be');
fwrite(flabel, numLabels, 'int32', 0, 'ieee-be');
fwrite(flabel, labels, 'unsigned char');
fclose(flabel);
代码里面反正我一开始不懂的地方我都加了注释,小伙伴们有疑问的话欢迎讨论。
今晚有点晚了,只实现了一张图片,后面有时间写个循环多加几张自己画的测试图片进去。
运行后matlab的工作空间如下:
生成的mnist格式测试数据属性如下:
可以看到test_img_ubyte大小刚好为4B+4B+4B+4B+28*28B = 800B;
test_lable_ubyte大小为4B+4B+1B。
然后修改lenet_train_test.prototxt里的
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: 1
backend: LMDB
}
}
batch_size为 1,因为只有一张图片嘛
然后调用想create_mnist.sh生成caffe需要的lmdb格式数据,然而这里我一直用的是win10系统,不能直接用linux下的脚本(找不到shell命令),于是再转到ubuntu16.04下继续工作,实际后面就与常规的训练mnist步骤差不多了,Caffe官网也有tutorial.
然后修改create_mnist.sh里面创建lmdb,输入对应要转换的自己的文件名,所以其实一开始直接将原测试文件替换掉也可以,可以省去这一步。
set -e
EXAMPLE=examples/mnist
DATA=data/mnist
BUILD=build/examples/mnist
BACKEND="lmdb"
echo "Creating ${BACKEND}..."
rm -rf $EXAMPLE/mnist_test_zfq_${BACKEND}
$BUILD/convert_mnist_data.bin $DATA/test_img_ubyte \
$DATA/test_lable_ubyte $EXAMPLE/mnist_test_zfq_${BACKEND} --backend=${BACKEND}
echo "Done.
然后调用Caffe的test语句,测试模型对于我画的6的accuracy,如下:
./build/tools/caffe test -model examples/mnist/lenet_train_test.prototxt \
-weights examples/mnist/lenet_iter_10000.caffemodel -iterations 1
输出的最后几行:
I1213 12:13:12.657213 4049 net.cpp:283] Network initialization done.
I1213 12:13:12.662529 4049 caffe.cpp:285] Running for 1 iterations.
I1213 12:13:12.668015 4049 caffe.cpp:308] Batch 0, accuracy = 1
I1213 12:13:12.668031 4049 caffe.cpp:308] Batch 0, loss = 3.26639e-05
I1213 12:13:12.668035 4049 caffe.cpp:313] Loss: 3.26639e-05
I1213 12:13:12.668042 4049 caffe.cpp:325] accuracy = 1
I1213 12:13:12.668048 4049 caffe.cpp:325] loss = 3.26639e-05 (* 1 = 3.26639e-05 loss)
有几行还不能完全看懂,不过貌似是成功了,这么丑它都能识别出来。。