bev-project/project/docs/BEV分辨率提升方案分析.md

17 KiB
Raw Blame History

BEV分辨率提升方案分析

问题: 如何提升BEVFusion的分辨率输入图像2倍 还是 BEV特征2倍

当前时间: 2025-10-25
当前配置: multitask_enhanced_phase1_HIGHRES.yaml


📊 当前分辨率配置

现状

输入图像分辨率:
  image_size: [256, 704]  # H × W
   经过SwinT backbone: [32, 88] (下采样8倍)
  
BEV特征分辨率:
  xbound: [-54.0, 54.0, 0.3]  # 108m / 0.3m = 360 grids
  ybound: [-54.0, 54.0, 0.3]  # 108m / 0.3m = 360 grids
   Camera BEV: (1, 80, 360, 360)
   LiDAR BEV:  (1, 256, 360, 360)
   Fused BEV:  (1, 256, 360, 360)

BEV输出分辨率:
  output_scope: [-50, 50, 0.5]  # 100m / 0.5m = 200 grids
   分割输出: (1, 6, 200, 200)

问题诊断

从Epoch 10评估结果看小目标分割效果差

  • Stop Line IoU: 0.24 ⚠️
  • Divider IoU: 0.17 ⚠️

根本原因: 分辨率不足0.5m/grid无法精确表达细线(宽度<0.3m)


🎯 方案对比

方案A: 提升输入图像分辨率

改动: 256×704 → 512×1408 (2倍)

优点:

✅ 图像细节更丰富
✅ 小目标识别能力增强
✅ 远距离检测更准确
✅ 实现简单,只需修改配置

缺点:

❌ 计算量大幅增加
   - Backbone FLOPs: ×4 (H×W)
   - 内存占用: ×4
   - 训练时间: ×3-4
   
❌ GPU显存压力
   - 当前: 256×704 × 6相机 ≈ 19GB
   - 提升后: 512×1408 × 6相机 ≈ 76GB ⚠️ 超过V100 32GB!
   
❌ BEV特征分辨率不变
   - 最终还是360×360 BEV
   - 细节可能在view transform丢失

计算量分析:

# SwinTransformer计算量
输入: (B, N, 3, H, W)
FLOPs  H × W × (patches数量)

256×704:  FLOPs_base
512×1408: FLOPs_base × 4  ⚠️

训练速度:
  当前: 2.7/iter
  预估: 10-12/iter (慢4倍)

显存分析:

# 显存占用主要来源
1. 图像: B × N × 3 × H × W × 4 bytes
   256×704: 1 × 6 × 3 × 256 × 704 × 4 = 8.6 MB
   512×1408: 1 × 6 × 3 × 512 × 1408 × 4 = 34.4 MB (+25.8MB)

2. Backbone中间特征 (多层)
   256×704: ~5GB
   512×1408: ~20GB (+15GB) ⚠️

3. BEV特征
   不变: ~8GB

4. 梯度+优化器
   翻倍: +15GB

总计:
  256×704:  ~19GB  当前
  512×1408: ~50GB  超显存!

结论: 不推荐显存不足需要减小batch size或使用gradient checkpointing


方案B: 提升BEV特征分辨率 (推荐)

改动: 360×360 (0.3m) → 720×720 (0.15m) (2倍)

优点:

✅ 直接提升BEV分辨率
✅ 更精确的空间表达 (0.5m → 0.25m)
✅ 小目标分割大幅改善
✅ 计算量增加可控 (主要在BEV阶段)
✅ 显存增加适中 (约+8GB)
✅ 可以在当前硬件上训练

缺点:

⚠️ 训练速度略慢 (约1.5-2倍)
⚠️ 需要调整多处配置
⚠️ LiDAR编码器可能需要调整

配置修改:

