yolox Head-Decoupled head源码解读

article/2025/8/5 20:05:17

目录

前言

yolox网络结构

 yolox head网络结构

 head组件及对应源码

解码


前言

yolox backbone部分介绍

yolox neck部分介绍

yolox:https://github.com/Megvii-BaseDetection/YOLOX

yolox详细解读可参考:https://jishuin.proginn.com/p/763bfbd628ce

yolox网络结构

 

 yolox head网络结构

 head组件及对应源码

三个大分支输入的是三种尺度特征图,自下而上分别对应前面提到的backbone输出的dark3, dark4, dark5。尺度由大到小,堆叠成金字塔型。

(1)每个分支的开头是一个CBL,是一个1x1的卷积,目的是通道降维。

# stem
self.stems = nn.ModuleList()  # 1x1卷积# 3个不同尺度的输出分支(对应dark3, dark4, dark5),期间用到的组件都是一样的。
for i in range(len(in_channels)):  # 3个通道数,开头1x1的卷积会用到。对应三种尺度输出,self.stems.append(BaseConv(in_channels=int(in_channels[i] * width),out_channels=int(256 * width),ksize=1,stride=1,act=act,))

 (2)然后是两个分支,一个是分类分支,一个是回归分支。都是开头2个CBL

# conv
self.cls_convs = nn.ModuleList()  # 两个3x3的卷积
self.reg_convs = nn.ModuleList()  # 两个3x3的卷积# 3个不同尺度的输出分支(对应dark3, dark4, dark5),期间用到的组件都是一样的。
for i in range(len(in_channels)):  # 3个通道数,开头1x1的卷积会用到。对应三种尺度输出,self.cls_convs.append(nn.Sequential(*[Conv(in_channels=int(256 * width),out_channels=int(256 * width),ksize=3,stride=1,act=act,),Conv(in_channels=int(256 * width),out_channels=int(256 * width),ksize=3,stride=1,act=act,),]))self.reg_convs.append(nn.Sequential(*[Conv(in_channels=int(256 * width),out_channels=int(256 * width),ksize=3,stride=1,act=act,),Conv(in_channels=int(256 * width),out_channels=int(256 * width),ksize=3,stride=1,act=act,),]))

(3)然后紫色的组件,这是1x1的卷积,目的是将特征的channel维度变成指定数量。

# pred
self.cls_preds = nn.ModuleList()  # 一个1x1的卷积,把通道数变成类别数,比如coco 80类
self.reg_preds = nn.ModuleList()  # 一个1x1的卷积,把通道数变成4通道,因为位置是xywh.
self.obj_preds = nn.ModuleList()  # 一个1x1的卷积,把通道数变成1通道,通过一个值即可判断有无目标.# 3个不同尺度的输出分支(对应dark3, dark4, dark5),期间用到的组件都是一样的。
for i in range(len(in_channels)):  # 3个通道数,开头1x1的卷积会用到。对应三种尺度输出,self.cls_preds.append(nn.Conv2d(in_channels=int(256 * width),out_channels=self.n_anchors * self.num_classes,kernel_size=1,stride=1,padding=0,))self.reg_preds.append(nn.Conv2d(in_channels=int(256 * width),out_channels=4,kernel_size=1,stride=1,padding=0,))self.obj_preds.append(nn.Conv2d(in_channels=int(256 * width),out_channels=self.n_anchors * 1,kernel_size=1,stride=1,padding=0,))

(4)concat + reshape + concat + transpose

  • 黄色的concat组件,是把分类和回归结果按channel维度,即dim=1拼接;
  • 然后是reshape,将特征图展平成向量,(b,c,h,w) -> (b,c, h*w),h*w即预测anchor个数;
  • 按dim=2,concat不同尺度下的输出,(b,c, h*w) -> (b,c, num_anchors);
  • 然后是转置操作,(b,c, num_anchors) -> (b, num_anchors, c)。

对应的代码很简洁,如下。

# channel维度,将分类和回归分支结果拼接。
output = torch.cat([reg_output, obj_output.sigmoid(), cls_output.sigmoid()], 1
)# 1, reshape + concat + transpose, (b,c,h,w) -> (b,c,h*w) -> (b,c, ?) -> (b, ?, c)
outputs = torch.cat([x.flatten(start_dim=2) for x in outputs], dim=2
).permute(0, 2, 1)

如上图,转置之后的输出维度是(b, num_anchors, c),其中每一行是一个预测的anchor信息。后面就是解码,即将这些输入翻译成对应的预测框。

解码

