DAB-Deformable-DETR代码学习记录之模型构建

news2024/11/15 13:30:47

DAB-DETR的作者在Deformable-DETR基础上,将DAB-DETR的思想融入到了Deformable-DETR中,取得了不错的成绩。今天博主通过源码来学习下DAB-Deformable-DETR模型。
首先我么看下Deformable的创新之处:

Deformable-DETR创新

多尺度融合

首先便是老生常谈但却不得不提的多尺度融合问题,作者将ResNet50的后三层作为输出,并将其融合在一起送入Transformer。

在这里插入图片描述

可变形注意力机制

这是Deformable-DETR的主要创新点,即可变形的注意力。详细介绍可以看博主这篇博文:

Deformable-DETR模型学习记录

在这里插入图片描述
接下来我们便从源码入手来学习DAB-Deformable-DETR

DAB-Deformable-DETR整体模型

首先来到DAB-Deformable-DETR的模型构建文件DAB-Deformable-DETR.py
通过预处理的样本数据samples
我们主要看两个数据,分别是tensors,这是送入DAB-Deformable-DETR模型的样本数据,为torch.Size([2, 3, 608, 760]),分别代表batch-size=2,channel=3,width=608,height=760。
其次便是mask,这是图像进行尺寸统一后进行填充后得到的掩码信息,其表示哪些是填充的部分,哪些是图像本身,为 torch.Size([2, 608, 760])。

在这里插入图片描述
随后将样本数据送入backbone,原本是resnet50,但这里博主由于显存限制便把backbone改为resnet18。

features, pos = self.backbone(samples)

得到features有三个,分别为 Resnet8 倒数3层的输出结果,对应论文中第一个创新点:多尺度特征信息。此外还有对应的位置编码信息pos。(pos数据的维度信息与feature相同)
feature数据的维度信息分别为:torch.Size([2, 256, 76, 95]) torch.Size([2, 256, 38, 48]) torch.Size([2, 256, 19, 24])

在这里插入图片描述
随后对feature进行处理,获得送入Transformer模块的src与mask。获得的srcs包含三个数据,即分别为torch.Size([2, 256, 76, 95]) torch.Size([2, 256, 38, 48]) torch.Size([2, 256, 19, 24]) ,mask相比只是少了通道维度。

srcs = []
masks = []
for l, feat in enumerate(features):
      src, mask = feat.decompose()
      srcs.append(self.input_proj[l](src))
      masks.append(mask)
      assert mask is not None

紧接着对feature最后一层进行处理,将其生成src,初次之外还生成了对应的mask,添加到srcs。其对应下图:

在这里插入图片描述

 if self.num_feature_levels > len(srcs):
            _len_srcs = len(srcs)
            for l in range(_len_srcs, self.num_feature_levels):
                if l == _len_srcs:
                    src = self.input_proj[l](features[-1].tensors)
                else:
                    src = self.input_proj[l](srcs[-1])
                m = samples.mask
                mask = F.interpolate(m[None].float(), size=src.shape[-2:]).to(torch.bool)[0]
                pos_l = self.backbone[1](NestedTensor(src, mask)).to(src.dtype)
                srcs.append(src)
                masks.append(mask)
                pos.append(pos_l)

其中mask = F.interpolate(m[None].float(), size=src.shape[-2:]).to(torch.bool)[0],m为torch.Size([2, 608, 760]),m 转化为与 src 相同大小的mask为 torch.Size([2, 10, 12])
在该项目中,不启用双阶段,并使用dab方法,紧接着执行下面的步骤。
得到 refanchor:torch.Size([300, 4]) tgt_embed:torch.Size([300, 256]),随后将其拼接得到
query_embeds torch.Size([300, 260])(按照dim=1维度拼接)

    if self.num_patterns == 0:
        tgt_embed = self.tgt_embed.weight           # nq, 256
        refanchor = self.refpoint_embed.weight      # nq, 4
        query_embeds = torch.cat((tgt_embed, refanchor), dim=1)

随后将数据送入Transformer模型,得到输出结果如下:

hs, init_reference, inter_references, enc_outputs_class, enc_outputs_coord_unact = self.transformer(srcs, masks, pos, query_embeds)

