1 はじめに
BEV 空間で視覚タスクを実行することは、自動運転の主流技術になりつつあります.BEV 空間で視覚タスクを実行する方法を理解するために、BEVFormer プロジェクトを使用してその手順を理解する予定です.この記事は BEVFormer の操作ですと全体のフレームワークの組み合わせ (ソース コードを参照してください。少し面倒です)、後でソース コードを読むことに慣れている場合は、より詳細なコメントを用意してください。
BEVFormer ソース コード: https://github.com/fundamentalvision/BEVFormer
BEVFormer 学習記事: https://zhuanlan.zhihu.com/p/543335939
BEVFormer 学習ビデオ: https://www.bilibili.com/video/BV1rK411o7PS/?spm_id_from=333.788&vd_source=2055b62125c0277a0d6878f41c89fec2
BEV モデルを学んでいる友人や学びたい友人は、交換グループに参加して議論したり、論文や問題を研究したりできます。コードの実装, 追加できます v グループ: Rex1586662742, q グループ: 468713665
2.実行する
プロジェクトを学習する唯一の方法は、最初にプロジェクトを実行することです. 問題を回避するために、公式のインストール環境に従うことをお勧めします. 環境がインストールされたら、公式のコマンドに従って実行してください. では、デバッグして行ごとに表示するにはどうすればよいでしょうか。デバッグには pycharm を使用します。これは、次の 2 つのステップに分けることができます。
- リンクlaunch.py
最初に現在のconda環境(bev)の下にあるlaunch.pyファイルを見つけて、このプロジェクトにリンクします
sudo ln -s /home/***/miniconda3/envs/bev/lib/python3.8/site-packages/torch/distributed/launch.py ./
- 構成を編集し
、[実行] をクリックして構成ファイルを選択し、スクリプト パスに launch.py の絶対パスを入力し、仮パラメーターに次のパラメーターを入力します。これらはローカルのパスに変更する必要があります。マシン. 一般的に言えば, あなたはデバッグすることができます. エラーが報告された場合, それは見つけることができません. データセットの写真については, 以下の3番目の記事を参照してください.
--nproc_per_node=1
--master_port=29503
/data/rex/BEV/BEVFormer-master/tools/test.py
/data/rex/BEV/BEVFormer-master/projects/configs/bevformer/bevformer_tiny.py
/data/rex/BEV/bevformer_tiny_epoch_24.pth
--launcher
pytorch
--eval
bbox
- リンクされたデータセット
の実行中に、データ データセットが見つからないというエラーが報告される場合があります。これは tools/test.py に追加できます。
print(os.getcwd())
sys.path.append(os.getcwd())
次に、プロジェクト内のデータ データセットのパスを os.getcwd() のパスにリンクすると、実行時にエラーが報告されなくなります。
3. ネットワーク構造
慣習上、まず紙に模式図を載せて図解する必要がありますが、下の図では主に左端のバックボーン、真ん中のエンコーダ×6の3つに分かれています、中央に Det/Seg Heads があります。
最初の部分は ResNet + FPN です。BEVFormer の主な革新は、2 番目の部分、つまり時間的自己注意と空間的相互注意のエンコーダーです。
4. モデル構成ファイル
この記事では、テスト用に小さなモデルを使用します. いくつかのモデルの主な違いは、bev_query のサイズと FPN のマルチスケール機能の数にあります. 構成ファイルは projects/configs/bevformer/bevformer_tiny.py であり、ネットワーク構造モデルの実装はこちら 定義、実行時は以下のモジュールが先に登録され、上から順に基本的に順を追って進みます。
model = dick(
type='BEVFormer',
...,
# 主干网络
img_backbone = dict(
type='ResNet',
...
)
# 提取不同尺度的特征
img_neck=dict(
type='FPN',
...
)
# 编解码
pts_bbox_head = dict(
type='BEVFormerHead',
...
transformer=dict(
type='PerceptionTransformer',
...
# 编码网络
encoder=dict(
type='BEVFormerEncoder',
...
# 单个block 推理时将会重复6次
transformerlayers=dict(
type='BEVFormerLayer',
attn_cfgs=[
dict(
type='TemporalSelfAttention'
...
),
dict(
type='SpatialCrossAttention',
deformable_attention=dict(
type='MSDeformableAttention3D'
...
)
)
]
)
)
# 解码网络
decoder=dict(
type='DetectionTransformerDecoder',
...
# decode block
transformerlayers=dict(
type='DetrTransformerDecoderLayer',
attn_cfgs=[
dict(
type='MultiheadAttention',
),
dict(
type='CustomMSDeformableAttention',
...
)
]
)
)
)
bbox_coder = dict(
type='NMSFreeCoder'
...
)
# 可学习的位置编码
positional_encoding = dict(
type='LearnedPositionalEncoding',
...
)
)
な
5.転送プロセス
デバッグができるようになったら、変数の形を1行ずつ確認していきます.プロジェクトは多くのモジュールが含まれており、openmmlabで実装されているため、最初に連絡を取ると少し混乱する.推論 一般的なプロセスは、基本的に次の番号に従って順番に実行できます。
1、ツール/test.py
outputs = custom_multi_gpu_test(model, data_loader, args.tmpdir,args.gpu_collect)
# 进入到projects/mmdet3d_plugin/bevformer/apis/test.py
2、projects/mmdet3d_plugin/bevformer/apis/test.py
def custom_multi_gpu_test(...):
...
for i, data in enumerate(data_loader):
with torch.no_grad():
result = model(return_loss=False, rescale=True, **data)
# 进入到 projects/mmdet3d_plugin/bevformer/detectors/bevformer.py
...
3、projects/mmdet3d_plugin/bevformer/detectors/bevformer.py
class BEVFormer(...):
def forward(...):
if return_loss:
return self.forward_train(**kwargs)
else:
return self.forward_test(**kwargs)
# 进入到下面 self.forward_test
def forward_test(...):
...
# forward
new_prev_bev, bbox_results = self.simple_test(...)
# 进入到下面 self.simple_test
...
def simple_test(...):
# self.extract_feat 主要包括两个步骤 img_backbone、img_neck,通过卷积提取特征
# 网络为resnet + FPN
# 如果是base模型,img_feats 为四个不同尺度的特征层
# 如果是small、tiny,img_feats 为一个尺度的特征层
img_feats = self.extract_feat(img=img, img_metas=img_metas)
# Temproral Self-Attention + Spatial Cross-Attention
new_prev_bev, bbox_pts = self.simple_test_pts(img_feats, img_metas, prev_bev, rescale=rescale)
# 进入到下面 self.simple_test_pts
def simple_test_pts(...):
# 对特征层进行编解码
outs = self.pts_bbox_head(x, img_metas, prev_bev=prev_bev)
# 进入到 projects/mmdet3d_plugin/bevformer/dense_heads/bevformer_head.py
4、projects/mmdet3d_plugin/bevformer/dense_heads/bevformer_head.py
class BEVFormerHead(DETRHead):
def forward(...):
...
if only_bev:
...
else:
outputs = self.transformer(...)
# 进入到 projects/mmdet3d_plugin/bevformer/modules/transformer.py
for lvl in range(hs.shape[0]):
# 类别
outputs_class = self.cls_branches[lvl](hs[lvl])
# 回归框信息
tmp = self.reg_branches[lvl](hs[lvl])
outs = ...
return out
# 返回到 projects/mmdet3d_plugin/bevformer/detectors/bevformer.py simple_test_pts函数中
5、projects/mmdet3d_plugin/bevformer/modules/transformer.py
class PerceptionTransformer(...):
def forward(...):
# 获得bev特征
bev_embed = self.get_bev_features(...)
def get_bev_features(...):
# 获得bev特征 block * 6
bev_embed = self.encoder(...)
# 进入到projects/mmdet3d_plugin/bevformer/modules/encoder.py
...
# decoder
inter_states, inter_references = self.decoder(...)
# 进入到 projects/mmdet3d_plugin/bevformer/modules/decoder.py 中
return bev_embed, inter_states, init_reference_out, inter_references_out
# 返回到projects/mmdet3d_plugin/bevformer/dense_heads/bevformer_head.py
6、projects/mmdet3d_plugin/bevformer/modules/encoder.py
class BEVFormerEncoder(...):
def forward(...):
...
for lid, layer in enumerate(self.layers):
out = ...
# 进入到下面的 class BEVFormerLayer
class BEVFormerLayer(...):
def forward(...):
# 通过layer进入到不同的模块中
for layer in self.operation_order:
# tmporal_self_attention
if layer == 'self_attn':
# self.attentions 为 temporal_self_attention模块
query = self.attentions[attn_index]
# 进入到projects/mmdet3d_plugin/bevformer/modules/temporal_self_attention.py
# Spatial Cross-Attention
elif layer == 'cross_attn':
query = self.attentions[attn_index]
# 进入到 projects/mmdet3d_plugin/bevformer/modules/spatial_cross_attention.py
7、projects/mmdet3d_plugin/bevformer/modules/temporal_self_attention.py
class TemporalSelfAttention(...):
def forward(...):
output = ...
# 残差链接 返回的结果为 Spatial Cross-Attention 模块的输入
return self.dropout(output) + identity
# 返回到projects/mmdet3d_plugin/bevformer/modules/encoder.py
8、projects/mmdet3d_plugin/bevformer/modules/spatial_cross_attention.py
class SpatialCrossAttention(...):
def forward(...):
queries = self.deformable_attention(...)
# 进入到下面的MSDeformableAttention3D
return self.dropout(slots) + inp_residual
# 返回到返回到projects/mmdet3d_plugin/bevformer/modules/encoder.py
# self.deformable_attention
class MSDeformableAttention3D(BaseModule):
def forward(...):eights
output = ...
return output
# 返回到上面 SpatialCrossAttention
9、projects/mmdet3d_plugin/bevformer/modules/decoder.py
class DetectionTransformerDecoder(...):
def forward(...):
for lid, layer in enumerate(self.layers):
output = layer(...)
# 进入到下面CustomMSDeformableAttention
...
if self.return_intermediate:
intermediate.append(output)
intermediate_reference_points.append(reference_points)
return output, reference_points
# 返回到 projects/mmdet3d_plugin/bevformer/modules/transformer.py
class CustomMSDeformableAttention(...):
def forward(...):
'''
query: [900, 1, 256]
query_pos:[900, 1, 256] 可学习的位置编码
'''
output = multi_scale_deformable_attn_pytorch(...)
output = self.output_proj(output)
return self.dropout(output) + identity
# 返回到上面的DetectionTransformerDecoder
6. まとめ
以上でBEVFormerの推論手順は大まかに片付いてきましたが、まだまだ細かい部分が多く、まだソースコードを見ている最中で解決していない問題もあるので、次回の詳細版では詳細な注釈をつけていきます。コード内の変数 (既に問題がなければ記述済み) の次元と関数は、一方では BEVFormer の理解を深め、他方では BEV モデルに対する自分自身の理解を深めます。