DINネットワーク構造部品コード説明

前回に続き、論文のネットワーク構造図とソースコードの該当箇所を比較し、簡単に紹介しますソースコードが多いため、一つずつ紹介することはできません興味のある方はダウンロードしてください彼らは自分たちで。

 

最終記事アドレス: (2 メッセージ) DIN: 集団行動認識のための動的時空間推論ネットワーク_Mr___WQ のブログ - CSDN ブログhttps://blog.csdn.net/Mr___WQ/article/details/129131100

ソース コード リンク: GitHub - JacobYuan7/DIN-Group-Activity-Recognition-Benchmark: グループ アクティビティ認識のための新しいコードベース. これには、ICCV 2021 論文のコードが含まれています: グループ アクティビティ認識のための時空間動的推論ネットワークとその他のメソッド。: //github.com/JacobYuan7/DIN-Group-Activity-Recognition-Benchmark 早速、ネットワーク構造図から始めましょう。

 この記事で提案する DIN の基本フレームワークは上の図に示されています。DIN の入力は短いビデオであり、これが選択されたバックボーン ネットワークに入力されて視覚的特徴が抽出されます。バックボーンネットワークについては、主にResNet-18とVGG-16で実験を行い、RoIAlignを適用してバウンディングボックスに合わせて文字特徴を抽出し、D次元空間に埋め込みます。著者は最初に初期化された時空間グラフを構築します。この時空間グラフの接続は、キャラクターの特徴の時空間的近傍になります (空間次元は人物の座標に従ってソートされます)。この初期化された時空間グラフ上で、定義された相互作用領域内で動的関係と動的ワンダー予測を実行し、異なる中心特徴を持つ相互作用グラフ (合計 T × N 個の相互作用グラフ) を取得すると、中心特徴は次のように表現できます。それぞれのインタラクション グラフ、機能の更新。最後に、DIN は、グローバルな時空間プーリングを通じてビデオの特徴表現を取得します。

プロジェクトディレクトリ:

backbone フォルダーには backbone.py があり、MyInception_v3、MyVGG16、MyVGG19、MyRes18、MyRes50、MyAlex などの構造が定義されており、構成に応じて柔軟に選択できます。

base_model.py: バレーボール データ セットとコレクティブ データ セットにそれぞれ対応する 2 つのモデルが定義されています。これら 2 つのモデルは、ネットワーク構造図における時空間特徴抽出に相当します。ここでは、バレーボールデータセットのコードのみを例に挙げます。コードは次のとおりです(内容には注釈が付けられているため、あまり詳しく説明しません。誤解や誤解がある場合はご容赦ください。以下同様) ):

