【BEV】BEVFormerの研究ノート(1)

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 モデルに対する自分自身の理解を深めます。

おすすめ

転載: blog.csdn.net/weixin_42108183/article/details/128426721