BEV (0)---DETR

news2024/11/24 19:07:37

1 DETR

1.1 DETR处理流程

在这里插入图片描述

1.1.1 将图像输入给Backbone获得图像特征与位置编码

①. 对给定的输入图像通过resnet进行特征提取,最终得到特征图C5∈RBx2048xhxw,其中h、w为输入图像尺寸得1/32。随后再用一层1×1卷积压缩一下通道,得到特征图P5∈RBx256xhxw。将其转换为Transformer输入格式:[B, h*w, 256]。
②. 为了保证图像二维的特性,需要在X和Y两个维度都去计算Position Embedding。利用Position Embedding为输入序列提供位置信息。
综上,经过self.backbone(),可以获得图像特征src以及对应得位置编码pos 。

class DETR(nn.Module):
    """ This is the DETR module that performs object detection """
    def __init__(self, backbone, transformer, num_classes, num_queries, aux_loss=False):
        super().__init__()
        self.num_queries = num_queries
        self.transformer = transformer
        hidden_dim = transformer.d_model
        self.class_embed = nn.Linear(hidden_dim, num_classes + 1)
        self.bbox_embed = MLP(hidden_dim, hidden_dim, 4, 3)
        self.query_embed = nn.Embedding(num_queries, hidden_dim)
        self.input_proj = nn.Conv2d(backbone.num_channels, hidden_dim, kernel_size=1)
        self.backbone = backbone
        self.aux_loss = aux_loss

    def forward(self, samples: NestedTensor):
        if isinstance(samples, (list, torch.Tensor)):
            samples = nested_tensor_from_tensor_list(samples)
        # features: [mask, src], pos: 位置编码[2, 256, 27, 24]
        features, pos = self.backbone(samples)
        # src:[2, 2048, 27, 24], mask:[2, 27, 24]
        src, mask = features[-1].decompose()
        assert mask is not None
        # 将图像特征src的通道数由2048降维至256然后进行encoder、decoder
        hs = self.transformer(self.input_proj(src), mask, self.query_embed.weight, pos[-1])[0]
        # 全连接,类别预测 [6, b, 100, 256]--->[6, b, 100, 92]
        outputs_class = self.class_embed(hs)
        # bbox (cx, cy, w, h), 均属于0~1区间,因此采用sigmoid映射到(0, 1), [6, b, 100, 256]--->[6, b, 100, 4]
        outputs_coord = self.bbox_embed(hs).sigmoid()
        # 只取最后一层的decoder结果
        out = {'pred_logits': outputs_class[-1], 'pred_boxes': outputs_coord[-1]}
        # 是否保留decoder中间层结果
        if self.aux_loss:
            out['aux_outputs'] = self._set_aux_loss(outputs_class, outputs_coord)
        return out
    
1.1.2 Encoder层

DETR中,只将Q、K和位置编码进行相加,然后进入Transformer中的Encoder层。共有N层Encoder,其中一层的具体步骤如图所示:
其中:Add主要类似于ResNet中的跳层链接,用于防止加深网络时的梯度消失,同时可以保留原始的输入序列信号,有助于Transformer对原始序列的学习。
Norm则是进行LayerNorm操作。
在这里插入图片描述

class TransformerEncoderLayer(nn.Module):

    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1,
                 activation="relu", normalize_before=False):
        super().__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        # Implementation of Feedforward model
        self.linear1 = nn.Linear(d_model, dim_feedforward)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(dim_feedforward, d_model)

        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

        self.activation = _get_activation_fn(activation)
        self.normalize_before = normalize_before

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

    def forward_post(self,
                     src,
                     src_mask: Optional[Tensor] = None,
                     src_key_padding_mask: Optional[Tensor] = None,
                     pos: Optional[Tensor] = None):
        # Q、K和位置编码相加,q、k = src + pos    src: [h*w, b, 256], pos:[h*w, b, 256]
        q = k = self.with_pos_embed(src, pos)
        # 使用mutil-head attention src2: [h*w, b, 256]
        src2 = self.self_attn(q, k, value=src, attn_mask=src_mask, key_padding_mask=src_key_padding_mask)[0]
        # src: [h*w, b, 256], src = x + H, 其中x为原始输入,H为Multi-head Attention的输出
        src = src + self.dropout1(src2)
        # LayerNorm 归一化
        src = self.norm1(src)
        # FFN
        src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
        # Add
        src = src + self.dropout2(src2)
        # LayerNorm 归一化
        src = self.norm2(src)
        return src
1.1.3 Decoder层

