基于Yolo的图像识别中的特征融合
目录
0.前言
结合之前对Yolo网络的学习(v8:笔记:对yolov8网络代码的学习_如何看yolo的源代码-CSDN博客 v11:理解Yolo网络运行规律并添加自制简易功能(以Yolo11为例)-CSDN博客 ) 我们对Yolo网络有了一个基本的了解,包括其运行过程和相关组件。
1.特征融合是什么?
首先我们重新说是一下特征融合是什么:特征融合是一个操作,在对输入图像进行处理后(卷积、池化等),我们会得到相应的特征图。不同层的特征图包含的信息侧重点不同——低层特征保留了更多空间细节和边缘纹理,高层特征则更偏向语义信息与目标类别特征。
特征融合的核心目的就是将不同尺度、不同语义层次的特征进行有效整合,使模型既能理解全局语义,又能保留关键细节,从而提升检测、分割等任务的精度和鲁棒性。
2.特征融合在单模态和多模态中的区别
2.1.单模态
在 YOLO 这类单模态(RGB)视觉网络中,常通过 UpSample(上采样) 与 DownSample(下采样) 操作将不同尺度的特征进行对齐,然后再进行 Concat(特征拼接)。其核心目标是让浅层特征(侧重纹理与边缘等细节)与深层特征(具备更强语义理解能力)实现互补,从而提升检测的鲁棒性和泛化能力。简而言之,这种融合是同一模态、不同尺度特征之间的融合,通过跨层连接增强模型对目标的表达能力。
2.2.多模态
然而,传统 RGB 单模态图像在夜晚、雨雪、逆光等复杂场景中表现受限,因此近年来研究逐渐转向 跨模态特征融合(如 RGB + IR 热成像、RGB + 深度图、RGB + 雷达等),以提升特定场景下的识别精度。需要强调的是,这里的“融合”与前述单模态多尺度融合并非同一概念。跨模态融合处理的是来自不同传感器、不同信息维度的特征,其挑战更大、流程更复杂。多模态系统往往涉及模态对齐、特征适配、权重分配与互补机制等步骤。换言之,多模态融合的难点在于:数据来源异构、特征分布差异大、时空同步与语义统一难度高。
| 难点 | 原因 |
|---|---|
| 特征大小不一致 | 例如红外、雷达、深度输入分辨率不一样 |
| 信号空间差异 | 红外反应热量,RGB反应光信号 |
| 语义差异 | 同一像素 RGB 和 IR 表达的语义不同 |
| 噪声 & 时间不同步 | 摄像头帧差、传感器延迟 |
2.3.主要区别
在单模态的Yolo网络中,我们常常在代码中的head部分(结构上是neck)找到命名为Concat的操作,其本质上是对同一模态中不同层(侧重点不同)的特征进行拼接。以方便模型学习到更好更全的特征。
而在多模态任务里,例如IVIF(Infrared and Visible Image Fusion,可见光–红外图像融合),模型并不是仅对单一模态内部做特征拼接,而是需要跨模态融合RGB和IR(红外)两种图像。由于 RGB 与 IR 图像特性上存在显著差异,因此跨模态融合不仅包含类似 YOLO 的层间特征汇聚,还涉及 模态对齐、特征映射、信息互补与冗余抑制等更复杂的机制。

