4、早期融合(输入级融合):特征拼接、通道叠加、像素级融合的实现方法与代码示例

在Edge AI的多传感器融合体系中,早期融合(Early Fusion)是最直观、也最符合人类感知直觉的一种策略。它发生在数据进入神经网络主干网络之前,将来自不同模态(如摄像头、毫米波雷达、激光雷达、IMU)的原始数据或浅层特征,在输入空间或特征图的早期阶段进行对齐与合并。这种融合方式的核心逻辑是:让模型在最早阶段就同时“看到”所有模态的信息,从而学习到跨模态的联合特征表示。本章将深入剖析早期融合的三种典型实现形态——特征拼接、通道叠加与像素级融合,并给出可直接运行的代码示例与工程注意事项。

早期融合(输入级融合)三种实现路径 摄像头 RGB (H, W, 3) 毫米波雷达 (H, W, 2) 距离+速度 激光雷达 (H, W, 1) 深度 方式A:特征拼接 torch.cat(dim=1) 输出通道 = C1+C2+C3 方式B:通道叠加 torch.stack(dim=1) 新增维度,保留模态身份 方式C:像素级融合 加权求和 / 归一化 输出通道 = 1(或保持) 统一特征图 (B, C_fused, H, W) 输入 Backbone 网络 进行特征提取 检测/分割 /分类头 关键差异 特征拼接:保留各模态独立通道,维度增加 | 通道叠加:新增维度,保留模态身份 | 像素级融合:空间对齐后加权合并,维度不变 共同前提:所有模态必须完成空间对齐(resize/仿射变换)与时间同步

4.1 核心原理:为什么在输入阶段融合?

早期融合的核心动机是利用原始数据的互补性。以自动驾驶场景为例,摄像头提供丰富的纹理和颜色信息,但受光照影响大;毫米波雷达对金属目标敏感且能直接测量速度,但分辨率低;激光雷达提供精确的深度信息,但在雨雪天气性能衰减。如果将这些数据在输入层就拼接在一起,神经网络的第一层卷积就能同时提取跨模态的联合特征,例如“红色的、快速接近的、距离30米的物体”。这种融合方式在理论上具有最强的信息保留能力,因为没有任何中间处理环节丢失原始信息。

从数学角度看,早期融合可以表示为:X_fused = F(X_camera, X_radar, X_lidar),其中 F 是拼接、叠加或加权操作。融合后的张量 X_fused 的维度为 (B, C_total, H, W),其中 C_total 是各模态通道数之和(拼接)或保持原通道数(像素级融合)。值得注意的是,早期融合要求所有输入必须具有相同的空间分辨率 H 和 W,这通常需要预处理阶段进行上采样或下采样对齐。

适用场景判断:早期融合最适合以下情况——①各模态传感器已经完成标定,空间对齐精度高;②输入数据本身噪声较小,不需要复杂的预处理;③模型结构相对简单,希望避免后期融合带来的多分支设计复杂度。如果传感器之间存在较大的视角差异或时间延迟,早期融合的效果会大打折扣。

4.2 三种实现方法详解与代码示例

4.2.1 特征拼接(Feature Concatenation)

特征拼接是最常用的早期融合方式。它将各模态的特征图在通道维度上直接连接,形成一个新的高维特征图。在PyTorch中,使用 torch.cat() 函数实现。假设我们有一个RGB图像 (B, 3, H, W)、一个雷达距离图 (B, 2, H, W) 和一个激光雷达深度图 (B, 1, H, W),拼接后的特征图为 (B, 6, H, W)。

import torch
import torch.nn as nn

class EarlyFusionConcat(nn.Module):
    def __init__(self, in_channels_list, out_channels):
        """
        in_channels_list: 各模态的通道数列表,如 [3, 2, 1]
        out_channels: 融合后经过卷积输出的通道数
        """
        super().__init__()
        total_channels = sum(in_channels_list)
        # 拼接后接一个1x1卷积,用于通道压缩和特征交互
        self.conv = nn.Conv2d(total_channels, out_channels, kernel_size=1)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    
    def forward(self, camera, radar, lidar):
        # 假设输入已经完成空间对齐,形状均为 (B, C, H, W)
        fused = torch.cat([camera, radar, lidar], dim=1)  # (B, 6, H, W)
        out = self.conv(fused)
        out = self.bn(out)
        out = self.relu(out)
        return out