DETR中共有M个Decoder模块,其中每一层decoder里是包含两个Attention模块的:
①. self-attention:
输入为DETR定义的Object queries可学习向量,维度为[B, N, 256], 初始化为0,其中N表示一张图片需要输出的预测结果数量,默认为100,即一张图像最多会被检测出100个物体,如果没有这么多物体,比如只检测出了20个,那么剩余的80个位置就是背景。
②. cross-attention:
第二个attention输入中的Q来自于Decoder中的第一个self-attention:
在这里插入图片描述
第二个attention输入中的K、V来自于Encoder最后输出的结果,其中K需要加上位置编码:
在这里插入图片描述

class TransformerDecoderLayer(nn.Module):

    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1,
                 activation="relu", normalize_before=False):
        super().__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.linear1 = nn.Linear(d_model, dim_feedforward)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(dim_feedforward, d_model)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)
        self.dropout3 = nn.Dropout(dropout)
        self.activation = _get_activation_fn(activation)
        self.normalize_before = normalize_before

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

    def forward_post(self, tgt, memory,
                     tgt_mask: Optional[Tensor] = None,
                     memory_mask: Optional[Tensor] = None,
                     tgt_key_padding_mask: Optional[Tensor] = None,
                     memory_key_padding_mask: Optional[Tensor] = None,
                     pos: Optional[Tensor] = None,
                     query_pos: Optional[Tensor] = None):
        # q = k = tgt + Qp, tgt: [100, b, 256], pos:[100, b, 256]
        q = k = self.with_pos_embed(tgt, query_pos)
        # 求解MultiHeadAttention输出的结果, tgt2: [100, b, 256]
        tgt2 = self.self_attn(q, k, value=tgt, attn_mask=tgt_mask, key_padding_mask=tgt_key_padding_mask)[0]
        # Add
        tgt = tgt + self.dropout1(tgt2)
        # layerNorm归一化
        tgt = self.norm1(tgt)
        # cross attention
        tgt2 = self.multihead_attn(query=self.with_pos_embed(tgt, query_pos),         # Decoder上一个attention的输出+ Qp作为Decoder的第二个MultiHeadAttention中的Q
                                   key=self.with_pos_embed(memory, pos),              # Encoder的输出K + Encoder的位置编码作为Decoder的第二个MultiHeadAttention中的K
                                   value=memory, attn_mask=memory_mask,               # Encoder的输出V作为Decoder的第二个MultiHeadAttention中的V
                                   key_padding_mask=memory_key_padding_mask)[0]
        # Add
        tgt = tgt + self.dropout2(tgt2)
        # layerNorm归一化
        tgt = self.norm2(tgt)
        # FFN
        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
        # Add
        tgt = tgt + self.dropout3(tgt2)
        # layerNorm归一化
        tgt = self.norm3(tgt)
        return tgt

1.1.4 全连接输出类别预测分支与bbox预测分支

由于M层Decoder的输出维度为[M, B, 100, 256], 我们通常只采用最后一层结果进行预测,同时接上全连接层分别预测类别和bbox框。
类别预测outputs_class 的维度是[M, B, 100, 92], 92是由于coco中类别索引共91个,加上背景类
位置预测outputs_coord 的维度是[M, B, 100, 4],分别是bbox的中心点坐标和宽高,都是相对坐标,即值域在(0, 1)范围内,所以DeTR后面用了sigmoid来做一次映射。

  outputs_class = self.class_embed(hs)
  outputs_coord = self.bbox_embed(hs).sigmoid()
  out = {'pred_logits': outputs_class[-1], 'pred_boxes': outputs_coord[-1]}
1.1.5 正负样本匹配

对于M个GT,在N个预测结果中找到最合适的M个预测结果与其一一对应,DeTR中,一个目标只有一个正样本,属于one-to-one匹配策略。
具体操作:
①. 计算N个预测结果和M个GT之间的类别cost和边界框cost
②. 根据cost值,利用匈牙利匹配给M个GT确定与其最匹配的M个预测框。

    @torch.no_grad()
    def forward(self, outputs, targets):
        bs, num_queries = outputs["pred_logits"].shape[:2]

        # We flatten to compute the cost matrices in a batch
        out_prob = outputs["pred_logits"].flatten(0, 1).softmax(-1)  # [batch_size * num_queries, num_classes]
        out_bbox = outputs["pred_boxes"].flatten(0, 1)  # [batch_size * num_queries, 4]

        # Also concat the target labels and boxes
        tgt_ids = torch.cat([v["labels"] for v in targets])
        tgt_bbox = torch.cat([v["boxes"] for v in targets])

        # Compute the classification cost. Contrary to the loss, we don't use the NLL,
        # but approximate it in 1 - proba[target class].
        # The 1 is a constant that doesn't change the matching, it can be ommitted.
        cost_class = -out_prob[:, tgt_ids]

        # Compute the L1 cost between boxes
        cost_bbox = torch.cdist(out_bbox, tgt_bbox, p=1)

        # Compute the giou cost betwen boxes
        cost_giou = -generalized_box_iou(box_cxcywh_to_xyxy(out_bbox), box_cxcywh_to_xyxy(tgt_bbox))

        # Final cost matrix
        C = self.cost_bbox * cost_bbox + self.cost_class * cost_class + self.cost_giou * cost_giou
        C = C.view(bs, num_queries, -1).cpu()

        sizes = [len(v["boxes"]) for v in targets]
        indices = [linear_sum_assignment(c[i]) for i, c in enumerate(C.split(sizes, -1))]
        return [(torch.as_tensor(i, dtype=torch.int64), torch.as_tensor(j, dtype=torch.int64)) for i, j in indices]