# 方案B配置
model:
  encoders:
    camera:
      vtransform:
        image_size: [256, 704]  # 保持不变
        feature_size: [32, 88]  # 保持不变
        xbound: [-54.0, 54.0, 0.15]  # 0.3 → 0.15 (2倍)
        ybound: [-54.0, 54.0, 0.15]  # 0.3 → 0.15 (2倍)
        # BEV输出: 720×720
    
    lidar:
      voxelize:
        voxel_size: [0.075, 0.075, 0.2]  # 保持不变
        # 或者也提升: [0.0375, 0.0375, 0.2]
      backbone:
        sparse_shape: [1440, 1440, 41]  # 保持
        # 或提升到: [2880, 2880, 41]
  
  heads:
    map:
      grid_transform:
        input_scope: [[-54.0, 54.0, 0.15], [-54.0, 54.0, 0.15]]  # 720×720
        output_scope: [[-50, 50, 0.25], [-50, 50, 0.25]]  # 200→400

计算量分析:

# BEV阶段计算量
当前 360×360:
  - Camera BEV pooling: ~2GB显存
  - Fuser: 360×360 卷积
  - Decoder: 360×360  180×180

提升 720×720:
  - Camera BEV pooling: ~8GB显存 (+6GB)
  - Fuser: 720×720 卷积 (4倍FLOPs)
  - Decoder: 720×720  360×360 (4倍FLOPs)

总增加: +8GB显存, 2倍训练时间

显存占用:

当前配置:
  图像: 8.6 MB
  Backbone特征: 5 GB
  BEV特征 (360×360): 8 GB
  Decoder: 3 GB
  其他: 2 GB
  总计: ~19 GB 

方案B (720×720):
  图像: 8.6 MB (不变)
  Backbone特征: 5 GB (不变)
  BEV特征 (720×720): 16 GB (+8GB)
  Decoder: 6 GB (+3GB)
  其他: 2 GB
  总计: ~28 GB  可行!

性能提升预估:

Stop Line IoU: 0.24 → 0.40 (+67%)
Divider IoU: 0.17 → 0.30 (+76%)
mIoU: 0.39 → 0.48 (+23%)

方案C: 混合方案 (折中)

改动:

  • 输入图像: 256×704 → 384×1056 (1.5倍)
  • BEV特征: 360×360 (0.3m) → 540×540 (0.2m) (1.5倍)
  • BEV输出: 200×200 (0.5m) → 300×300 (0.33m) (1.5倍)

优点:

✅ 平衡输入和BEV分辨率
✅ 性能提升明显
✅ 计算量增加温和 (约2.5倍)
✅ 显存可控 (~25GB)

配置:

# 变量定义
image_size: [384, 1056]  # 1.5倍

model:
  encoders:
    camera:
      vtransform:
        xbound: [-54.0, 54.0, 0.2]  # 540 grids
        ybound: [-54.0, 54.0, 0.2]
  
  heads:
    map:
      grid_transform:
        output_scope: [[-50, 50, 0.33], [-50, 50, 0.33]]  # 303×303

📐 详细技术分析

影响链路

输入分辨率的影响

图像分辨率 ↑
  ↓ (通过Backbone)
特征图细节 ↑
  ↓ (通过DepthNet)
深度估计精度 ↑
  ↓ (BEV Pooling)
BEV特征质量 ↑ (但尺寸受xbound/ybound限制)
  ↓
最终精度 ↑ (有限)

关键瓶颈: BEV Pooling时会被resample到固定的360×360输入细节可能丢失

BEV分辨率的影响

BEV grid分辨率 ↑ (0.3m → 0.15m)
  ↓
空间表达能力 ↑ (直接)
  ↓
小目标分割能力 ↑ (显著)
  ↓
检测精度 ↑ (中心点定位更准)
  ↓
最终精度 ↑ (显著)

优势: 直接作用于空间表达,效果立竿见影

为什么BEV分辨率更关键

1. 分割任务的本质

分割任务关心的是"空间位置",而非"图像细节"

例子:
  车道线宽度: 0.15m
  
  0.5m分辨率: 车道线仅占1个grid  ← 模糊
  0.25m分辨率: 车道线占2-3个grid ← 清晰
  0.15m分辨率: 车道线占4-5个grid ← 精确

2. 信息传递路径

方案A (提升输入):
  图像细节 ↑ → Backbone → ... → BEV Pooling → 360×360 (瓶颈) → 输出

方案B (提升BEV):
  图像细节 → Backbone → ... → BEV Pooling → 720×720 (直接提升) → 输出
                                              ^^^^^^^^
                                              关键!

3. 实验证据