对网络的输出进行解码,这里需要解码信息是回归的位置信息(分类信息不需要解码),因为输出的xywh是相对位置,简单来说解码过程就是(x+x_c, y+y_c, w, h) * stride,即预测的相对于网格左上角偏移的位置加上网格的位置,再乘以下采样倍数,映射到原图位置。解码模块的输入是 (b, num_anchors, c)

# 2, decode
if self.decode_in_inference:return self.decode_outputs(outputs, dtype=xin[0].type())
else:return outputs

解码代码非常精简,很是服气!拜读了!

def decode_outputs(self, outputs, dtype):grids = []  # 所有网格行列号位置。strides = []  # 所有网格的下采样倍数。# 计算每个尺度下所有网格的位置和对应的下采样倍数for (hsize, wsize), stride in zip(self.hw, self.strides):  # 特征图尺度,下采样倍数# yv和xv分别存储了每个网格的行和列。shape都是(hsize, wsize)yv, xv = torch.meshgrid([torch.arange(hsize), torch.arange(wsize)])# (hsize, wsize) -> (hsize, wsize, 2) -> (1, hsize*wsize, 2)# 这样每一行对应的是一个网络的行列号。grid = torch.stack((xv, yv), 2).view(1, -1, 2)  # (1, hsize*wsize, 2)# 存储每个尺度下所有网格的位置和对应的下采样倍数grids.append(grid)shape = grid.shape[:2]strides.append(torch.full((*shape, 1), stride))  # (1, hsize*wsize, 1) 存储放大倍数# 多个(1,hsize*wsize,2) -> (1,all_num_grids,2),并转换类型。主要是把所有不同尺度下的网格位置信息拼接起来。grids = torch.cat(grids, dim=1).type(dtype)strides = torch.cat(strides, dim=1).type(dtype)  # 同理。 多个(1,hsize*wsize,1) -> (1,all_num_grids,1)# x,y位置偏移outputs[..., :2], shape=(1, all_num_grids, 2)# grids所有网格的xy行列号, shape=(1, all_num_grids, 2)# strides所有网格的下采样倍数, shape=(1, all_num_grids, 1)outputs[..., :2] = (outputs[..., :2] + grids) * strides  # 乘以strides,映射到原图尺度下的xy位置。outputs[..., 2:4] = torch.exp(outputs[..., 2:4]) * strides  # wh, 乘以strides,映射到原图尺度下的anchor尺度。return outputs

其中有两行代码生成每个网格xy行列号位置信息和其对应的下采样倍数,示例如下。

比如,网格个数是2行4列

a, b = torch.meshgrid([torch.arange(2), torch.arange(4)])
a
tensor([[0, 0, 0, 0],[1, 1, 1, 1]])
b
tensor([[0, 1, 2, 3],[0, 1, 2, 3]])grid = torch.stack((a, b), 2).view(1, -1, 2)
grid
tensor([[[0, 0], [0, 1],[0, 2],[0, 3],[1, 0],[1, 1],[1, 2],[1, 3]]])

求loss

网络分类层输出没有经过sigmoid.

(1)首先,head部分会将分类和回归concat

output = torch.cat([reg_output, obj_output, cls_output], 1)

(2)然后,映射位置到原图尺度。

pred[..., :2] = (pred[..., :2] + grid) * stride  # xy, 这里xy也是没有经过sigmoid,网络应该是直接输出相对位置(0-1)。
pred[..., 2:4] = torch.exp(pred[..., 2:4]) * stride  # wh, e(x)指数函数。

a,初步正样本提取

根据锚框中心点来判断。 规则:寻找anchor_box中心点,落在ground_truth_boxes矩形范围的所有anchors。

(1)计算网格锚框在原图尺度下的中心位置。

# x_shifts: Tensor. (1, 8400). [[0.,1.,2.,...,17.,18.,19.]]
# y_shifts: Tensor. (1, 8400). [[0.,0.,0.,...,19.,19.,19.]]
x_centers_per_image = (x_shifts[0] + 0.5) * expanded_strides_per_image. [8400]
y_centers_per_image = (y_shifts[0] + 0.5) * expanded_strides_per_image. [8400]

 (2)计算ground_true目标框范围,即左上角坐标,和右下角坐标。