1.1.6 损失函数计算

其中类别损失采用交叉熵损失,回归损失采用giou + L1损失。

    def loss_labels(self, outputs, targets, indices, num_boxes, log=True):
        assert 'pred_logits' in outputs
        # 类别预测结果 [b, 100, 92]
        src_logits = outputs['pred_logits']
        # 先获取样本的索引idx变量,去索引那些被匈牙利算法匹配上的预测框和目标框
        idx = self._get_src_permutation_idx(indices)
        # 正样本类别
        target_classes_o = torch.cat([t["labels"][J] for t, (_, J) in zip(targets, indices)])
        # 初始化为背景类91
        target_classes = torch.full(src_logits.shape[:2], self.num_classes, dtype=torch.int64, device=src_logits.device)
        # idx正样本处设置正样本类别
        target_classes[idx] = target_classes_o
        # 交叉熵损失
        loss_ce = F.cross_entropy(src_logits.transpose(1, 2), target_classes, self.empty_weight)

        losses = {'loss_ce': loss_ce}

        if log:
            # TODO this should probably be a separate loss, not hacked in this one here
            losses['class_error'] = 100 - accuracy(src_logits[idx], target_classes_o)[0]
        return losses
    def loss_boxes(self, outputs, targets, indices, num_boxes):
        assert 'pred_boxes' in outputs
        # 先获取样本的索引idx变量,去索引那些被匈牙利算法匹配上的预测框和目标框
        idx = self._get_src_permutation_idx(indices)
        # bbox预测结果 [7, 4]
        src_boxes = outputs['pred_boxes'][idx]
        # bbox标签
        target_boxes = torch.cat([t['boxes'][i] for t, (_, i) in zip(targets, indices)], dim=0)
        # l1 损失
        loss_bbox = F.l1_loss(src_boxes, target_boxes, reduction='none')

        losses = {}

        losses['loss_bbox'] = loss_bbox.sum() / num_boxes
        # giou损失
        loss_giou = 1 - torch.diag(box_ops.generalized_box_iou(box_ops.box_cxcywh_to_xyxy(src_boxes), box_ops.box_cxcywh_to_xyxy(target_boxes)))

        losses['loss_giou'] = loss_giou.sum() / num_boxes
        return losses

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

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

相关文章

jvm调优策略

jvm调优主要是内存管理方面的调优,包括各个代的大小,GC策略等。 代大小调优 JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物…

数据结构学习记录——什么是堆(优先队列、堆的概念、最大堆最小堆、优先队列的完全二叉树表示、堆的特性、堆的抽象数据类型描述)

目录 优先队列 若采用数组或链表实现优先队列 数组 链表 有序数组 有序链表 总结 若采用二叉搜索树来实现优先队列 最大堆 堆的概念 优先队列的完全二叉树表示 堆的两个特性 结构性 有序性 【例】最大堆和最小堆 【例】不是堆 堆的抽象数据类型描述 优先队列…

安排超市 -- BFS分割搜索

4.安排超市 给定一个n*n的地图。地图是上下左右四联通的,不能斜向行走: *代表障碍,不可通行。 .代表路,可以通行。 #代表房子。房子也是可以通行的。 小红现在需要在一些地方安排一些超市(不能安排在障碍物上&#xf…

山东专升本计算机第七章-计算机网络基础

计算机网络基础 计算机网络系统 考点 6 计算机网络硬件 主体设备 • 称为主机 • 一般可分为中心站(又称服务器)和工作站(客户机) 连接设备 • 网卡 • 工作在数据链路层 • 网卡又称网络适配器,是连接主机和网…

【C++初阶】引用

一.概念 引用就是取别名,在语法上它不会开空间,而是和它引用的变量共用同一块空间。对引用的操作也就是对原来变量的操作。就像现实生活中给人取外号一样,不管是喊外号还是本名,指的都是那个人。 二.引用特性 1.引用类型必须和引用…

Java8 新特性讲解

一、Lambda表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。 二、函数式接口 &#…

【网课平台】Day15.Devops:持续集成与持续交付

文章目录 一、Devops1、什么是Devops2、什么是CI/CD3、Devops方案参考 二、人工部署1、项目打jar包2、生成镜像、创建容器 三、自动化部署1、代码提交到git2、修改pom.xml文件3、前端部署 一、Devops 1、什么是Devops 一个软件的生命周期包括:需求分析阶、设计、开…