论文BEVFusion原文:
- 提升图像分辨率: NDS +1-2%
- 提升BEV分辨率: mIoU +5-10%

特别对于分割任务BEV分辨率是核心!

🎯 推荐方案

优先推荐: 方案B (提升BEV分辨率)

阶段1: BEV 2倍 (立即可做)

# configs/vehicle_4cam/high_resolution_bev.yaml

model:
  encoders:
    camera:
      vtransform:
        image_size: [256, 704]  # 保持不变
        xbound: [-54.0, 54.0, 0.15]  # 0.3→0.15, 720 grids
        ybound: [-54.0, 54.0, 0.15]
        downsample: 1  # 从2改为1
  
  heads:
    map:
      grid_transform:
        output_scope: [[-50, 50, 0.25], [-50, 50, 0.25]]  # 400×400

数据pipeline:
  LoadBEVSegmentation:
    xbound: [-50.0, 50.0, 0.25]  # GT也要400×400
    ybound: [-50.0, 50.0, 0.25]

预期效果:

✅ Stop Line IoU: 0.24 → 0.38 (+58%)
✅ Divider IoU: 0.17 → 0.28 (+65%)
✅ mIoU: 0.39 → 0.47 (+20%)
✅ NDS: 0.697 → 0.705 (+1%)

计算成本:
  显存: 19GB → 27GB (+8GB) ✅ 可行
  速度: 2.7s/iter → 4.5s/iter (慢1.7倍)
  训练时间: 4天 → 7天

实施步骤:

# 1. 修改配置
cp configs/.../multitask_enhanced_phase1_HIGHRES.yaml \
   configs/.../multitask_enhanced_phase1_HIGHRES_BEV2X.yaml

# 编辑xbound/ybound为0.15

# 2. 从当前checkpoint继续训练
torchpack dist-run -np 6 python tools/train.py \
    configs/.../multitask_enhanced_phase1_HIGHRES_BEV2X.yaml \
    --load_from runs/enhanced_from_epoch19/epoch_10.pth \
    --run-dir runs/highres_bev_2x

# 3. 训练5-10 epochs微调适应新分辨率

次优方案: 方案C (混合1.5倍)

改动: 图像384×1056, BEV 540×540 (0.2m)

适用场景: 如果显存紧张

image_size: [384, 1056]  # 1.5倍

model:
  encoders:
    camera:
      vtransform:
        xbound: [-54.0, 54.0, 0.2]  # 540 grids
        ybound: [-54.0, 54.0, 0.2]
  
  heads:
    map:
      grid_transform:
        output_scope: [[-50, 50, 0.33], [-50, 50, 0.33]]  # 303×303

预期效果:

Stop Line IoU: 0.24 → 0.32 (+33%)
Divider IoU: 0.17 → 0.23 (+35%)
mIoU: 0.39 → 0.44 (+13%)

计算成本:
  显存: 19GB → 23GB
  速度: 2.7s/iter → 3.8s/iter

不推荐: 方案A (仅提升输入2倍)

原因:

❌ 显存不足 (需要76GB)
❌ 训练时间过长 (×4)
❌ BEV瓶颈未解决
❌ 性价比低

如果坚持:

需要的妥协:
1. 减小batch size: 2 → 1 (samples_per_gpu)
2. 使用gradient checkpointing (节省50%显存慢20%)
3. 减少相机数量: 6 → 4
4. 混合精度训练: FP32 → FP16

仍然可能不够,不建议

💡 最佳实践建议

推荐路径

短期 (Phase 3完成后立即):

✅ 优先提升BEV分辨率 (方案B)
   配置: xbound/ybound 0.3 → 0.15
   效果: mIoU +20%
   成本: 显存+8GB, 时间×1.7

中期 (Phase 6实车微调):

✅ 根据实车相机分辨率调整输入
   实车: 1920×1080
   训练: 384×1056 (1.5倍提升,可接受)
   或: 320×880 (保守)

长期 (Phase 7部署):

✅ 部署时可以降低分辨率节省计算
   训练: BEV 720×720
   部署: BEV 540×540 或 360×360 (动态调整)

实施建议

立即可做 (Phase 3完成后):

实验1: BEV 2倍
  配置: xbound 0.3 → 0.15
  训练: 5 epochs
  评估: 重点看小目标IoU
  预算: 1-2天