在方法上,单模态和多模态有如下区别:
| 单模态 YOLO(Concat) | 多模态(RGB+IR / RGB+Depth) |
|---|---|
| Concat 是几何对齐+通道拼接 | 需要 语义对齐 + 空间对齐 + 分布映射 |
| 不考虑模态差异 | 考虑模态差异(热、光、深度、雷达) |
| 特征空间一致 | 特征空间不同,需要投影 & attention |
在总体差异上,有如下区别:
| 项目 | 单模态 YOLO | 多模态 IVIF / RGBT |
|---|---|---|
| 融合对象 | 同一模态、不同层特征 | 不同模态(RGB/IR/Depth 等) |
| 难度 | 较低(尺度与语义差异) | 较高(成像机理、分布差异) |
| 核心挑战 | 多尺度特征表达 | 模态对齐、互补与噪声抑制 |
| 目的 | 强调细节 + 语义融合 | 强化鲁棒性、低光/恶劣场景表现 |
3.网络分析
3.1. 代码来源
我们参考https://github.com/wandahangFY/YOLOv11-RGBT此项目中的代码进行观摩分析,这是一个基于Yolo11的RGBT模型项目,主要针对IVIF设计了六种多光谱融合模式,优化了特征融合,减少了冗余和不匹配,并提升了模型的整体性能。这里我们主要看他的其中典型的特征融合部分。其中论文地址:https://arxiv.org/abs/2506.14696
3.2. Concat融合
我们再看多模态特征融合的第一步就是要看他的网络是怎设计的,由于本项目是基于Yolo设计的,因此我们到
..\ultralytics\cfg\models
这个路径去寻找我们的模型yaml文件,在上几次实践和代码阅读中,我们知道,特征融合一般在中段进行融合最好,因为这个时候既能保留纹理还能增强语义,所以我们应该看RGBT而且应该是中段特征融合(midfusion)的代码 。我们以这个 yolo11-RGBT-midfusion.yaml 为例:
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
# Ultralytics YOLO11 object detection model with P3/8 - P5/32 outputs
# Model docs: https://docs.ultralytics.com/models/yolo11
# Task docs: https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
ch: 4
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Silence, []] # 0-P1/2
# visible
- [0, 1, SilenceChannel, [0,3]] # 1-P1/2
- [-1, 1, Conv, [64, 3, 2]] # 2-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 3-P2/4
- [-1, 2, C3k2, [256, False, 0.25]]
- [-1, 1, Conv, [256, 3, 2]] # 5-P3/8
- [-1, 2, C3k2, [512, False, 0.25]]
- [-1, 1, Conv, [512, 3, 2]] # 7-P4/16
- [-1, 2, C3k2, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 9-P5/32
- [-1, 2, C3k2, [1024, True]] # 10
# infrared
- [ 0, 1, SilenceChannel, [ 3,4 ] ] # 11-P1/2
- [ -1, 1, Conv, [ 64, 3, 2 ] ] # 12-P1/2
- [ -1, 1, Conv, [ 128, 3, 2 ] ] # 13-P2/4
- [ -1, 2, C3k2, [ 256, False, 0.25 ] ]
- [ -1, 1, Conv, [ 256, 3, 2 ] ] # 15-P3/8
- [ -1, 2, C3k2, [ 512, False, 0.25 ] ]
- [ -1, 1, Conv, [ 512, 3, 2 ] ] # 17-P4/16
- [ -1, 2, C3k2, [ 512, True ] ]
- [ -1, 1, Conv, [ 1024, 3, 2 ] ] # 19-P5/32
- [ -1, 2, C3k2, [ 1024, True ] ] #20
- [[6, 16], 1, Concat, [1]] # cat backbone P3 21
- [[8, 18], 1, Concat, [1]] # cat backbone P4 22
- [[10, 20], 1, Concat, [1]] # cat backbone P5 23
- [ -1, 1, SPPF, [ 1024, 5 ] ] # 24
- [ -1, 2, C2PSA, [ 1024 ] ] # 25
# YOLO11n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 22], 1, Concat, [1]] # cat backbone P4
- [-1, 2, C3k2, [512, False]] # 28
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 21], 1, Concat, [1]] # cat backbone P3
- [-1, 2, C3k2, [256, False]] # 31 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 28], 1, Concat, [1]] # cat head P4
- [-1, 2, C3k2, [512, False]] # 34 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 25], 1, Concat, [1]] # cat head P5
- [-1, 2, C3k2, [1024, True]] # 37 (P5/32-large)
- [[31, 34, 37], 1, Detect, [nc]] # Detect(P3, P4, P5)
我们发现不同于传统的Yolo11网络,这个RGBT网络设计了两个backbone,通过对SilenceChannel这个组件的参数分析,分别是[0,3][3,4],也就是0,1,2和3。不难看出,上面的backbone是对于可见光RGB图片的处理,而下面的backbone对红外的灰度图进行处理。因为R-G-B分为三张图所以通道数是红外的三倍。
..\ultralytics\nn\modules\conv.py
class Concat(nn.Module):
"""Concatenate a list of tensors along dimension."""
def __init__(self, dimension=1):
"""Concatenates a list of tensors along a specified dimension."""
super().__init__()
self.d = dimension
def forward(self, x):
"""Forward pass for the YOLOv8 mask Proto module."""
return torch.cat(x, self.d)
那让我们看看这个项目是怎么进行特征融合的,也就是Concat组件进行的操作:
如果 RGB 特征 = F_rgb ∈ R^(C1×H×W)
IR 特征 = F_ir ∈ R^(C2×H×W)
简而言之,两个特征图直接叠一块,相当于两张饼直接叠一起了,一张是三层的,一张是单层的。
两个特征图放一起不需要归一化么?
传统的 concat 融合,一般不会主动做归一化。但 Concat 不是加法,是把通道扩宽:
- RGB 特征 → 64 通道
- IR 特征 → 64 通道
- Concat后 → 128 通道(不是 64 通道)
那不归一化,会不会导致数值范围不一样?
可能会,但是网络会自己学着修正:
- 拼完后一般接 1×1 或 3×3 卷积
- BN(BatchNorm)会帮你自动调整分布
- 激活函数也会控制尺度
什么时候确实要归一化?
场景 原因 方式 模态差异巨大 夜间 RGB 弱 IR强 自适应权重 W_RGB、W_IR 不同网络深度输出 尺度差异大 LayerNorm / InstanceNorm 研究方向是“融合策略” 做算法实验 学习门控 / 注意力
3.3. CBFuse融合
我们仍然在刚才的目录中
..\ultralytics\cfg\models
找到yolo11-RGBT-midfusion-P3-PGI.yaml文件,这个文件不同于刚才的纯叠加,通过文件名和论文结合:“P3” 指的是在 特征金字塔的第三层 (P3/8) 进行可见光与红外特征的融合,而 “PGI” 则是论文代码实现中的一种可学习的融合模块 (Projection-Guided Integration / Pairwise-Guided Interaction),它的作用是:在 P3 层将来自 RGB 与 IR 的特征图通过投影引导的方式进行融合。
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
# Ultralytics YOLO11 object detection model with P3/8 - P5/32 outputs
# Model docs: https://docs.ultralytics.com/models/yolo11
# Task docs: https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
ch: 4
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Silence, []] # 0-P1/2
# visible
- [0, 1, SilenceChannel, [0,3]] # 1-P1/2
- [-1, 1, Conv, [64, 3, 2]] # 2-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 3-P2/4
- [-1, 2, C3k2, [256, False, 0.25]]
- [-1, 1, Conv, [256, 3, 2]] # 5-P3/8
- [-1, 2, C3k2, [512, False, 0.25]] #6
# infrared
- [ 0, 1, SilenceChannel, [ 3,4 ] ] # 7-P1/2
- [ -1, 1, Conv, [ 64, 3, 2 ] ] # 6-P1/2
- [ -1, 1, Conv, [ 128, 3, 2 ] ] # 9-P2/4
- [ -1, 2, C3k2, [ 256, False, 0.25 ] ] # 10
- [-1, 1, Conv, [256, 3, 2]] # 11-P3/8
- [-1, 2, C3k2, [512, False, 0.25]] #12
- [[6, 12], 1, Concat, [1]] # cat backbone P2 13
- [-1, 1, Conv, [512, 1, 1]] # 14-P3/8
- [-1, 1, Conv, [512, 3, 2]] # 15-P4/16
- [-1, 2, C3k2, [512, True]] # 16
- [-1, 1, Conv, [1024, 3, 2]] # 17-P5/32
- [-1, 2, C3k2, [1024, True]] # 18
- [ -1, 1, SPPF, [ 1024, 5 ] ] # 19
- [ -1, 2, C2PSA, [ 1024 ] ] # 20
# YOLO11n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 16], 1, Concat, [1]] # cat backbone P4
- [-1, 2, C3k2, [512, False]] # 23
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 14], 1, Concat, [1]] # cat backbone P3
- [-1, 2, C3k2, [256, False]] # 26 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 23], 1, Concat, [1]] # cat head P4
- [-1, 2, C3k2, [512, False]] # 29 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 20], 1, Concat, [1]] # cat head P5
- [-1, 2, C3k2, [1024, True]] # 32 (P5/32-large)
#
# - [[26, 29, 32], 1, Detect, [nc]] # Detect(P3, P4, P5)
# # routing
- [ 6, 1, CBLinear, [ [ 256 ] ] ] # 33
# # routing
- [ 12, 1, CBLinear, [ [ 256 ] ] ] # 34
- [ 16, 1, CBLinear, [ [ 256, 512 ] ] ] # 35
- [ 20, 1, CBLinear, [ [ 256, 512, 1024 ] ] ] # 36
# conv down
- [ 0, 1, Conv, [ 64, 3, 2 ] ] # 37-P1/2
# conv down
- [ -1, 1, Conv, [ 128, 3, 2 ] ] # 38-P2/4
- [ -1, 2, C3k2, [ 128, False ] ] # 39-P2/4
# conv down fuse
- [ -1, 1, Conv, [ 256, 3, 2 ] ] # 40-P3/8
- [ [ 33,34,35,36, -1 ], 1, CBFuse, [ [ 0, 0, 0, 0 ] ] ] # 41
- [ -1, 2, C3k2, [ 256, False ] ] # 42
# conv down fuse
- [ -1, 1, Conv, [ 512, 3, 2 ] ] # 43-P4/16
- [ [ 35,36, -1 ], 1, CBFuse, [ [ 1, 1 ] ] ] # 44
- [ -1, 2, C3k2, [ 512, False ] ] # 45
# conv down fuse
- [ -1, 1, Conv, [ 1024, 3, 2 ] ] # 46-P5/32
- [ [ 36, -1 ], 1, CBFuse, [ [ 2 ] ] ] # 47
- [ -1, 2, C3k2, [ 1024, True ] ] # 48
- [[26, 29, 32, 42,45,48], 1, DetectAux, [nc]] # Detect(P3, P4, P5)
很明显的能看出来,在32层之前,结构和我们看的RGBT-midfusion中差不多,仅仅是RGBT进行了浅层中层深层的三次特征concat,而在33层开始才是这个代码真正不一样的地方。
我们先引出这里特别用到的组件CBLinear和CBFuse的代码:
class CBLinear(nn.Module):
"""CBLinear."""
def __init__(self, c1, c2s, k=1, s=1, p=None, g=1):
"""Initializes the CBLinear module, passing inputs unchanged."""
super().__init__()
self.c2s = c2s
self.conv = nn.Conv2d(c1, sum(c2s), k, s, autopad(k, p), groups=g, bias=True)
def forward(self, x):
"""Forward pass through CBLinear layer."""
return self.conv(x).split(self.c2s, dim=1)
输出通道:sum(c2s)
(例如 [256,512,1024] → 总输出 1792)
最后用 split(self.c2s, dim=1) 按 [256,512,1024] 切分成三份
CBLinear = 一个一次性输出多个尺度引导特征的小型卷积器。
CBLinear的功能:
对同一个输入特征图,使用一次卷积运算,产生多个不同输出通道组的结果——每一组相当于一个独立的投影分支(多路投影)。
为什么看起来没复制输入,但效果等价于“多次卷积”?
PyTorch 的卷积内部并不会真正复制输入:
在内存和计算上复制是浪费的,所以Conv2d 的每个输出通道就是对同一个输入特征图的一次独立卷积计算不需要显式复制输入,因为底层矩阵乘法会自动完成“多核并行卷积”。
普通卷积(一个输出分支) CBLinear(多分支合并) “拿一张图,套一个卷积核组,输出一堆通道” “拿同一张图,同时用多个不同的卷积核组处理,一次输出所有通道” 每次卷积都是独立操作 把所有卷积核堆在一起一次算完 多个输入副本(逻辑上) 一个输入,共享底层数据流
CBLinear输出多尺度投影的意义在哪?
CBLinear 的意义,是为了在 不同尺度(语义层级) 上实现多模态信息的“跨层融合引导”。
这里使用CBLinear的目的是从不同层抽取的信息,不只是各自检测用,还能反哺、引导其他层去更好融合红外与可见光特征。
另外,我们观察发现,CBLinear一共挑选了:可见光的一个特征图(6层),红外的一个特征图(12层),concat的特征图(16层),concat+池化注意力的特征图(20层)。
这么挑选的好处是什么?
挑选这四层,是为了构建“跨模态 × 跨层级 × 有语义梯度”的引导体系,让 CBLinear + CBFuse 能从最细节到最语义,从可见光到红外,从浅层到深层,实现全局指导式融合,而不引入冗余计算。
我们还发现,本来在backbone中已经达到512通道的强语义特征图(6,12层),在routing中又被CBLinear重新变成了256通道。
这么做的意义是什么?为什么不直接保留原本的高通道语义?
因为浅层 P3 不需要高通道语义,而需要“跨层可对齐的、轻量的语义引导”。
把 512 通道降到 256 通道,使中层/深层语义能以与 P3 兼容的方式注入浅层融合,而不破坏浅层特征的分布。
那为什么不直接取他们的上一层?
6 和 12 是经过 C3k2 强化后的“稳定浅层语义”,更适合作为引导源(guidance)。
层号 代表性 为什么不能取上一层? 6 RGB 经过局部语义增强的 P3 上一层 5 语义太弱,噪声高 12 IR 的稳定浅层语义 上一层 11 热噪声多,未增强
class CBFuse(nn.Module):
"""CBFuse."""
def __init__(self, idx):
"""Initializes CBFuse module with layer index for selective feature fusion."""
super().__init__()
self.idx = idx
def forward(self, xs):
"""Forward pass through CBFuse layer."""
target_size = xs[-1].shape[2:]
res = [F.interpolate(x[self.idx[i]], size=target_size, mode="nearest") for i, x in enumerate(xs[:-1])]
return torch.sum(torch.stack(res + xs[-1:]), dim=0)
CBFuse是YOLOv11-RGBT 融合结构中的 多源引导融合单元。
CBFuse的作用是:
在指定层(如 P3、P4、P5)上,把来自多个层(和模态)的引导特征(CBLinear 输出)
与当前主干特征图(Conv 下采样后的输出)在空间上对齐、通道上相加,形成融合特征。
举例:
# yaml第41层
- [ [33,34,35,36,-1], 1, CBFuse, [[0,0,0,0]] ]
等价于:
xs = [layer33_out, layer34_out, layer35_out, layer36_out, main_feature]
idx = [0,0,0,0]
其中:
layer33_out是CBLinear(P3)的输出[B,256,H3,W3]layer35_out是CBLinear(P4)的输出[ [B,256,H4,W4], [B,512,H4,W4] ]main_feature是主干当前层(P3)的特征[B,256,H3,W3]
这里注意idx的含义:
CBLinear(c1, [256,512,1024])
- out[0] → 256通道(对齐P3)
- out[1] → 512通道(对齐P4)
- out[2] → 1024通道(对齐P5)
idx 代表取出的 CBLinear 子特征 0取 P3 的引导(256通道) 1取 P4 的引导(512通道) 2取 P5 的引导(1024通道)
CBLinear和CBFuse的联合机制可以简述为:
CBLinear:对单一输入特征图生成不同通道的多版本。
CBFuse:根据当前层需要的通道数,从 CBLinear 中挑选对应版本融合。
我们基于此yaml简单绘图:
- [ 6, 1, CBLinear, [ [ 256 ] ] ] # 33
- [ 12, 1, CBLinear, [ [ 256 ] ] ] # 34
- [ 16, 1, CBLinear, [ [ 256, 512 ] ] ] # 35
- [ 20, 1, CBLinear, [ [ 256, 512, 1024 ] ] ] # 36
- [ 0, 1, Conv, [ 64, 3, 2 ] ] # 37-P1/2
- [ -1, 1, Conv, [ 128, 3, 2 ] ] # 38-P2/4
- [ -1, 2, C3k2, [ 128, False ] ] # 39-P2/4
- [ -1, 1, Conv, [ 256, 3, 2 ] ] # 40-P3/8
- [ [ 33,34,35,36, -1 ], 1, CBFuse, [ [ 0, 0, 0, 0 ] ] ] # 41
- [ -1, 2, C3k2, [ 256, False ] ] # 42
- [ -1, 1, Conv, [ 512, 3, 2 ] ] # 43-P4/16
- [ [ 35,36, -1 ], 1, CBFuse, [ [ 1, 1 ] ] ] # 44
- [ -1, 2, C3k2, [ 512, False ] ] # 45
- [ -1, 1, Conv, [ 1024, 3, 2 ] ] # 46-P5/32
- [ [ 36, -1 ], 1, CBFuse, [ [ 2 ] ] ] # 47
- [ -1, 2, C3k2, [ 1024, True ] ] # 48
最上面一排(仅33,34,35,36)表示CBLinear分割出的特征映像(面积大小表示通道大小,256/512/1024),一共进行三次CBFuse操作。

