用了两天时间完成了如标题的任务,中间也遇到了不少的问题,在此记录一下整个过程。
- 我的环境
- win10 x64
- Intel® Core™ i5-8400 CPU @ 2.80GHz
- VS 2015 x64 (注:caffe2 支持VS 2015 和 2017)
- C++
- CMake 3.14 rc(至少3.4以上吧)
1. caffe2的下载
链接:https://github.com/BVLC/caffe/tree/windows
在下载页面有readme,这里我简单介绍一下我在搭建环境时所做的步骤。
2. 添加本地环境变量
(1)添加cmake.exe文件的路径到PATH下
(2) 添加环境变量
CMAKE_C_COMPILER=your/path/to/cl.exe
CMAKE_CXX_COMPILER=your/path/to/cl.exe
3. 修改执行文件
在caffe-windows\scripts文件夹下有一个执行文件build_win.cmd。进行对应修改后,可正确运行。我主要修改了70-99行。具体包括VS的版本、只使用CPU、CMake配置为Release、不使用python。
4. 进行编译
双击执行build_win.cmd文件或者在cmd窗口运行,区别是后者在执行错误时可以看到错误信息。编译完成后build目录如下所示。(注:执行该步骤前最好删除build文件夹内部的所有文件)
5. 生成caffe工具
双击打开caffe.sln文件,选择Release x64编译环境,在解决方案窗口,右击整个解决方案,选择生成。 生成完成后在build\tools\Release文件夹内会有我们需要的文件和程序生成。
————————————————————————————————————————————————————
至此,caffe 作为工具来说,已经配置完成了,接下来就可以进行实际的应用了。比如你可以在网上下载一些models,进行物体的识别。下面我会以人脸为例子,利用这个工具,生成我想要的识别人脸的model。
6. 准备人脸数据集
我使用的是IMM人脸数据集,下载地址:http://www.imm.dtu.dk/~aam/
7. 数据处理(这一步是最麻烦的,我做的时候出问题最多)
IMM数据库里的图片是jpg格式的图片,是不能直接使用的,需要将这很多张图片转化为两个Imdb数据文件。转换工具caffe也已经为我们提供了,即convert_imageset工具。为此,我们需要先写一个索引文件(txt格式),文件内容如下。
表示的含义也很简单,我们下载的人脸库,共有40个人,每人6张,共计240张图片,这个txt文件就表示每张图片是哪个人,本次训练中,在每人6张图片中取前4张作为训练图片,第5张用作测试,第6张暂不使用。
下面我们从caffe2使用的角度来解释为什么生成这个txt文件。
(1)在caffe/tools路径下有一个convert_imageset.cpp文件,文件开头就介绍了。
// This program converts a set of images to a lmdb/leveldb by storing them
// as Datum proto buffers.
// Usage:
// convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME
// where ROOTFOLDER is the root folder that holds all the images,# ROOTFOLDER是图片文件夹的绝对路径
//and LISTFILE should be a list of files as well as their labels, in the format as # LISTFILE是一个包含图片名称和 label 的 .txt文件,图片名称和 label 之间要有一个空格。
// subfolder1/file1.JPEG 7 # DB_NAME是要存放LMDB文件的文件夹名称,DB_NAME后面还可以跟一些可选的参数设置,具体有哪些可选的参数参见“可选参数设置部分”
注意:ROOTFOLDER 后面的“/”不能少。
(2)可选参数的介绍
gray:bool类型,默认为false,如果设置为true,则代表将图像当做灰度图像来处理,否则当做彩色图像来处理
shuffle:bool类型,默认为false,如果设置为true,则代表将图像集中的图像的顺序随机打乱
backend:string类型,可取的值的集合为{"lmdb", "leveldb"},默认为"lmdb",代表采用何种形式来存储转换后的数据
resize_width:int32的类型,默认值为0,如果为非0值,则代表图像的宽度将被resize成resize_width
resize_height:int32的类型,默认值为0,如果为非0值,则代表图像的高度将被resize成resize_height
check_size:bool类型,默认值为false,如果该值为true,则在处理数据的时候将检查每一条数据的大小是否相同
encoded:bool类型,默认值为false,如果为true,代表将存储编码后的图像,具体采用的编码方式由参数encode_type指定
encode_type:string类型,默认值为"",用于指定用何种编码方式存储编码后的图像,取值为编码方式的后缀(如'png','jpg',...)
(3)示例
convert_imageset ImgSetRootDir/ ImgFileList.txt imgSet.lmdb --gray=true --resize_width=160 --resize_height=160
从上述对convert_imageset工具的使用方法进行了介绍,也说明确实需要这样一个文件。但是手动去编写这样一个文件是复杂的,并且是不可复用的,所以建议使用脚本或者可执行程序去生成这样一个文件,这里给出一个在python版本,修改对应路径就可以使用了。
import os
def file_name(file_dir):
for root, dirs, files in os.walk(file_dir):
return files
def writeToFile(fileName, lines):
with open(fileName, 'w') as f: # 若文件不存在则会创建一个
f.writelines(lines)
lines = []
fileNames = file_name('yourPath/train') # 图片路径
for i in range(0, len(fileNames)):
num = str(int(fileNames[i].split('-')[0]) - 1)
line = fileNames[i] + ' ' + num + '\n'
lines.append(line)
writeToFile('yourPath/train.txt', lines) # 生成的txt文件保存路径
好的,我们生成了txt索引文件,接下来,对图片进行格式转换。
转换命令如上面示例,这里在重申一下,不啰嗦了,自行理解。运行成功后,会生成一个文件夹,里面有两个文件,注意他们的大小,如果偏差离谱,可能就是错误的。
convert_imageset ImgSetRootDir/ ImgFileList.txt imgSet.lmdb --gray=true --resize_width=160 --resize_height=160
在此步骤中可能会遇到的问题
- 后面的参数,并不都是可选参数,resize_width和resize_height是必须要加的,否则在后面训练时会出现 Check failed:data_ 的错误。
- 出现有些图片文件不能打开的错误,Could not open or find file,如果只是部分图片出现这个提示,说明文件名有误,进行对应修改即可。如果全部图片都提示,说明路径写错,正确修改后,再次尝试即可。
- 路径问题,在cmd的指令里,路径间隔使用“/”,不使用“\”,拷贝路径时,应该手动修改。
8. 生成均值文件
caffe样本均值文件的作用,就不在这里介绍了,有兴趣的同学可以自行百度。
用法:
compute_image_mean yourPath/img_train_lmdb yourPath/mean.binaryproto
运行成功后,yourPath下面会生成一个mean.binaryproto的均值文件。
9. 编修/修改prototxt文件
下面我们要创建一个train_test.prototxt和solver.prototxt文件,该文件内容是神经网络结构的描述,你可以自己写,也可以使用我提供的模板,注意要改动的地方都标出来了。若了解参数的含义可以自行按照自己的需求优化调整。
10. 训练
在训练之前,我们先创建一个snapshot文件夹,该文件夹保存训练过程中的caffemodel文件,solver.prototxt文件中的snapshot_prefix项指定路径,snapshot:20表示迭代20次保存一次caffemodel。
caffe train -solver yourPath/solver.prototxt
注:训练使用的程序并不是你文件目录下的那个train-net程序,而是caffe,在上面的命令中,caffe是可执行程序的程序名,train是参数。
训练结果也很理想,但是时间还是挺长的,用了近4个小时。