# 使用示例
model = EarlyFusionConcat([3, 2, 1], out_channels=32)
camera_input = torch.randn(2, 3, 224, 224)
radar_input = torch.randn(2, 2, 224, 224)
lidar_input = torch.randn(2, 1, 224, 224)
output = model(camera_input, radar_input, lidar_input)
print(f"拼接融合输出形状: {output.shape}")  # (2, 32, 224, 224)

拼接方式的优点是实现简单、信息无损,缺点是通道数增加导致计算量上升,且模型需要自行学习各模态的权重关系。在实际工程中,通常会在拼接后紧跟一个1×1卷积或3×3卷积,以实现跨通道的信息混合和降维。

4.2.2 通道叠加(Channel Stacking)

通道叠加与特征拼接类似,但区别在于它新增了一个维度来区分模态,而不是简单地在通道维度上合并。在PyTorch中,使用 torch.stack() 函数。这种方式保留了模态的身份信息,使得后续网络可以显式地知道每个通道来自哪个传感器。

import torch
import torch.nn as nn

class EarlyFusionStack(nn.Module):
    def __init__(self, num_modalities, in_channels, out_channels):
        """
        num_modalities: 模态数量,如 3
        in_channels: 每个模态的通道数(假设各模态相同)
        out_channels: 输出通道数
        """
        super().__init__()
        # 输入形状变为 (B, num_modalities, C, H, W)
        # 使用3D卷积或reshape后接2D卷积
        self.conv3d = nn.Conv3d(in_channels, out_channels, kernel_size=(1, 3, 3), padding=(0, 1, 1))
        self.bn = nn.BatchNorm3d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    
    def forward(self, modalities):
        """
        modalities: 一个张量列表,每个元素形状为 (B, C, H, W)
        """
        # 在dim=1处堆叠,形成 (B, num_modalities, C, H, W)
        stacked = torch.stack(modalities, dim=1)
        # 调整维度顺序为 (B, C, num_modalities, H, W) 以适配Conv3d
        # Conv3d 期望输入为 (B, C_in, D, H, W)
        stacked = stacked.permute(0, 2, 1, 3, 4)
        out = self.conv3d(stacked)
        out = self.bn(out)
        out = self.relu(out)
        # 压缩模态维度,输出 (B, out_channels, H, W)
        out = out.squeeze(2)
        return out

# 使用示例
model = EarlyFusionStack(num_modalities=3, in_channels=3, out_channels=32)
mods = [torch.randn(2, 3, 224, 224) for _ in range(3)]
output = model(mods)
print(f"通道叠加融合输出形状: {output.shape}")  # (2, 32, 224, 224)

通道叠加的优点是显式保留了模态维度,便于模型学习模态间的相对重要性;缺点是引入了3D卷积,计算开销更大,且对内存占用较高。在Edge AI设备上,如果算力有限,建议优先考虑特征拼接。

4.2.3 像素级融合(Pixel-level Fusion)

像素级融合是在空间维度上对每个像素位置进行加权合并。这种方式不增加通道数,而是将多模态数据在像素级别“混合”成一个新的单通道或多通道特征图。常见的操作包括加权平均、最大值融合或基于注意力机制的动态加权。

import torch
import torch.nn as nn
import torch.nn.functional as F

class PixelLevelFusion(nn.Module):
    def __init__(self, in_channels, out_channels):
        """
        in_channels: 每个模态的通道数
        out_channels: 融合后的输出通道数
        """
        super().__init__()
        # 学习每个模态的权重(可学习的标量或通道级权重)
        self.weights = nn.Parameter(torch.ones(3) / 3)  # 假设3个模态
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    
    def forward(self, camera, radar, lidar):
        # 对权重进行softmax归一化,确保和为1
        w = F.softmax(self.weights, dim=0)
        # 像素级加权求和
        fused = w[0] * camera + w[1] * radar + w[2] * lidar
        out = self.conv(fused)
        out = self.bn(out)
        out = self.relu(out)
        return out

