Warning: Undefined variable $position in /home/pystyles/pystyle.info/public_html/wp/wp-content/themes/lionblog/functions.php on line 4897

YOLOv3 – 物体検出モデル YOLOv3 の仕組みについて解説

YOLOv3 – 物体検出モデル YOLOv3 の仕組みについて解説

概要

物体検出の YOLOv3 モデルについて解説します。実装例は nekobean/pytorch_yolov3 を参照してください。

Advertisement

関連記事

DarkNet 53

DarkNet 53 は、53層の畳み込み層で構成されるクラス分類用のモデルです。YOLOv3 では ImageNet で DarkNet 53 を学習し、出力層の 1×1 の畳み込みを除いた52層の畳み込み層を特徴抽出するための Backbone に使います。

層の種類 出力の形状 out_channels kernel_size padding stride 畳み込み層の数
入力 (N, 32, 608, 608)
conv (N, 32, 608, 608) 32 3 1 1 1
conv (N, 64, 304, 304) 64 3 1 2 1
Residual Block x1 (N, 64, 304, 304) 2
conv (N, 128, 152, 152) 128 3 1 2 1
Residual Block x 2 (N, 128, 152, 152) 4
conv (N, 256, 76, 76) 256 3 1 2 1
Residual Block x 8 (N, 256, 76, 76) 16
conv (N, 512, 38, 38) 512 3 1 2 1
Residual Block x 8 (N, 512, 38, 38) 16
conv (N, 1024, 19, 19) 1024 3 1 2 1
Residual Block x 4 (N, 1024, 19, 19) 8
conv (N, 1000, 19, 19) 1000 1 0 1 1
Global Average Pooling (N, 1000, 1, 1)
Flatten (N, 1000)
  • 最後の 1×1 の畳み込みは特徴マップのチャンネル数を ImageNet のクラス数1000に変更するためのものです。
  • Global Average Pooling を行うため、入力サイズは (608, 608) 以外でも可能です。
  • Residual Block は 1×1 の畳み込みでチャンネル数を半分にしたあと、3×3 の畳み込み層でチャンネル数を元に戻す Residual Block です

    Residual Block

  • プーリング層の代わりに stride=2 の 3×3 の畳み込みで特徴マップのサイズを削減します。

  • すべての層のあとには、Batch Normalization と Leaky ReLU がついてます。

YOLOv3

モデルの構造

YOLOv3

  • DarkNet 53 のうち、出力層の 1×1 の畳み込みを除いた52層の畳み込み層を特徴抽出するための Backbone に使います。
  • 様々なスケールの物体に対応するために、特徴マップの大きさに応じて、3つの出力層があります。
  • 出力チャンネル数が C となっているところは YOLO Layer が必要とするチャンネル数 C = (クラス数 + 5) * Anchor Box の数 を意味します。
  • YOLO Layer は出力または損失を計算するレイヤーです。YOLOv3 – 損失計算や推論結果の生成を行う YOLO レイヤーについて解説 という記事を参照してください。
  • 入力サイズは 608×608 でなくてもよいですが、YOLOv3 の構造上、32の倍数である必要があります。