class Basenet_volleyball(nn.Module):
    """
    main module of base model for the volleyball
    """
    def __init__(self, cfg):
        super(Basenet_volleyball, self).__init__()
        #cfg的定义在Config.py中,如Config('volleyball')
        self.cfg=cfg
        #NFB:每个box的特征数
        NFB=self.cfg.num_features_boxes
        #D:Embedding的维度,源码中为512,Lite-DIN为128
        D=self.cfg.emb_features
        # K用作ROI_Align剪裁
        K=self.cfg.crop_size[0]
        
        #backbone的选择
        if cfg.backbone=='inv3':
            self.backbone=MyInception_v3(transform_input=False,pretrained=True)
        elif cfg.backbone=='vgg16':
            self.backbone=MyVGG16(pretrained=True)
        elif cfg.backbone=='vgg19':
            self.backbone=MyVGG19(pretrained=True)
        elif cfg.backbone == 'res18':
            self.backbone = MyRes18(pretrained = True)
        else:
            assert False
        
        #通过roi_align进行crop
        self.roi_align=RoIAlign(*self.cfg.crop_size)
        
        #Embedding的全连接
        self.fc_emb = nn.Linear(K*K*D,NFB)
        self.dropout_emb = nn.Dropout(p=self.cfg.train_dropout_prob)
        
        #全连接后的actions和activities
        self.fc_actions=nn.Linear(NFB,self.cfg.num_actions)
        self.fc_activities=nn.Linear(NFB,self.cfg.num_activities)
        
        #权重初始化
        for m in self.modules():
            if isinstance(m,nn.Linear):
                nn.init.kaiming_normal_(m.weight)
                nn.init.zeros_(m.bias)

    #
    #保存模型
    def savemodel(self,filepath):
        state = {
            'backbone_state_dict': self.backbone.state_dict(),
            'fc_emb_state_dict':self.fc_emb.state_dict(),
            'fc_actions_state_dict':self.fc_actions.state_dict(),
            'fc_activities_state_dict':self.fc_activities.state_dict()
        }
        
        torch.save(state, filepath)
        print('model saved to:',filepath)

    #加载模型
    def loadmodel(self,filepath):
        state = torch.load(filepath)
        self.backbone.load_state_dict(state['backbone_state_dict'])
        self.fc_emb.load_state_dict(state['fc_emb_state_dict'])
        self.fc_actions.load_state_dict(state['fc_actions_state_dict'])
        self.fc_activities.load_state_dict(state['fc_activities_state_dict'])
        print('Load model states from: ',filepath)

    #建立网络结构,前向传播,_init_中定义了网络结构里的各个小块,在此建立关系
    def forward(self,batch_data):
        images_in, boxes_in = batch_data
        
        # read config parameters
        #B:batch_size
        B=images_in.shape[0]
        #T:frames,帧数
        T=images_in.shape[1]
        #H:height, W:width,图片高宽
        H, W=self.cfg.image_size
        #需要输出得到的尺寸
        OH, OW=self.cfg.out_size
        #每一帧中框的个数
        N=self.cfg.num_boxes
        #NFB同上
        NFB=self.cfg.num_features_boxes
        
        # Reshape the input data,作shape变换
        images_in_flat=torch.reshape(images_in,(B*T,3,H,W))  #B*T, 3, H, W
        boxes_in_flat=torch.reshape(boxes_in,(B*T*N,4))  #B*T*N, 4

        boxes_idx=[i * torch.ones(N, dtype=torch.int)   for i in range(B*T) ]
        boxes_idx=torch.stack(boxes_idx).to(device=boxes_in.device)  # B*T, N
        boxes_idx_flat=torch.reshape(boxes_idx,(B*T*N,))  #B*T*N,
        
        
        # Use backbone to extract features of images_in
        # Pre-precess first
        images_in_flat=prep_images(images_in_flat)
        
        outputs=self.backbone(images_in_flat)
        
        
        # Build multiscale features
        features_multiscale=[]
        for features in outputs:
            if features.shape[2:4]!=torch.Size([OH,OW]):
                features=F.interpolate(features,size=(OH,OW),mode='bilinear',align_corners=True)
            features_multiscale.append(features)
        
        features_multiscale=torch.cat(features_multiscale,dim=1)  #B*T, D, OH, OW
        
        
        
        # ActNet
        boxes_in_flat.requires_grad=False
        boxes_idx_flat.requires_grad=False
#         features_multiscale.requires_grad=False
        
    
        # RoI Align
        boxes_features=self.roi_align(features_multiscale,
                                            boxes_in_flat,
                                            boxes_idx_flat)  #B*T*N, D, K, K,
        
        
        boxes_features=boxes_features.reshape(B*T*N,-1) # B*T*N, D*K*K
        
            
        # Embedding to hidden state
        boxes_features=self.fc_emb(boxes_features)  # B*T*N, NFB
        boxes_features=F.relu(boxes_features)
        boxes_features=self.dropout_emb(boxes_features)
       
    
        boxes_states=boxes_features.reshape(B,T,N,NFB)
        
        # Predict actions
        boxes_states_flat=boxes_states.reshape(-1,NFB)  #B*T*N, NFB

        actions_scores=self.fc_actions(boxes_states_flat)  #B*T*N, actn_num
        
        
        # Predict activities
        boxes_states_pooled,_=torch.max(boxes_states,dim=2)  #B, T, NFB
        boxes_states_pooled_flat=boxes_states_pooled.reshape(-1,NFB)  #B*T, NFB
        
        activities_scores=self.fc_activities(boxes_states_pooled_flat)  #B*T, acty_num
        
        if T!=1:
            actions_scores=actions_scores.reshape(B,T,N,-1).mean(dim=1).reshape(B*N,-1)
            activities_scores=activities_scores.reshape(B,T,-1).mean(dim=1)
            
        return actions_scores, activities_scores

