README.md 15.1 KB
Newer Older
K
Kaipeng Deng 已提交
1
# YOLOv3 目标检测
2 3

---
K
Kaipeng Deng 已提交
4
## 内容
5

K
Kaipeng Deng 已提交
6 7 8 9 10 11 12 13
- [简介](#简介)
- [快速开始](#快速开始)
- [进阶使用](#进阶使用)
- [FAQ](#faq)
- [参考文献](#参考文献)
- [版本更新](#版本更新)
- [如何贡献代码](#如何贡献代码)
- [作者](#作者)
14

K
Kaipeng Deng 已提交
15
## 简介
16

K
Kaipeng Deng 已提交
17
[YOLOv3](https://arxiv.org/abs/1804.02767) 是由 [Joseph Redmon](https://arxiv.org/search/cs?searchtype=author&query=Redmon%2C+J)[Ali Farhadi](https://arxiv.org/search/cs?searchtype=author&query=Farhadi%2C+A) 提出的单阶段检测器, 该检测器与达到同样精度的传统目标检测方法相比,推断速度能达到接近两倍.
18

K
Kaipeng Deng 已提交
19
在我们的实现版本中使用了 [Bag of Freebies for Training Object Detection Neural Networks](https://arxiv.org/abs/1902.04103v3) 中提出的图像增强和label smooth等优化方法,精度优于darknet框架的实现版本,在COCO-2017数据集上,我们达到`mAP(0.50:0.95)= 38.9`的精度,比darknet实现版本的精度(33.0)要高5.9.
20

K
Kaipeng Deng 已提交
21
同时,在推断速度方面,基于Paddle预测库的加速方法,推断速度比darknet高30%.
22

K
Kaipeng Deng 已提交
23
## 快速开始
24

K
Kaipeng Deng 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
### 安装

**安装[COCO-API](https://github.com/cocodataset/cocoapi):**

训练前需要首先下载[COCO-API](https://github.com/cocodataset/cocoapi)

    git clone https://github.com/cocodataset/cocoapi.git
    cd cocoapi/PythonAPI
    # if cython is not installed
    pip install Cython
    # Install into global site-packages
    make install
    # Alternatively, if you do not have permissions or prefer
    # not to install the COCO API into global site-packages
    python2 setup.py install --user
40

K
Kaipeng Deng 已提交
41
**安装[PaddlePaddle](https://github.com/PaddlePaddle/Paddle):**
42

K
Kaipeng Deng 已提交
43
在当前目录下运行样例代码需要PadddlePaddle Fluid的v.1.4或以上的版本。如果你的运行环境中的PaddlePaddle低于此版本,请根据[安装文档](http://paddlepaddle.org/documentation/docs/zh/1.4/beginners_guide/install/index_cn.html)中的说明来更新PaddlePaddle。
44

K
Kaipeng Deng 已提交
45
### 数据准备
46

K
Kaipeng Deng 已提交
47
**COCO数据集:**
48

K
Kaipeng Deng 已提交
49
[MS-COCO数据集](http://cocodataset.org/#download)上进行训练,通过如下方式下载数据集。
50 51 52 53

    cd dataset/coco
    ./download.sh

K
Kaipeng Deng 已提交
54
数据目录结构如下:
T
tink2123 已提交
55 56

```
K
Kaipeng Deng 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
dataset/coco/
├── annotations
│   ├── instances_train2014.json
│   ├── instances_train2017.json
│   ├── instances_val2014.json
│   ├── instances_val2017.json
|   ...
├── train2017
│   ├── 000000000009.jpg
│   ├── 000000580008.jpg
|   ...
├── val2017
│   ├── 000000000139.jpg
│   ├── 000000000285.jpg
|   ...
u010070587's avatar
u010070587 已提交
72

T
tink2123 已提交
73
```
74

K
Kaipeng Deng 已提交
75
**自定义数据集:**
76

77
用户可使用自定义的数据集,我们推荐自定义数据集使用COCO数据集格式的标注,并可通过设置`--data_dir`或修改[reader.py](./reader.py#L39)指定数据集路径。使用COCO数据集格式标注时,目录结构可参考上述COCO数据集目录结构。
78

K
Kaipeng Deng 已提交
79
### 模型训练
80

K
Kaipeng Deng 已提交
81
**下载预训练模型:** 本示例提供DarkNet-53预训练[模型](https://paddlemodels.bj.bcebos.com/yolo/darknet53.tar.gz),该模型转换自作者提供的预训练权重[pjreddie/darknet](https://pjreddie.com/media/files/darknet53.conv.74),采用如下命令下载预训练模型:
T
tink2123 已提交
82 83 84

    sh ./weights/download.sh

K
Kaipeng Deng 已提交
85 86
通过设置`--pretrain` 加载预训练模型。同时在fine-tune时也采用该设置加载已训练模型。
请在训练前确认预训练模型下载与加载正确,否则训练过程中损失可能会出现NAN。
T
tink2123 已提交
87

K
Kaipeng Deng 已提交
88
**开始训练:** 数据准备完毕后,可以通过如下的方式启动训练:
T
tink2123 已提交
89 90 91

    python train.py \
       --model_save_dir=output/ \
K
Kaipeng Deng 已提交
92 93 94
       --pretrain=${path_to_pretrain_model} \
       --data_dir=${path_to_data} \
       --class_num=${category_num}
T
tink2123 已提交
95

K
Kaipeng Deng 已提交
96
- 通过设置`export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7`指定8卡GPU训练。
97
- 若在Windows环境下训练模型,建议设置`--use_multiprocess=False`
K
Kaipeng Deng 已提交
98
- 可选参数见:
T
tink2123 已提交
99 100 101

    python train.py --help

K
Kaipeng Deng 已提交
102
**注意:** YOLOv3模型总batch size为64,这里使用8 GPUs每GPU上batch size为8来训练
103

K
Kaipeng Deng 已提交
104
**模型设置:**
105

K
Kaipeng Deng 已提交
106 107
*  模型使用了基于COCO数据集生成的9个先验框:10x13,16x30,33x23,30x61,62x45,59x119,116x90,156x198,373x326
*  YOLOv3模型中,若预测框不是该点最佳匹配框但是和任一ground truth框的重叠大于`ignore_thresh=0.7`,则忽略该预测框的目标性损失
108

K
Kaipeng Deng 已提交
109
**训练策略:**
110

K
Kaipeng Deng 已提交
111 112 113
*  采用momentum优化算法训练YOLOv3,momentum=0.9。
*  学习率采用warmup算法,前4000轮学习率从0.0线性增加至0.001。在400000,450000轮时使用0.1,0.01乘子进行学习率衰减,最大训练500000轮。
*  通过设置`--syncbn=True`可以开启Synchronized batch normalization,该模式下精度会提高
114

115 116
**注意:** Synchronized batch normalization只能用于多GPU训练,不能用于CPU训练和单GPU训练。

K
Kaipeng Deng 已提交
117
下图为模型训练结果:
118
<p align="center">
K
Kaipeng Deng 已提交
119
<img src="image/train_loss.png" height="400" width="550" hspace="10"/><br />
120 121 122
Train Loss
</p>

K
Kaipeng Deng 已提交
123
### 模型评估
124

K
Kaipeng Deng 已提交
125 126 127
模型评估是指对训练完毕的模型评估各类性能指标。本示例采用[COCO官方评估](http://cocodataset.org/#detections-eval), 用户可通过如下方式下载Paddle发布的YOLOv3[模型](https://paddlemodels.bj.bcebos.com/yolo/yolov3.tar.gz)

    sh ./weights/download.sh
128

K
Kaipeng Deng 已提交
129
`eval.py`是评估模块的主要执行程序,调用示例如下:
130 131 132 133

    python eval.py \
        --dataset=coco2017 \
        --weights=${path_to_weights} \
K
Kaipeng Deng 已提交
134
        --class_num=${category_num}
135

K
Kaipeng Deng 已提交
136
- 通过设置`export CUDA_VISIBLE_DEVICES=0`指定单卡GPU评估。
137

K
Kaipeng Deng 已提交
138
若训练时指定`--syncbn=False`, 模型评估精度如下:
139 140 141

|   input size  | mAP(IoU=0.50:0.95) | mAP(IoU=0.50) | mAP(IoU=0.75) |
| :------: | :------: | :------: | :------: |
D
dengkaipeng 已提交
142
| 608x608 | 37.7 | 59.8 | 40.8 |
143 144 145
| 416x416 | 36.5 | 58.2 | 39.1 |
| 320x320 | 34.1 | 55.4 | 36.3 |

K
Kaipeng Deng 已提交
146
若训练时指定`--syncbn=True`, 模型评估精度如下:
D
dengkaipeng 已提交
147 148 149 150

|   input size  | mAP(IoU=0.50:0.95) | mAP(IoU=0.50) | mAP(IoU=0.75) |
| :------: | :------: | :------: | :------: |
| 608x608 | 38.9 | 61.1 | 42.0 |
D
dengkaipeng 已提交
151 152
| 416x416 | 37.5 | 59.6 | 40.2 |
| 320x320 | 34.8 | 56.4 | 36.9 |
153

K
Kaipeng Deng 已提交
154
- **注意:** 评估结果基于`pycocotools`评估器,没有滤除`score < 0.05`的预测框,其他框架有此滤除操作会导致精度下降。
D
dengkaipeng 已提交
155

K
Kaipeng Deng 已提交
156
### 模型推断及可视化
157

K
Kaipeng Deng 已提交
158
模型推断可以获取图像中的物体及其对应的类别,`infer.py`是主要执行程序,调用示例如下:
159 160 161 162

    python infer.py \
       --dataset=coco2017 \
        --weights=${path_to_weights}  \
K
Kaipeng Deng 已提交
163
        --class_num=${category_num} \
164 165
        --image_path=data/COCO17/val2017/  \
        --image_name=000000000139.jpg \
X
xiaoting 已提交
166
        --draw_thresh=0.5
167

K
Kaipeng Deng 已提交
168 169
- 通过设置`export CUDA_VISIBLE_DEVICES=0`指定单卡GPU预测。
- 推断结果显示如下,并会在`./output`目录下保存带预测框的图像
170

K
Kaipeng Deng 已提交
171 172 173 174 175 176 177
```
Image person.jpg detect:
   person          at [190, 101, 273, 372]      score: 0.98832
   dog             at [63, 263, 200, 346]       score: 0.97049
   horse           at [404, 137, 598, 366]      score: 0.97305
Detect result save at ./output/person.png
```
178

K
Kaipeng Deng 已提交
179
下图为模型可视化预测结果:
180 181 182 183 184
<p align="center">
<img src="image/000000000139.png" height=300 width=400 hspace='10'/>
<img src="image/000000127517.png" height=300 width=400 hspace='10'/>
<img src="image/000000203864.png" height=300 width=400 hspace='10'/>
<img src="image/000000515077.png" height=300 width=400 hspace='10'/> <br />
K
Kaipeng Deng 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
YOLOv3 预测可视化
</p>

### Benchmark

模型训练benchmark:

| 数据集 | GPU | CUDA | cuDNN | batch size | 训练速度(1 GPU) | 训练速度(8 GPU) | 显存占用(1 GPU) | 显存占用(8 GPU) |
| :-----: | :-: | :--: | :---: | :--------: | :-----------------: | :-----------------: | :------------: | :------------: |
| COCO | Tesla P40 | 8.0 | 7.1 | 8 (per GPU) | 30.2 images/s | 59.3 images/s | 10642 MB/GPU | 10782 MB/GPU |

模型单卡推断速度:

| GPU | CUDA | cuDNN | batch size | infer speed(608x608) | infer speed(416x416) | infer speed(320x320) |
| :-: | :--: | :---: | :--------: | :-----: | :-----: | :-----: |
| Tesla P40 | 8.0 | 7.1 | 1 | 48 ms/frame | 29 ms/frame |24 ms/frame |

### 服务部署

204
进行YOLOv3的服务部署,用户可以在[eval.py](./eval.py#L54)[infer.py](./infer.py#L47)中保存可部署的推断模型,该模型可以用Paddle预测库加载和部署,参考[Paddle预测库](http://paddlepaddle.org/documentation/docs/zh/1.4/advanced_usage/deploy/index_cn.html)
K
Kaipeng Deng 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

## 进阶使用

### 背景介绍

传统目标检测方法通过两阶段检测,第一阶段生成预选框,第二阶段对预选框进行分类得到类别,而YOLO将目标检测看做是对框位置和类别概率的一个单阶段回归问题,使得YOLO能达到近两倍的检测速度。而YOLOv3在YOLO的基础上引入的多尺度预测,使得YOLOv3网络对于小物体的检测精度大幅提高。

### 模型概览

[YOLOv3](https://arxiv.org/abs/1804.02767) 是一阶段End2End的目标检测器。其目标检测原理如下图所示:
<p align="center">
<img src="image/YOLOv3.jpg" height=400 width=600 hspace='10'/> <br />
YOLOv3检测原理
</p>

### 模型结构

YOLOv3将输入图像分成S\*S个格子,每个格子预测B个bounding box,每个bounding box预测内容包括: Location(x, y, w, h)、Confidence Score和C个类别的概率,因此YOLOv3输出层的channel数为S\*S\*B\*(5 + C)。YOLOv3的loss函数也有三部分组成:Location误差,Confidence误差和分类误差。

YOLOv3的网络结构如下图所示:
<p align="center">
<img src="image/YOLOv3_structure.jpg" height=400 width=400 hspace='10'/> <br />
YOLOv3网络结构
228
</p>
K
Kaipeng Deng 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241

YOLOv3 的网络结构由基础特征提取网络、multi-scale特征融合层和输出层组成。

1. 特征提取网络。YOLOv3使用 [DarkNet53](https://arxiv.org/abs/1612.08242)作为特征提取网络:DarkNet53 基本采用了全卷积网络,用步长为2的卷积操作替代了池化层,同时添加了 Residual 单元,避免在网络层数过深时发生梯度弥散。

2. 特征融合层。为了解决之前YOLO版本对小目标不敏感的问题,YOLOv3采用了3个不同尺度的特征图来进行目标检测,分别为13\*13,26\*26,52\*52,用来检测大、中、小三种目标。特征融合层选取 DarkNet 产出的三种尺度特征图作为输入,借鉴了FPN(feature pyramid networks)的思想,通过一系列的卷积层和上采样对各尺度的特征图进行融合。

3. 输出层。同样使用了全卷积结构,其中最后一个卷积层的卷积核个数是255:3\*(80+4+1)=255,3表示一个grid cell包含3个bounding box,4表示框的4个坐标信息,1表示Confidence Score,80表示COCO数据集中80个类别的概率。

### 模型fine-tune

对YOLOv3进行fine-tune,用户可用`--pretrain`指定下载好的Paddle发布的YOLOv3[模型](https://paddlemodels.bj.bcebos.com/yolo/yolov3.tar.gz),并把`--class_num`设置为用户数据集的类别数。

242
在fine-tune时,若用户自定义数据集的类别数不等于COCO数据集的80类,则加载权重时不应加载`yolo_output`层的权重,可通过在[train.py](./train.py#L76)使用如下方式加载非`yolo_output`层的权重:
K
Kaipeng Deng 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300

```python
if cfg.pretrain:
    if not os.path.exists(cfg.pretrain):
        print("Pretrain weights not found: {}".format(cfg.pretrain))

    def if_exist(var):
        return os.path.exists(os.path.join(cfg.pretrain, var.name)) \
               and var.name.find('yolo_output') < 0

    fluid.io.load_vars(exe, cfg.pretrain, predicate=if_exist)

```

若用户自定义数据集的类别是COCO数据集类别的子集,`yolo_output`层的权重可以进行裁剪后导入。例如用户数据集有6类分别对应COCO数据集80类中的第`[3, 19, 25, 41, 58, 73]`类,可通过如下方式裁剪`yolo_output`层权重:

```python
if cfg.pretrain:
    if not os.path.exists(cfg.pretrain):
        print("Pretrain weights not found: {}".format(cfg.pretrain))

    def if_exist(var):
        return os.path.exists(os.path.join(cfg.pretrain, var.name))

    fluid.io.load_vars(exe, cfg.pretrain, predicate=if_exist)

    cat_idxs = [3, 19, 25, 41, 58, 73]
    # the first 5 channels is x, y, w, h, objectness, 
    # the following 80 channel is for 80 categories
    channel_idxs = np.array(range(5) + [idx + 5 for idx in cat_idxs])
    # we have 3 yolo_output layers
    for i in range(3): 
        # crop conv weights
        weights_tensor = fluid.global_scope().find_var(
                          "yolo_output.{}.conv.weights".format(i)).get_tensor()
        weights = np.array(weights_tensor)
        # each yolo_output layer has 3 anchors, 85 channels of each anchor
        weights = np.concatenate(weights[channel_idxs], 
                                 weights[85 + channel_idxs], 
                                 weights[170 + channel_idxs])
        weights_tensor.set(weights.astype('float32'), place)
        # crop conv bias
        bias_tensor = fluid.global_scope().find_var(
                        "yolo_output.{}.conv.bias".format(i)).get_tensor()
        bias = np.array(bias_tensor)
        bias = np.concatenate(bias[channel_idxs], 
                              bias[85 + channel_idxs], 
                              bias[150 + channel_idxs])
        bias_tensor.set(bias.astype('float32'), place)

```

## FAQ

**Q:** 我使用单GPU训练,训练过程中`loss=nan`,这是为什么?  
**A:** YOLOv3中`learning_rate=0.001`的设置是针对总batch size为64的情况,若用户的batch size小于该值,建议调小学习率。

**Q:** 我训练YOLOv3速度比较慢,要怎么提速?  
301
**A:** YOLOv3的数据增强比较复杂,速度比较慢,可通过在[reader.py](./reader.py#L284)中增加数据读取的进程数来提速。若用户是进行fine-tune,也可将`--no_mixup_iter`设置大于`--max_iter`的值来禁用mixup提升速度。
K
Kaipeng Deng 已提交
302

303 304 305
**Q:** 我使用YOLOv3训练两个类别的数据集,训练`loss=nan`或推断结果不符合预期,这是为什么?  
**A:** `--label_smooth`参数会把所有正例的目标值设置为`1-1/class_num`,负例的目标值设为`1/class_num`,当`class_num`较小时,这个操作影响过大,可能会出现`loss=nan`或者训练结果错误,类别数较小时建议设置`--label_smooth=False`。若使用Paddle Fluid v1.5及以上版本,我们在C++代码中对这种情况作了保护,设置`--label_smooth=True`也不会出现这些问题。

K
Kaipeng Deng 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
## 参考文献

- [You Only Look Once: Unified, Real-Time Object Detection](https://arxiv.org/abs/1506.02640v5), Joseph Redmon, Santosh Divvala, Ross Girshick, Ali Farhadi.
- [YOLOv3: An Incremental Improvement](https://arxiv.org/abs/1804.02767v1), Joseph Redmon, Ali Farhadi.
- [Bag of Freebies for Training Object Detection Neural Networks](https://arxiv.org/abs/1902.04103v3), Zhi Zhang, Tong He, Hang Zhang, Zhongyue Zhang, Junyuan Xie, Mu Li.

## 版本更新

- 1/2019, 新增YOLOv3模型。
- 4/2019, 新增YOLOv3模型Synchronized batch normalization模式。

## 如何贡献代码

如果你可以修复某个issue或者增加一个新功能,欢迎给我们提交PR。如果对应的PR被接受了,我们将根据贡献的质量和难度进行打分(0-5分,越高越好)。如果你累计获得了10分,可以联系我们获得面试机会或者为你写推荐信。

## 作者

- [heavengate](https://github.com/heavengate)
- [tink2123](https://github.com/tink2123)