No ブロック名 層の種類 出力の形状 out_channels kernel_size padding stride
入力 (N, 32, 608, 608)
1 Darknet 53 conv (N, 32, 608, 608) 32 3 1 1
2 Darknet 53 conv (N, 64, 304, 304) 64 3 1 2
3 Darknet 53 Residual Block x1 (N, 64, 304, 304)
4 Darknet 53 conv (N, 128, 152, 152) 128 3 1 2
5 Darknet 53 Residual Block x 2 (N, 128, 152, 152)
6 Darknet 53 conv (N, 256, 76, 76) 256 3 1 2
7 Darknet 53 Residual Block x 8 (N, 256, 76, 76)
8 Darknet 53 conv (N, 512, 38, 38) 512 3 1 2
9 Darknet 53 Residual Block x 8 (N, 512, 38, 38)
10 Darknet 53 conv (N, 1024, 19, 19) 1024 3 1 2
11 Darknet 53 Residual Block x 4 (N, 1024, 19, 19)
12 A conv (N, 512, 19, 19) 512 1 0 1
13 A conv (N, 1024, 19, 19) 1024 3 1 1
14 A conv (N, 512, 19, 19) 512 1 0 1
15 A conv (N, 1024, 19, 19) 1024 3 1 1
16 A conv (N, 512, 19, 19) 512 1 0 1
17 B conv (N, 1024, 19, 19) 1024 3 1 1
18 B conv (N, C, 19, 19) C 1 0 1
19 YOLO Layer
20 C conv (N, 256, 19, 19) 256 1 0 1
21 C Upsampling (N, 256, 38, 38)
22 Concat (No21 + No9) (N, 768, 38, 38)
23 A conv (N, 256, 38, 38) 256 1 0 1
24 A conv (N, 512, 38, 38) 512 3 1 1
25 A conv (N, 256, 38, 38) 256 1 0 1
26 A conv (N, 512, 38, 38) 512 3 1 1
27 A conv (N, 256, 38, 38) 256 1 0 1
28 B conv (N, 512, 38, 38) 512 3 1 1
29 B conv (N, C, 38, 38) C 1 0 1
30 YOLO Layer
31 C conv (N, 128, 38, 38) 128 1 0 1
32 C Upsampling (N, 128, 76, 76)
33 Concat (No32 + No7) (N, 384, 76, 76)
34 A conv (N, 128, 76, 76) 128 1 0 1
35 A conv (N, 256, 76, 76) 256 3 1 1
36 A conv (N, 128, 76, 76) 128 1 0 1
37 A conv (N, 256, 76, 76) 256 3 1 1
38 A conv (N, 128, 76, 76) 128 1 0 1
39 B conv (N, 256, 76, 76) 256 3 1 1
40 B conv (N, C, 76, 76) C 1 0 1
41 YOLO Layer
Advertisement

出力や損失の計算

以下を参照してください

前処理

モデルに入力する前に以下の処理を行います。

  1. Letter Box 処理: モデルの入力サイズと同じ固定の色で塗りつぶした画像を作成し、そこに入力画像をアスペクト比を固定して長辺が丁度一致するようにリサイズしたあと、貼り付けます。推論時は余白が均等になるように貼り付けます。
  2. 値の範囲を [0, 255] を [0, 1] に正規化します。

Letter Box 処理

後処理

  1. Score = Object Score * Class Score とし、この値が閾値未満の矩形は削除します。
  2. クラスごとに独立して Non Maximum Suppression を行い、1つの物体に対して重複している矩形は削除します。

オーグメンテーション

Darknet の学習では以下のオーグメンテーションを行っています。

  1. jitter: アスペクト比をランダムに変更にします。
  2. random_placing: Letter Box 処理のときに画像を貼り付ける位置をランダムにします。
  3. ランダムに左右反転にします。
  4. ランダムに Hue、Saturation、Brightness を変更します。
  5. ランダムにモデルの入力サイズを変更します。(320, 352, 384, 416, 448, 480, 512, 544, 576, 608 のいずれか)

学習

学習率は、ベースとなる学習率 (base_lr) を以下のスケジューラーで調整します。

  1. 最初の burnin_step イテレーションは burn-in フェーズといって、小さい学習率から徐々に base_lr まで増やします。
  2. burnin_step 以上、steps[0] 未満のイテレーションは、学習率を base_lr にします。
  3. steps[0] 以上、steps[1] 未満のイテレーションは、学習率を base_lr * 0.1 にします。
  4. steps[1] 以上のイテレーションは、学習率を base_lr * 0.01 にします。
In [1]:
import matplotlib.pyplot as plt
import numpy as np


def schedule(i, burnin_step, steps):
    if i < burnin_step:
        factor = (i / 1000) ** 4
    elif i < steps[0]:
        factor = 1.0
    elif i < steps[1]:
        factor = 0.1
    else:
        factor = 0.01

    return factor

base_lr = 0.1
max_steps = 10000
steps = np.arange(max_steps)
lr = [base_lr * schedule(i, burnin_step=1000, steps=[8000, 9000]) for i in range(max_steps)]

fig, ax = plt.subplots()
ax.plot(steps, lr)
ax.set_xlabel("Steps")
ax.set_ylabel("Learning Rate")
plt.show()