上次使用mnist对caffe进行了初步了解,这一次就要开始对毕设的任务进行尝试了。这次打算对ImageNet进行fine tune,使用的是行人检测数据集INRIA。
首先下载下来INRIA数据集,发现有Train和Test文件夹里的图片大小不固定,而且没有进行crop。而其中有两个子文件夹96X160H96和70X134H96下的图片是crop好的,可以直接拿来用作行人检测的正样本。但因为INRIA并不是针对caffe设计的,所以需要先对它进行一定处理。这里就需要明确哪些数据作为训练数据,哪些数据作为测试数据。因为只是对fine tuning进行测试,所以我这里没有对准备训练数据花很多精力,直接拿96X160H96/Train/pos文件夹下的图片作为训练正样本,Train/neg/下的图片作为训练负样本,70X134H96/Test/pos下的作为测试正样本,Test/neg/下的作为测试负样本。
然后需要做的是从这些数据产生lmdb数据,我直接借鉴了ImageNet的配置文件create_imagenet.sh稍作修改写了一个create_pedenet.sh。
为了方便管理路径起见,我又单独建立了一个data/pedestrian文件夹,下面有两个子文件夹train和val,分别装训练和测试数据,每个文件夹下又分别有pos和neg两个文件夹装正负样本。
在create_pedenet文件中,首先将DATA、EXAMPLE、TRAIN_DATA_ROOT和VAL_DATA_ROOT几个变量设置如下:
EXAMPLE=project/pedestrian/newdata/(是处理好之后的lmdb数据存储的路径,事先这个路径下一定不能有pedenet_train_lmdb和pedenet_val_lmdb两个文件夹)
DATA=data/pedestrian/(是存储图片路径的train.txt和val.txt两个文件的路径)
TOOLS=build/tools
TRAIN_DATA_ROOT=data/pedestrian/train/(存储训练数据的路径,TRAIN_DATA_ROOT + train.txt的每一行路径就对应着每个图片文件)
VAL_DATA_ROOT=data/pedestrian/val/(同上)
然后需要做的就是生成对应各个样本路径的train.txt和val.txt文件。这里我自己写了一个几十行的C程序专门生成这两个文件,将每个文件写成如下的格式:
pos/person_and_bike_209b.png 1
neg/00000002a.png 0
其中1表示正样本,0表示负样本(其实这个只是自己约定的label,机器并不知道哪个是正,哪个是负)。需要注意的是在windows系统中文件路径的分隔号是\\,而在linux系统中合法的分隔号是/,要注意区分,否则会出现找不到文件的错误:
E0321 21:50:27.440016 2448 io.cpp:80] Could not open or find file data/pedestrian/val/pos\person_037a.png
然后执行create_pedenet.sh,完成数据预处理:
~/caffe-master$ ./project/pedestrian/create_pedenet.sh
Creating train lmdb...
I0321 21:58:04.069706 2493 convert_imageset.cpp:82] Shuffling data
I0321 21:58:05.401593 2493 convert_imageset.cpp:85] A total of 3328 images.
I0321 21:58:05.402035 2493 db_lmdb.cpp:23] Opened lmdb project/pedestrian/newdata//pedenet_train_lmdb
I0321 21:58:19.187896 2493 convert_imageset.cpp:146] Processed 1000 files.
I0321 21:58:31.109478 2493 convert_imageset.cpp:146] Processed 2000 files.
I0321 21:58:43.797771 2493 convert_imageset.cpp:146] Processed 3000 files.
I0321 21:58:47.886440 2493 convert_imageset.cpp:152] Processed 3328 files.
Creating val lmdb...
I0321 21:58:48.577513 2502 convert_imageset.cpp:82] Shuffling data
I0321 21:58:49.921418 2502 convert_imageset.cpp:85] A total of 1426 images.
I0321 21:58:49.921867 2502 db_lmdb.cpp:23] Opened lmdb project/pedestrian/newdata//pedenet_val_lmdb
I0321 21:58:59.190737 2502 convert_imageset.cpp:146] Processed 1000 files.
I0321 21:59:03.346020 2502 convert_imageset.cpp:152] Processed 1426 files.
Done.
这样就完成了数据的预处理,产生的文件data.mdb和lock.mdb存储在EXAMPLE路径下面。
下一步就是计算图像的平均值。这个步骤在imagenet中是通过compute_imagenet_mean.sh脚本来进行的,这里我就直接把这个文件拿过来修改了一下它的变量,就可以直接使用:
EXAMPLE=project/pedestrian/newdata(是存储处理后的图片的路径)
DATA=data/pedestrian(是生成的目标文件pedenet_mean.binaryproto的存储路径)
TOOLS=build/tools
$TOOLS/compute_image_mean $EXAMPLE/pedenet_train_lmdb \
$DATA/pedenet_mean.binaryproto
这样就生成了data/pedestrian/pedenet_mean.binaryproto文件,网络训练的前期准备工作完成了。
下面需要对网络的核心配置文件进行编辑,主要是train_val.prototxt和solver.prototxt两个文件。
solver.prototxt主要是训练阶段的一些配置,首先net变量要修改为train_val.prototxt文件的路径,然后根据caffe官网上的提示,为了使网络中除了最后一层之外的其它层权重改变尽可能的缓慢,最好把网络前部的学习率lr改小一些,为了方便,直接将全局的base_lr变量调小,我第一次试验将其从0.01改到了0.005。
train_val.prototxt主要是对网络结构和参数的定义,因为我是对imagenet网络进行调优,所以这里我直接在imagenet的配置文件的基础上进行修改,主要是需要修改一下TRAIN和TEST两个阶段分别对应的data层的数据输入路径,然后还需要修改fc8层的输出数量,从1000改到了2(是否是行人),然后顺便将fc8层的名称改为fc8_pede以便区分。除此之外,为了让结构改变的fc8_pede的权重学习速度加快,要将这一层的lr_mult调大一些,因为前面base_lr减小了一半,我直接将这一层的lr_mult提高了10倍。
这样就完成了两个核心配置文件的修改。这里发现,要对已经训练好的imagenet进行参数的fine-tuning,现在服务器上没有这个网络已经训练好的权重...所以现下载一个权重数据caffemodel文件。这可以执行如下的命令行:
./scripts/download_model_binary.py models/bvlc_reference_caffenet
这个数据文件大约200多M,下载过程视网速而定,我往实验室的服务器上下载的速度只有10k/s左右,大约需要7~8个小时= = 所以果断选择了先用迅雷下载到本地,再传到服务器上。下载的链接可以在./models/bvlc_reference_caffenet/readme.md文件里找到,其他需要调优的参数权重也可以用类似的方法找到。
然后修改train_pedenet.sh脚本为:
caffe % ./build/tools/caffe train -solver project/pedestrian/solver.prototxt -weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel -gpu 0
然后执行这个脚本文件,就开始了网络的训练。首先会跳出一些提示信息显示网络的结构正在搭建,然后就开始了optimization过程,每1000次迭代都会显示一次accuracy,开始迭代之前accuracy是0.492,1000次之后是0.7897,再之后每1000次基本都维持在这个水平上。我分析这个网络精度不高的原因之一是网络的训练样本数量较少(训练正样本只有2400多,负样本只有1200多),与imagenet原来使用的动辄上万的训练样本量相差太大,而且网络结构上面主要优化的是最后一个全连接层的参数,所以参数的优化很快就达到了“饱和”状态,没有了继续提升的空间。因此要想提升网络的性能,一方面要用更大的数据集去进行训练,另一方面也需要考虑是否需要对网络结构进行调整。