4. 特征提取基础:基于CNN的图像特征提取(ResNet、MobileNet)

好,咱们进入实战环节的第一个硬核模块——特征提取。

说白了,ADAS感知的第一步,就是让车子“看懂”图像里有什么。而看懂图像,靠的就是特征提取。我这些年做感知算法,最深的体会就是:特征提取的质量,直接决定了后续检测、跟踪的上限。你想想看,如果连特征都提歪了,后面再怎么调参也是白搭。

4.1 为什么ADAS需要CNN做特征提取?

传统方法用HOG、SIFT手工设计特征,在简单场景还行。但到了自动驾驶这种复杂环境——光照突变、雨雪遮挡、行人姿态千奇百怪——手工特征就彻底崩了。

CNN的优势在于:它能自动学习从低级边缘到高级语义的分层特征。我举个例子:

  • 浅层卷积:检测边缘、纹理、颜色斑点
  • 中层卷积:组合出车轮、车窗、车灯等部件
  • 深层卷积:抽象出“这是一辆车”的完整概念

我在项目中遇到过最典型的案例:用传统方法检测夜间行人,漏检率高达30%。换成ResNet做特征提取后,漏检率直接降到5%以下。为什么?因为CNN能学到“红外热辐射”这种手工特征根本想不到的模式。

核心观点:特征提取是ADAS感知的“地基”。地基不牢,地动山摇。

4.2 ResNet:解决“越深越差”的怪圈

2015年之前,大家有个共识:网络越深,效果越好。但实际训练时发现,56层的网络比20层的训练误差还大。这不是过拟合,而是梯度消失/爆炸导致的退化问题。

ResNet的解决方案简单粗暴——引入残差连接(Skip Connection)。

4.2.1 残差块的核心思想

传统网络学习的是映射 H(x),而残差块学习的是残差 F(x) = H(x) - x。说白了,网络只需要学习“输入和输出之间的差异”。

为什么有效?我个人的理解是:残差结构让梯度可以“抄近道”直接回传到浅层。即使中间层梯度消失,跳跃连接也能保证浅层参数得到更新。

# 一个典型的ResNet残差块(PyTorch风格)
class BasicBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride, padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        # 跳跃连接:如果维度不匹配,用1x1卷积调整
        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 = self.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)  # 关键:残差相加
        out = self.relu(out)
        return out

避坑指南:我曾经在部署时踩过一个坑——残差相加后忘记加ReLU激活。结果特征图数值范围不对,检测头直接崩了。记住:残差块输出一定要过激活函数。

4.2.2 ADAS场景下的ResNet选型

模型 参数量 推理速度(1080Ti) ADAS适用场景
ResNet-18 11M ~2ms 低算力平台、前视单目
ResNet-34 21M ~3ms 中端域控制器
ResNet-50 25M ~4ms 高端平台、多传感器融合
ResNet-101 44M ~7ms 离线训练、高精度需求

我个人习惯:量产项目首选ResNet-34或ResNet-50。ResNet-18精度不够,ResNet-101又太慢。你想想看,ADAS要求30fps以上,单帧处理时间不能超过33ms。特征提取这一步就得控制在5ms以内,否则留给检测头的时间就不够了。

4.3 MobileNet:轻量化的艺术

ResNet虽然好,但参数量大,不适合嵌入式设备。这时候MobileNet就派上用场了。

MobileNet的核心创新是深度可分离卷积(Depthwise Separable Convolution)。说白了,就是把标准卷积拆成两步:

  1. 深度卷积(Depthwise Conv):每个通道单独做卷积,不跨通道
  2. 逐点卷积(Pointwise Conv):用1x1卷积融合通道信息

这样做的好处是什么?计算量直接降到原来的1/8到1/9。我算过一笔账:标准3x3卷积的计算量是 3×3×C_in×C_out×H×W,而深度可分离卷积是 3×3×C_in×H×W + C_in×C_out×H×W。当C_in和C_out都是256时,后者只有前者的1/9左右。

# MobileNet的核心模块:深度可分离卷积
class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        # 第一步:深度卷积(每个通道独立)
        self.depthwise = nn.Conv2d(
            in_channels, in_channels, 
            kernel_size=3, stride=stride, padding=1,
            groups=in_channels  # 关键:groups=输入通道数
        )
        self.bn1 = nn.BatchNorm2d(in_channels)
        
        # 第二步:逐点卷积(1x1融合通道)
        self.pointwise = nn.Conv2d(
            in_channels, out_channels, 
            kernel_size=1, stride=1
        )
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    
    def forward(self, x):
        x = self.relu(self.bn1(self.depthwise(x)))
        x = self.relu(self.bn2(self.pointwise(x)))
        return x

注意:深度可分离卷积虽然快,但精度会有轻微下降。我在实际项目中对比过:MobileNetV2比ResNet-34精度低约1.5%,但推理速度快了3倍。对于ADAS来说,如果算力紧张,这1.5%的精度损失是可以接受的。

4.3.1 MobileNetV2的改进:倒残差结构

MobileNetV2在V1基础上做了两个关键改进:

  • 线性瓶颈(Linear Bottleneck):在逐点卷积后去掉ReLU,保留线性输出。为什么?因为低维空间用ReLU会丢失信息。
  • 倒残差(Inverted Residual):先升维再降维。传统残差是“压缩-卷积-扩张”,MobileNetV2反过来“扩张-卷积-压缩”。

嗯,这里要注意:倒残差结构在低维输入时先升维到高维,做完深度卷积后再降维回去。这样做的目的是让深度卷积在高维空间提取特征,信息更丰富。

4.4 实战建议:如何选择特征提取网络?

我根据多年项目经验,总结了一个选型决策树:

  1. 算力充足(如Orin、EyeQ6):直接上ResNet-50或ResNet-101,精度优先
  2. 算力中等(如TDA4、EyeQ5):ResNet-34或MobileNetV2,平衡精度和速度
  3. 算力受限(如MCU、低端DSP):MobileNetV3-Small或ShuffleNet,牺牲精度保实时性

我的经验:不要盲目追求大模型。我曾经在一个项目中用ResNet-101做特征提取,精度确实高,但推理延迟超标,导致整个系统帧率掉到15fps。后来换成ResNet-34,精度只降了0.8%,但帧率稳定在30fps。ADAS是实时系统,帧率就是生命线。

4.5 小结

特征提取是ADAS感知的基石。ResNet用残差连接解决了深度网络的退化问题,MobileNet用深度可分离卷积实现了轻量化。两者各有千秋,选型时要根据实际算力和实时性要求来权衡。

下一章,我会讲如何把这些特征提取网络和检测头结合起来,构建完整的ADAS感知模型。到时候咱们再细聊。