# groundtruth [x_c,y_c,w,h] -> [l, r, t, b]
gt_bboxes_per_image_l = (  # l: x_c - 0.5*w. (3, 8400)(gt_bboxes_per_image[:, 0] - 0.5 * gt_bboxes_per_image[:, 2]).unsqueeze(1).repeat(1, total_num_anchors))gt_bboxes_per_image_r = (  # r: x_c + 0.5*w(gt_bboxes_per_image[:, 0] + 0.5 * gt_bboxes_per_image[:, 2]).unsqueeze(1).repeat(1, total_num_anchors))gt_bboxes_per_image_t = (  # t: y - 0.5*h. (3, 8400)(gt_bboxes_per_image[:, 1] - 0.5 * gt_bboxes_per_image[:, 3]).unsqueeze(1).repeat(1, total_num_anchors)
)gt_bboxes_per_image_b = (  # b: y + 0.5*h.(gt_bboxes_per_image[:, 1] + 0.5 * gt_bboxes_per_image[:, 3]).unsqueeze(1).repeat(1, total_num_anchors)
)

 (3)计算锚框中心点和标注框边界的距离。如下图所示。

b_l = x_centers_per_image - gt_bboxes_per_image_l
b_r = gt_bboxes_per_image_r - x_centers_per_image
b_t = y_centers_per_image - gt_bboxes_per_image_t
b_b = gt_bboxes_per_image_b - y_centers_per_imagebbox_deltas = torch.stack([b_l, b_t, b_r, b_b], 2)
is_in_boxes = bbox_deltas.min(dim=-1).values > 0.0
is_in_boxes_all = is_in_boxes.sum(dim=0) > 0

 待续。。。


http://chatgpt.dhexx.cn/article/5PUF942h.shtml

相关文章

Decoupled Knowledge Distillation——目标分布与非目标分布相解耦

通过传统知识蒸馏的解耦分析,DKD(Decoupled Knowledge Distillation)重新审视了暗知识的本质,并通过蒸馏损失函数的改进、获得DKD loss,显著改善了任务相关知识迁移的效果: Paper地址:https://a…

【GCN-CTR】DC-GNN: Decoupled GNN for Improving and Accelerating Large-Scale E-commerce Retrieval WWW22

《DC-GNN: Decoupled Graph Neural Networks for Improving and Accelerating Large-Scale E-commerce Retrieval》(WWW’22) 在工业场景中,数百亿节点和数千亿的边直接端到端的GNN-based CTR模型开销太大,文章把整个GNN框架解耦成三阶段:预…

(2019.01, iclr) Decoupled Weight Decay Regularization

code: https://github.com/loshchil/AdamW-and-SGDW 除了纯SGD, L2 ! weight_decay 背景知识: sgd with momentum和adam,详见《深度学习》: L2 regulization and weight decay: https://benihime91.github.io/blog/machinelearning/deeplearning/python3…

Decoupled Novel Object Captioner

Decoupled Novel Object Captioner AbstractIntroductionMethodsPreliminariesZero-Shot Novel Object Captioning.Sequence Model with the PlaceholderKey-Value Object MemoryFramework OverviewTraining Reference Reference[原文]: Joselynzhao.top & 夏木青 | Decoup…

Video Anomaly Detection by Solving Decoupled Spatio-Temp

Video Anomaly Detection by Solving Decoupled Spatio-Temp 什么是SSL? Self-Supervised Learning,又称为自监督学习什么是多标签分类问题: 一个数据有多个标签pretext 任务: 简单的来说,通过另一个任务简介完成主任务 比如,要训…

魔改YOLOv5/YOLOv7高阶版——改进之结合解耦头Decoupled_Detect

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨

Distilling Object Detectors via Decoupled Features

Abstract 相比于图像分类而言&#xff0c;目标检测器更加复杂&#xff0c;具有多个损失函数。而目前的的检测中&#xff0c;其主要将注意力集中在对象的区域中&#xff0c;但本文指出&#xff0c;从背景中提取的特征信息对于学生模型的学习也是必不可少的。且由于目标区域和背…

Decoupled Attention Network for Text Recognition

摘要&#xff1a; 最流行的文字检测的方法是注意力机制&#xff0c;但是大多数的注意力机制方法由于循环的对齐操作会导致严重的对齐问题。因为对齐操作依赖于历史解码信息。 本文提出的DAN将对齐操作与历史解码信息解耦。 原理&#xff1a; Connectionist temporal classifi…

涨点技巧:Detect系列---Yolov5/Yolov7加入解耦头Decoupled_Detect,涨点明显

目录 1. Decoupled Head介绍 2.Yolov5加入Decoupled_Detect 2.1 DecoupledHead加入common.py中&#xff1a; 2.2 Decoupled_Detect加入yolo.py中&#xff1a; 2.3修改yolov5s_decoupled.yaml 3.数据集下验证性能 &#x1f3c6; &#x1f3c6;&#x1f3c6;&#x1f3c6;&…

