将 R-CNN 用在自己的数据集上

前面写了 将 Faster R-CNN 用在自己的数据集上 ,这次来写一下 RCNN 的环境搭建和训练过程。

深坑,请注意。

为什么说是坑呢?

首先,它是 Matlab 的……没有歧视 Matlab 的意思,只是前面都是在 py,突然换成 Matlab 很不适应。

其次,里面使用的 Caffe 是一个非常早的 v0.999 版本。里面的各种用法都和现在版本的不一样!

再次……遇到问题,搜出来的都是 py-faster-rcnn 的处理办法……

虽然官方 README 里面写得比较清楚了,但还是遇到不少麻烦。

好吧,不吐槽,开工。

源代码的获取

这个不多说,git clone https://github.com/rbgirshick/rcnn 就好了。

然后,下载 Caffe v0.999,放到 $RCNN_ROOT/external 里面,改名为 caffe

编译 Caffe

首先,修改一些代码:

找到 Makefile 文件,大约 266 行左右关于 Matlab 的说明段,整体替换成如下代码(必须使用 Tab 缩进):

这样就能解决“错误使用 -o 参数”和其他关于链接的问题。

然后,找到 $CAFFE_ROOT/include/caffe/util/math_functions.hpp,将

改为

这样能解决一个很奇怪的编译问题。

如果你的 GPU 比较老(capability<3),那么即使顺利通过编译,运行的时候也会直接报错的。修改方法如下:

找到 $CAFFE_ROOT/include/caffe/common.hpp,在第 162 行左右找到 CAFFE_GET_BLOCKS 函数,改成

最后,按照 这个文章 里的记录进行配置和编译。我们需要编译 alltestpycaffematcaffe

如果你的 GPU 比较新,可以把 CUDA_ARCH(我也不知道这个东西是干啥的)设置稍微高一点。例如,在 GTX1060 上,我用的是

不报错。在 GTX540 上,只能用到 52。

当然,跑 test 是有一定概率出错的,并且 100% 通不过全部测试。忽略吧……

第一仗 编译 结束。

Matlab 准备

这个,照着 README 来就好了。

$RCNN_ROOT 里面启动 matlab,会出现 R-CNN startup done。如果没有,那么手动运行一下 >> startup

然后是自动下载和编译依赖库,只需要我们 >> rcnn_build() 一下就好了。一般不会出问题。

最后进行一个小测试: >> key = caffe('get_init_key'),如果结果是 -2,恭喜。

第二仗 Matlab,轻松结束。

跑 Demo

这个也要按照 README 来。

进入 $RCNN_ROOT

执行 ./data/fetch_models.sh 获取训练好的 Model。如遇到网络问题,请挂梯子。

执行 ./data/fetch_selective_search_data.sh 获取在 ImageNet 上使用 SelectiveSearch 提取出来的 Window 信息(这个不知道是干啥的)。如遇到网络问题,请挂梯子。

启动 Matlab,执行 rcnn_demo

第三仗 Demo ,轻松结束。

Fine Tune 自己的数据

简直了!

准备数据

可以按照 将 Faster R-CNN 用在自己的数据集上 来准备数据。也就是说,用 PascalVOC 的壳,里面装的是自己的数据。

RCNN 需要依赖 VOCdevkit 里面的一些文件的。我们也需要对这些文件进行一些修改。找到 VOCdevkit/VOCcode/VOCinit.m,将里面的类别标签替换成你自己的。同时,如果你的图片是 PNG 格式的,需要在这个文件中查找 jpg,替换成 png

由于 这篇文章 中使用的是 Python,比较灵活,做 Annotation 的时候一些细节没有考虑到,导致后面会出错。如果你在后面的训练过程中发现“网络加载完毕就崩溃”,那么可以看看这里:

VodDevkit 里面会将 xml 转为 Matlab Struct,而转换用的代码是非常……额……原始……的。XML 稍有问题,就会死给你看。比较容易出错的地方有:

  • XML 文件头部,不能有 <?xml version="1.0" ?> 记号
  • 使用 LabelImage 标注的话,不能有 verified="no" 或者 verified="yes" 记号
  • 图片中没有物体,会报错
  • 必须设置 folderVOC2007
  • 有多余空格会报错

所以,我们需要做两个修改:

  • VOCdevkit/VOCcode/VOCxml2struct.m 中,将 xml(xml==9|xml==10|xml==13)=[]; 改为 xml(xml==9|xml==10|xml==13|xml==' ')=[];
  • VOCdevkit/VOCcode/VOCreadxml.m 中,改为下面的代码

或者,我们可以借鉴一下 LabelImage 的代码,将 Annotation 再进行一次转换。由于长度原因,代码我放 gist 上面了,有两个文件,分别是 pascal_voc_io.pyvoc_annotation_correct.py。经过转换后,我们的 RCNN 就会听话许多。

进行训练

训练分为四步: 计算候选区域、CNN 的 Fine-Tune、提取特征、训练 SVM

计算候选区域

如果已经运行过 R-CNN 的 demo,那就一定执行过 rcnn/data 下的 fetch_selective_search_data.sh 脚本,在 selective_selective_data 文件夹下就一定有一系列的 .mat 文件,这就是作者预先计算好的候选区域信息(实际上是候选区域的坐标)。如果不自行计算候选区域,下一步提取特征的时候就会使用 voc_2007_*.mat 中储存的候选区域信息,会使结果出错。

所以,应对方法是…… 删除!

然后我们还需要再根据自己的数据集来计算这些文件。这里对 这篇博客 中的代码进行了一些修改:

