如何正确训练一个 SVM + HOG 行人检测器
这几个月一直在忙着做大论文,一个基于 SVM 的新的目标检测算法。为了做性能对比,我必须训练一个经典的 Dalal05 提出的行人检测器,我原以为这个任务很简单,但是我错了。
为了训练出一个性能达标的行人检测器,我花了半个月的时间,中间遇到各种 BUG 我就不提了,下面只说正确的步骤。(基于 MATLAB 环境,但是没有代码,请您自己写~)
步骤
1. 训练数据集及其它准备工作
训练检测器的正例(Positive examples)数据库最好采用“全图+标注”的形式,不要是那种切出来的行人小图片,这有助于后续的 bootstrap 操作(当然,在 Daimler 这样的数据库上似乎无法做到),初始的负例(Negative examples)采用一组不包含行人的场景图片。其实负例的选择并不重要,因为在 bootstrap 环节,真正影响性能的,靠近分类超平面的数据点会被自动找到。
大多数靠谱的数据库都有自带的标注读取工具库,或者遵循某种标注格式(例如 PASCAL),你也可以使用进一步封装的标注读取工具,例如 Dollar 的 toolbox。
为了计算 HOG 特征,你可以采用 VLFeat 的 HOG 计算函数,这个 HOG 是我见过的检测性能最好的之一(比 OpenCV 的 HOG 好很多,性能提高超过 5%)。
2. 训练
训练必须进行两轮:初始训练,以及 bootstrap 训练。下面的所说的“切图”操作,就是从某个位置切一个 128 * 64 的子图片的意思,当然,你也可以采用其他的切图尺寸。下面,谈一个很重要的问题,就是标注(ground truth)尺寸的归一化,以及标注的外边距(padding)对训练结果的影响。
由于数据库中的行人标注,并不是依照某种固定的长宽比,而 Dalal 检测器的输出是固定长宽比的,所以,在切图之前,必须将标注框(bounding box)的尺寸调整到统一比例。你也许会试图不调整标注框,而是按照原始标注框切出图片,然后再将图片缩放到 128 * 64,但是这样是不对的!检测结果会打折扣。
此外,Dalal 论文里还指出,如果将行人周围的一圈背景也包含到训练子图片里,那么检测器效果又会提升几个百分点,并且论文指出上下左右个加 16 像素的外边距(padding)效果最好,我做了一下实验,确实如此。
调整标注框可以采用 Dollar 工具箱里的 bbApply 函数。下面是我采用的标注框预处理策略:
- 计算不包含 padding 时的行人长宽比 r,即(128 - 32):(64 - 32)
- 使用 bbApply 将标注框的长宽比调整到 r
- 再使用 bbApply 将标注框的高度和宽度分别放大 128 / (128 - 32),以及 64 / (64 - 32) 倍。
经过上述步骤,所有标注框的比例都一致了,并且包含了 padding。
2.1 初始训练
正例直接从训练集中切出来,负例随机从场景图片中切取,假设我需要 9000 个负例,数据集中有 1000 个场景图片,那么平均每幅图片随机切九个即可。切负例时没必要考虑尺度,因为初始负例对整体训练而言没有显著影响,所以随便切即可,任性。
对切出来的数据提取特征,然后丢到 liblinear 里训练一个 SVM,参数:
-B 1 -c 0.01
其中-B 1
是为了让 SVM 具有 bias term(就是 wx+b 中的那个 b),-c 0.01
是 Dalal 论文里的 C 参数(你可以试着调一下,不过我试了一下,的确 0.01 最好)。
于是,我们得到了一个初始的检测器。SVM 的支撑矢量个数大概在 1000 ~ 3000 个不等。
2.2 Bootstrap 训练
初始检测器是一个非常非常差的检测器,所以千万不要试图一次训练后就直接拿来用。我们需要新一轮 bootstrap 训练。
所谓 Bootstrap 训练,就是用初始检测器在训练数据集中进行检测,搜集所有的误检,将它们作为额外的新的负例加入先前的负例集合,然后再次训练一个 SVM。所谓误检,就是一个与所有标注位置重合都小于 50% 的检测结果,这个“重合”可以用 area(B1 & B2) / area(B1 | B2)
来计算,其中 B1 和 B2 都是矩形框,“与”运算符是矩形框的交叠,“或”运算符是两个矩形框覆盖的区域相加。
一般来说,初始检测器在训练集上能找到大量误检,例如,我在 INRIA 上训练时就有 2 万个误检,这是正常的(所以你一定要把电脑的虚拟内存调大,否则 MATLAB 会挂),bootstrap 训练后,得到的新的 SVM 的支撑矢量个数将达到 6000 个以上,这从一个侧面说明了,该轮训练找到的负例重新约束了分类超平面的位置。
3. 检测
前面我提到,训练时需要将标注框扩大,但是,扩大的标注框,将使检测结果也被扩大,这时,你需要按照与扩大框的步骤相反的方式,将检测结果框缩小。而且,为了使图片边缘的行人不被漏掉,你可以将输入图片的四周都加上 padding 后再进行检测。
4. 性能
按照这种策略训练出来的检测器,在 INRIA 上达到了 Precision:50%,Recall:71% 的性能,超过了 OpenCV 自带的 HOG Detector(其实没有可比性,因为我用了更好的 HOG 特征),如果您有更好的方案,请告诉我,谢谢。