hs:torch.Size([6, 2, 300, 256]) 语义特征信息
init_reference:torch.Size([300, 4])
inter_references:torch.Size([6, 2, 300, 4]) 参考点box
enc_outputs_class:None
enc_outputs_coord_unact:None

在这里插入图片描述

最终,hs还要经过分类头与回归头,回归头获得的box与inter_references相加。

        outputs_classes = []#存放分类结果
        outputs_coords = []#存放box结果
        for lvl in range(hs.shape[0]):#6层
            if lvl == 0:
                reference = init_reference#初始化的,没变
            else:
                reference = inter_references[lvl - 1]
            reference = inverse_sigmoid(reference)#反归一化
            outputs_class = self.class_embed[lvl](hs[lvl])#6个分类头,各自预测各自的 torch.Size([2, 300, 91])
            tmp = self.bbox_embed[lvl](hs[lvl])#6个回归头 torch.Size([2, 300, 4])
            if reference.shape[-1] == 4:
                tmp += reference#预测出box结果加上reference,默认为4
            else:
                assert reference.shape[-1] == 2
                tmp[..., :2] += reference
            outputs_coord = tmp.sigmoid()#获取输出box结果并进行归一化
            outputs_classes.append(outputs_class)
            outputs_coords.append(outputs_coord)
        outputs_class = torch.stack(outputs_classes)#凭借输出结果
        outputs_coord = torch.stack(outputs_coords)

reference = inverse_sigmoid(reference)后的结果:

在这里插入图片描述
outputs_coord = tmp.sigmoid()获取输出box结果并进行归一化

在这里插入图片描述

最终获得outputs_classes:list值有6个,每个为 torch.Size([2, 300, 91])

在这里插入图片描述
经过outputs_class = torch.stack(outputs_classes)后为:torch.Size([6, 2, 300, 91])

在这里插入图片描述

outputs_coord也预祝同理,变为 torch.Size([6, 2, 300, 4])
最终将结果返回即可。由此可见,DAB-Deformable-DETR的外部逻辑并没有发生太大变化,我们重点来看看Transformer内部如何变化。

Transformer模块

首先由于在骨干网络模块引入了多尺度特征,那么在Encoder前的处理也发生了变化。
进入Transformer模块是由此开始的。

hs, init_reference, inter_references, enc_outputs_class, enc_outputs_coord_unact = self.transformer(srcs, masks, pos, query_embeds)

