bev-project/archive/docs_old/BEVFusion_Batch机制分析_2025110...

6.8 KiB
Raw Blame History

BEVFusion Batch Size机制深度分析

分析时间: 2025-11-02 12:30 UTC
目的: 理解BEVFusion的batch处理机制寻找可优化的batch维度


🔍 核心发现

数据输入结构

一个样本sample的组成:

1个样本 = 6个相机视图 + 1个LiDAR点云
├─ Camera: 6个视图的图像 (N=6)
│   ├─ CAM_FRONT
│   ├─ CAM_FRONT_RIGHT  
│   ├─ CAM_FRONT_LEFT
│   ├─ CAM_BACK
│   ├─ CAM_BACK_LEFT
│   └─ CAM_BACK_RIGHT
└─ LiDAR: 1个点云数据

Batch维度分析

从代码中发现的batch处理流程:

1. Forward函数签名

def forward(
    self,
    img,              # Shape: (B, N, C, H, W) - B=batch, N=6 cameras
    points,           # List[Tensor] - length B, 每个是点云
    camera2ego,       # Shape: (B, N, 4, 4)
    lidar2ego,        # Shape: (B, 4, 4)
    ...
):

关键维度:

  • B = batch_size (samples_per_gpu)
  • N = 6 (相机数量,固定)
  • 多个样本在最外层batch维度处理

2. Features处理

# Line 295-325
features = []
for sensor in ["camera", "lidar"]:
    if sensor == "camera":
        feature = self.extract_camera_features(...)  # 输出: (B, C, H, W)
    elif sensor == "lidar":
        feature = self.extract_features(...)         # 输出: (B, C, H, W)
    features.append(feature)

# features = [camera_feat, lidar_feat]
# 两个元素每个shape都是 (B, C, H, W)

重要: len(features) 不是batch size

  • len(features) = 2 (camera + lidar)
  • features[0].shape[0] = B (真正的batch size)

3. Fuser处理

# Line 331-335
if self.fuser is not None:
    x = self.fuser(features)  # 融合camera和lidar特征
else:
    assert len(features) == 1, features  # ← 这就是为什么不支持没有fuser的多模态
    x = features[0]

batch_size = x.shape[0]  # ← 真正的batch size在这里

📊 Batch Size的真实含义

当前配置 (samples_per_gpu=1)

GPU 0: 处理1个sample
├─ img: (1, 6, 3, 256, 704)      # 1个样本6个相机
├─ points: List[1个点云]
└─ 输出: camera_feat (1, 80, H, W)
         lidar_feat (1, 256, H, W)
         融合后: (1, 336, H, W)

如果 samples_per_gpu=2

GPU 0: 处理2个samples
├─ img: (2, 6, 3, 256, 704)      # 2个样本每个6个相机
├─ points: List[2个点云]
└─ 输出: camera_feat (2, 80, H, W)
         lidar_feat (2, 256, H, W)
         融合后: (2, 336, H, W)

关键点:

  • Batch维度是在样本级别,不是相机级别
  • 每个样本包含6个相机是固定的
  • 增加batch就是同时处理多个样本每个样本都是6cam+1lidar

🚨 为什么Batch=2会失败

错误原因分析

断言失败的代码:

assert len(features) == 1, features

这个断言的真实含义:

  • 要求只有1个模态特征camera或lidar其一
  • 但实际BEVFusion配置是camera+lidar两个模态
  • 所以 len(features) = 2

与batch size无关

  • 不管batch=1还是batch=2只要同时用camera+lidar就会有2个features
  • 断言失败是因为配置了fuser但代码走到了else分支

真实问题

检查fuser配置:

# configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/convfuser.yaml
fuser:
  type: ConvFuser
  in_channels: [80, 256]  # camera: 80, lidar: 256
  out_channels: 256

如果fuser正常应该走这个分支:

if self.fuser is not None:
    x = self.fuser(features)  # ← 应该走这里

结论: 断言失败说明 self.fuser is None


🔧 可能的原因

1. 配置加载问题

检查fuser是否正确初始化:

# mmdet3d/models/fusion_models/bevfusion.py: Line 58-61
if fuser is not None:
    self.fuser = build_fuser(fuser)
else:
    self.fuser = None

2. 配置文件继承问题

# multitask_BEV2X_phase4a_stage1_fp16.yaml
_base_: ./multitask_BEV2X_phase4a_stage1.yaml

# 可能没有继承fuser配置

解决方案建议

方案1: 检查fuser配置推荐

# 检查配置文件中是否包含fuser
grep -r "fuser" configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/

# 检查实际加载的配置
python -c "
from mmcv import Config
cfg = Config.fromfile('configs/.../multitask_BEV2X_phase4a_stage1.yaml')
print('Fuser config:', cfg.model.get('fuser', 'NOT FOUND'))
"

方案2: 显式添加fuser配置

# multitask_BEV2X_phase4a_stage1_fp16.yaml

model:
  fuser:
    type: ConvFuser
    in_channels: [80, 256]  # camera + lidar
    out_channels: 256

方案3: 使用单模态(不推荐)

如果真的要单模态可以禁用一个encoder:

model:
  encoders:
    camera: null  # 只用lidar
    # 或
    lidar: null   # 只用camera

📈 Batch Size优化潜力

理论上batch=2是可行的

如果fuser配置正确:

Batch=1:
  camera_feat: (1, 80, H, W)
  lidar_feat: (1, 256, H, W)
  fuser输出: (1, 256, H, W)
  
Batch=2:
  camera_feat: (2, 80, H, W)
  lidar_feat: (2, 256, H, W)
  fuser输出: (2, 256, H, W)  ← 应该没问题

Fuser的ConvFuser实现:

# 卷积操作对batch维度是透明的
# Conv2d会自动处理 (B, C, H, W)

预期性能如果batch=2可行

配置 显存 速度 完成时间
FP32 Batch=1 29GB 2.65s/iter 9天
FP16 Batch=1 19GB 1.9s/iter 6.5天
FP16 Batch=2 ~25GB ~1.5s/iter 5天

🎯 下一步行动

立即检查

  1. 验证fuser配置
cd /workspace/bevfusion
python -c "
from mmcv import Config
cfg = Config.fromfile('configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/multitask_BEV2X_phase4a_stage1.yaml')
print('Model keys:', cfg.model.keys())
print('Fuser:', cfg.model.get('fuser', 'NOT FOUND'))
"
  1. 检查基础配置文件
cat configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/convfuser.yaml | grep -A 5 "fuser"
  1. 检查配置继承链
grep "_base_" configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/*.yaml

如果fuser存在但batch=2仍失败

可能的其他限制:

  1. DataLoader的collate_fn
  2. 某些head不支持batch>1
  3. 内存分配问题

需要进一步调试。


💡 结论

核心理解:

  1. Features是多模态列表camera+lidar不是batch
  2. Batch size是样本维度每个样本=6cam+1lidar
  3. 理论上batch=2应该可行
  4. 断言失败可能是fuser配置问题不是batch size问题

建议:

  1. 先验证fuser配置是否正确加载
  2. 如果fuser正常batch=2理论上应该可以工作
  3. 现阶段保守使用FP16 batch=1 (已经有30%加速)

状态: 分析完成需要验证fuser配置
优先级: 中FP16已有显著效果batch=2是锦上添花