Decoupled Contrastive Learning 论文解读和感想

本文首先提出了当前对比学习的三大痛点&#xff1a; 1、当前的sota方法结构都过于复杂 2、对比学习要想取得效果&#xff0c;必须要用大batch 3、超参敏感(个人认为这里说的超参是指数据增强方式) 然后本文以SimCLR为例&#xff0c;通过对对比损失的梯度进行分析&#xff0c;发…

DECOUPLED WEIGHT DECAY REGULARIZATION

引言 Adam作为一个常用的深度学习优化方法&#xff0c;提出来的时候论文里的数据表现都非常好&#xff0c;但实际在使用中发现了不少问题&#xff0c;在许多数据集上表现都不如SGDM这类方法。 后续有许多工作针对Adam做了研究&#xff0c;之前整理过关于优化算法的发展历程&am…

Decoupled Dynamic Filter Networks

转载自:https://www.cnblogs.com/liuyangcode/p/14755924.html 对depth-wise的改进&#xff0c;将卷积核的参数改为根据输入变化的方式 Introduction 卷积缺点在于&#xff1a;内容不变&#xff0c;计算量高动态filter可以根据内容自适应&#xff0c;但是会提高计算量。depth…

Analyzing and Leveraging Decoupled L1 Caches in GPUs

introduction 我们都知道L1/L2/L3cache解决了内存墙的问题&#xff0c;但是作者分析出现有的缓存架构有着天然缺陷&#xff0c; 作者列出的many to few communication&#xff0c;也就是L1ache中大量的数据传输到L2cache中&#xff0c;可能对于L1cache的带宽使用率不是很高&a…

Decoupled network

Decoupled network https://zhuanlan.zhihu.com/p/37598903 神经网络机制存在的缺陷&#xff1f; 过拟合&#xff0c;梯度消失或者是膨胀&#xff0c;训练依靠大量样本&#xff0c;对网络初始化及其敏感记忆协迁移等等。 Decupled network是对operator的改进 现在的卷积操作…

Decoupled Knowledge Distillation论文阅读+代码解析

本文来自2022年CVPR的文章&#xff0c;论文地址点这里 一. 介绍 知识蒸馏&#xff08;KD&#xff09;的通过最小化师生预测对数之间的KL-Divergence来传递知识(下图a)。目前大部分的研究注意力都被吸引到从中间层的深层特征中提取知识。与基于logit的精馏方法相比&#xff0c…

令牌桶算法

一 算法 令牌桶算法和漏桶算法不同的是&#xff0c;有时后端能够处理一定的突发情况&#xff0c;只是为了系统稳定&#xff0c;一般不会让请求超过正常情况的60%&#xff0c;给容灾留有余地。但漏桶算法中后端处理速度是固定的&#xff0c;对于短时的突发情况&#xff0c;后端…

动态分区分配算法(1、首次适应算法 2、最佳适应算法 3、最坏适应算法 4、邻近适应算法)

文章目录 前言知识总览1、首次适应算法2、最佳适应算法3、最坏适应算法4、邻近适应算法知识回顾与重要考点 前言 此篇文章是我在B站学习时所做的笔记&#xff0c;大部分图片都是课件老师的PPT&#xff0c;方便复习用。此篇文章仅供学习参考。 提示&#xff1a;以下是本篇文章…

《算法4》读书笔记(一)

写在前面&#xff1a;配套网站algs4.cs.princeton.edu&#xff0c;可以把这个网站作为编程的时候的参考资料。这本书比较实用&#xff08;某瓣评分9.3&#xff09;&#xff0c;但没有动态规划部分&#xff0c;作为两三年没怎么碰过算法和数据结构的菜狗&#xff0c;看了《图解算…

《算法4》深入理解红黑树

红黑树是一种性能非常优秀的数据结构&#xff0c;关键在于它能保证最坏的性能也是对数的&#xff0c;主要是因为它是一种平衡的树&#xff0c;所以也叫平衡查找树。要理解红黑树&#xff0c;最好先看看我的上一篇博客《算法4》符号表以及二叉查找树&#xff0c;了解二叉查找树以…

【算法4总结】第四章:图

目录备份 第四章&#xff1a;图 概述 图可以根据是否有向和带权分成以下四种&#xff1a; 无向图 &#xff08;无向不带权&#xff09;有向图 &#xff08;有向不带权&#xff09;加权无向图&#xff08;无向带权&#xff09;加权有向图&#xff08;有向带权&#xff09; …