我们可以观察得到,33,34这两个也就是可见光和红外的backbone特征只利用了一次,而两着Concat后的特征在CBFuse中在不同尺度被反复利用。
为什么融合使用的频率不相同?
RGB 和 IR 的原生浅层特征主要提供“细节级提示(fine-grained cues)”,只能在浅层指导一次;
而融合后的中层与深层特征包含“稳定的跨模态语义”,因此可以重复用于多层指导,实现跨尺度一致性。
3.4. 总结
经过前面对 YOLO 官方结构的系统学习,我们已经完整理解了 Backbone(特征提取) 和 Neck(多尺度特征融合) 的核心功能。在单模态 YOLO 网络中,Neck 主要通过 FPN/PAN 或 CSP 结构实现多尺度信息的融合;而在本次多模态 RGBT 网络中,我们进一步扩展了 Neck 的能力 —— 利用 CBLinear 与 CBFuse 实现跨模态、跨尺度的引导式融合,使 RGB 与 IR 特征在不同语义层次上实现更加稳定、有效的交互。这部分内容不仅加深了我们对 YOLO 融合机制的理解,也契合了当前多模态视觉研究的热门方向。
至此,我们对特征如何 从原始图像逐层编码、提取、融合 已经有了体系化的认识。
接下来,我们将重点学习 YOLO 中看似“只有一行”的 检测头(Detection Head)。
检测头是整个模型设计中
从特征到结果的关键桥梁(Feature → Predictions)。
在深入理解检测头之后,我们才能真正掌握 YOLO 全流程的推理逻辑。
如果觉得文章有帮助可以点个赞,如果思路上有一致或者共鸣可以点个关注,如果有问题请批评指正。
更多推荐
所有评论(0)