bev-project/project/docs/CHECKPOINT_MISMATCH_EXPLANA...

9.5 KiB
Raw Blame History

为什么从Epoch 1重新开始训练

问题加载epoch_19.pth后训练从Epoch 1开始而不是Epoch 20继续


🔍 根本原因

模型架构不匹配

epoch_19.pth训练时使用BEVSegmentationHead(原始简单版本)
当前训练使用EnhancedBEVSegmentationHead(增强复杂版本)

这两个分割头的网络结构完全不同,导致权重无法对应。


📊 架构对比

原始 BEVSegmentationHeadepoch_19.pth中

classifier: Sequential(
    Conv2d(512, 512, 3×3)           # 第1层卷积
    BatchNorm2d(512)                # BN
    ReLU
    Conv2d(512, 512, 3×3)           # 第2层卷积
    BatchNorm2d(512)                # BN
    ReLU
    Conv2d(512, 6, 1×1)             # 输出层
)

参数结构

heads.map.classifier.0.weight    [512, 512, 3, 3]
heads.map.classifier.1.weight    [512]
heads.map.classifier.1.bias      [512]
heads.map.classifier.3.weight    [512, 512, 3, 3]
heads.map.classifier.4.weight    [512]
heads.map.classifier.4.bias      [512]
heads.map.classifier.6.weight    [6, 512, 1, 1]
heads.map.classifier.6.bias      [6]

总参数量约2.4M


增强 EnhancedBEVSegmentationHead当前使用

# 1. ASPP模块多尺度特征提取
aspp:
    - convs[0]: Conv2d(512, 256, 1×1) + GroupNorm(32, 256)
    - convs[1]: Conv2d(512, 256, 3×3, dilation=6) + GroupNorm
    - convs[2]: Conv2d(512, 256, 3×3, dilation=12) + GroupNorm
    - convs[3]: Conv2d(512, 256, 3×3, dilation=18) + GroupNorm
    - global branch: Conv2d + GroupNorm
    - project: Conv2d(256×5, 256) + GroupNorm

# 2. 注意力模块
channel_attn:
    - avg_pool + max_pool
    - fc: Conv2d(256, 16) + ReLU + Conv2d(16, 256)
    
spatial_attn:
    - Conv2d(2, 1, 7×7)

# 3. 深层解码器4层
decoder:
    - layer1: Conv2d(256, 256) + GroupNorm + ReLU + Dropout
    - layer2: Conv2d(256, 128) + GroupNorm + ReLU + Dropout
    - layer3: Conv2d(128, 128) + GroupNorm + ReLU + Dropout

# 4. 分类器6个独立分类器每个类别一个
classifiers[0-5]:  # 每个类别独立
    - Conv2d(128, 64) + GroupNorm + ReLU
    - Conv2d(64, 1, 1×1)

# 5. 辅助分类器(深度监督)
aux_classifier:
    - Conv2d(256, 6, 1×1)

参数结构

heads.map.aspp.convs.0.weight              [256, 512, 1, 1]
heads.map.aspp.bns.0.weight                [256]
heads.map.aspp.convs.1.weight              [256, 512, 3, 3]
heads.map.aspp.bns.1.weight                [256]
... (ASPP继续)
heads.map.channel_attn.fc.0.weight         [16, 256, 1, 1]
heads.map.channel_attn.fc.2.weight         [256, 16, 1, 1]
heads.map.spatial_attn.conv.weight         [1, 2, 7, 7]
heads.map.decoder.0.weight                 [256, 256, 3, 3]
heads.map.decoder.1.weight                 [256]  # GroupNorm
... (解码器继续)
heads.map.classifiers.0.0.weight           [64, 128, 3, 3]
heads.map.classifiers.0.1.weight           [64]  # GroupNorm
heads.map.classifiers.0.3.weight           [1, 64, 1, 1]
... (6个分类器)
heads.map.aux_classifier.weight            [6, 256, 1, 1]

总参数量约5.6M是原始的2.3倍)


⚠️ 权重加载冲突

Checkpoint加载日志

WARNING: The model and loaded state dict do not match exactly

unexpected key in source state dict:
- heads.map.classifier.0.weight
- heads.map.classifier.1.weight
- heads.map.classifier.1.bias
- heads.map.classifier.3.weight
- heads.map.classifier.4.weight
- heads.map.classifier.6.weight
- heads.map.classifier.6.bias

missing keys in source state dict:
- heads.map.aspp.convs.0.weight (新增)
- heads.map.aspp.bns.0.weight (新增)
- heads.map.aspp.bns.1.weight (新增)
- heads.map.channel_attn.fc.0.weight (新增)
- heads.map.spatial_attn.conv.weight (新增)
- heads.map.decoder.0.weight (新增)
- heads.map.classifiers.0.0.weight (新增)
- heads.map.classifiers.1.0.weight (新增)
... (共70+个missing keys)

🔄 实际加载情况

成功复用的部分占总模型90%

模块 状态 说明
Camera Encoder 完全复用 SwinTransformer backbone (97M参数)
Camera Neck 完全复用 GeneralizedLSSFPN
View Transform 完全复用 DepthLSSTransform
LiDAR Encoder 完全复用 SparseEncoder
Fuser 完全复用 ConvFuser
Decoder Backbone 完全复用 SECOND + SECONDFPN
Object Head 完全复用 TransFusionHead (检测)