将上述文件保存成 selective_search_boxes_generator.m,然后启动 Matlab,执行

下面是照着官方教程来的:

继续在 Matlab 里面执行

以获取“Window File”。执行完成后,建议去 $CAFFE_ROOT/examples/pascal-finetuning/ 里面看看 window_file_voc_2007_test.txtwindow_file_voc_2007_trainval.txt 两个文件。

这两个文件的格式是这样的:

其中如果 class_index 是 0 的话,说明这个框框是背景类。如果 class_index 全部都是 0,请照着上面的说明转一下标注文件。

修改网络配置

  • pascal_finetune_solver.prototxt
    • 调整迭代次数 max_iter(作者说在 Pascal VOC 2012 上训练,4w iter 就收敛了)
    • 调整每次测试数量 test_iter(别太大)
    • 调整测试频率 test_interval(别太频繁)
    • 调整快照频率 snapshot(别太频繁)
  • pascal_finetune_train.prototxt
    • 305 行左右的 num_output:改成你的类别数+1
    • 如果报显存不足的话,调整 batch_size
  • pascal_finetune_val.prototxt
    • 305 行左右的 num_output:改成你的类别数+1
    • 如果报显存不足的话,调整 batch_size

最理想状态是 max_iter/test_interval*test_iter=你的测试图片总数

好像不用修改层的名字。

训练特征提取网络

进入 $CAFFE_ROOT/examples/pascal-finetuning

训练完毕后,在当前文件夹下会生成一大堆 pascal_finetune_train_iter_XXXXpascal_finetune_train_iter_XXXX.solverstate 文件,这就是我们的快照。数值最大的那个就是我们的最终模型。

选择一个不带 .solverstate 的文件,改名为 finetune_voc_2007_trainval_iter_70k,复制到 $RCNN_ROOT/data/caffe_nets 下备用。

回顾训练过程

这个版本的 Caffe 提供了一些 Log 可视化工具,位于 $CAFFE_ROOT/tools/extra。使用前需要将这里面的 .py.sh.example 文件加上运行位(chmod +x)。

$CAFFE_ROOT/examples/pascal-finetuning/log.txt 复制到 $CAFFE_ROOT/tools/extra 里面。执行 ./parse_log.sh log.txt,然后执行

其中曲线类型可以为(请把前面的序号-1……我偷懒不想打表格)

  1. Test accuracy vs. Iters
  2. Test accuracy vs. Seconds
  3. Test loss vs. Iters
  4. Test loss vs. Seconds
  5. Train learning rate vs. Iters
  6. Train learning rate vs. Seconds
  7. Train loss vs. Iters
  8. Train loss vs. Seconds

Train Loss
Test Loss
Test Accuracy

想问这里的 Test Accuracy 是什么鬼?为什么这么高?

进行特征提取

回到 $RCNN_ROOT,进入 Matlab,执行

程序分别将 train、val、test 前半部分和后半部分的图片送入网络,提取 pool5 层的输出作为特征,最后保存到硬盘上。生成文件非常多,速度非常满。不仅会用到 GPU,而且由于代码中使用了 parfor,CPU 大多也是慢负荷运转的。

速度有多慢呢?

这里遇到了一个坑: 我们的数据集中,不能保证每张图片上面都有目标物体……好像是会导致 bbox 坐标出现 NaN NaN NaN NaN 的奇葩状况。为了解决这个问题,我对 $RCNN_ROOT/rcnn_extract_regions.m 做了一点小修改,在此函数的最前面加入了一句 boxes(isnan(boxes))=0;。这样做能解决掉上述问题,但是不知道对后续流程有没有什么影响。

训练 SVM

最后还要再使用 liblinear 训练一个分类器嘛……

一句话就能解决了。如果 liblinear 出错,请删除 $RCNN_ROOT/liblinear_train.mexa64,到 $RCNN_ROOT 里面打开 Matlab,运行 >> rcnn_build() 重新编译一下 liblinear 即可。

在测试结束时,程序绘制出 precision/recall 曲线和 ap。precision/recall 曲线保存在 $RCNN_ROOT/cachedir/voc_2007_test/face_pr_voc_2007_test.jpg,以方便没有界面的苦逼孩子观看。同时,训练好的模型被保存在了 $RCNN_ROOT/cachedir/voc_2007_trainval/rcnn_demo.m 里。

最终结果

好像……好像结果不怎么好的样子……

还有,我知道什么是 Recall,什么是 Accuracy,但是这里的 Recall-Accuracy 曲线就不明白了。

后续

由于整个过程太长了,所以写了个自动脚本。在 这里 。如有需要,请修改后使用。比如,第一步删文件要不要那么狠,第三步替换一下你的迭代次数。

总结

第一次感受到快被坑哭了是什么感觉。莫名其妙地各种报错,报各种莫名其妙的错,最后 AP 也是超级低。

环境折腾小一周,训练折腾小一周……十分感谢 这个博主 ,这是我能找到的最全面的一个 RCNN 使用笔记了,使我少跳了许多坑。

参考资料

《将 R-CNN 用在自己的数据集上》上有1条评论

  1. 您好,我在用自己的数据训练 RCNN 时遇到了一些问题。
    我按照博客中将 annotation 的 folder 改为 VOC2007, 但在生成 Windows file 时产生了 the specified key is not present in this contianer 的错误,如果您想要截图和我的训练集可以通过邮件联系我,非常感谢您能回答我的问题

发表评论

电子邮件地址不会被公开。 必填项已用*标注