传入的参数为:
srcs:经过backbone获得的语义特征信息,list形式,共4个值,分别为:
torch.Size([2, 256, 76, 95]) torch.Size([2, 256, 38, 48])
torch.Size([2, 256, 19, 24]) torch.Size([2, 256, 10, 12])`

在这里插入图片描述

masks:掩码值,有4个,每个大小与对应层的src相同,只是没有通道维度,如 mask0:torch.Size([2, 76, 95]),mask1:torch.Size([2, 38, 48])

在这里插入图片描述

pos:位置编码,维度与srcs相同。
query_embeds:torch.Size([300, 260]),这个是通过下面代码拼接得到的。

tgt_embed = self.tgt_embed.weight           # nq, 256
refanchor = self.refpoint_embed.weight      # nq, 4
query_embeds = torch.cat((tgt_embed, refanchor), dim=1)

随后我们进行Transformer模块,首先是先对输入的backbone数据进行预处理,先定义变量:

src_flatten = []
mask_flatten = []
lvl_pos_embed_flatten = []
spatial_shapes = []

对srcs,masks,pos进行转换:
注意:lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)(可学习的参数)

self.level_embed = nn.Parameter(torch.Tensor(num_feature_levels, d_model))

self.level_embed[lvl]是Transformer模块自定义的参数,为【4,256】,取其中一个即为256,随后经过view变为1X1X256,则可与pos_embed(torch.Size([2, 7220, 256]))相加,即在前2X7220中,每个256相加。相加完维度lvl_pos_embed与pos_embed相同,为torch.Size([2, 7220, 256])

for lvl, (src, mask, pos_embed) in enumerate(zip(srcs, masks, pos_embeds)):
            bs, c, h, w = src.shape
            spatial_shape = (h, w)
            spatial_shapes.append(spatial_shape)
            src = src.flatten(2).transpose(1, 2)# 由bs,c,h,w变为bs, hw, c
            mask = mask.flatten(1)#由bs,h,w变为 bs, hw
            pos_embed = pos_embed.flatten(2).transpose(1, 2)#由bs,c,h,w变为bs, hw, c
            lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)
            lvl_pos_embed_flatten.append(lvl_pos_embed)
            src_flatten.append(src)
            mask_flatten.append(mask)

最终得到转换后的src mask pos (其实就是做了个维度转换)。如下:

在这里插入图片描述

随后src在第二维进行拼接,即将所有的src(共四层,list形式)变为 torch.Size([2, 9620, 256])
同理,mask,pos也做相同变换。

src_flatten = torch.cat(src_flatten, 1)     # bs, \sum{hxw}, c 
mask_flatten = torch.cat(mask_flatten, 1)   # bs, \sum{hxw}
lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)

spatial_shapes:[(76, 95), (38, 48), (19, 24), (10, 12)],truple格式经转换:

spatial_shapes = torch.as_tensor(spatial_shapes, dtype=torch.long, device=src_flatten.device)

得到Tensor格式

tensor([[76, 95],
[38, 48],
[19, 24],
[10, 12]], device=‘cuda:0’)

获取每个leavel的初始index(我们将4个level的值拼接在一起了)

level_start_index = torch.cat((spatial_shapes.new_zeros((1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))

得到:level_start_index 为 tensor([ 0, 7220, 9044, 9500], device=‘cuda:0’)

valid_ratios = torch.stack([self.get_valid_ratio(m) for m in masks], 1)

get_valid_ratio方法定义如下:该方法的作用其实是返回一个扩张比例值(即占长宽比例),维度为torch.Size([2, 4, 2]),即batch=2,4为每个layer,2为长宽比例

def get_valid_ratio(self, mask):
    _, H, W = mask.shape
    valid_H = torch.sum(~mask[:, :, 0], 1)
    valid_W = torch.sum(~mask[:, 0, :], 1)
    valid_ratio_h = valid_H.float() / H
    valid_ratio_w = valid_W.float() / W
    valid_ratio = torch.stack([valid_ratio_w, valid_ratio_h], -1)
    return valid_ratio

在这里插入图片描述

完整代码如下:

		src_flatten = []
        mask_flatten = []
        lvl_pos_embed_flatten = []
        spatial_shapes = []
        for lvl, (src, mask, pos_embed) in enumerate(zip(srcs, masks, pos_embeds)):
            bs, c, h, w = src.shape
            spatial_shape = (h, w)
            spatial_shapes.append(spatial_shape)
            src = src.flatten(2).transpose(1, 2)                # bs, hw, c
            mask = mask.flatten(1)                              # bs, hw
            pos_embed = pos_embed.flatten(2).transpose(1, 2)    # bs, hw, c
            lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)
            lvl_pos_embed_flatten.append(lvl_pos_embed)
            src_flatten.append(src)
            mask_flatten.append(mask)
        src_flatten = torch.cat(src_flatten, 1)     # bs, \sum{hxw}, c 
        mask_flatten = torch.cat(mask_flatten, 1)   # bs, \sum{hxw}
        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)
        spatial_shapes = torch.as_tensor(spatial_shapes, dtype=torch.long, device=src_flatten.device)
        level_start_index = torch.cat((spatial_shapes.new_zeros((1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))
        valid_ratios = torch.stack([self.get_valid_ratio(m) for m in masks], 1)

Encoder模块

随后将数据送入Encoder模块

memory = self.encoder(src_flatten, spatial_shapes, level_start_index, valid_ratios, lvl_pos_embed_flatten, mask_flatten)

首先看看送入的参数值:
src_flatten:torch.Size([2, 9620, 256]) 展平后的src(backbone的提取结果)
spatial_shapes:各个层的大小。

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

level_start_index:tensor([ 0, 7220, 9044, 9500], device=‘cuda:0’)
valid_ratios:torch.Size([2, 4, 2]) 各层的缩放比例:

tensor([[[1.0000, 1.0000],
         [1.0000, 1.0000],
         [1.0000, 1.0000],
         [1.0000, 1.0000]],
        [[0.8421, 0.8421],
         [0.8542, 0.8421],
         [0.8750, 0.8421],
         [0.9167, 0.9000]]], device='cuda:0')

上面比例中第一张全为1,说明其比较大,第二张是padding到与第一张相同大小。

lvl_pos_embed_flatten:torch.Size([2, 9620, 256]) 展平后的位置编码信息
mask_flatten:torch.Size([2, 9620]) 展平后的掩码信息

进入Encoder中,该模块从整体结构上没有变化:

生成参考点坐标。

reference_points = self.get_reference_points(spatial_shapes, valid_ratios, device=src.device)

得到 reference_points 为 torch.Size([2, 9620, 4, 2]) 其各维度代表的含义为:batch=2
4个WH相加后为9620,4为4层,2为x,y值。
生成时,以0.5开始,以1为间隔,减去0.5结束,生成网格点(参考点坐标),为防止网格点不存在,还要乘以其缩放比例。

meshgrid 函数用来生成网格矩阵,可以是二维网格矩阵
linspace函数:通过定义均匀间隔创建数值序列。指定间隔起始点、终止端,以及指定分隔值总数(包括起始点和终止点);最终函数返回间隔类均匀分布的数值序列。请看示例:

np.linspace(start = 0, stop = 100, num = 5)
   def get_reference_points(spatial_shapes, valid_ratios, device):
        reference_points_list = []
        for lvl, (H_, W_) in enumerate(spatial_shapes):
            ref_y, ref_x = torch.meshgrid(torch.linspace(0.5, H_ - 0.5, H_, dtype=torch.float32, device=device),
                                          torch.linspace(0.5, W_ - 0.5, W_, dtype=torch.float32, device=device))
            ref_y = ref_y.reshape(-1)[None] / (valid_ratios[:, None, lvl, 1] * H_)
            ref_x = ref_x.reshape(-1)[None] / (valid_ratios[:, None, lvl, 0] * W_)
            ref = torch.stack((ref_x, ref_y), -1)
            reference_points_list.append(ref)
        reference_points = torch.cat(reference_points_list, 1)
        reference_points = reference_points[:, :, None] * valid_ratios[:, None]
        return reference_points

对应该部分的网格点参考坐标。
在这里插入图片描述

EncoderLayer模块

开启循环送入EncoderLayer模块,首先查看送入的参数信息:
在一层循环时:
output 为 torch.Size([2, 9620, 256]),即backbone提取的特征信息。
pos:位置编码信息 torch.Size([2, 9620, 256])
reference_points:参考点 torch.Size([2, 9620, 4, 2])
spatial_shapes:torch.Size([4, 2]) 其值为:

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

level_start_index:tensor([ 0, 7220, 9044, 9500], device=‘cuda:0’)
padding_mask:torch.Size([2, 9620])

在这里插入图片描述

output = layer(output, pos, reference_points, spatial_shapes, level_start_index, padding_mask)

进入EncoderLayer后,进行自注意力的计算,由于这里使用了Deformable-Attention(可变形注意力),即self.self_attn,该部分的注意力计算重新进行了编写。

		src2 = self.self_attn(self.with_pos_embed(src, pos), reference_points, src, spatial_shapes, level_start_index, padding_mask)
        src = src + self.dropout1(src2)
        src = self.norm1(src)
        src = self.forward_ffn(src)
        return src

随后将得到结果 src2 为 torch.Size([2, 9620, 256]),之后便是与普通的 EncoderLayer 完全相同了。最终返回 src 为 torch.Size([2, 9620, 256])

可变形注意力模块

终于到了最关键的部分了:可变形注意力。
可变形注意力的思想很简单,即每个query在每个head上采样K个位置,只需和这些位置的特征进行交互,不同于detr那样,每个query需要与全局位置进行交互。需要注意的是,位置偏移量Δp mqx 是由query经过全连接得到的,注意力权重也是由query经全连接层得到的,同时在K个采样点之间进行权重归一化
在这里插入图片描述

src2 = self.self_attn(self.with_pos_embed(src, pos), reference_points, src, spatial_shapes, level_start_index, padding_mask)

我们来看看送入self_attn的参数信息:

def forward(self, query, reference_points, input_flatten, input_spatial_shapes, input_level_start_index, input_padding_mask=None): 

参考点坐标

首先是位置编码信息与经过backbone提取的特征信息进行相加。
with_pos_embed(src, pos)
reference_points:torch.Size([2, 9620, 4, 2])这里我们记住,reference_points只是一个坐标值而已,没有实际语义信息,或者是个网格,是要套在特征图上使用的。

在这里插入图片描述

spatial_shapes:torch.Size([4, 2])

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

src:torch.Size([2, 9620, 256])
padding_mask:torch.Size([2, 9620])

def with_pos_embed(tensor, pos):
    return tensor if pos is None else tensor + pos

特征图value

进入ms_deform_attn.py 进行计算:
首先 query 为 torch.Size([2, 9620, 256])
input_flatten即src ,维度为 torch.Size([2, 9620, 256])

value = self.value_proj(input_flatten)

其对应下图操作:
在这里插入图片描述

masked_fill方法有两个参数,mask和value,mask是一个pytorch张量(Tensor),元素是布尔值,value是要填充的值,填充规则是mask中取值为True位置对应于self的相应位置用value填充。

if input_padding_mask is not None:
    value = value.masked_fill(input_padding_mask[..., None], float(0))

对value进行维度变换为 torch.Size([2, 9620, 8, 32])

value = value.view(N, Len_in, self.n_heads, self.d_model // self.n_heads)

偏移量与权重值

sampling_offsets = self.sampling_offsets(query).view(N, Len_q, self.n_heads, self.n_levels, self.n_points, 2)
attention_weights = self.attention_weights(query).view(N, Len_q, self.n_heads, self.n_levels * self.n_points)
attention_weights = F.softmax(attention_weights, -1).view(N, Len_q, self.n_heads, self.n_levels, self.n_points)

上述代码对应下面的部分,即将query通过linear获得:
偏移量sampling_offsets(该偏移量是在特征图上的偏移量):torch.Size([2, 9620, 8, 4, 4, 2]),batch=2,4个WH之和,8个头,4个特征层,4个采样点(图中画了3个),2为偏移量坐标(x,y)

通过linear获得权重值。
attention_weights,最开始时为 torch.Size([2, 9620, 8, 16]) ,16代表的是4个特征层与4个采样点相乘,后面经过softmax后变为 torch.Size([2, 9620, 8, 4,4])

从这里可以看出:deformable atten不需要通过Q点乘K来获取attention_weight,其attention_weight是通过object query学出来的。

在这里插入图片描述
对应的全连接层:

self.sampling_offsets = nn.Linear(d_model, n_heads * n_levels * n_points * 2)

随后判断采样点最后的一维是否为2,这里reference_points为 torch.Size([2, 9620, 4, 2]),sampling_offsets 为 torch.Size([2, 9620, 8, 4, 4, 2]) ,offset_normalizer 为 torch.Size([4, 2])
即参考点的坐标加上偏移量(这里的偏移量还要除以特征图的大小),从而得到真正的采样点的位置。

  if reference_points.shape[-1] == 2:
        offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)
        sampling_locations = reference_points[:, :, None, :, None, :] \
                             + sampling_offsets / offset_normalizer[None, None, None, :, None, :]

第一句代码为对 input_spatial_shapes的宽高进行位置调换为高宽,如原来为:

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

变为offset_normalizer:

tensor([[95, 76],
        [48, 38],
        [24, 19],
        [12, 10]], device='cuda:0')

第二句为求出真正的参考点坐标。其中[:, :, None, :, None, :] 中None的作用是升维,因为其要与sampling_offsets进行加法运算,而其为 torch.Size([2, 9620, 4, 2]) ,而 sampling_offsets 为 torch.Size([2, 9620, 8, 4, 4, 2]) ,在相加时注意力头不考虑(所有的头都加上),4个采样点也需要全部加上,故扩充时使用 [:, :, None, :, None, :] 。同理,由于其偏移值为在特征图上的(src)所以要除以其变换后的宽高值(层数对应层数,x,y对应宽高)

最终我们将获取到的上述值送入CUDA的实现(可以认为是注意力值的计算)

output = MSDeformAttnFunction.apply(
            value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)
     

output输出结果为 torch.Size([2, 9620, 256])

在这里插入图片描述

输出结果

最后通过一个全连接层将计算结果输出:

output = self.output_proj(output)

其转换后结果依旧为:torch.Size([2, 9620, 256])

全连接层的定义:

self.output_proj = nn.Linear(d_model, d_model)

完成的便是下面过程:

在这里插入图片描述
经过上面一系列过程,便完成了下面公式的计算:
多头下注意力计算:

在这里插入图片描述
多尺度多头注意力计算:
在这里插入图片描述

Two-Stage 双阶段

经过一系列运算,memory为encoder的输出结果 torch.Size([2, 9620, 256])

memory = self.encoder(src_flatten, spatial_shapes, level_start_index, valid_ratios, lvl_pos_embed_flatten, mask_flatten)

随后判断是否开启双阶段,这是Deformable-DETR的第二个创新点。
memory 为 torch.Size([2, 9620, 256])
memory_padding_mask 为 torch.Size([2, 9620])
spatial_shapes 为

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

gen_encoder_output_proposals该方法是对encoder的输出值memory进行一系列的处理,最终将用于Decoder中的参考点初始化。

def gen_encoder_output_proposals(self, memory, memory_padding_mask, spatial_shapes):
    N_, S_, C_ = memory.shape#batch_size ,长度,通道数
    base_scale = 4.0  #多尺度数为4
    proposals = []
    _cur = 0
    for lvl, (H_, W_) in enumerate(spatial_shapes):
        mask_flatten_ = memory_padding_mask[:, _cur:(_cur + H_ * W_)].view(N_, H_, W_, 1)  #按照尺度大小得出mask值 并转换维度为:torch.Size([2, 76, 95, 1])
        valid_H = torch.sum(~mask_flatten_[:, :, 0, 0], 1)  #计算高有多少为真  tensor([76, 64], device='cuda:0')  第一张图片最大,全部为真,第二张图片64为真
        valid_W = torch.sum(~mask_flatten_[:, 0, :, 0], 1)  #计算宽多少为真tensor([95, 80], device='cuda:0')

        grid_y, grid_x = torch.meshgrid(torch.linspace(0, H_ - 1, H_, dtype=torch.float32, device=memory.device),
                                        torch.linspace(0, W_ - 1, W_, dtype=torch.float32, device=memory.device))#生成矩阵网格点坐标
        grid = torch.cat([grid_x.unsqueeze(-1), grid_y.unsqueeze(-1)], -1)
#unsqueeze(-1)  再加一层维度
        scale = torch.cat([valid_W.unsqueeze(-1), valid_H.unsqueeze(-1)], 1).view(N_, 1, 1, 2)
        grid = (grid.unsqueeze(0).expand(N_, -1, -1, -1) + 0.5) / scale
        wh = torch.ones_like(grid) * 0.05 * (2.0 ** lvl)
        proposal = torch.cat((grid, wh), -1).view(N_, -1, 4)
        proposals.append(proposal)
        _cur += (H_ * W_)
    output_proposals = torch.cat(proposals, 1)
    output_proposals_valid = ((output_proposals > 0.01) & (output_proposals < 0.99)).all(-1, keepdim=True)
    output_proposals = torch.log(output_proposals / (1 - output_proposals))
    output_proposals = output_proposals.masked_fill(memory_padding_mask.unsqueeze(-1), float('inf'))
    output_proposals = output_proposals.masked_fill(~output_proposals_valid, float('inf'))

    output_memory = memory
    output_memory = output_memory.masked_fill(memory_padding_mask.unsqueeze(-1), float(0))
    output_memory = output_memory.masked_fill(~output_proposals_valid, float(0))
    output_memory = self.enc_output_norm(self.enc_output(output_memory))
    return output_memory, output_proposals

最终输出值
output_memory:torch.Size([2, 9620, 256])

在这里插入图片描述

output_proposals:torch.Size([2, 9620, 4])

在这里插入图片描述

开始二阶段计算,调用分类头与回归头,初始化时初始化了7个,class_embed[self.decoder.num_layers]刚好是第7个。

enc_outputs_class = self.decoder.class_embed[self.decoder.num_layers](output_memory)#对encoder的结果进行分类预测 torch.Size([2, 9620, 91])
enc_outputs_coord_unact = self.decoder.bbox_embed[self.decoder.num_layers](output_memory) + output_proposals#对encoder进行回归并加上output_proposals
torch.Size([2, 9620, 4])

Decoder模块

有些累了,稍后补充

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/459114.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何真正认识 Linux 系统结构?这篇文章告诉你

Linux 系统一般有 4 个主要部分&#xff1a;内核、shell、文件系统和应用程序。内核、shell 和文件系统一起形成了基本的操作系统结构&#xff0c;它们使得用户可以运行程序、管理文件并使用系统。 Linux内核 内核是操作系统的核心&#xff0c;具有很多最基本功能&#xff0c;…

Springboot获取jar包中resources资源目录下的文件

阿萨斯多问题现象&#xff1a; 今天在项目中遇到一个业务场景&#xff0c;需要用到resources资源目录下的文件&#xff0c;然后就在思考一个问题&#xff1a; 当项目打成jar后&#xff0c;Springboot要如何获取resources资源目录下的文件呢&#xff1f; 问题分析&#xff1a; 如…

基于GPT-3和DALL-E的原创漫画生成

我花了上个月的时间探索如何使用 OpenAI 的 GPT-3 文本生成器和他们的 DALL-E 图像到文本系统来创建新的漫画&#xff0c;而无需编写任何源代码。 请注意&#xff0c;这两个系统都处于测试阶段&#xff0c;在我用完所有免费代币后&#xff0c;它们开始收取象征性的使用费。 不过…

数据湖Iceberg-简介(1)

文章目录 Iceberg简介概述特性数据存储、计算引擎插件化实时流批一体数据表演化&#xff08;Table Evolution&#xff09;模式演化&#xff08;Schema Evolution&#xff09;分区演化&#xff08;Partition Evolution&#xff09;列顺序演化&#xff08;Sort Order Evolution&a…

Inpaint9.1最新网页版下载及inpaint怎么去水印教程

市面上去水印的软件很多&#xff0c;但专业去除水印的&#xff0c;除了Photoshop&#xff0c;inpaint占有一席之地。在今天的数字时代&#xff0c;我们经常会遇到需要处理图片的情况。然而&#xff0c;当我们遇到水印在图片上&#xff0c;我们就需要寻找一个有效的方式来去除它…

总结831

学习目标&#xff1a; 4月&#xff08;复习完高数18讲内容&#xff0c;背诵21篇短文&#xff0c;熟词僻义300词基础词&#xff09; 学习内容&#xff1a; 暴力英语&#xff1a;背诵《start with you》,《大独裁者》第四段&#xff0c;默写《start with you》 高等数学&#x…

信号量【Linux】

文章目录 1. POSIX信号量1.1 引入1.2 概念1.3 PV原语&#xff08;原理&#xff09;1.4 相关接口 2. 二进制信号量3. 基于环形队列的生产消费模式3.1 介绍3.2 为什么要使用环形队列3.3 环形队列的作用3.4 实现互斥和同步两个信号量框架生产和消费的逻辑完善环形队列1信号量完善环…

MyBatis-学习修改尚硅谷最新教程笔记

一、MyBatis mybatis mybatis.xml配置文件 mapper接口类 mapper.xml映射处理配置文件 1、MyBatis简介 1.1、MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下…

【图像抠图】【深度学习】Ubuntu18.04下GFM官方代码Pytorch实现

【图像抠图】【深度学习】Ubuntu18.04下GFM官方代码Pytorch实现 提示:最近开始在【图像抠图】方面进行研究,记录相关知识点,分享学习中遇到的问题已经解决的方法。 文章目录 【图像抠图】【深度学习】Ubuntu18.04下GFM官方代码Pytorch实现前言数据集说明1.AM-2k【自然动物】2.B…

银行数字化转型导师坚鹏:学习贯彻二十大精神 解码乡村振兴之道

学习贯彻二十大精神 解码乡村振兴之道&#xff08;保险客户&#xff09; 课程背景&#xff1a; 很多保险公司从业人员存在以下问题&#xff1a; 不知道如何准确解读二十大精神&#xff1f; 不清楚乡村振兴相关政策要求&#xff1f; 不知道如何有效推动乡村振兴&#xf…

【JavaEE初阶】多线程(三)volatile wait notify关键字 单例模式

摄影分享~~ 文章目录 volatile关键字volatile能保证内存可见性 wait和notifywaitnotifynotifyAllwait和sleep的区别小练习 多线程案例单例模式饿汉模式懒汉模式 volatile关键字 volatile能保证内存可见性 import java.util.Scanner;class MyCounter {public int flag 0; }p…

2023新型智慧城市解决方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除 智慧城市建设整体框架 智慧城市建设内容 智慧城市云基础架构的必要性 智慧城市建设效益 智慧城市建设效益17 智慧城市建设模式建议 智慧城市建设-整体解决法方案 智慧城市建设…

关于Nginx

一、常见的“服务器中间件”&#xff08;即http server-web中间件&#xff09;有哪些 Tomcat、Jboss、Apache、WeBlogic、Jetty、webSphere、Nginx、IIS 二、nginx的特点 1.性能高&#xff0c;能承受5万并发每秒&#xff1b; 2.内存、磁盘&#xff0c;读取消耗空间小。 三、…

「背包问题-步入」失衡天平

失衡天平 https://ac.nowcoder.com/acm/contest/24213/1021 题目描述 终于Alice走出了大魔王的陷阱&#xff0c;可是现在傻傻的她忘了带武器了&#xff0c;这可如何是好???这个时候&#xff0c;一个神秘老人走到她面前答应无偿给她武器&#xff0c;但老人有个条件&#xff…

2023-04-24 算法面试中常见的贪心算法问题

贪心算法 1 贪心选择例题 455.饼干分配 假设你想给小朋友们饼干。每个小朋友最多能够给一块儿饼干。每个小朋友都有一个“贪心指数”&#xff0c;称为g(i)&#xff0c;g(i)表示的是这名小朋友需要的饼干大小的最小值。同时&#xff0c;每个饼干都有一个大小值s(i)。如果s(j)…

转换json格式的日期为Javascript对象的函数

项目中碰到了用jQuery从后台获取的json格式的日期的字符串&#xff0c;需要将此字符串转换成JavaScript的日期对象。 代码如下: //转换json格式的日期&#xff08;如&#xff1a;{ServerDatetime:"\/Date(1278930470649)\/"}&#xff09;为Javascript的日期对象 fu…

【离散系统】传递函数和状态空间方程离散化

本文如有错误&#xff0c;恳请指正。 目录 离散系统 采样控制系统 数字控制系统 信号采样 采样定理&#xff08;香农定理&#xff09; 信号保持—零阶保持器 Z变换 Z 变换方法 级数求和法 部分分式法 基本定理 Z反变换 Z反变换方法 长除法 部分分式法&#xff0…

hook函数,toRef家族

自定义hook函数 什么是hook?——本质是一个函数&#xff0c;把setup函数中使用的组合式API 进行了封装。 类似于vue2.x中的mixin。 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。 定义钩子&#xff1a; 使用钩子 toRef&#xff0c;toRefs 作用:创建一个ref对象…

WinScope实现录制视频与是Timeline时间轴同步设置方法-千里马framework车载手机系统开发实战

hi&#xff0c;粉丝朋友们&#xff01; 背景&#xff1a; 今天来分享一个粉丝朋友提出的问题&#xff0c;那就是他在学习wms课程时候有用到winscope工具&#xff0c;提出一个疑问&#xff0c;就是google官网说的有录屏可以结合起来一起看。具体如下&#xff1a; 其实这个以…

Vue 手搓轮播效果

tiptop: 为啥需要写这个功能&#xff0c;因为我遇到了每个轮播层内要放3个左右的商品块&#xff0c;如果使用element自带的轮播就需要将一维数组切成二维数组&#xff0c;导致处理一些情况下就会变得很麻烦&#xff0c;当然那种我也写了如果你们有需要&#xff0c;在下方留言我…