上記のコードはネットワーク構造の左側に相当します メモリの制限により、ネットワーク全体を 2 段階に分けて学習します 上記は最初の段階の構造です 学習には Train_volleyball_stage1.py を使用します 学習後完了すると、第 2 段階用のモデル ファイルが保存されるので、トレーニング中に直接ロードするだけです。

2 番目の部分は推論部分であり、ファイルのネスト順序は次のとおりです。

infer_model.py->/infer_module/dynamic_infer_module.py

stage1 と比較して、推論部分が追加されています。ネットワーク構造の 2 番目の部分は infer_model.py で定義されています。次のコードは stage1 とは異なります。作成者はネットワークの 2 番目の段階をクラスにカプセル化して、dynamic_infer_module で定義します。 .ぴー。

        if not self.cfg.hierarchical_inference:
            # self.DPI = Dynamic_Person_Inference(
            #     in_dim = in_dim,
            #     person_mat_shape = (10, 12),
            #     stride = cfg.stride,
            #     kernel_size = cfg.ST_kernel_size,
            #     dynamic_sampling=cfg.dynamic_sampling,
            #     sampling_ratio = cfg.sampling_ratio, # [1,2,4]
            #     group = cfg.group,
            #     scale_factor = cfg.scale_factor,
            #     beta_factor = cfg.beta_factor,
            #     parallel_inference = cfg.parallel_inference,
            #     cfg = cfg)
            self.DPI = Multi_Dynamic_Inference(
                in_dim = in_dim,
                person_mat_shape = (10, 12),
                stride = cfg.stride,
                kernel_size = cfg.ST_kernel_size,
                dynamic_sampling=cfg.dynamic_sampling,
                sampling_ratio = cfg.sampling_ratio, # [1,2,4]
                group = cfg.group,
                scale_factor = cfg.scale_factor,
                beta_factor = cfg.beta_factor,
                parallel_inference = cfg.parallel_inference,
                num_DIM = cfg.num_DIM,
                cfg = cfg)
            print_log(cfg.log_path, 'Hierarchical Inference : ' + str(cfg.hierarchical_inference))
        else:
            self.DPI = Hierarchical_Dynamic_Inference(
                in_dim = in_dim,
                person_mat_shape=(10, 12),
                stride=cfg.stride,
                kernel_size=cfg.ST_kernel_size,
                dynamic_sampling=cfg.dynamic_sampling,
                sampling_ratio=cfg.sampling_ratio,  # [1,2,4]
                group=cfg.group,
                scale_factor=cfg.scale_factor,
                beta_factor=cfg.beta_factor,
                parallel_inference=cfg.parallel_inference,
                cfg = cfg,)
            print(cfg.log_path, 'Hierarchical Inference : ' + str(cfg.hierarchical_inference))
        self.dpi_nl = nn.LayerNorm([T, N, in_dim])
        self.dropout_global = nn.Dropout(p=self.cfg.train_dropout_prob)


        # Lite Dynamic inference
        if self.cfg.lite_dim:
            self.point_conv = nn.Conv2d(NFB, in_dim, kernel_size = 1, stride = 1)
            self.point_ln = nn.LayerNorm([T, N, in_dim])
            self.fc_activities = nn.Linear(in_dim, self.cfg.num_activities)
        else:
            self.fc_activities=nn.Linear(NFG, self.cfg.num_activities)

        for m in self.modules():
            if isinstance(m,nn.Linear):
                nn.init.kaiming_normal_(m.weight)
                if m.bias is not None:
                    nn.init.zeros_(m.bias)

Lite Dynamic 推論では、特徴量の埋め込み次元が 512 から 128 に変更され、計算量が削減されることに注意してください。

