DAB-DETRコード学習記録のモデル分析

DAB-DETR は、Deformable-DETR、Conditional-DETR、Anchor-DETR などの吸収に基づいて完成されています。その主な貢献は、クエリを x、y、w、h 思考座標の形式に初期化することです。
このブログ投稿では、主に DAB-DETR によって行われる作業をコードの観点から分析します。
DAB-DETR は主に Decoder モデルを改善します。ブロガーは主に Decoder モジュールのモデルを分析します。

ここに画像の説明を挿入

ここに画像の説明を挿入

位置コード化された温度値の調整

1 つ目は、position_encoding.py ファイルです。このファイルは、PositionEmbeddingSineHW高周波位置エンコード部分の幅と高さの温度値を分離する機能を持つメソッドを再定義し、幅と高さが異なる温度値を持つようにします。このファイルは、sincos 位置エンコード方法と学習可能な位置エンコード方法も改善します。

class PositionEmbeddingSineHW(nn.Module):
    """
    This is a more standard version of the position embedding, very similar to the one
    used by the Attention is all you need paper, generalized to work on images.
    """
    def __init__(self, num_pos_feats=64, temperatureH=10000, temperatureW=10000, normalize=False, scale=None):
        super().__init__()
        self.num_pos_feats = num_pos_feats
        self.temperatureH = temperatureH
        self.temperatureW = temperatureW
        self.normalize = normalize
        if scale is not None and normalize is False:
            raise ValueError("normalize should be True if scale is passed")
        if scale is None:
            scale = 2 * math.pi
        self.scale = scale
    def forward(self, tensor_list: NestedTensor):
        x = tensor_list.tensors
        mask = tensor_list.mask
        assert mask is not None
        not_mask = ~mask
        y_embed = not_mask.cumsum(1, dtype=torch.float32)
        x_embed = not_mask.cumsum(2, dtype=torch.float32)
        # import ipdb; ipdb.set_trace()
        if self.normalize:
            eps = 1e-6
            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
        dim_tx = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
        dim_tx = self.temperatureW ** (2 * (dim_tx // 2) / self.num_pos_feats)
        pos_x = x_embed[:, :, :, None] / dim_tx
        dim_ty = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
        dim_ty = self.temperatureH ** (2 * (dim_ty // 2) / self.num_pos_feats)
        pos_y = y_embed[:, :, :, None] / dim_ty
        pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
        pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
        pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
        # import ipdb; ipdb.set_trace()
        return pos

トランス全体のアーキテクチャ

まず、Transformer の全体的なアーキテクチャを理解しましょう。
まず、forward によって渡されるパラメーターを見てみましょう。
src: バックボーンによって抽出された特徴情報。形状は最初は torch.Size([2, 256,19,24] ) になり、 torch .Size([456, 2, 256])
マスク: 画像のマスク情報を完成させます。形状は最初 torch.Size([2, 19, 24]) で、その後 torch.Size( [2, 456] )

refpoint_embed: 参照点座標のエンコーディング、つまりobject_query、 torch.Size([300, 4])。DAB-DETR モジュール定義で初期化される Decoder モジュールで使用されます: self.refpoint_embed = nn.Embedding(num_queries, query_dim)、最初は torch.Size([300,4])、その後 refpoint_embed = refpoint_embed.unsqueeze( 1) ).repeat(1, bs, 1) は torch.Size([300, 4]) になります。

pos_embed: 位置エンコード情報。形状は最初 torch.Size([2, 256,19,24]) で、その後 torch.Size([456, 2, 256]) になります。 上記処理の実行コードは以下のとおりです

    # flatten NxCxHxW to HWxNxC
    bs, c, h, w = src.shape  #初始为2,256,19,24
    src = src.flatten(2).permute(2, 0, 1)#拉平:
    pos_embed = pos_embed.flatten(2).permute(2, 0, 1)
    refpoint_embed = refpoint_embed.unsqueeze(1).repeat(1, bs, 1)
    mask = mask.flatten(1)  

次に、データは Encoder モジュールに送信され、出力メモリは torch.Size([456, 2, 256]) になります。

 memory = self.encoder(src, src_key_padding_mask=mask, pos=pos_embed)

次に tgt を初期化し、self.num_patterns に従ってそのパターンを判断します。ここではデフォルトは 0 です。tgt はすべて 0 に初期化され、形状は torch.Size([300, 2, 256]) で、初期デコーダ入力として使用される DETR に似ています。

 num_queries = refpoint_embed.shape[0]
if self.num_patterns == 0:
    tgt = torch.zeros(num_queries, bs, self.d_model, device=refpoint_embed.device)
else:
    tgt = self.patterns.weight[:, None, None, :].repeat(1, self.num_queries, bs, 1).flatten(0, 1) # n_q*n_pat, bs, d_model
    refpoint_embed = refpoint_embed.repeat(self.num_patterns, 1, 1) # n_q*n_pat, bs, d_model

次に、それを Decoder モジュールに送信します。

 hs, references = self.decoder(tgt, memory, memory_key_padding_mask=mask,
                      pos=pos_embed, refpoints_unsigmoid=refpoint_embed)
 return hs, references

エンコーダモジュールの構造

DAB-DETR のエンコーダ モジュールは DETR とあまり変わりません。

エンコーダーレイヤー

src_mask=None
src_key_padding_mask: [2, 456] の形状で画像を補完します
src。 ResNet によって抽出された特徴は 2 次元から 1 次元に変換され、形状は torch.Size([456, 2, 256]) になります
pos。位置エンコード情報には、本来sincos位置エンコードと学習型位置エンコードの2種類があり、さらにDAB-DETRでは幅と高さをジャンプできる位置エンコード方式も提案されています。形状は
src2セルフアテンションで得られた torch.Size([456, 2, 256]) で、その後にドロップアウト層、ノルム層が続きます。最終的な出力結果は src: torch.Size([456, 2, 256]) で、Decoder に送信されます。

q = k = self.with_pos_embed(src, pos)
src2 = self.self_attn(q, k, value=src, attn_mask=src_mask,
                              key_padding_mask=src_key_padding_mask)[0]
src = src + self.dropout1(src2)
src = self.norm1(src)
src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
src = src + self.dropout2(src2)
src = self.norm2(src)
return src

DETR と同様、with_pos_embed直接加算です。

def with_pos_embed(self, tensor, pos: Optional[Tensor]):
        return tensor if pos is None else tensor + pos

エンコーダモジュール

Encoder は 6 つの EncoderLayer で構成されます。

class TransformerEncoder(nn.Module):
    def __init__(self, encoder_layer, num_layers, norm=None, d_model=256):
        super().__init__()
        self.layers = _get_clones(encoder_layer, num_layers)
        self.num_layers = num_layers
        self.query_scale = MLP(d_model, d_model, d_model, 2)
        self.norm = norm
    def forward(self, src,
                mask: Optional[Tensor] = None,
                src_key_padding_mask: Optional[Tensor] = None,
                pos: Optional[Tensor] = None):
        output = src
        for layer_id, layer in enumerate(self.layers):
            # rescale the content and pos sim
            pos_scales = self.query_scale(output)
            output = layer(output, src_mask=mask,
                           src_key_padding_mask=src_key_padding_mask, pos=pos*pos_scales)
        if self.norm is not None:
            output = self.norm(output)
        return output

デコーダの概要

デコーダ部分query ancor(アンカー ボックス) では、[2, 300, 4] に初期化され、Anchor Sine Encodingx、y、w、h がすべて 128 次元に変換され、4 は 512 次元に変換されて渡されますMLP。 256.
位置エンコード方式は次のとおりです。合計 4 で、エンコード次元は 128 次元になります。

ここに画像の説明を挿入
ここに画像の説明を挿入

主な革新の 1 つは、幅と高さの調整によるアテンション メカニズムが追加されたことです。これは、アテンションが幅と高さに対してより敏感になるようにするためです。

ここに画像の説明を挿入
ここに画像の説明を挿入

デコーダモジュールコードの実装

まず、tgtの値を与えますoutput。ここでわかるように、出力結果は でoutput、その形状は torch.Size([300, 2, 256]) です。

output = tgt

正規化されますreference_pointsが、形状は torch のままです。Size([300, 2, 4])

reference_points = refpoints_unsigmoid.sigmoid()

Decoder ループに入ると、まずreference_points高周波位置をエンコードします。つまり、すべての値を取り出し、高周波位置エンコード モジュールに入り、torch.Size([300, 2, 4]) から torch に変更します。 Size([300, 2, 512]) は、次のようにそれぞれ 128 になります。torch.Size([300, 2, 256]) に変更する
ここに画像の説明を挿入
と、self.ref_point_head(MLP)

obj_center = reference_points[..., :self.query_dim]  #torch.Size([300, 2, 4])  
query_sine_embed = gen_sineembed_for_position(obj_center) #torch.Size([300,2,512])
query_pos = self.ref_point_head(query_sine_embed) #torch.Size([300, 2, 256])

gen_sineembed_for_position以下のような方法:

def gen_sineembed_for_position(pos_tensor):
    # n_query, bs, _ = pos_tensor.size()
    # sineembed_tensor = torch.zeros(n_query, bs, 256)
    scale = 2 * math.pi
    dim_t = torch.arange(128, dtype=torch.float32, device=pos_tensor.device)
    dim_t = 10000 ** (2 * (dim_t // 2) / 128)
    x_embed = pos_tensor[:, :, 0] * scale
    y_embed = pos_tensor[:, :, 1] * scale
    pos_x = x_embed[:, :, None] / dim_t
    pos_y = y_embed[:, :, None] / dim_t
    pos_x = torch.stack((pos_x[:, :, 0::2].sin(), pos_x[:, :, 1::2].cos()), dim=3).flatten(2)
    pos_y = torch.stack((pos_y[:, :, 0::2].sin(), pos_y[:, :, 1::2].cos()), dim=3).flatten(2)
    if pos_tensor.size(-1) == 2:
        pos = torch.cat((pos_y, pos_x), dim=2)
    elif pos_tensor.size(-1) == 4:
        w_embed = pos_tensor[:, :, 2] * scale
        pos_w = w_embed[:, :, None] / dim_t
        pos_w = torch.stack((pos_w[:, :, 0::2].sin(), pos_w[:, :, 1::2].cos()), dim=3).flatten(2)
        h_embed = pos_tensor[:, :, 3] * scale
        pos_h = h_embed[:, :, None] / dim_t
        pos_h = torch.stack((pos_h[:, :, 0::2].sin(), pos_h[:, :, 1::2].cos()), dim=3).flatten(2)
        pos = torch.cat((pos_y, pos_x, pos_w, pos_h), dim=2)
    else:
        raise ValueError("Unknown pos_tensor shape(-1):{}".format(pos_tensor.size(-1)))
    return pos

次に、何らかの初期化が実行され、self.query_scale出力MLPは Decoder の前の層の出力結果と見なすことができます。
最初の 256 次元を取り出しますquery_sine_embed。つまり、pos_transformationx、y に (最初のレイヤーの 1) を掛けます。これは、入力次元が 256、中間層の幅が 256、出力次元が 2、隠れ層が 2 の
ref_anchor_headMLP です。refHW_cond は torch.Size([300, 2, 2]) query_sine_embed は最初は torch.Size([300, 2, 512]) ですが、次のようにtorch.Size([300, 2, 256]) に変更すると、この文はこのコードは、最初の 256 次元を取得することを意味します。self.ref_anchor_head = MLP(d_model, d_model, 2, 2)

query_sine_embed = query_sine_embed[...,:self.d_model] * pos_transformation

if self.query_scale_type != 'fix_elewise':#执行
      if layer_id == 0:#第一层时执行
          pos_transformation = 1
      else:
          pos_transformation = self.query_scale(output) #query_scale为MLP
else:
     pos_transformation = self.query_scale.weight[layer_id]
#取出  query_sine_embed的前256维,即x,y与pos_transformation相乘 
query_sine_embed = query_sine_embed[...,:self.d_model] * pos_transformation
if self.modulate_hw_attn:
      refHW_cond = self.ref_anchor_head(output).sigmoid() #将其送入MLP后进行归一化 torch.Size([300, 2, 2])
      query_sine_embed[..., self.d_model // 2:] *= (refHW_cond[..., 0] / obj_center[..., 2]).unsqueeze(-1)
      query_sine_embed[..., :self.d_model // 2] *= (refHW_cond[..., 1] / obj_center[..., 3]).unsqueeze(-1)

上記のコードは実際に次のプロセスを実行します。 この時点で PE(Xref) と PE(Yref) が乗算されていないのではなく、それが 1 に設定されているため、つまり 2 番目のコードでそれが確認できることに注意してください。 DecoderLayer のレイヤーpos_transformation = 1

ここに画像の説明を挿入

ここに画像の説明を挿入

次に、データを DecoderLayer に送信します。この時点では DecoderLayer が最初のレイヤーであることに注意してください。

output = layer(output, memory, tgt_mask=tgt_mask,
                           memory_mask=memory_mask,
                           tgt_key_padding_mask=tgt_key_padding_mask,
                           memory_key_padding_mask=memory_key_padding_mask,
                           pos=pos, query_pos=query_pos, query_sine_embed=query_sine_embed,
                           is_first=(layer_id == 0))

最初の層 DecoderLayer モジュール

自己注意

まず、DecoderLayer でセルフ アテンション メカニズムを計算します。
データがどのように変化するかを見てみましょう。
tgtつまり、DecoderLayer の前の層の出力はこの時点ではすべて 0 であり、形状は torch.Size([300, 2, 256]) が最初に線形層を通過します
。 (sa_qcontent_proj = nn.Linear (d_model, d_model)) q_content
torch.Size([300, 2, 256]) として形状を取得
します。tgt線形層を介して qkv 初期化が完了すると、tgtすべて 0 ですが、 q、k、v はそうではありません

ここに画像の説明を挿入

次に、q_pos(高周波位置エンコーディングと MLP を通じてアンカーによって取得された xywh 情報) も線形層のsa_qpos_proj次元を変更せずに通過します。形状は torch.Size([300, 2, 256])
で、k、v も次のように初期化されます。同じように。DETR と同様に、v には位置情報がありません。

自分なりにまとめると、Anchor Boxから変換されたquery_posは位置情報を提供し、コンテンツ情報はオール0または前のDecoderLayerの出力結果に初期化して提供され、位置情報とコンテンツ情報も追加されます。たとえば、結合します。q = q_content + q_pos
以下については、DETR とまったく同じで、q、k、v を入力するだけで操作に参加できます。

 if not self.rm_self_attn_decoder:
            # Apply projections here
            # shape: num_queries x batch_size x 256
            q_content = self.sa_qcontent_proj(tgt)      # target is the input of the first decoder layer. zero by default.
            q_pos = self.sa_qpos_proj(query_pos)
            k_content = self.sa_kcontent_proj(tgt)
            k_pos = self.sa_kpos_proj(query_pos)
            v = self.sa_v_proj(tgt)
            num_queries, bs, n_model = q_content.shape
            hw, _, _ = k_content.shape
            q = q_content + q_pos
            k = k_content + k_pos
            tgt2 = self.self_attn(q, k, value=v, attn_mask=tgt_mask,
                                key_padding_mask=tgt_key_padding_mask)[0] 
                     #tgt2为Attention计算结果,torch.Size([300, 2, 256])
            # ========== End of Self-Attention =============
            tgt = tgt + self.dropout1(tgt2)
            tgt = self.norm1(tgt)

最終的に self-attention の出力 tgt が得られ、その形状は torch.Size([300, 2, 256]) となり、上記のコードは下枠の部分を実行します。

ここに画像の説明を挿入

cross-attentionこれで計算に入力できるようになります

クロスアテンション

1 つ目はq k v初期化プロセスで、q はセルフアテンションの出力から来ており、線形層の後、k と v はエンコーダーの出力から来ていることがわかります。メモリの次元は torch.Size([456, 2, 256])

q_content = self.ca_qcontent_proj(tgt)#torch.Size([300, 2, 256])
k_content = self.ca_kcontent_proj(memory)#torch.Size([456, 2, 256])
v = self.ca_v_proj(memory)#torch.Size([456, 2, 256])

k_pos = self.ca_kpos_proj(pos)#对K进行位置编码,pos来自于Encoder。torch.Size([456, 2, 256])

これは最初の層であるため、次の操作を実行する必要があります。つまり、最初にquery_pos[torch.Size([300, 2, 256])] を完全に接続された層に通過させます。寸法は変更されません。q_pos生成のプロセス、

if is_first or self.keep_query_pos:#self.keep_query_pos默认为False
    q_pos = self.ca_qpos_proj(query_pos)# query_pos:torch.Size([300, 2, 256])
    q = q_content + q_pos
    k = k_content + k_pos
else:
    q = q_content
    k = k_content

次のステップは、Cross_Attention送信された Q、K、および V の初期化プロセスです。別個のアテンション操作は外部に配置されており、元々はアテンションの内部で完了していることに注意してください。

q = q.view(num_queries, bs, self.nhead, n_model//self.nhead)# q分头:torch.Size([300, 2, 8, 32])
query_sine_embed = self.ca_qpos_sine_proj(query_sine_embed)#query_sine_embed即
query_sine_embed = query_sine_embed.view(num_queries, bs, self.nhead, n_model//self.nhead)
q = torch.cat([q, query_sine_embed], dim=3).view(num_queries, bs, n_model * 2)
#q经过拼接变为torch.Size([300, 2, 512])
k = k.view(hw, bs, self.nhead, n_model//self.nhead)#torch.Size([456, 2, 8, 32])
k_pos = k_pos.view(hw, bs, self.nhead, n_model//self.nhead)#torch.Size([456, 2, 8, 32])
k = torch.cat([k, k_pos], dim=3).view(hw, bs, n_model * 2)#torch.Size([456, 2, 512])

次にQ,K,V、計算のために Cross_Attend に送信されます。合計すると、 q: torch.Size([300, 2, 512])、k: torch.Size([456, 2, 512])、v: torch.Size( [456、2、256])

tgt2 = self.cross_attn(query=q, key=k, value=v, attn_mask=memory_mask, key_padding_mask=memory_key_padding_mask)[0]    

具体的には以下の処理を実行します:QKVの異次元化

return multi_head_attention_forward(
                query, key, value, self.embed_dim, self.num_heads,
                self.in_proj_weight, self.in_proj_bias,
                self.bias_k, self.bias_v, self.add_zero_attn,
                self.dropout, self.out_proj.weight, self.out_proj.bias,
                training=self.training,
                key_padding_mask=key_padding_mask, need_weights=need_weights,
                attn_mask=attn_mask, out_dim=self.vdim)

Cross_attention の計算が完了すると、tgt2 のディメンションは torch.Size([300, 2, 256]) になります。ディメンションの変更については、アテンションの計算式を参照してください。

ここに画像の説明を挿入
次に、一連の残りの接続の後、バッチ正規化操作によって結果が出力されますが、最終結果は依然として torch.Size([300, 2, 256]) です。

アンカー更新戦略

このモジュールは、DAB-DETR の革新ポイント、つまりアンカー更新戦略 Anchor Update でもあります。

つまり、DecoderLayer のcross_attention 計算の後、出力値は DecoderLayer の次の層に渡され、MLP ネットワークを使用して x、y、w、h、および のオフセットを取得するアンカー ポイントの更新にも使用されます。形状はトーチです。サイズ ([300, 2, 4])。それを初期化された参照点の座標に追加しますreference_points(つまり、アンカー ボックス、形状は torch.Size([300, 2, 4]))。これはアンカー ポイント更新戦略であり、以前の DETR モデルの初期化アンカーは常に変更されません。

ここに画像の説明を挿入

if self.bbox_embed is not None:
    if self.bbox_embed_diff_each_layer:#是否共享参数:false
        tmp = self.bbox_embed[layer_id](output)
    else:
        tmp = self.bbox_embed(output)#经过MLP获得output偏移量x,y,w,h torch.Size([300, 2, 4])
    # import ipdb; ipdb.set_trace()
    tmp[..., :self.query_dim] += inverse_sigmoid(reference_points)
    new_reference_points = tmp[..., :self.query_dim].sigmoid()
    if layer_id != self.num_layers - 1:
        ref_points.append(new_reference_points)
    reference_points = new_reference_points.detach()
if self.return_intermediate:
    intermediate.append(self.norm(output))

上記のコードから、reference_points が継続的に更新されること、つまり Anchor update 戦略であることがわかります。自動
微分を達成するために、PyTorch はテンソルを含むすべての操作を追跡し、それらの勾配を計算する必要がある場合があります (つまり、require_gradは真です)。これらの操作は有向グラフとして記録されます。detach() メソッドは、勾配を必要としないことが宣言されたテンソル上に新しいビューを構築します。
上記のコードが実行するのは、次のフレーム化プロセスです。
ここに画像の説明を挿入

2 層目の DecoderLayer モジュール

DecoderLayer の最初の層と比較すると、2 番目の層の構造は最初の層と同じですが、最初の層の Decoder-Embedding の初期化 tgt がすべて 0 であり、2 番目の層が の出力になる点が異なります。さらに、アンカー更新戦略により、2 番目の層のアンカー ボックスも、最初の層のアンカー ボックスに xywh のオフセットを加えたものになります。

reference_points1 つ目は、アンカー ボックス (つまり、アンカー ボックス) の変更です。Decoderlayer の前の層を通過した後、値が更新され、再度高周波位置エンコードが行われた後、MLP 層はデータの次元を torch に変更しますサイズ([300, 2, 256])

obj_center = reference_points[..., :self.query_dim]  
query_sine_embed = gen_sineembed_for_position(obj_center)  
query_pos = self.ref_point_head(query_sine_embed) 

その直後の差分がここで強調表示されています まず、このときのquery_scale_type変更点cond_elewiseですが、2層目なので出力(つまり前の層の出力結果)が通ります。

self.query_scale = MLP(d_model, d_model, d_model, 2)

pos_transformation寸法を取得するためのエンコーディングは torch.Size([300, 2, 256]) です。

ここに画像の説明を挿入

次にquery_sine_embed[...,:self.d_model] * pos_transformation、ここでの query_sine_embed は torch.Size([300, 2, 512]) で、前の 256 次元を取得します。つまり、対応する取得は x、y です。pos_transformation を掛ける、pos_transformationつまりXref,Yref、ここで行われるのは次の操作です。
ここに画像の説明を挿入

最初の層に乗算がないのではなくPE(Xref),PE(Yref)、その値が 1 であることがわかります。
以降の処理はDecodeLayerの1層目と全く同じです。

デコーダモジュール

DecoderLayer のループ終了直後に実行します。intermediate各レイヤーの結果を保存します。これは 6 つの値を含む List であり、各値の形状は torch.Size([300, 2, 256]) であり、6 番目は破棄されます。レイヤー (ポップ操作) を作成し、最終的な出力値を追加します。

 if self.norm is not None:
        output = self.norm(output)
        if self.return_intermediate:
            intermediate.pop()
            intermediate.append(output)

ここに画像の説明を挿入
次に、bbox_embed (MLP ボックス予測ヘッダー) が None であり、torch.stack がスプライシング操作であるかどうかを判断します。

 if self.return_intermediate:
            if self.bbox_embed is not None:
                return [
                    torch.stack(intermediate).transpose(1, 2),
                    torch.stack(ref_points).transpose(1, 2),
                ]
            else:
                return [
                    torch.stack(intermediate).transpose(1, 2), 
                    reference_points.unsqueeze(0).transpose(1, 2)
                ]

Transformer のデコーダ モジュールは最終的に結果を返します。

hs, references = self.decoder(tgt, memory, memory_key_padding_mask=mask,
                          pos=pos_embed, refpoints_unsigmoid=refpoint_embed)

この内、torch.Size([6, 2, 300, 4])を参照、hsはtorch.Size([6, 2, 300, 256])、結果もDecoderの戻り結果、参照はそれぞれの後ボックスのアップデートが完了しました。hs は意味特徴情報とみなされます。

ここに画像の説明を挿入

DAB-DETR一体型モジュール

Transformer の次のステップは、分類ヘッドと回帰ヘッドを設計することです。
1 つ目は、Decoder モジュールの参照 (Anchor Box) の値を非正規化し、hs (Decoder の出力値、DETR の出力に相当) に対して回帰頭部予測を実行して、形状が torch.Size( [6, 2, 300, 4])、この値を処理された参照に追加します。(self.query_dim は 4、つまりすべて加算されます)、最後に tmp が正規化されます。

   if not self.bbox_embed_diff_each_layer:#是否权值共享
        reference_before_sigmoid = inverse_sigmoid(reference)#反归一化
        tmp = self.bbox_embed(hs)#torch.Size([6, 2, 300, 4])
        tmp[..., :self.query_dim] += reference_before_sigmoid
        outputs_coord = tmp.sigmoid()

ここに画像の説明を挿入
Output_coord 値は予測ボックスの xywh で、
ここに画像の説明を挿入
最終的な戻り結果は次のとおりです。
pred_logits はカテゴリ予測 (ここでは 91 カテゴリ) torch.Size([2, 300, 91])
pred_boxes はボックス ボックス予測 torch.Size( [2, 300, 4 ])
aux_outputs は、DecoderLayer の最初の 5 層の結果です。5 つの値を含むリストです。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/pengxiang1998/article/details/130208479