SpringCloud:ElasticSearch之集群

单机的elasticsearch做数据存储,必然面临两个问题:海量数据存储问题、单点故障问题。 海量数据存储问题:将索引库从逻辑上拆分为N个分片(shard),存储到多个节点单点故障问题:将分片数据在不同节…

【原创】运维的终点是开发~chatGPT告诉你真相

文章目录 软件技术岗位鄙视链,你在哪层呢?让chatGPT告诉你运维工作好,还是开发工作好问它几个问题1. 一个三年运维成长的案例和薪资2. 一个三年开发成长的案例和薪资3. 一个五年运维成长的案例和薪资4. 一个五年开发成长的案例和薪资5. 一个十…

云分析迁移:顺应需求

云提供了对新分析功能、工具和生态系统的访问,可以快速利用这些功能、工具和生态系统来测试、试点和推出新产品。然而,尽管迫在眉睫,但企业在将分析迁移到云时仍感到担忧。组织正在寻找能够帮助他们分配资源和集成业务流程的服务提供商&#…

Linux 服务器上安装和使用 Redis,只需这 4 步!

一、使用 yum 安装 Redis 使用以下命令,直接将 redis 安装到 linux 服务器: yum -y install redis 二、配置远程连接 a)首先第一步,将 redis 配置文件下载到本地(如果你熟悉 vim 操作,直接用 vim 编辑即可…

论文阅读《PIDNet: A Real-time Semantic Segmentation Network Inspired by PID》

论文地址:https://arxiv.org/pdf/2206.02066.pdf 源码地址:https://github.com/XuJiacong/PIDNet 概述 针对双分支模型在语义分割任务上直接融合高分辨率的细节信息与低频的上下文信息过程中细节特征会被上下文信息掩盖的问题,提出了一种新的…

【五一创作】Springboot+多环境+多数据源(MySQL+Phoenix)配置及查询(多知识点)

文章目录 1. 背景2. 技术点3 子模块依赖SpringBoot设置4. 多环境配置4.1 application.yml4.2 application-pro.yml 5. 多数据源配置5.1 yml配置5.2 自定义数据源在Java中配置5.2.1 PhoenixDataSourceConfig5.2.2 MysqlDataSourceConfig 6. 完整的Pom6. 测试6.1 Mapper配置6.2 方…

字符、块、网络设备

设备模型(的意义) 降低设备多样性带来的Linux驱动开发的复杂度,以及设备热拔插处理、电源管理等,Linux内核提出了设备模型概念。设备模型将硬件设备归纳、分类,然后抽象出一套标准的数据结构和接口。驱动的开发&#…

Java项目上线之云服务器环境篇(四)——Redis的安装与配置

Java项目上线之云服务器环境篇(四)——Redis的安装与配置 在我们的项目里可能会用到Redis缓存,需要对Redis进行简单的配置。 1、我们的redis最好放在一个事先安装好的文件夹里,这样更方便于管理。 例如: redis我是放在…

前端开发:JS的事件循环执行机制详解

前言 在前端开发中,涉及到JS原生的使用原理是非常重要的知识点,尤其是在实际工作过程中会遇到各种复杂的业务需求场景,以及具体开发中可能会遇到一些涉及基于JS原理的使用,这都要求开发者能够很好的了解和掌握JS原生的常用原理。J…

jvm之类加载器

写在前面 当我们通过javac命令将java源代码编译为Java字节码后,必须通过类加载器将其加载到jvm中才能运行,所以类加载器是jvm中非常重要的一个组成部分,本文我们就一起来看下吧! 1:类的生命周期 类的生命周期如下图…

leetcode刷题之回文链表and最长回文子串

234.回文链表 方法一:找中间结点,断开链表,后一段链表进行反转 思路:①找中间结点:使用快慢指针fast,slow,fast每次走两个,slow每次走一个; 如果链表的个数是奇数个,那么最后slow指向中间节点 如果链表的个数是偶数个,那么最后slow指向中间两个节点的后一个 ②使用prev指针保…

文心一言 VS chatgpt (13)-- 算法导论3.1 8题 3.2 1题

八、可以扩展我们的记号到有两个参数n和m的情形,其中的n和m可以按不同速率独立地趋于无穷。对于给定的函数g(n, m),用O(g(n, m))来表示以下函数集: O(g(n, m)) { f(n, m): 存在正常量c、和,使得对所有n>n0或m>m0&#xff…

unity 性能优化之合批和剔除

批次对渲染的性能影响是比较大的,批次过多会导致cpu提交的次数过多,导致每帧渲染时间过长,所以我们需要对其优化,减少Bathches数量和SetPassCall次数。 批次合并的方法有多种,下面一一列出: 手动合批 将相…