さらに、dynamic_infer_module.py を調べてください。コードが長すぎるため、DR および DW 関連のコードのみをここに掲載します。興味のある方は完全なコードを参照してください。具体的な詳細については、論文を参照してください。

    def parallel_infer(self, person_features, ratio):
        assert self.dynamic_sampling and self.scale_factor

        # Dynamic affinity infer
        scale = self.scale_conv[str(ratio)](person_features).permute(0, 2, 3, 1) # shape [B, T, N, k2]
        # DR
        scale = F.softmax(scale, dim = -1)
        pos = self._get_plain_pos(ratio, person_features) # [B, T, N, 2*k2]

        pad_ft = self.zero_padding[str(ratio)](person_features).permute(0, 2, 3, 1) # [B, H, W, NFB]
        pad_ft = pad_ft.view(pad_ft.shape[0], -1, pad_ft.shape[-1])
        ft_pos = self._get_ft(pad_ft, pos.long(), ratio)

        ft_infer_scale =  torch.sum(ft_pos * scale.unsqueeze(-1), dim = 3)


        # Dynamic walk infer
        offset = self.p_conv[str(ratio)](person_features).permute(0, 2, 3, 1)  # shape [B, T, N,  2*k2]
        pos = self._get_pos(offset, ratio)  # [B, T, N, 2*k2]

        # Original
        lt = pos.data.floor()
        rb = lt + 1

        # Calclate bilinear coefficient
        # corner point position. lt shape # [B, T, N, 2*k2]
        k2 = self.kernel_size[0]*self.kernel_size[1]
        lt = torch.cat((torch.clamp(lt[..., :k2], 0, self.T + 2 * ratio - 1),
                        torch.clamp(lt[..., k2:], 0, self.N + 2 * ratio - 1)), dim=-1)
        rb = torch.cat((torch.clamp(rb[..., :k2], 0, self.T + 2 * ratio - 1),
                        torch.clamp(rb[..., k2:], 0, self.N + 2 * ratio - 1)), dim=-1)
        lb = torch.cat((rb[..., :k2], lt[..., k2:]), dim=-1)
        rt = torch.cat((lt[..., :k2], rb[..., k2:]), dim=-1)

        # coefficient for cornor point pixel.  coe shape [B, T, N, k2]
        pos = torch.cat((torch.clamp(pos[..., :k2], 0, self.T + 2 * ratio),
                         torch.clamp(pos[..., k2:], 0, self.N + 2 * ratio)), dim=-1)
        coe_lt = (1 - torch.abs(pos[..., :k2] - lt[..., :k2])) * (1 - torch.abs(pos[..., k2:] - lt[..., k2:]))
        coe_rb = (1 - torch.abs(pos[..., :k2] - rb[..., :k2])) * (1 - torch.abs(pos[..., k2:] - rb[..., k2:]))
        coe_lb = (1 - torch.abs(pos[..., :k2] - lb[..., :k2])) * (1 - torch.abs(pos[..., k2:] - lb[..., k2:]))
        coe_rt = (1 - torch.abs(pos[..., :k2] - rt[..., :k2])) * (1 - torch.abs(pos[..., k2:] - rt[..., k2:]))


        # corner point feature.  ft shape [B, T, N, k2, NFB]
        # pad_ft = self.zero_padding[ratio](person_features).permute(0, 2, 3, 1)
        # pad_ft = pad_ft.view(pad_ft.shape[0], -1, pad_ft.shape[-1])
        ft_lt = self._get_ft(pad_ft, lt.long(), ratio)
        ft_rb = self._get_ft(pad_ft, rb.long(), ratio)
        ft_lb = self._get_ft(pad_ft, lb.long(), ratio)
        ft_rt = self._get_ft(pad_ft, rt.long(), ratio)
        ft_infer_walk = ft_lt * coe_lt.unsqueeze(-1) + \
                   ft_rb * coe_rb.unsqueeze(-1) + \
                   ft_lb * coe_lb.unsqueeze(-1) + \
                   ft_rt * coe_rt.unsqueeze(-1)

        ft_infer_walk = torch.mean(ft_infer_walk, dim=3)

        return ft_infer_scale + ft_infer_walk

最近、この論文の関連コードを実行するために実験用マシンを使用しています。バックボーンには vgg16 が使用されています。30 エポックが使用される場合、stage1 の最高の値は次の図に示すとおりです。

マシンのメモリ制限のため、ステージ 2 コードは実行できず、再現できません。その後の条件が許せば、ステージ 2 トレーニングが実行されます。

おすすめ

転載: blog.csdn.net/Mr___WQ/article/details/129322868
おすすめ