最近接到任务,需要对目标检测进行一下调研。于是我面向GitHub进行了一波编程。经过一番比较发现,RFBNet貌似是非常不错的选择。所以就打算和跟我一样的新手们分享一下经验,也算是记录一下踩过的坑。哪里有错的地方欢迎大佬指出。
关于RFBNet
Receptive Field Block Net for Accurate and Fast Object Detection
这是发表于ECCV2018的目标检测文章,主要贡献是在SSD网络中引入Receptive Field Block (RFB)。RFB结构主要有两个特点:
1、该结构是由多个不同尺寸的卷积核构成的多分枝结构。
2、引入了dilated卷积,增大了感受野。
论文具体实现的RFB块有以下两种版本:
其整体结构与SSD一样是基于VGG16的backbone(还有其他版本的,如MobileNet),之所以采用VGG16是因为这是一个比较轻量的骨干网络,在计算速度和精度方面都有很好的表现,这是经过SSD实践的。以下是RFBNet-300的整体结构,此外还有RFBNet-512版本,这里的300和512是输入的尺寸。
论文给出了基于VOC和COCO数据集的实验结果:
结果基本上可以总结为一句话,精度高的没我算的快(能够实时),算的快的没我精度高。
关于论文更多的讲解可以自行百度。。
训练及遇到问题
在大体看完论文后,我就开始了面向GitHub编程了[滑稽]。由于论文是2018年的,官方代码是基于PyTorch0.4.0的,而我装的是1.3.0,想都不用想肯定很多不兼容的。所以我就找了一个基于1.3版本的,开始了一段辛酸路程。
Pytorch1.3版本(失败)
相关配置
pytorch1.3
OpenCV
CUDA-9.2
VOC2007&VOC2012
GTX980 4GB * 4
MobaXterm
首先是克隆一波代码,之后下载数据集。关于数据集的下载,由于COCO太大下载需要很长时间,所以我只在VOC2007和2012上进行了实验。官方给出了下载脚本:
# specify a directory for dataset to be downloaded into, else default is ~/data/
sh data/scripts/VOC2007.sh # <directory>
# specify a directory for dataset to be downloaded into, else default is ~/data/
sh data/scripts/VOC2012.sh # <directory>
但VOC下载官网是在不敢恭维,推荐在这个网站下载之后解压到相应目录即可。
训练及测试
首先需要下载预训练好去全连接层的VGG16权重,将其放到weights目录下。
mkdir weights
cd weights
#如果手动下载VGG16就不需要下面语句
wget https://s3.amazonaws.com/amdegroot-models/vgg16_reducedfc.pth
然后就cd到项目根目录下,运行以下语句开始训练
python train_RFB.py -d VOC -v RFB_vgg -s 300
具体参数的含义就是:
-d VOC 表示使用的数据集,这里是VOC数据集。
-v RFB_vgg 表示是基于VGG,还有RFB_E_VGG和RFB_mobile可选。
-s 300 表示输入尺寸为300,可选512.
当然还可以手动调整其他参数,可以在train_RFB.py中查看。特别地,官方还说明了,对于论文中的精度,在VOC上训练的epoches在240左右,COCO上训练应该是130epoches左右。
由于训练时间较长,建议将程序放入后台运行,关于Linux后台运行程序可以参考这篇博文,对于本程序只需要将以上语句修改成以下形式即可:
nohup python train_RFB.py -d VOC -v RFB_vgg -s 300 &
将程序放入后台之后想看看是否正在运行,可以在终端输入查看GPU占用情况:
nvidia-smi
可以看到980老干部都已经开始干活了,现在需要做的就是等待结果了。
Two thousand years later…
经过300epoches后终于训练完成,我兴致勃勃的开始进行测试了,之前提到过所以这里选用240轮的训练权重。
python test_RFB.py -d VOC -v RFB_vgg -s 300 --trained_model ./weights/RFB_vgg_VOC_epoches_240.pth
作者给出的结果如下:
我以为我的结果也能达到差不多的精度,但是现实却泼了一盘冷水。。
出现了CudaCheck的错误信息,但是还能继续跑,最后还出现了Segmentation fault (core dumped),一开始以为没什么关系:
但是结果却令人沮丧:
mAP只有45.92%,跟官方结果差了不是一星半点,还出现了Segmentation fault这种问题,至今还是不明白这是为什么,有知道的大佬请赐教。于是乎我就开始找问题,看大家的解决方法。看到issue区很多人用高版本pytorch在复现的时候都遇到mAP过低的问题,我就想是不是版本间的差异导致的,我一菜鸟也搞不定,所以一狠心就决定再装一个0.4.0的pytorch环境。
PyTorch0.4.0版本(成功)
相关配置
pytorch0.4.0
OpenCV
CUDA-9.2
VOC2007&VOC2012
GTX980 4GB * 4
MobaXterm
首先是创建一个新的虚拟环境避免污染原来环境。
conda create -n pytorch04 python=3.6
这样就创建了一个新的环境,可以运行以下命令查看系统安装的环境。
conda env list
我这里就有原来的环境和新建的pytorch04环境。
创建完之后激活该环境:
source activate pytorch04 #windows 下可能有所不同
激活之后就可以在环境中安装0.4.0版本的pyotrch了。
conda install pytorch=0.4.0 torchvision cuda92
#或者
pip install torch==0.4.0 torchvision cuda92 -i https://pypi.tuna.tsinghua.edu.cn/simple
关于在Windows安装PyTorch可以参考我之前的博文。
训练及测试
在装完pytorch0.4.0之后,就可以训练了。经过一番比较之后,最后还是回到了官方代码哈哈。。
下载代码后,要确保运行的环境是0.4版的,第一次我就搞错了,在1.2环境下运行该代码,结果又是一顿乱错,找了半天才发现,费时费力。。。
首先,运行项目根目录下的make.sh
./make.sh
然后根据自己GPU算力(自行百度)修改utils/build.py中第131行的
'nvcc': ['-arch=sm_52'
之后步骤就与PyTorch1.2版本无异。
下载VGG16预训练权重到weights文件夹中,将VOC2007和2012数据集放到data目录下,安装相应的依赖库。
之后输入以下命令:
python train_RFB.py -d VOC -v RFB_vgg -s 300
经过20多个小时的训练后进行测试:
python test_RFB.py -d VOC -v RFB_vgg -s 300 --trained_model ./weights/RFB_vgg_VOC_epoches_240.pth
测试过程没有提示任何错误,一路执行下来十分顺畅,最后得出的结果令我泪目:
mAP=80.56%与官方的80.7相当接近,很是令我满意!!!
其实官方也提供了预训练好的模型,我也下载了进行了对比测试,结果与我训练的结果基本一致,所以这份代码是可以复现论文结果的。至于1.2版本的代码,我猜测可能是作者实现的时候有缺陷,或者框架本身不同版本底层的实现有差别导致的,这个还得花点精力研究研究。
有什么错误和问题欢迎大家指出交流!
破案了!!! 2020.8.04
之前出现高版本运行mAP过低的情况,后来经过我检查,发现是在./make.sh的时候出现的问题,make.sh的主要作用就是构建nms和pycocotools 。 可能由于新版本的pytorch1.2和utils中用到的编译器版本不一致导致mAP过低。我的解决方法是不用make.sh构建的nms,直接使用github上的基于python的nms程序来进行测试,程序如下:
def nms_py(dets, thresh):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
ndets = dets.shape[0]
suppressed = np.zeros((ndets), dtype=np.int)
keep = []
for _i in range(ndets):
i = order[_i]
if suppressed[i] == 1:
continue
keep.append(i)
ix1 = x1[i]
iy1 = y1[i]
ix2 = x2[i]
iy2 = y2[i]
iarea = areas[i]
for _j in range(_i + 1, ndets):
j = order[_j]
if suppressed[j] == 1:
continue
xx1 = max(ix1, x1[j])
yy1 = max(iy1, y1[j])
xx2 = min(ix2, x2[j])
yy2 = min(iy2, y2[j])
w = max(0.0, xx2 - xx1 + 1)
h = max(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (iarea + areas[j] - inter)
if ovr >= thresh:
suppressed[j] = 1
return keep
具体就是在test_RFB.py中找到
keep = nms(c_dets, 0.45, force_cpu=args.cpu)
将这一句替换成
keep = nms_py(c_dets, 0.45)
最后测试结果与0.4.0版本的结果一致,说明这个程序是合理的。
由于没有用到COCO,所以pycocotools就暂时不管。
参考
官方代码
https://github.com/ruinmessi/RFBNet/issues/13
RFBNet相关设置
https://www.cnblogs.com/hizhaolei/p/9911029.html
https://blog.csdn.net/u014380165/article/details/81556769
Windows配置pytorch、CUDA及cuDNN
https://www.cnblogs.com/andylhc/p/9721705.html