如果效果好 → 继续训练15 epochs
如果显存不足 → 降为1.5倍 (0.2m)

实车部署时:

实验2: 输入1.5倍
  配置: image_size [256,704] → [384,1056]
  原因: 实车相机1920×1080更高
  训练: 实车数据10 epochs
  预算: 2-3天

📊 性能 vs 成本对比

方案 输入分辨率 BEV分辨率 输出分辨率 显存 速度 mIoU提升 推荐度
当前 256×704 360×360 (0.3m) 200×200 (0.5m) 19GB 2.7s - -
A: 输入2倍 512×1408 360×360 200×200 50GB 10s +5%
B: BEV 2倍 256×704 720×720 (0.15m) 400×400 (0.25m) 27GB 4.5s +20%
C: 混合1.5倍 384×1056 540×540 (0.2m) 300×300 (0.33m) 23GB 3.8s +13%

结论: 方案B最优


🚀 实施计划

立即行动 (10月30日)

# Step 1: 创建高分辨率BEV配置
cat > configs/nuscenes/det/transfusion/secfpn/camera+lidar/swint_v0p075/multitask_enhanced_HIGHRES_BEV2X.yaml << 'CONFIG'
_base_: ./multitask_enhanced_phase1_HIGHRES.yaml

# 覆盖BEV分辨率设置
model:
  encoders:
    camera:
      vtransform:
        xbound: [-54.0, 54.0, 0.15]  # 720 grids
        ybound: [-54.0, 54.0, 0.15]
        downsample: 1  # 关键: 不要额外下采样
    
    lidar:
      voxelize:
        voxel_size: [0.0375, 0.0375, 0.2]  # LiDAR也提升
      backbone:
        sparse_shape: [2880, 2880, 41]  # 对应调整
  
  heads:
    map:
      grid_transform:
        input_scope: [[-54.0, 54.0, 0.15], [-54.0, 54.0, 0.15]]
        output_scope: [[-50, 50, 0.25], [-50, 50, 0.25]]  # 400×400

# 数据pipeline也要调整GT分辨率
train_pipeline:
  - type: LoadBEVSegmentation
    xbound: [-50.0, 50.0, 0.25]  # 400×400
    ybound: [-50.0, 50.0, 0.25]
    classes: ${map_classes}

val_pipeline:
  - type: LoadBEVSegmentation
    xbound: [-50.0, 50.0, 0.25]
    ybound: [-50.0, 50.0, 0.25]
    classes: ${map_classes}
CONFIG

# Step 2: 快速验证实验 (5 epochs)
torchpack dist-run -np 6 python tools/train.py \
    configs/.../multitask_enhanced_HIGHRES_BEV2X.yaml \
    --load_from runs/enhanced_from_epoch19/epoch_10.pth \
    --train.max_epochs=5 \
    --run-dir runs/highres_bev_2x_test

# Step 3: 观察显存和性能
# 如果显存OK且性能提升明显 → 完整训练20 epochs

保守方案 (如果显存不足)

# 降为1.5倍
# xbound: 0.2m → 540×540
# 显存预计: 23GB ✅ 更安全

⚠️ 注意事项

1. 训练策略调整

# 更高分辨率需要更长的warmup
lr:
  warmup_iters: 1000  # 从500增加
  max_lr: 0.0002  # 可能需要微调

# batch size可能需要减小
data:
  samples_per_gpu: 1  # 从2减到1 (如果显存不足)

2. 数据增强调整

# 更高分辨率可以使用更强的增强
augment2d:
  resize: [[0.35, 0.55], [0.48, 0.48]]  # 略微收紧
  rotate: [-8, 8]  # 可以略微增大

3. LiDAR分辨率匹配

# 如果Camera BEV提升到720×720 (0.15m)
# LiDAR最好也匹配否则融合时会有分辨率不一致

选项1: LiDAR也提升到0.15m
  voxel_size: [0.0375, 0.0375, 0.2]  # 从0.075减半
  sparse_shape: [2880, 2880, 41]     # 从1440翻倍
  显存增加: +3GB

