Paper Portal: YOLO9000: より良く、より速く、より強力に
Yolov2 の改良点:
1.バッチ正規化 (Batch Normalization) : conv の後に BN を追加 (conv はバイアスを使用しなくなりました)、モデルの収束を改善し、同時にドロップアウトを削除します; 2. 高解像度分類器 (高解像度分類器) : 完全な値を使用
します。 448x448 の解像度アンカー ボックスによる畳み込み
3. bbox を予測するアンカーの概念を導入します。各アンカーはターゲットとカテゴリを個別に予測し、ターゲットは実際のフレームとカテゴリの条件付き確率の間の以前のボックス (アンカー) IOU を予測します。カテゴリがターゲットを保存するように予測されるという前提の下で; 4.ディメンション クラスター: K 平均法クラスタリング手法を使用して、アンカーのプリセット サイズを取得します; 5.直接位置予測: によって出力されたネットワークの中心座標をオフセットします。パラメータtx t_x
t×、ty t_ytはいとt_o への信頼 (IOU)tああシグモイド活性化関数により、その値は (0,1) で圧縮されます;
6.きめの細かい特徴: バックボーンの浅い特徴を抽出し、パススルー層 (フォーカス) の後の深い特徴と接続します ( concat);
7.マルチスケール トレーニング (マルチスケール トレーニング) : 10 バッチごとに、ネットワーク入力画像サイズを[ 320 , 352 , ... , 608 ] [320,352,...,608]にランダムに変更します。[ 320 、352 、... 、608 ] (画像はネットワークを通じて 32 倍に縮小されるため、32 の整数倍)、モデルの堅牢性を強化します。
Yolov2 の構造:
Darknet19 (分類部分を削除) をバックボーンとして使用して特徴を抽出し、畳み込み層とパススルー層を使用して浅い特徴を処理し、2 つの畳み込み層を使用して深い特徴を処理し、2 つの特徴層で Concat を実行して、最終的に数値を取得します。畳み込み変換後に 125 になるチャンネル数を出力します。
この図では、Darknet19 の入力画像サイズは 224 ですが、Yolov2 のバックボーンとして、入力画像サイズは[ 320 , 352 , . . . , 608 ] [320,352,...,608] であることがわかります。[ 320 、352 、... 、608 ]。
Yolov2 の出力:
VOC データセットの場合、入力画像サイズが 416 の場合、ネットワーク出力は (125,13,13) となり、13x13 は 169 個のアンカー位置を表します; 125 = ( 4 + 1 + 20 ) ∗ 5 125=(4+1+) 20)*5125=( 4+1+20 )∗図5において、4はターゲット回帰パラメータを表し、1はターゲット信頼度を表し、20は20のカテゴリの条件付き確率を表し、最後の5はアンカーのサイズを表す。つまり、各位置に5つのサイズのアンカーがある。
Yolo9000の意味:
著者は、モデルが同時に 9000 以上のカテゴリを検出できるように共同学習法を採用しているため、Yolo9000 と名付けられています。
(コードはモデル構造部分のみを実装しています)
import torch
import torch.nn as nn
import random
def conv(in_channels, out_channels, kernel_size): # conv+bn+leakyrelu
padding = 1 if kernel_size == 3 else 0
return nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size, 1, padding, bias=False),
nn.BatchNorm2d(out_channels),
nn.LeakyReLU(0.1)
)
class Darknet19(nn.Module): # darknet19
def __init__(self):
super(Darknet19, self).__init__()
self.maxpool = nn.MaxPool2d(2, 2)
self.conv1 = conv(3, 32, 3)
self.conv2 = conv(32, 64, 3)
self.bottleneck1 = nn.Sequential(
conv(64, 128, 3),
conv(128, 64, 1),
conv(64, 128, 3)
)
self.bottleneck2 = nn.Sequential(
conv(128, 256, 3),
conv(256, 128, 1),
conv(128, 256, 3)
)
self.bottleneck3 = nn.Sequential(
conv(256, 512, 3),
conv(512, 256, 1),
conv(256, 512, 3),
conv(512, 256, 1),
conv(256, 512, 3)
)
self.bottleneck4 = nn.Sequential(
conv(512, 1024, 3),
conv(1024, 512, 1),
conv(512, 1024, 3),
conv(1024, 512, 1),
conv(512, 1024, 3)
)
def forward(self, x):
x = self.conv1(x)
x = self.maxpool(x)
x = self.conv2(x)
x = self.maxpool(x)
x = self.bottleneck1(x)
x = self.maxpool(x)
x = self.bottleneck2(x)
x = self.maxpool(x)
shallow_x = self.bottleneck3(x) # 浅层特征
deep_x = self.maxpool(shallow_x)
deep_x = self.bottleneck4(deep_x) # 深层特征
return shallow_x, deep_x
class Yolov2(nn.Module):
def __init__(self):
super(Yolov2, self).__init__()
self.backbone = Darknet19()
self.deep_conv = nn.Sequential(
conv(1024, 1024, 3),
conv(1024, 1024, 3)
)
self.shallow_conv = conv(512, 64, 1)
self.final_conv = nn.Sequential(
conv(1280, 1024, 3),
nn.Conv2d(1024, 125, 1, 1, 0)
)
def passthrough(self, x): # passthrough layer
return torch.cat([x[:, :, ::2, ::2], x[:, :, ::2, 1::2], x[:, :, 1::2, ::2], x[:, :, 1::2, 1::2]], dim=1)
def forward(self, x):
shallow_x, deep_x = self.backbone(x) # (B,512,26,26)、(B,1024,13,13)
shallow_x = self.shallow_conv(shallow_x) # (B,512,26,26)-->(B,64,26,26)
shallow_x = self.passthrough(shallow_x) # (B,64,26,26)-->(B,256,13,13)
deep_x = self.deep_conv(deep_x) # (B,1024,13,13)-->(B,1024,13,13)
feature = torch.cat([deep_x, shallow_x], dim=1) # (B,1024,13,13)cat(B,256,13,13)-->(B,1280,13,13)
return self.final_conv(feature) # (B,1280,13,13)-->(B,1024,13,13)-->(B,125,13,13)
if __name__ == "__main__":
batch_size = 8
image_channels = 3
image_size = random.randrange(320, 608 + 32, 32) # [320,352,...,608]
images = torch.randn(batch_size, image_channels, image_size, image_size)
print(images.shape)
yolov2 = Yolov2()
print(yolov2(images).shape)