0 はじめに
ご存知のとおり、ターゲット検出アルゴリズムはより複雑で詳細が多く、再現が困難です。私たちが立ち上げた MMDetection オープンソース フレームワークは、上記の問題を解決することを期待しています。現在、MMdetection は、Faster R-CNN シリーズ、Mask R-CNN シリーズ、YOLO シリーズ、比較的新しい DETR など、主流および最先端のモデルのほとんどを再現しています。モデル ライブラリは非常に豊富で、スターは13kに近く、学術研究や産業において着陸に広く使用されています。
上記の豊富なモデルに基づいて、非常に柔軟で便利な拡張モードもサポートしています。このフレームワークに慣れ、関連ドキュメントを読んだ後は、さまざまなモデルを簡単に構築できます。この一連のチュートリアルの目的は、フレームワークの使用と拡張の難しさをさらに軽減し、MMDetection を使いやすく、理解しやすい主流のターゲット検出フレームワークにするよう努めます。
1 ターゲット検出アルゴリズムの抽象的なプロセス
簡単に言えば、ターゲット検出アルゴリズムは 3 つの次元に分割できます。
- 段階数に応じて分けると、通常は 1 段階と 2 段階になりますが、実際にはその境界は特に明確ではありません。たとえば、リファイン段階を持つアルゴリズム RepPoints は、実際には 1.5 段階のアルゴリズムと考えることができます。カスケード R-CNN は多段階アルゴリズムと考えることができます。位相アルゴリズムは、簡単にするために、上の図ではあまり詳しく説明されていません。
- 事前定義されたアンカーが必要かどうかに応じて、従来のものにはアンカーベースとアンカーフリーがあり、もちろん一部のアルゴリズムはこの 2 つが混合されています。
- 変圧器構造が使用されているかどうかに応じて、変圧器構造に基づく現在のターゲット検出アルゴリズムは急速に発展しており、大きな注目を集めているため、このカテゴリはここに特別に追加されます
分割方法に関係なく、実際にはいくつかの固定モジュールに分割することができ、モジュールのスタッキングを通じて全体の検出アルゴリズム システムが構築されます。
2 MMDetectionの全体構築プロセスと考え方
現在のコード実装に基づいて、すべてのターゲット検出アルゴリズムは次のプロセスに従って分割されます。
以上のプロセスがMMDetectionのコード構築プロセスに相当し、各コンポーネントの機能を理解することは、アルゴリズムのソースコードを読むのに役立つだけでなく、新たに提案されたアルゴリズムに対応する改良部分を素早く理解することができます。各モジュールについては以下で詳しく説明します。
2.1 コアコンポーネントのトレーニング
トレーニング部分には通常 9 つのコアコンポーネントが含まれており、全体的なプロセスは次のとおりです。
- バッチの画像は、特徴抽出のために最初にバックボーンに入力されます。典型的なバックボーン ネットワークは ResNet です。
- 出力されたシングルスケールまたはマルチスケールの特徴マップは、特徴の融合または強化のためにネック モジュールに入力されます。典型的なネックは FPN です。
- 上記のマルチスケール特徴は最終的にヘッド部分に入力されます。ヘッド部分には通常、分類と回帰分岐の出力が含まれます。
- ネットワーク構築フェーズ全体で、SPP、DCN などの抽出機能を強化するために、いくつかのプラグ アンド プレイ拡張オペレーターを導入できます。
- ターゲット検出ヘッドの出力は一般に特徴マップですが、分類タスクの場合、陽性サンプルと陰性サンプルの間には深刻な不均衡があり、これは陽性サンプルと陰性サンプルの属性の分布とサンプリングによって制御できます。
- 収束を促進し、複数のブランチのバランスをとるために、gt bbox は通常エンコードされます。
- 最後のステップは、トレーニングの分類と回帰損失を計算することです。
- トレーニングプロセスにはオプティマイザーの選択などの多くのトリックも含まれており、パラメーターの調整も非常に重要です
上記の 9 つのコンポーネントはすべてのアルゴリズムで必要なわけではないため、以下で詳しく分析することに注意してください。
2.1.1 バックボーン
バックボーンの役割は主に特徴抽出です。現在、ほとんどのスケルトン ネットワークは MMDetection に統合されています。詳細については、ファイルを参照してください。V2.7mmdet/models/backbones
で実装されているスケルトンは次のとおりです。
__all__ = [
'RegNet', 'ResNet', 'ResNetV1d', 'ResNeXt', 'SSDVGG', 'HRNet', 'Res2Net',
'HourglassNet', 'DetectoRS_ResNet', 'DetectoRS_ResNeXt', 'Darknet',
'ResNeSt', 'TridentResNet'
]
最も一般的に使用されているのは、ResNet シリーズ、ResNetV1d シリーズ、Res2Net シリーズです。スケルトンを拡張する必要がある場合は、上記のネットワークを継承し、レジストリ メカニズムを通じて使用できるように登録できます。典型的な使用法は次のとおりです。
# 骨架的预训练权重路径
pretrained='torchvision://resnet50',
backbone=dict(
type='ResNet', # 骨架类名,后面的参数都是该类的初始化参数
depth=50,
num_stages=4,
out_indices=(0, 1, 2, 3),
frozen_stages=1,
norm_cfg=dict(type='BN', requires_grad=True),
norm_eval=True,
style='pytorch'),
MMCV の登録メカニズムを使用すると、辞書形式の構成を通じて登録されたクラスをインスタンス化できます。これは非常に便利で柔軟です。
2.1.2 ネック
首は背骨と頭の間の接続層と考えることができ、主に背骨の機能を効率的に融合および強化する役割を果たし、入力シングルスケールまたはマルチスケールの出力を融合および強化することができます。特徴。詳細については、ファイルを参照してくださいmmdet/models/necks
。V2.7 で実装されたネックは次のとおりです。
__all__ = [
'FPN', 'BFP', 'ChannelMapper', 'HRFPN', 'NASFPN', 'FPN_CARAFE', 'PAFPN',
'NASFCOS_FPN', 'RFP', 'YOLOV3Neck'
]
最も一般的に使用されるのは FPN です。一般的な使用法は次のとおりです。
neck=dict(
type='FPN',
in_channels=[256, 512, 1024, 2048], # 骨架多尺度特征图输出通道
out_channels=256, # 增强后通道输出
num_outs=5), # 输出num_outs个多尺度特征图
2.1.3 頭部
ターゲット検出アルゴリズムの出力には、通常、分類とフレーム座標回帰の 2 つのブランチが含まれており、アルゴリズムが異なれば、複雑さと柔軟性の高い異なるヘッド モジュールが使用されます。ネットワーク構築の観点から見ると、ターゲット検出アルゴリズムを理解するには、主にヘッド モジュールを理解する必要があります。
MMDetection のヘッド モジュールは、2 段階で必要な RoIHead と 1 段階で必要な DenseHead に分かれています。つまり、1 段階アルゴリズムのすべてのヘッド モジュールが含まれており、2 段階アルゴリズムには追加のものも含まれていmmdet/models/dense_heads
ますmmdet/models/roi_heads
。
V2.7 で現在実装されている dense_heads には次のものが含まれます。
__all__ = [
'AnchorFreeHead', 'AnchorHead', 'GuidedAnchorHead', 'FeatureAdaption',
'RPNHead', 'GARPNHead', 'RetinaHead', 'RetinaSepBNHead', 'GARetinaHead',
'SSDHead', 'FCOSHead', 'RepPointsHead', 'FoveaHead',
'FreeAnchorRetinaHead', 'ATSSHead', 'FSAFHead', 'NASFCOSHead',
'PISARetinaHead', 'PISASSDHead', 'GFLHead', 'CornerHead', 'YOLACTHead',
'YOLACTSegmHead', 'YOLACTProtonet', 'YOLOV3Head', 'PAAHead',
'SABLRetinaHead', 'CentripetalHead', 'VFNetHead', 'TransformerHead'
]
ほぼすべてのアルゴリズムには独立したヘッドが含まれており、roi_heads はより複雑であるため、リストされていません。
2 段階または複数段階のアルゴリズムには、異なるサイズの RoI 特徴マップを同じサイズに統合するために使用される領域抽出器 roi 抽出器がさらに含まれることに注意してください。
ヘッド部分のネットワーク構成は比較的単純ですが、MMDetectionで最も複雑なモジュールはヘッドです。これは、正および負のサンプル属性定義、正および負のサンプル サンプリング、および bbox コーデック モジュールが結合されてヘッド モジュールで呼び出されるからです。モジュールは、プロセス全体の最後の部分で詳細に分析されます。
2.1.4 強化
Enhance は機能を拡張できるプラグアンドプレイモジュールで、その特定のコードを dict の形式でバックボーン、ネック、ヘッドに登録でき、非常に便利です (まだ完全ではありません)。一般的に使用される拡張モジュールは、SPP、ASPP、RFB、Dropout、Dropblock、DCN、およびさまざまな注目モジュール SeNet、Non_Local、CBA などです。現在、MMDetection の一部のモジュールは、ResNet スケルトンのプラグインなど、拡張へのアクセスをサポートしています。この部分の解釈は、特定のアルゴリズム モジュールで説明されています。
2.1.5 BBoxの割り当て
正および負のサンプル属性割り当てモジュールの機能は、正および負のサンプルを定義すること、または正および負のサンプルを割り当てることです (サンプル定義を無視することも含まれる場合があります)。ターゲットの検出は分類と回帰の問題であるため、分類シナリオに対して正のサンプルと負のサンプルを決定する必要があります。そうしないとトレーニングできません。このモジュールは非常に重要です。ポジティブとネガティブのサンプル割り当て戦略が異なると、パフォーマンスに大きな違いが生じます。現在のターゲット検出アルゴリズムのほとんどは、この部分を改善する予定であり、これは非常に重要です。いくつかの典型的な割り当て戦略は次のとおりです。
対応するコードは にありmmdet/core/bbox/assigners
、V2.7 には主に次のものが含まれます。
__all__ = [
'BaseAssigner', 'MaxIoUAssigner', 'ApproxMaxIoUAssigner',
'PointAssigner', 'ATSSAssigner', 'CenterRegionAssigner', 'GridAssigner',
'HungarianAssigner'
]
2.1.6 BBox サンプラー
各サンプルのプラスとマイナスの属性を決定した後、サンプルのバランス操作も必要になる場合があります。このモジュールの機能は、上で定義した正のサンプルと負のサンプルの不均衡をサンプリングし、この問題を克服するよう努めることです。一般に、ターゲット検出には gt bbox が非常に少ないため、陽性サンプルと陰性サンプルの比率は 1 よりもはるかに小さくなります。機械学習の観点に基づくと、データが極端に不均衡な場合、分類ではより多くのサンプルでカテゴリを予測する傾向があり、過剰適合が発生します。この問題を克服するには、適切な正および負のサンプル サンプリング戦略が非常に重要です。典型的なサンプリングの戦略は次のとおりです。
対応するコードは にありmmdet/core/bbox/samplers
、V2.7 には主に次のものが含まれます。
__all__ = [
'BaseSampler', 'PseudoSampler', 'RandomSampler',
'InstanceBalancedPosSampler', 'IoUBalancedNegSampler', 'CombinedSampler',
'OHEMSampler', 'SamplingResult', 'ScoreHLRSampler'
]
2.1.7 BBox エンコーダ
複数の損失をより適切に収束させてバランスをとるために、多くの具体的な解決策があり、bbox エンコードおよびデコード戦略もその 1 つです。bbox エンコード段階は、陽性サンプルの gt bbox に特定のエンコード変換を使用することに対応します (逆の操作は bbox デコードです)、最も単純なエンコードは、gt bbox を画像の幅と高さで割って、分類と回帰の分岐を正規化することです。いくつかの典型的なエンコードおよびデコード戦略は次のとおりです。
対応するコードは にありmmdet/core/bbox/coder
、V2.7 には主に次のものが含まれます。
__all__ = [
'BaseBBoxCoder', 'PseudoBBoxCoder', 'DeltaXYWHBBoxCoder',
'LegacyDeltaXYWHBBoxCoder', 'TBLRBBoxCoder', 'YOLOBBoxCoder',
'BucketingBBoxCoder'
]
2.1.8 損失
損失は通常、分類損失と回帰損失に分けられます。回帰損失は、ネットワーク ヘッドによって出力された予測値と bbox エンコーダによって取得されたターゲットに対して勾配降下反復トレーニングを実行します。
損失の設計は、主要なアルゴリズムの主要な改善対象でもあり、一般的に使用される損失は次のとおりです。
対応するコードは にありmmdet/models/losses
、V2.7 には主に次のものが含まれます。
__all__ = [
'cross_entropy', 'binary_cross_entropy',
'mask_cross_entropy', 'CrossEntropyLoss', 'sigmoid_focal_loss',
'FocalLoss', 'smooth_l1_loss', 'SmoothL1Loss', 'balanced_l1_loss',
'BalancedL1Loss', 'mse_loss', 'MSELoss', 'iou_loss', 'bounded_iou_loss',
'IoULoss', 'BoundedIoULoss', 'GIoULoss', 'DIoULoss', 'CIoULoss', 'GHMC',
'GHMR', 'reduce_loss', 'weight_reduce_loss', 'weighted_loss', 'L1Loss',
'l1_loss', 'isr_p', 'carl_loss', 'AssociativeEmbeddingLoss',
'GaussianFocalLoss', 'QualityFocalLoss', 'DistributionFocalLoss',
'VarifocalLoss'
]
MMDetection では多くの損失が実装されており、これを直接使用できることがわかります。
2.1.9 トレーニングのコツ
トレーニング手法は数多くありますが、パラメーター調整の作業の大部分は、ハイパーパラメーターのこの部分の設定であると言われることがよくあります。この部分は内容がごちゃごちゃしていて完全な統一が難しいのですが、現在主流となっている手口は以下の通りです。
現時点では、MMDetection のこの部分は引き続き改善される予定であり、皆様の貢献を歓迎します。
2.2 コアコンポーネントのテスト
テストのコア コンポーネントはトレーニングと非常に似ていますが、より単純です。必要なネットワーク構築部分 (バックボーン、ネック、ヘッド、エンハンスメント) を除いて、陽性サンプルと陰性サンプルの定義、陽性サンプルの 3 つの最も難しい部分は必要ありません。負のサンプルのサンプリングと損失の計算が含まれますが、さらに bbox 後処理モジュールとテスト トリックが必要です。
2.2.1 BBox デコーダ
エンコードはトレーニング中に実行され、対応するテスト リンクをデコードする必要があります。エンコードに応じて、デコードも異なります。簡単な例を挙げると、トレーニング中に正規化のために幅と高さがピクチャの幅と高さで直接除算されると仮定すると、デコード プロセスにはピクチャの幅と高さを乗算するだけで済みます。そのコードと bbox エンコーダは にまとめられていますmmdet/core/bbox/coder
。
2.2.2 BBox ポストプロセス
元の画像のスケール bbox を取得した後、bbox が重なり合う可能性があるため、通常は後処理が必要になります。最も一般的に使用される後処理は、非最大値抑制とその変形です。
対応するファイルは にありmmdet/core/post_processing
、V2.7 には主に次のものが含まれます。
__all__ = [
'multiclass_nms', 'merge_aug_proposals', 'merge_aug_bboxes',
'merge_aug_scores', 'merge_aug_masks', 'fast_nms'
]
2.2.3 テストのコツ
検出パフォーマンスを向上させるために、テスト段階でもトリックが使用されます。この段階でも多くのトリックがあり、それらを完全に統一することは困難です。最も代表的なものは、マルチスケール テストとさまざまなモデル統合手法です。典型的な構成は次のとおりです。
dict(
type='MultiScaleFlipAug',
img_scale=(1333, 800),
flip=True,
transforms=[
dict(type='Resize', keep_ratio=True),
dict(type='RandomFlip'),
dict(type='Normalize', **img_norm_cfg),
dict(type='Pad', size_divisor=32),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img']),
])
2.3 アルゴリズムのトレーニングとテストのプロセス
各トレーニング プロセスのコア コンポーネントを分析した後、誰もがアルゴリズム全体の構造を理解しやすくするために、MMDetection がトレーニングのためにさまざまなコンポーネントをどのように組み合わせているかを分析します。ここでは 1 段階の検出器を例として取り上げ、2 段階の検出器を例に挙げます。検出器も同様です。
class SingleStageDetector(---):
def __init__(...):
# 构建骨架、neck和head
self.backbone = build_backbone(backbone)
if neck is not None:
self.neck = build_neck(neck)
self.bbox_head = build_head(bbox_head)
def forward_train(---):
# 先运行backbone+neck进行特征提取
x = self.extract_feat(img)
# 对head进行forward train,输出loss
losses = self.bbox_head.forward_train(x, img_metas, gt_bboxes,
gt_labels, gt_bboxes_ignore)
return losses
def simple_test(---):
# 先运行backbone+neck进行特征提取
x = self.extract_feat(img)
# head输出预测特征图
outs = self.bbox_head(x)
# bbox解码和还原
bbox_list = self.bbox_head.get_bboxes(
*outs, img_metas, rescale=rescale)
# 重组结果返回
bbox_results = [
bbox2result(det_bboxes, det_labels, self.bbox_head.num_classes)
for det_bboxes, det_labels in bbox_list
]
return bbox_results
bbox_head.forward_train
上記は、検出器アルゴリズム全体のトレーニングとテストのための最も単純なロジックです。トレーニング部分のコアがテスト部分のコアであることがわかります。以下は、個別に簡単にbbox_head.get_bboxes
分析したものです。
2.3.1 bbox_head.forward_train
forward_train は次のような汎用関数です。
def forward_train(...):
# 调用每个head自身的forward方法
outs = self(x)
if gt_labels is None:
loss_inputs = outs + (gt_bboxes, img_metas)
else:
loss_inputs = outs + (gt_bboxes, gt_labels, img_metas)
# 计算每个head自身的loss方法
losses = self.loss(*loss_inputs, gt_bboxes_ignore=gt_bboxes_ignore)
# 返回
return losses
異なるヘッドでは、転送内容は異なりますが、それでも次のように抽象化できます。outs = self(x)
def forward(self, feats):
# 多尺度特征图,一个一个迭代进行forward_single
return multi_apply(self.forward_single, feats)
def forward_single(self, x):
# 运行各个head独特的head forward方法,得到预测图
....
return cls_score, bbox_pred...
ヘッドが異なると、損失計算部分もより複雑になります。これは次のように単純に抽象化できます。losses = self.loss(...)
def loss(...):
# 1 生成anchor-base需要的anchor或者anchor-free需要的points
# 2 利用gt bbox对特征图或者anchor计算其正负和忽略样本属性
# 3 进行正负样本采样
# 4 对gt bbox进行bbox编码
# 5 loss计算,并返回
return dict(loss_cls=losses_cls, loss_bbox=losses_bbox,...)
2.3.2 bbox_head.get_bboxes
get_bboxes 関数の方が簡単です
def get_bboxes(...):
# 1 生成anchor-base需要的anchor或者anchor-free需要的points
# 2 遍历每个输出层,遍历batch内部的每张图片,对每张图片先提取指定个数的预测结果,缓解后面后处理压力;对保留的位置进行bbox解码和还原到原图尺度
# 3 统一nms后处理
return det_bboxes, det_labels...
3 まとめ
この記事では、ターゲット検出器が複数のコア コンポーネントによってどのようにスタックされるかに焦点を当てています。特定のコードは必要ありません。一般的な理解だけが必要です。理解する必要がある最も重要なことは、あらゆるターゲット検出アルゴリズムは n 個のコア コンポーネントに分割できるということです。 、コンポーネントとコンポーネントは、再利用と設計を容易にするために分離されています。新しいアルゴリズムに直面したとき、まずどのコアコンポーネントが主に改善されるかを分析し、それからアルゴリズムを効率的に習得できます。
また、特にデータセット、データローダー、分散トレーニング関連の検出コードなど、解析されていない重要なモジュールがいくつかありますが、紙面の都合上、紹介は省略しますので、必要な場合はコメント欄にメッセージを残してください。
MMDetection を再びご利用いただけるよう皆様を歓迎します。また、コミュニティへの貢献も大歓迎です。
最後に全体像を添付します。