选项2: 保持LiDAR分辨率融合时插值
  Camera BEV 720×720  插值到360×360  与LiDAR融合
  损失一些细节但节省显存

💻 代码示例

动态分辨率配置

# tools/train_adaptive_resolution.py

def train_with_adaptive_resolution(
    base_config,
    bev_resolution=0.15,  # BEV grid size in meters
    image_scale=1.0,      # 输入图像缩放倍数
):
    """
    自适应分辨率训练
    
    Args:
        bev_resolution: BEV网格分辨率 (米)
            - 0.3: 当前 (360×360)
            - 0.2: 1.5倍 (540×540)
            - 0.15: 2倍 (720×720)
        
        image_scale: 输入图像缩放
            - 1.0: 256×704
            - 1.5: 384×1056
            - 2.0: 512×1408
    """
    
    # 计算BEV grid数量
    bev_range = 108  # -54 to 54
    bev_grids = int(bev_range / bev_resolution)
    
    # 计算输出分辨率
    output_range = 100  # -50 to 50
    output_resolution = bev_resolution * 1.5  # 略粗一点
    output_grids = int(output_range / output_resolution)
    
    # 计算输入尺寸
    base_h, base_w = 256, 704
    input_h = int(base_h * image_scale)
    input_w = int(base_w * image_scale)
    
    print(f"配置:")
    print(f"  输入图像: {input_h}×{input_w}")
    print(f"  BEV特征: {bev_grids}×{bev_grids} ({bev_resolution}m/grid)")
    print(f"  输出分割: {output_grids}×{output_grids} ({output_resolution}m/grid)")
    
    # 估算显存
    image_mem = input_h * input_w * 6 * 3 * 4 / 1e9  # GB
    bev_mem = bev_grids * bev_grids * 256 * 4 / 1e9 * 4  # ×4 layers
    total_mem = 5 + image_mem + bev_mem + 10  # backbone + image + bev + other
    
    print(f"  预估显存: {total_mem:.1f} GB")
    
    if total_mem > 30:
        print(f"  ⚠️ 显存可能不足建议减小batch size")
    
    return {
        'image_size': [input_h, input_w],
        'bev_resolution': bev_resolution,
        'bev_grids': bev_grids,
        'output_resolution': output_resolution,
        'output_grids': output_grids,
    }


# 使用示例
config_2x = train_with_adaptive_resolution(
    base_config='multitask_enhanced_phase1_HIGHRES.yaml',
    bev_resolution=0.15,  # 2倍BEV
    image_scale=1.0       # 输入保持
)

config_1_5x = train_with_adaptive_resolution(
    base_config='multitask_enhanced_phase1_HIGHRES.yaml',
    bev_resolution=0.2,   # 1.5倍BEV
    image_scale=1.5       # 输入也1.5倍
)

📋 总结

核心结论

问题: 提升输入分辨率 vs BEV分辨率

答案: 优先提升BEV分辨率

原因:

  1. 直接提升空间表达能力
  2. 小目标分割立竿见影 (+20% mIoU)
  3. 显存可控 (27GB vs 50GB)
  4. 训练时间可接受 (×1.7 vs ×4)
  5. 适用于检测+分割双任务

实施时机:

  • 立即: Phase 3完成后 (10月30日)
  • 实验: 5 epochs快速验证
  • 全量: 如效果好训练20 epochs

配置推荐

最优配置:

输入图像: 256×704 (保持)
BEV特征: 720×720 (0.15m分辨率)
输出分割: 400×400 (0.25m分辨率)

预期: mIoU 0.39 → 0.47 (+20%)
成本: 显存27GB, 训练慢1.7倍

折中配置 (如显存紧张):

输入图像: 256×704 (保持)
BEV特征: 540×540 (0.2m分辨率)
输出分割: 300×300 (0.33m分辨率)

预期: mIoU 0.39 → 0.44 (+13%)
成本: 显存23GB, 训练慢1.5倍

下一步

Phase 3完成后 (10月30日):

  1. 创建高分辨率BEV配置文件
  2. 进行5 epochs快速实验
  3. 评估性能提升
  4. 决定是否全量训练

实车部署时 (2026年1月):

  • 可以根据Orin性能动态调整
  • 训练用高分辨率,部署时可降低
  • 精度与速度的权衡