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

news/2024/4/20 21:54:53/文章来源:https://blog.csdn.net/pengxiang1998/article/details/130295796

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.maskmask = 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, 256refanchor = self.refpoint_embed.weight      # nq, 4query_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,默认为4else:assert reference.shape[-1] == 2tmp[..., :2] += referenceoutputs_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.shapespatial_shape = (h, w)spatial_shapes.append(spatial_shape)src = src.flatten(2).transpose(1, 2)# 由bs,c,h,w变为bs, hw, cmask = mask.flatten(1)#由bs,h,w变为 bs, hwpos_embed = pos_embed.flatten(2).transpose(1, 2)#由bs,c,h,w变为bs, hw, clvl_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.shapevalid_H = torch.sum(~mask[:, :, 0], 1)valid_W = torch.sum(~mask[:, 0, :], 1)valid_ratio_h = valid_H.float() / Hvalid_ratio_w = valid_W.float() / Wvalid_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.shapespatial_shape = (h, w)spatial_shapes.append(spatial_shape)src = src.flatten(2).transpose(1, 2)                # bs, hw, cmask = mask.flatten(1)                              # bs, hwpos_embed = pos_embed.flatten(2).transpose(1, 2)    # bs, hw, clvl_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  #多尺度数为4proposals = []_cur = 0for 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) / scalewh = 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 = memoryoutput_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.luyixian.cn/news_show_103160.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

layui 表格中嵌入下拉框被遮挡

1、代码 单元格样式&#xff1a; * 设置下拉框的高度与表格单元相同 */.layui-table-cell {width: 100%;height: 100%;border: 1px;border-color: #F2F2F2;} 表格初始化后的回调&#xff1a; done: function (res, curr, count) {$(".layui-table-body, .layui-tabl…

MC9S12G128开发板—实现按键发送CAN报文指示小车移动功能

实验环境&#xff1a;MC9S12G128开发板 基本功能&#xff1a;控制开发板上的按键&#xff0c;模拟车辆移动的上下左右四个方位&#xff0c;通过can通信告诉上位机界面&#xff0c;车辆轨迹的移动方位。 1. 1939报文发送的示例代码 MC9S12G128开发板1939协议发送can报文数据的…

php+vue 校友交流平台

1.普通用户功能分析 &#xff08;1&#xff09;用户注册&#xff1a;用于注册校友录用户。 &#xff08;2&#xff09;用户登录&#xff1a;供校友录用户登录。 &#xff08;3&#xff09;资料修改&#xff1a;修改当前登录使用者信息。 &#xff08;4&#xff09;…

“量子+生成式AI”!IBM联合生物制药公司Moderna进行疫苗研究

​ &#xff08;图片来源&#xff1a;网络&#xff09; 4月20日&#xff0c;以COVID-19疫苗而闻名的生物技术和制药公司Moderna Inc.表示&#xff0c;宣布正在与IBM公司合作&#xff0c;利用量子计算和生成式人AI探索推进研究mRNA技术的方法。 双方签署了一项协议&#xff0c;允…

【社区图书馆】Fundamentals Of Computer Graphics——The beginning of computer graphics

目录 English 中文 English "Fundamentals Of Computer Graphics" is a classic textbook on computer graphics, also known as the "Tiger Book". It is considered one of the best introductory texts in the field of computer graphics. The book …

022 - C++ 析构函数

上期我们讨论了构造函数。认识了它是什么以及如何使用它。如果你没有看上一期&#xff0c;那么你一定要回去看一下。 今天我们要讨论一下它的“孪生兄弟”&#xff0c;析构函数&#xff0c;它们在某些方面非常相似。 构造函数是你创建一个新的实例对象时运行&#xff0c;而析…

【iOS】AVPlayer 视频播放

视频播放器的类别 iOS开发中不可避免地会遇到音视频播放方面的需求。 常用的音频播放器有 AVAudioPlayer、AVPlayer 等。不同的是&#xff0c;AVAudioPlayer 只支持本地音频的播放&#xff0c;而 AVPlayer 既支持本地音频播放&#xff0c;也支持网络音频播放。 常用的视频播放…

深入理解Javascript事件处理机制

深入理解javascript事件处理机制 前言 在开发web应用程序时&#xff0c;事件处理机制是javascript中至关重要的一部分。许多高级特性&#xff0c;如事件冒泡、事件捕获和事件委托&#xff0c;都是通过事件处理来实现的。熟练掌握这些技术可以帮助我们更好地组织代码、提高代码…

