6.8 KiB
6.8 KiB
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天 ⭐ |
🎯 下一步行动
立即检查
- 验证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'))
"
- 检查基础配置文件
cat configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/convfuser.yaml | grep -A 5 "fuser"
- 检查配置继承链
grep "_base_" configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/*.yaml
如果fuser存在但batch=2仍失败
可能的其他限制:
- DataLoader的collate_fn
- 某些head不支持batch>1
- 内存分配问题
需要进一步调试。
💡 结论
核心理解:
- ✅ Features是多模态列表(camera+lidar),不是batch
- ✅ Batch size是样本维度,每个样本=6cam+1lidar
- ✅ 理论上batch=2应该可行
- ❌ 断言失败可能是fuser配置问题,不是batch size问题
建议:
- 先验证fuser配置是否正确加载
- 如果fuser正常,batch=2理论上应该可以工作
- 现阶段保守使用FP16 batch=1 (已经有30%加速)
状态: 分析完成,需要验证fuser配置
优先级: 中(FP16已有显著效果,batch=2是锦上添花)