18 KiB
18 KiB
BEV分割任务输入输出尺寸详细分析
配置: fusion-det-seg-swint-enhanced.yaml
任务: BEV Map Segmentation
类别数: 6类 (drivable_area, ped_crossing, walkway, stop_line, carpark_area, divider)
📐 完整数据流尺寸追踪
0. 原始输入数据
# 相机图像
images: (B, N, 3, H, W)
B = 1 (batch size, 单GPU)
N = 6 (相机数量)
H = 256 (图像高度)
W = 704 (图像宽度)
形状: (1, 6, 3, 256, 704)
# LiDAR点云
points: List[Tensor]
每个sample: (N_points, 5) # x, y, z, intensity, timestamp
范围: [-54m, 54m] × [-54m, 54m] × [-5m, 3m]
1. Camera Encoder输出 → BEV特征
1.1 Backbone (SwinTransformer)
Input: (1, 6, 3, 256, 704)
↓
SwinT Backbone (3个输出尺度)
├─ Stage1: (1, 6, 192, 64, 176) # H/4, W/4
├─ Stage2: (1, 6, 384, 32, 88) # H/8, W/8
└─ Stage3: (1, 6, 768, 16, 44) # H/16, W/16
1.2 Neck (GeneralizedLSSFPN)
Input: 3个尺度 [192, 384, 768]通道
↓
FPN处理
↓
Output: (1, 6, 256, 32, 88) # 统一到32×88尺寸,256通道
1.3 View Transform (DepthLSSTransform)
Input: (1, 6, 256, 32, 88)
↓
DepthNet: 256 → 199 (119 depth + 80 context)
↓
3D Volume: (1, 6, 80, 119, 32, 88)
↓
BEV Pooling (投影到BEV平面)
↓
Camera BEV: (1, 80, 360, 360) # 80通道
计算:
xbound: [-54, 54, 0.3] → 108m / 0.3m = 360 grids
ybound: [-54, 54, 0.3] → 108m / 0.3m = 360 grids
2. LiDAR Encoder输出 → BEV特征
2.1 Voxelization
Input: Points (N_points, 5)
↓
Voxelization
voxel_size: [0.075m, 0.075m, 0.2m]
point_range: [-54m, 54m] × [-54m, 54m] × [-5m, 3m]
↓
Voxels: (N_voxels, 10, 5) # 每voxel最多10个点
Sparse Shape:
X: 108m / 0.075m = 1440 grids
Y: 108m / 0.075m = 1440 grids
Z: 8m / 0.2m = 40 grids
→ (1440, 1440, 40)
2.2 Sparse Encoder
Input: Sparse Voxels (1440, 1440, 40)
↓
SparseEncoder (4个stage)
↓
Output: Dense BEV (1, 256, 360, 360)
计算:
Sparse (1440, 1440) → Dense (360, 360)
降采样倍数: 1440 / 360 = 4x
3. Fuser输出 → 融合BEV特征
Camera BEV: (1, 80, 360, 360)
LiDAR BEV: (1, 256, 360, 360)
↓
ConvFuser (Camera 80→256, 然后相加)
↓
Fused BEV: (1, 256, 360, 360)
4. Decoder输出 → 多尺度特征
4.1 SECOND Backbone
Input: (1, 256, 360, 360)
↓
SECOND (2个stage)
├─ Stage1: (1, 128, 360, 360) # stride=1
└─ Stage2: (1, 256, 180, 180) # stride=2, 降采样
4.2 SECONDFPN Neck
Input:
├─ Stage1: (1, 128, 360, 360)
└─ Stage2: (1, 256, 180, 180)
↓
FPN处理
├─ Feature1: (1, 256, 360, 360) # Stage1 → 256通道
└─ Feature2: (1, 256, 360, 360) # Stage2上采样2倍 → 256通道
↓
Concat
↓
Output: (1, 512, 360, 360) # 256×2 = 512通道
关键: Decoder输出是512通道,360×360空间尺寸
🎯 5. 分割头详细尺寸分析
配置参数
map:
in_channels: 512 # Decoder输出
grid_transform:
input_scope: [[-54.0, 54.0, 0.75], [-54.0, 54.0, 0.75]]
output_scope: [[-50, 50, 0.5], [-50, 50, 0.5]]
计算:
# Input scope计算
input_x_size = (54.0 - (-54.0)) / 0.75 = 108 / 0.75 = 144 grids
input_y_size = (54.0 - (-54.0)) / 0.75 = 108 / 0.75 = 144 grids
# Output scope计算
output_x_size = (50 - (-50)) / 0.5 = 100 / 0.5 = 200 grids
output_y_size = (50 - (-50)) / 0.5 = 100 / 0.5 = 200 grids
5.1 原始BEVSegmentationHead尺寸流
输入 (从Decoder):
Shape: (B, 512, 360, 360)
Size: 1 × 512 × 360 × 360
↓ Step 1: BEVGridTransform
# Grid Transform详细过程:
# 1. 从360×360 resample到144×144 (input_scope)
# 2. 生成grid坐标
# 范围: [-54, 54] → 144个grid点,步长0.75m
# 3. Grid sample插值到200×200 (output_scope)
# 范围: [-50, 50] → 200个grid点,步长0.5m
BEV Grid Transform:
Input: (B, 512, 360, 360)
Output: (B, 512, 200, 200)
说明:
- Decoder输出360×360,但分割只关注中心区域
- 从360×360裁剪/插值到200×200
- 空间范围从±54m缩小到±50m
↓ Step 2: Classifier Layer 1
Conv2d(512, 512, 3, padding=1) + BN + ReLU
Input: (B, 512, 200, 200)
Output: (B, 512, 200, 200)
↓ Step 3: Classifier Layer 2
Conv2d(512, 512, 3, padding=1) + BN + ReLU
Input: (B, 512, 200, 200)
Output: (B, 512, 200, 200)
↓ Step 4: Final Classifier
Conv2d(512, 6, 1) # 6类
Input: (B, 512, 200, 200)
Output: (B, 6, 200, 200) # Logits
↓ Step 5 (推理时): Sigmoid激活
torch.sigmoid(logits)
Output: (B, 6, 200, 200) # 概率值 [0, 1]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
最终输出:
Shape: (B, 6, 200, 200)
Batch: B = 1 (单GPU)
Classes: 6 (每个类别一个通道)
Spatial: 200 × 200 (空间分辨率)
Range: ±50m × ±50m (实际覆盖范围)
Resolution: 0.5m per grid (每格0.5米)
5.2 增强EnhancedBEVSegmentationHead尺寸流
输入 (从Decoder):
Shape: (B, 512, 360, 360)
↓ Step 1: BEV Grid Transform
BEVGridTransform:
Input: (B, 512, 360, 360)
Output: (B, 512, 200, 200)
↓ Step 2: ASPP 多尺度特征提取
ASPP (5个分支):
Branch 1 (1×1): (B, 512, 200, 200) → (B, 256, 200, 200)
Branch 2 (3×3@d6): (B, 512, 200, 200) → (B, 256, 200, 200)
Branch 3 (3×3@d12): (B, 512, 200, 200) → (B, 256, 200, 200)
Branch 4 (3×3@d18): (B, 512, 200, 200) → (B, 256, 200, 200)
Branch 5 (Global): (B, 512, 200, 200) → (B, 256, 200, 200)
↓
Concat: (B, 1280, 200, 200) # 256 × 5
↓
Project Conv 1×1: (B, 1280, 200, 200) → (B, 256, 200, 200)
↓ Step 3: Channel Attention
ChannelAttention:
Input: (B, 256, 200, 200)
Output: (B, 256, 200, 200) # 通道加权
↓ Step 4: Spatial Attention
SpatialAttention:
Input: (B, 256, 200, 200)
Output: (B, 256, 200, 200) # 空间加权
↓ Step 5: Auxiliary Classifier (Deep Supervision)
Conv2d(256, 6, 1) [仅训练时]
Input: (B, 256, 200, 200)
Output: (B, 6, 200, 200) # 辅助监督
↓ Step 6: Deep Decoder (4层)
Layer 1: Conv(256, 256, 3) + BN + ReLU + Dropout
Input: (B, 256, 200, 200)
Output: (B, 256, 200, 200)
Layer 2: Conv(256, 128, 3) + BN + ReLU + Dropout
Input: (B, 256, 200, 200)
Output: (B, 128, 200, 200)
Layer 3: Conv(128, 128, 3) + BN + ReLU + Dropout
Input: (B, 128, 200, 200)
Output: (B, 128, 200, 200)
↓ Step 7: Per-class Classifiers (6个独立分类器)
For each class (×6):
Conv(128, 64, 3) + BN + ReLU
Input: (B, 128, 200, 200)
Output: (B, 64, 200, 200)
Conv(64, 1, 1)
Input: (B, 64, 200, 200)
Output: (B, 1, 200, 200)
Concat 6个输出:
Output: (B, 6, 200, 200) # Logits
↓ Step 8 (推理时): Sigmoid
torch.sigmoid(logits)
Output: (B, 6, 200, 200) # 概率 [0, 1]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
最终输出:
Shape: (B, 6, 200, 200)
尺寸: 1 × 6 × 200 × 200 = 240,000 个值
内存: 240,000 × 4 bytes (float32) = 960 KB
📊 关键尺寸总结
输入尺寸
| 层级 | 名称 | 形状 | 空间尺寸 | 通道数 |
|---|---|---|---|---|
| Decoder输出 | Decoder Neck输出 | (B, 512, 360, 360) | 360×360 | 512 |
| Grid Transform后 | BEV网格变换 | (B, 512, 200, 200) | 200×200 | 512 |
中间尺寸 (增强版)
| 层级 | 名称 | 形状 | 通道数 |
|---|---|---|---|
| ASPP输出 | 多尺度特征 | (B, 256, 200, 200) | 256 |
| 注意力后 | 特征增强 | (B, 256, 200, 200) | 256 |
| Decoder Layer 1 | 深层解码 | (B, 256, 200, 200) | 256 |
| Decoder Layer 2 | 深层解码 | (B, 128, 200, 200) | 128 |
| Decoder Layer 3 | 深层解码 | (B, 128, 200, 200) | 128 |
输出尺寸
| 层级 | 名称 | 形状 | 说明 |
|---|---|---|---|
| 分类器输出 | Logits | (B, 6, 200, 200) | 6类,未归一化 |
| 最终输出 | Probabilities | (B, 6, 200, 200) | Sigmoid后,[0,1] |
🗺️ 空间范围详解
BEV网格配置
grid_transform:
input_scope: [[-54.0, 54.0, 0.75], [-54.0, 54.0, 0.75]]
output_scope: [[-50, 50, 0.5], [-50, 50, 0.5]]
详细计算:
Input Scope (Decoder输出空间)
范围: [-54m, 54m] × [-54m, 54m]
分辨率: 0.75m per grid
网格数量:
X轴: (54 - (-54)) / 0.75 = 108 / 0.75 = 144 grids
Y轴: (54 - (-54)) / 0.75 = 108 / 0.75 = 144 grids
实际Decoder输出: 360×360
Grid Transform处理: 360×360 → 144×144 (下采样)
问题: Decoder实际输出360×360,但input_scope期望144×144
解释: BEVGridTransform通过grid_sample插值处理尺寸不匹配
# grid_sample会自动处理
# 从360×360采样到144×144,然后插值到200×200
F.grid_sample(
x, # (B, 512, 360, 360)
grid, # 200×200个采样坐标,范围对应到360×360
mode='bilinear',
)
# Output: (B, 512, 200, 200)
Output Scope (分割输出空间)
范围: [-50m, 50m] × [-50m, 50m]
分辨率: 0.5m per grid
网格数量:
X轴: (50 - (-50)) / 0.5 = 100 / 0.5 = 200 grids
Y轴: (50 - (-50)) / 0.5 = 100 / 0.5 = 200 grids
最终输出: 200×200 BEV grid
📐 尺寸变化可视化
完整Pipeline尺寸流:
原始图像: (1, 6, 3, 256, 704)
↓
SwinT → FPN
↓ (1, 6, 256, 32, 88)
DepthLSS → BEV Pooling
↓ Camera BEV: (1, 80, 360, 360)
LiDAR Sparse Encoder
↓ LiDAR BEV: (1, 256, 360, 360)
ConvFuser (融合)
↓ Fused BEV: (1, 256, 360, 360)
SECOND Backbone
↓ (1, 128, 360, 360) + (1, 256, 180, 180)
SECONDFPN Neck
↓ (1, 512, 360, 360) ← 分割头输入
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
分割头处理:
BEVGridTransform
Input: (1, 512, 360, 360)
Output: (1, 512, 200, 200) ← 空间降采样
ASPP (增强版)
Output: (1, 256, 200, 200) ← 通道降维
双注意力
Output: (1, 256, 200, 200)
Deep Decoder (4层)
Output: (1, 128, 200, 200) ← 通道进一步降维
Per-class Classifiers
Output: (1, 6, 200, 200) ← 最终分割mask
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 核心尺寸总结
输入尺寸
分割头输入:
形状: (B, 512, 360, 360)
批次: B = 1 (单GPU)
通道: 512 (来自SECONDFPN concat)
空间: 360 × 360 grids
分辨率: 0.3m per grid
范围: ±54m × ±54m
内存: 1 × 512 × 360 × 360 × 4 bytes = 264 MB
输出尺寸
分割头输出:
形状: (B, 6, 200, 200)
批次: B = 1
类别: 6 (每类一个通道)
空间: 200 × 200 grids
分辨率: 0.5m per grid
范围: ±50m × ±50m (100m × 100m = 10,000平方米)
内存: 1 × 6 × 200 × 200 × 4 bytes = 960 KB
🔍 各类别输出详解
# 最终输出
output: (B, 6, 200, 200)
# 按类别拆分
output[0, 0, :, :] → (200, 200) drivable_area
output[0, 1, :, :] → (200, 200) ped_crossing
output[0, 2, :, :] → (200, 200) walkway
output[0, 3, :, :] → (200, 200) stop_line
output[0, 4, :, :] → (200, 200) carpark_area
output[0, 5, :, :] → (200, 200) divider
# 每个类别
每个像素值: 0.0 ~ 1.0 (概率)
> 0.5 → 该像素属于此类别
< 0.5 → 该像素不属于此类别
# 空间对应
grid[0, 0] → 实际位置 (-50m, -50m)
grid[100, 100] → 实际位置 (0m, 0m) - 车辆中心
grid[199, 199] → 实际位置 (+50m, +50m)
📊 不同配置的输出尺寸对比
官方单分割
grid_transform:
input_scope: [[-51.2, 51.2, 0.8], [-51.2, 51.2, 0.8]]
output_scope: [[-50, 50, 0.5], [-50, 50, 0.5]]
计算:
Input: 102.4m / 0.8m = 128 × 128
Output: 100m / 0.5m = 200 × 200
输出: (B, 6, 200, 200)
我们的双任务
grid_transform:
input_scope: [[-54.0, 54.0, 0.75], [-54.0, 54.0, 0.75]]
output_scope: [[-50, 50, 0.5], [-50, 50, 0.5]]
计算:
Input: 108m / 0.75m = 144 × 144
Output: 100m / 0.5m = 200 × 200
输出: (B, 6, 200, 200)
结论: 最终输出尺寸相同,都是 (B, 6, 200, 200)
💾 内存占用分析
分割头内存占用 (单样本)
# 前向传播中间tensor
BEV Grid Transform输出:
(1, 512, 200, 200) = 40.96 MB
ASPP中间态:
5个分支concat: (1, 1280, 200, 200) = 102.4 MB
Project后: (1, 256, 200, 200) = 20.48 MB
注意力模块:
Channel attention: (1, 256, 200, 200) = 20.48 MB
Spatial attention: (1, 256, 200, 200) = 20.48 MB
Decoder中间态:
Layer 1: (1, 256, 200, 200) = 20.48 MB
Layer 2: (1, 128, 200, 200) = 10.24 MB
Layer 3: (1, 128, 200, 200) = 10.24 MB
Per-class分类:
每个class: (1, 64, 200, 200) = 5.12 MB × 6 = 30.72 MB
最终输出:
(1, 6, 200, 200) = 0.96 MB
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
峰值内存 (ASPP concat时): ~102 MB (单样本)
总显存占用 (含梯度): ~19 GB (8 GPUs, 完整模型)
🎯 实际应用中的尺寸
物理空间覆盖
输出: (B, 6, 200, 200)
物理空间:
范围: ±50m × ±50m
面积: 100m × 100m = 10,000 平方米
分辨率: 0.5m per grid
网格尺寸:
每个grid: 0.5m × 0.5m = 0.25平方米
总grid数: 200 × 200 = 40,000 grids
总覆盖: 40,000 × 0.25 = 10,000 平方米 ✅
空间精度
每个grid: 0.5m × 0.5m
对于不同对象:
├─ 车辆 (4m × 2m): 约 8×4 = 32 grids ✅ 足够精确
├─ 人行道 (1.5m宽): 约 3 grids ✅ 可识别
├─ 车道线 (0.15m宽): 约 0.3 grids ⚠️ 亚像素级,困难
└─ 停止线 (0.3m宽): 约 0.6 grids ⚠️ 难以精确识别
说明: 这就是为什么stop_line和divider性能低的原因之一!
🔬 分辨率影响分析
如果提升输出分辨率
选项1: 提高到250×250
output_scope: [[-50, 50, 0.4], [-50, 50, 0.4]]
计算:
100m / 0.4m = 250 × 250 grids
影响:
✅ 车道线识别更准确 (0.15m → 0.4 grids)
⚠️ 计算量增加 56% (200² → 250²)
⚠️ 显存增加 ~3GB
选项2: 提高到400×400
output_scope: [[-50, 50, 0.25], [-50, 50, 0.25]]
计算:
100m / 0.25m = 400 × 400 grids
影响:
✅ 车道线识别显著提升 (0.15m → 0.6 grids)
❌ 计算量增加 4倍
❌ 显存爆炸 (+8GB)
❌ 不推荐
建议: 保持200×200,通过增强网络架构提升性能
📈 尺寸选择的权衡
为什么选择200×200?
优势:
- ✅ 计算量适中 (200² = 40K pixels)
- ✅ 显存占用合理
- ✅ 对大目标(车道、停车区)足够精确
- ✅ 与官方benchmark一致
劣势:
- ⚠️ 对线性小目标(车道线0.15m)精度有限
- ⚠️ 亚像素级特征难以捕获
解决方案:
- 不提高分辨率(成本太高)
- 用更强的网络架构(ASPP, 注意力)
- 用Dice Loss优化小目标
- 用类别权重强化学习
🎯 与检测输出对比
检测头输出
Detection Head输出:
boxes_3d: (N_objects, 9) # N_objects个3D框
每个框: [x, y, z, l, w, h, yaw, vx, vy]
scores_3d: (N_objects,) # 置信度
labels_3d: (N_objects,) # 类别
特点:
- 稀疏输出 (只有检测到的对象)
- 可变数量 (N_objects通常10-50)
- 每个对象9维信息
分割头输出
Segmentation Head输出:
masks_bev: (B, 6, 200, 200) # 密集输出
特点:
- 密集输出 (每个grid都有预测)
- 固定尺寸 (200×200)
- 每个位置6类概率
- 总计: 240,000个预测值
对比:
- 检测: 稀疏、动态数量、高维表示
- 分割: 密集、固定尺寸、2D平面
🔢 详细尺寸计算表
完整流程尺寸
| 步骤 | 模块 | 输入形状 | 输出形状 | 空间尺寸 |
|---|---|---|---|---|
| 1 | 原始图像 | - | (1, 6, 3, 256, 704) | 256×704 |
| 2 | SwinT | (1, 6, 3, 256, 704) | (1, 6, 768, 16, 44) | 16×44 |
| 3 | FPN | (1, 6, 768, 16, 44) | (1, 6, 256, 32, 88) | 32×88 |
| 4 | DepthLSS | (1, 6, 256, 32, 88) | (1, 80, 360, 360) | 360×360 |
| 5 | LiDAR Encoder | Points | (1, 256, 360, 360) | 360×360 |
| 6 | ConvFuser | 2个BEV | (1, 256, 360, 360) | 360×360 |
| 7 | SECOND | (1, 256, 360, 360) | (1, 128, 360, 360) | 360×360 |
| 8 | SECONDFPN | 2个尺度 | (1, 512, 360, 360) | 360×360 |
| 9 | Grid Transform | (1, 512, 360, 360) | (1, 512, 200, 200) | 200×200 ← 降采样 |
| 10 | ASPP | (1, 512, 200, 200) | (1, 256, 200, 200) | 200×200 |
| 11 | 注意力 | (1, 256, 200, 200) | (1, 256, 200, 200) | 200×200 |
| 12 | Decoder | (1, 256, 200, 200) | (1, 128, 200, 200) | 200×200 |
| 13 | Classifiers | (1, 128, 200, 200) | (1, 6, 200, 200) | 200×200 |
📊 快速参考
关键尺寸速查
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
分割头输入:
✓ 形状: (1, 512, 360, 360)
✓ 通道: 512
✓ 空间: 360×360 grids
✓ 分辨率: 0.3m/grid
✓ 范围: ±54m
分割头输出:
✓ 形状: (1, 6, 200, 200)
✓ 类别: 6
✓ 空间: 200×200 grids
✓ 分辨率: 0.5m/grid
✓ 范围: ±50m
✓ 总面积: 10,000平方米
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💡 设计考虑
为什么360×360 → 200×200?
原因:
-
计算效率:
- 360×360太大,分割头计算量爆炸
- 200×200是性能和效率的平衡点
-
感兴趣区域:
- ±54m太远,分割精度低
- ±50m是自动驾驶关注的主要区域
-
标注精度:
- nuScenes标注范围主要在±50m内
- 远距离区域标注可能不准确
-
与官方一致:
- 官方benchmark都用200×200输出
- 便于性能对比
🎓 总结
核心尺寸
输入: (1, 512, 360, 360) - 512通道, 360×360空间
↓
Grid Transform (360×360 → 200×200)
↓
输出: (1, 6, 200, 200) - 6类别, 200×200空间
空间范围: ±50m × ±50m = 10,000平方米
空间分辨率: 0.5m per grid (50cm)
生成时间: 2025-10-19
文档版本: 1.0