无法复用的部分(需要重新训练)

模块 状态 说明
Map Head 随机初始化 EnhancedBEVSegmentationHead (5.6M参数)

📈 训练策略差异

场景1继续训练相同架构

# 如果使用原始BEVSegmentationHead
--load_from epoch_19.pth

结果
 所有权重完全匹配
 从Epoch 20继续训练
 只需训练剩余4个epochs (2023)
 约14小时完成

场景2迁移学习架构改变- 当前情况

# 使用EnhancedBEVSegmentationHead
--load_from epoch_19.pth

结果
 Encoder/Decoder权重复用预训练特征提取器
 Map Head随机初始化需要重新学习
⚠️ 从Epoch 1开始训练
⚠️ 需要完整23个epochs
⚠️ 约6天完成

🎯 为什么从Epoch 1开始

技术原因

  1. Optimizer State不匹配

    • epoch_19.pth中保存的Adam optimizer statemomentum、variance
    • 这些state是针对原始classifier参数
    • EnhancedHead的参数完全不同optimizer state无法对应
  2. Learning Rate Schedule重置

    • CosineAnnealing LR scheduler从epoch 19的位置
    • 但Map Head是随机初始化需要从头开始学习
    • 如果从epoch 20继续LR会非常小5e-6不利于新模块训练
  3. 训练逻辑设计

    • --load_from只加载模型权重weight transfer
    • 不加载训练状态epoch、optimizer、scheduler
    • 训练会自动从epoch 1开始

如果想从Epoch 20继续

需要使用--resume_from而不是--load_from

# 继续训练(相同架构)
--resume_from epoch_19.pth  
# 加载:模型权重 + optimizer + scheduler + epoch number

# 迁移学习(不同架构)
--load_from epoch_19.pth
# 只加载:匹配的模型权重

但在架构不匹配时,--resume_from会失败因为optimizer state无法对应。


💡 优势分析

虽然从Epoch 1开始训练时间更长但有以下优势

1. 更充分的特征学习

Encoder (已预训练) → 提供高质量BEV特征
    ↓
EnhancedHead (从0开始) → 充分学习如何使用这些特征

2. 避免负迁移

  • 如果强制从epoch 20继续极小的LR会导致
    • EnhancedHead学习缓慢
    • 可能陷入次优解
    • 无法发挥增强架构的优势

3. 训练曲线更健康

Epoch 1-5:   Encoder微调 + Head快速学习
Epoch 6-15:  整体收敛
Epoch 16-23: 精细调优

📊 预期性能对比

原始配置如果继续epoch 19→23

训练时间14小时
分割mIoU42-45%
稳定性:✅ 高

增强配置当前epoch 1→23

训练时间6天
分割mIoU55-60%(预期)
提升:+13-18%
稳定性:✅ 已修复GroupNorm

🔧 技术细节Checkpoint结构

epoch_19.pth包含

{
    'state_dict': {
        # 模型权重
        'encoders.camera.backbone.xxx': tensor(...),
        'heads.map.classifier.0.weight': tensor([512,512,3,3]),
        'heads.object.xxx': tensor(...),
        ...
    },
    'optimizer': {
        # Adam optimizer状态
        'state': {...},
        'param_groups': [{'lr': 5.089e-06, ...}]
    },
    'meta': {
        'epoch': 19,
        'iter': 77240,
        'lr': [5.089e-06],
        ...
    }
}

使用--load_from时

# PyTorch加载逻辑
checkpoint = torch.load('epoch_19.pth')
model.load_state_dict(checkpoint['state_dict'], strict=False)
# strict=False: 允许部分匹配
# 只加载state_dict忽略optimizer和meta

匹配结果:

 匹配: encoders.camera.* (完全复用)
 匹配: encoders.lidar.* (完全复用)
 匹配: fuser.* (完全复用)
 匹配: decoder.* (完全复用)
 匹配: heads.object.* (完全复用)
 不匹配: heads.map.classifier.* (被忽略)
⚠️ 缺失: heads.map.aspp.* (随机初始化)
⚠️ 缺失: heads.map.channel_attn.* (随机初始化)
⚠️ 缺失: heads.map.decoder.* (随机初始化)
⚠️ 缺失: heads.map.classifiers.* (随机初始化)

📝 总结

核心原因

EnhancedBEVSegmentationHead与原始BEVSegmentationHead是完全不同的网络架构

  • 原始3层简单CNN2.4M参数)
  • 增强ASPP+注意力+深层解码器5.6M参数)

权重无法对应

  • 90%的模型backbone/encoder/detector可以复用
  • 10%的模型(分割头)需要从零开始训练

训练策略

  • 使用--load_from只加载匹配的权重从epoch 1开始
  • 这是迁移学习的标准做法不是bug

类比理解

就像:

有一辆车epoch_19已经跑了19万公里
现在要把发动机map head换成涡轮增压版enhanced head
虽然车身、底盘、变速箱都是原来的encoder/decoder
但新发动机需要重新磨合从epoch 1训练
不能直接从19万公里继续跑

生成时间2025-10-21 11:40 UTC
当前训练Epoch 1/23正常进行中
预计完成2025-10-276天后