深度学习基础回顾:CNN特征提取、Transformer注意力机制、特征金字塔(FPN)
好,咱们开始第四讲。说实话,每次讲到这部分,我都挺感慨的。BEV感知能火起来,靠的就是这几块技术的组合拳。CNN、Transformer、FPN,缺一个都不行。我当年刚入行时,还只会用CNN做2D检测,后来慢慢摸索,才把这些东西串起来。
一、CNN特征提取:BEV感知的"眼睛"
CNN说白了就是卷积神经网络。它的核心任务,是从图像里提取有用的特征。你想想看,一张图片几百万像素,直接扔给全连接网络,那参数多到爆炸。CNN用卷积核滑动扫描,参数共享,效率高得多。
核心要点:CNN通过局部连接和权值共享,大幅减少参数量。每个卷积核只关注局部区域,多个卷积核叠加,就能提取从边缘到纹理再到语义的层次化特征。
我在做BEV项目时,遇到过一个问题:输入图像分辨率太高,模型跑不动。后来我用了ResNet作为backbone,配合空洞卷积,在保持感受野的同时降低了计算量。嗯,这里要注意,空洞卷积的rate不能设太大,否则特征会变得稀疏。
常见的CNN backbone有:
- VGG:结构简单,但参数量大,现在用得少了
- ResNet:引入残差连接,解决了深层网络退化问题。我个人习惯用ResNet-50作为基线
- EfficientNet:通过复合缩放,在精度和效率间取得平衡
- MobileNet:轻量化设计,适合部署在车端
代码示例,一个简单的ResNet残差块:
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride, 1)
self.bn1 = nn.BatchNorm2d(out_channels)
self.conv2 = nn.Conv2d(out_channels, out_channels, 3, 1, 1)
self.bn2 = nn.BatchNorm2d(out_channels)
# 捷径连接
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, 1, stride),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += self.shortcut(x) # 残差连接
out = F.relu(out)
return out
小技巧:在BEV感知中,我建议使用ResNet-34或ResNet-50作为backbone。太深了容易过拟合,太浅了特征表达能力不够。另外,记得在ImageNet上预训练,能省不少训练时间。
二、Transformer注意力机制:让模型学会"看哪里"
Transformer最初是给NLP用的,后来被引入视觉领域。它的核心是自注意力机制。说白了,就是让每个像素都跟其他像素做"关联",找出哪些位置更重要。
为什么会这样?因为CNN的感受野有限,高层特征虽然感受野大,但细节丢失严重。Transformer通过全局注意力,能直接建模任意两个位置的关系。这在BEV感知中特别有用——你想想,远处的车和近处的车,在图像上可能隔得很远,但它们在BEV空间里是相邻的。
我曾经在项目中踩过一个坑:直接用标准Transformer处理高分辨率特征图,显存直接爆了。后来改用Swin Transformer的窗口注意力机制,才把问题解决。
注意力机制的核心公式:
Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) * V
其中Q是查询,K是键,V是值。QK^T计算相似度,softmax归一化成权重,最后加权求和得到输出。
在BEV感知中,Transformer的典型应用:
- BEVFormer:用可变形注意力,把图像特征投影到BEV空间
- DETR:端到端的目标检测,不需要NMS后处理
- Cross-Attention:融合多视角图像特征,生成统一的BEV表示
避坑指南:我曾经在训练BEVFormer时,发现模型收敛很慢。后来检查发现,是位置编码没加对。Transformer对位置信息很敏感,一定要加上合适的位置编码。另外,注意力头数建议设为8或16,太多反而会降低性能。
三、特征金字塔(FPN):多尺度特征的"融合器"
FPN全称Feature Pyramid Network。它的出现,解决了CNN中多尺度检测的难题。你想想看,小目标在浅层特征里清晰,但语义信息弱;大目标在深层特征里语义强,但位置信息模糊。FPN把两者结合起来。
FPN的结构很简单:自底向上的通路(CNN前向传播),自顶向下的通路(上采样),再加上横向连接(1x1卷积)。这样每一层特征都融合了高层语义和低层细节。
我记得第一次在BEV感知里用FPN,效果提升很明显。原来检测不到的小目标,比如远处的行人,现在能检测到了。但要注意,FPN会增加计算量,需要权衡速度和精度。
FPN的典型实现:
class FPN(nn.Module):
def __init__(self, in_channels_list, out_channels):
super().__init__()
# 横向连接:1x1卷积降维
self.lateral_convs = nn.ModuleList([
nn.Conv2d(in_c, out_channels, 1)
for in_c in in_channels_list
])
# 自顶向下:上采样
self.top_down_convs = nn.ModuleList([
nn.Conv2d(out_channels, out_channels, 3, 1, 1)
for _ in range(len(in_channels_list)-1)
])
def forward(self, features):
# features: [C3, C4, C5] 从浅到深
laterals = [conv(f) for conv, f in zip(self.lateral_convs, features)]
# 自顶向下融合
for i in range(len(laterals)-1, 0, -1):
laterals[i-1] += F.interpolate(
laterals[i], size=laterals[i-1].shape[-2:],
mode='nearest'
)
# 输出前用3x3卷积平滑
outputs = [conv(l) for conv, l in zip(self.top_down_convs, laterals)]
return outputs # [P3, P4, P5]
关键点:在BEV感知中,FPN的输出通常用于多尺度检测头。P3负责小目标,P4负责中目标,P5负责大目标。每个尺度的检测头共享权重,但特征分辨率不同。
FPN的变体也很多:
- PANet:在FPN基础上增加自底向上的通路,进一步强化底层特征
- BiFPN:加权融合,让网络自己学习不同层的重要性
- NAS-FPN:用神经架构搜索自动设计FPN结构
我个人建议,在BEV感知中先用标准的FPN,效果不错且稳定。如果追求极致性能,可以试试BiFPN,但训练时间会翻倍。
四、三者如何协同工作?
好,咱们把这三块串起来。在BEV感知中,典型的流程是这样的:
- CNN backbone:从多视角图像中提取多尺度特征图
- FPN:融合多尺度特征,输出P3、P4、P5
- Transformer:把图像特征通过注意力机制,投影到BEV空间
- 检测头:在BEV特征上做目标检测或语义分割
举个例子,BEVFormer就是这么干的。它用ResNet-50提取特征,用FPN生成多尺度特征,然后用可变形注意力把特征投影到BEV空间。最后在BEV特征上做检测。
经验之谈:我在实际项目中,发现FPN的横向连接用1x1卷积就够了,不需要太复杂。Transformer的注意力头数设为8,层数设为6,效果和效率比较平衡。CNN backbone用ResNet-50,预训练权重一定要加载。
最后说一句,这三块技术不是孤立的。CNN负责提取特征,FPN负责多尺度融合,Transformer负责全局建模和跨模态投影。三者配合,才能构建出强大的BEV感知系统。嗯,今天就讲到这里,下一章咱们聊聊BEV空间中的坐标变换。