7.8 KiB
7.8 KiB
FP16训练Map Head缺失问题深度分析
分析时间: 2025-11-02 12:35 UTC
问题: FP16训练中BEV分割任务完全缺失
🚨 问题症状
1. Loss输出差异
FP32训练(正常):
Epoch [1][50]
loss/map/drivable_area/dice: 0.1315 ✅
loss/map/ped_crossing/dice: 0.3073 ✅
loss/map/walkway/dice: 0.2667 ✅
loss/map/stop_line/dice: 0.4295 ✅
loss/map/carpark_area/dice: 0.2706 ✅
loss/map/divider/dice: 0.6023 ✅
loss/object/loss_heatmap: 0.2326 ✅
loss: 2.8185 ← 包含map+object
FP16训练(异常):
Epoch [1][50]
loss/object/loss_heatmap: 0.6090 ✅
loss/object/layer_-1_loss_cls: 0.0996 ✅
loss/object/layer_-1_loss_bbox: 0.7440 ✅
loss: 1.4526 ← 只有object,缺少map
差异:
- FP32总loss ~2.82 (map+object)
- FP16总loss ~1.45 (仅object)
- Map相关loss完全缺失!
2. 模型结构差异
FP32模型打印:
BEVFusion(
(encoders): ModuleDict(...)
(fuser): ConvFuser(...) ✅
(decoder): ModuleDict(...)
(heads): ModuleDict(
(object): TransFusionHead(...) ✅
(map): EnhancedBEVSegmentationHead( ✅
(transform): BEVGridTransform()
(aspp): ASPP(...)
(channel_attn): ChannelAttention(...)
(spatial_attn): SpatialAttention(...)
(decoder): Sequential(...)
(classifiers): ModuleList(...)
(aux_classifier): Conv2d(...)
)
)
)
FP16模型打印:
BEVFusion(
(encoders): ModuleDict(...)
(fuser): ConvFuser(...) ✅
(decoder): ModuleDict(...)
(heads): ModuleDict(
(object): TransFusionHead(...) ✅
# ← 模型打印在这里就结束了,没有map!
)
)
结论: FP16模型中map head完全没有被创建!
3. Checkpoint加载警告
FP16训练警告:
unexpected key in source state_dict:
heads.map.aspp.convs.0.weight
heads.map.aspp.convs.1.weight
heads.map.aspp.convs.2.weight
...
heads.map.classifiers.0.0.weight
...
heads.map.aux_classifier.weight
heads.map.aux_classifier.bias
含义:
- Checkpoint中有完整的map head权重
- 但当前模型中没有对应的结构
- 所以这些权重被标记为"unexpected"并被丢弃
🔍 根本原因分析
可能原因1: 配置继承问题(最可能)⭐
FP16配置文件:
# multitask_BEV2X_phase4a_stage1_fp16.yaml
_base_: ./multitask_BEV2X_phase4a_stage1.yaml
model:
fuser: # ← 显式声明了fuser
type: ConvFuser
in_channels: [80, 256]
out_channels: 256
# ← 但没有显式声明heads!
问题:
- 在FP16配置中显式声明
model.fuser - 这可能导致mmcv的配置合并机制认为要覆盖整个model字段
- 结果:base配置中的
model.heads.map被丢弃!
可能原因2: FP16与EnhancedBEVSegmentationHead不兼容
假设: EnhancedBEVSegmentationHead在FP16模式下初始化失败
验证方法:
# 检查是否有初始化错误被静默处理
可能原因3: 配置文件语法问题
YAML配置合并规则:
# 错误的覆盖方式
model:
fuser: {...}
# 这会导致model下其他字段(encoders, heads, decoder)被丢弃
# 正确的覆盖方式
model:
fuser: {...}
heads:
map: ${_base_.model.heads.map} # 显式继承
✅ 解决方案
方案A: 修复FP16配置文件(推荐)⭐
修改: 显式继承所有model字段
# multitask_BEV2X_phase4a_stage1_fp16.yaml
_base_: ./multitask_BEV2X_phase4a_stage1.yaml
work_dir: /data/runs/phase4a_stage1_fp16
# ⚠️ 不要覆盖model字段,改为merge
# 方法1: 完全不声明model(让base配置生效)
# 方法2: 显式继承heads
model:
fuser:
type: ConvFuser
in_channels: [80, 256]
out_channels: 256
heads:
object: ${_base_.model.heads.object} # 显式继承
map: ${_base_.model.heads.map} # 显式继承
fp16:
loss_scale: dynamic
方案B: 不显式声明fuser(测试)
# multitask_BEV2X_phase4a_stage1_fp16.yaml
_base_: ./multitask_BEV2X_phase4a_stage1.yaml
work_dir: /data/runs/phase4a_stage1_fp16
# 完全不声明model,让base配置生效
# model: # ← 删除这部分
# fuser:
# ...
fp16:
loss_scale: dynamic
方案C: 完整声明model(最保险)
# 复制base文件中的完整model配置
model:
encoders: ${_base_.model.encoders}
fuser: ${_base_.model.fuser}
decoder: ${_base_.model.decoder}
heads: ${_base_.model.heads}
loss_scale: ${_base_.model.loss_scale}
fp16:
loss_scale: dynamic
🧪 验证测试
测试1: 检查配置合并结果
cd /workspace/bevfusion
# 使用修复后的配置
python -c "
from mmcv import Config
cfg = Config.fromfile('configs/.../multitask_BEV2X_phase4a_stage1_fp16.yaml')
print('Model keys:', list(cfg.model.keys()))
print('Heads keys:', list(cfg.model.heads.keys()))
print()
print('Object head type:', cfg.model.heads.get('object', {}).get('type', 'ConfigDict'))
print('Map head type:', cfg.model.heads.get('map', {}).get('type', 'NOT FOUND'))
"
测试2: Dry-run模型构建
# 测试模型是否能正确构建
python -c "
from mmcv import Config
from mmdet3d.models import build_model
cfg = Config.fromfile('configs/.../multitask_BEV2X_phase4a_stage1_fp16.yaml')
model = build_model(cfg.model)
print('Model heads:', list(model.heads.keys()))
for name, head in model.heads.items():
print(f' {name}: {type(head).__name__}')
"
📊 配置合并机制说明
YAML配置继承规则
mmcv Config合并行为:
# base.yaml
model:
encoders: {...}
fuser: {...}
heads:
object: {...}
map: {...}
# derived.yaml
_base_: ./base.yaml
# 情况1: 不声明model → 完全继承 ✅
# 结果: model有完整的encoders, fuser, heads
# 情况2: 部分声明model → 覆盖整个model! ❌
model:
fuser: {...}
# 结果: model只有fuser,丢失encoders和heads!
# 情况3: 显式继承 → 正确合并 ✅
model:
fuser: {...}
heads: ${_base_.model.heads}
# 结果: fuser被覆盖,heads继承自base
🎯 FP16失败的真正原因
确认:❌ YAML配置合并错误
过程:
1. base配置定义了完整model(encoders, fuser, heads)
2. FP16配置中显式声明了model.fuser
3. mmcv配置合并时,认为要覆盖整个model字段
4. 结果:model.heads被丢弃
5. 模型构建时只有object head(从哪来?默认配置?)
6. Map head完全没有被创建
7. 训练时只有object loss
✅ 修复建议
立即修复方案(最简单)
删除FP16配置中的model字段:
# multitask_BEV2X_phase4a_stage1_fp16.yaml
_base_: ./multitask_BEV2X_phase4a_stage1.yaml
work_dir: /data/runs/phase4a_stage1_fp16
# 删除model字段,完全继承base配置
# model: # ← 删除
# fuser:
# ...
fp16:
loss_scale: dynamic
原因:
- Base配置中已经有正确的fuser
- 不需要在FP16配置中重复声明
- 让配置完全继承即可
📝 经验教训
- mmcv配置继承陷阱: 部分声明会覆盖整个字段
- 调试方法: 对比模型打印,查看哪些模块缺失
- 验证配置: 修改后先用python加载检查,再启动训练
- 保守优化: 重大训练时先保证功能完整,再追求性能
🔧 下一步行动
选项1: 继续FP32训练(当前)✅
优势:
- 已验证稳定
- 双任务完整
- 无风险
时间: 9天完成
选项2: 修复FP16后重试
步骤:
- 修复FP16配置(删除model字段)
- 验证配置加载正确
- 小规模测试(50 iters)
- 确认map loss存在后正式训练
时间: 调试1-2小时,如成功可节省2.5天
状态: ✅ 问题分析完成,FP32训练恢复成功
建议: 先让FP32训练跑,后续再优化FP16
FP32训练正常运行中,预计11/10完成!