C++篇----类、封装、类访问权限、类实例化

文章目录 一、面向过程和面向对象二、类 一、面向过程和面向对象 c语言是面向过程的编程语言 c是面向对象的编程语言 面向过程&#xff1a;关注过程&#xff0c;对于求解问题的不走&#xff0c;调用函数逐步解决问题 就洗衣服来说&#xff1a;洗衣服需要放水&#xff0c;倒洗衣…

10个必备的建筑可视化3dmax插件

当日复一日地处理项目时&#xff0c;很容易陷入舒适但效率不高的工作流程中。 插件是在不牺牲工作质量的情况下改进和加快工作流程的好方法。 尤其是在建筑可视化时&#xff0c;快节奏的行业往往需要艺术家灵活机智。 在本文中&#xff0c;我们将介绍 10 个最好的 3ds Max 插件…

C语言从入门到精通第11天(数组的基本操作)

数组的基本操作 数组的概念一维数组二维数组 数组的概念 在程序设计中&#xff0c;为了方便处理数据把具有相同类型的若干变量按有序形式集合在一起&#xff0c;这些按序排列的同类数据元素的集合称为数组。 在C语言中&#xff0c;数组属于构造数据类型&#xff0c;一个数组可…

Linux文本处理三大利器Grep、AWK、Sed

写在前面 Linux三剑客是文本处理工具&#xff0c;它们可以帮助我们快速、高效地对文本进行处理。其中包括了grep、awk、以及sed这三个强大的命令行工具。 Linux 三剑客主要作用: grep&#xff0c;它可以根据正则表达式查找相关内容并打印对应的数据。awk&#xff0c;它可以根…

C. Painting the Fence(思维 + 前缀和)

Problem - C - Codeforces You需要油漆一个由n个部分组成的长围栏。不幸的是&#xff0c;它没有被涂漆&#xff0c;所以你决定雇用q名画家来完成这项工作。第i名画家将会油漆所有满足lisxsri的部分x. 不幸的是&#xff0c;你的预算很紧&#xff0c;所以你只能雇用q-2名画家。显…

数据湖Iceberg-简介(1)

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

itop-3568开发板驱动学习笔记(22)设备树(一)设备树基础

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记 文章目录 设备树简介设备树编译设备树语法设备根节点设备子节点节点名称reg 属性#address-cell 和 #size-cells 属性model 属性status 属性compatible 属性aliases 节点chosen 节点device_type 属性自定义属性 设备树…

Linux云服务器的使用,以及运行Python程序

目录 1、使用Linux云服务器的软件 2、Linux系统运行Python程序 3、Linux系统查看包、虚拟环境、安装包等 以下几个深度学习服务器都不错&#xff1a;智星云、AutoDL、恒源云 1、使用Linux云服务器的软件 MobaXterm_Personal 推荐MobaXterm_Personal mobaxterm是一款方便网站…

数据库管理新定义:一款纯Web化免费SQL开发工具,免安装

SQL Studio是一款由麦聪软件研发的多数据库管理工具&#xff0c;提供Windows、Linux 和 MacOS三种版本的软件包&#xff0c;支持中英文两种语言。SQL Studio是用Java编写的&#xff0c;默认使用 JDK 8进行编译。 下载看这里: [SQLStudio] (http://www.maicongs.com/#/home/web)…

地热井监测控制系统解决方案

概述 地热井监测控制系统主要是对地热井采水和回灌进行流量、温度、水位&#xff08;压力&#xff09;等参数的实时监测&#xff0c;对地热站现场环境进行实时视频监控。地热井现场和取水井、回灌井安装监测装置&#xff0c;通过无线传输设备将数据实时传输至自然资源局已建中…

上海车展:预售价109.8万元,仰望U8见证国产品牌崛起

如果要评选2023上海车展上比亚迪展台“最亮的星”&#xff0c;估计很多媒体和观众都会毫不迟疑地把票投给仰望U8。 没办法&#xff0c;因为在本届车展上&#xff0c;仰望U8的表现实在是太吸睛了。 作为比亚迪旗下的高端新能源品牌&#xff0c;仰望汽车在上海车展上携两款车型—…

【Leetcode -141.环形链表 -2.两数相加】

Leetcode Leetcode -141.环形链表Leetcode -2.两数相加 Leetcode -141.环形链表 题目&#xff1a;给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给…