# 使用示例
model = PixelLevelFusion(in_channels=3, out_channels=32)
camera_input = torch.randn(2, 3, 224, 224)
radar_input = torch.randn(2, 3, 224, 224)  # 注意:像素级融合要求各模态通道数一致
lidar_input = torch.randn(2, 3, 224, 224)
output = model(camera_input, radar_input, lidar_input)
print(f"像素级融合输出形状: {output.shape}")  # (2, 32, 224, 224)

像素级融合的优点是计算效率高、参数少,适合资源受限的Edge AI设备;缺点是要求各模态数据已经高度对齐,且简单的加权平均可能无法充分挖掘跨模态的复杂关系。更高级的像素级融合会引入注意力机制,根据像素位置动态调整权重。

4.3 案例拆解:智能座舱驾驶员监控系统(DMS)

我们以一个实际的Edge AI项目——驾驶员疲劳检测系统为例,说明早期融合的工程实现。该系统部署在车载边缘计算单元(如NVIDIA Jetson Orin)上,需要实时分析驾驶员状态。

传感器配置与数据规格:

  • 红外摄像头:输出灰度图像,分辨率 640×480,通道数1,用于捕捉驾驶员面部表情和眼睛状态。
  • 毫米波雷达:输出微动特征图,分辨率 64×64,通道数2(距离变化率和微多普勒能量),用于检测呼吸和心跳引起的微小胸腔运动。
  • IMU(惯性测量单元):输出头部姿态角,经过投影后生成 64×64 的2通道特征图(俯仰角和横滚角),用于判断头部是否异常倾斜。

初始现象与目标:单一摄像头在黑暗环境下效果差,单一雷达无法区分具体面部动作。目标是通过早期融合,在输入阶段将三种数据合并,提升疲劳检测的准确率至95%以上,同时保证在Jetson Orin上的推理延迟低于15ms。

关键操作步骤:

  1. 空间对齐:将雷达和IMU特征图通过双线性插值上采样至 640×480,与摄像头分辨率一致。使用仿射变换将IMU姿态角投影到图像坐标系。
  2. 时间同步:采用最近邻时间戳匹配,确保三种数据的时间差小于10ms。
  3. 融合实现:选择特征拼接方式,将1通道红外图像、2通道雷达特征、2通道IMU特征拼接为5通道输入。在拼接后使用一个步长为2的3×3卷积进行降采样,输出16通道特征图,送入MobileNetV3主干网络。
  4. 模型量化:使用INT8量化将模型从FP32的120MB压缩至15MB,推理延迟从28ms降至11ms。

结果对比与复盘:

方案 准确率 夜间表现 推理延迟 内存占用
仅摄像头 82% 差(光照依赖) 8ms 8MB
仅雷达 71% 优 5ms 5MB
早期融合(拼接) 96% 优 11ms 15MB

复盘发现,早期融合的收益主要来自雷达对呼吸信号的补充,使得模型能够识别“微睡眠”状态下的呼吸频率变化。但代价是内存占用增加了近一倍,且需要额外的预处理时间。经验是:在算力允许的情况下,早期融合的收益通常大于后期融合,但必须做好数据对齐和同步。

4.4 常见误区与工程建议

误区一:忽略空间对齐的精度。很多初学者直接将不同分辨率的传感器数据拼接,导致模型学习到错误的空间对应关系。正确做法是使用标定参数进行投影变换,并在拼接前检查对齐误差(通常应小于2个像素)。
误区二:盲目增加模态数量。并非模态越多越好。当某个模态数据质量差或与任务相关性低时,早期融合反而会引入噪声。建议先单独评估每个模态的基线性能,再决定是否纳入融合。
经验建议:①在Edge AI设备上,优先使用特征拼接而非通道叠加,因为3D卷积在嵌入式GPU上的优化不成熟;②在拼接后加入1×1卷积进行通道压缩,可以有效降低计算量;③使用可学习的权重(如注意力机制)替代简单的固定权重,可以提升融合的鲁棒性;④务必在目标硬件上测试融合后的内存带宽,因为高通道数可能导致内存访问成为瓶颈。