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

367 lines
9.5 KiB
Markdown
Raw Normal View History

# 为什么从Epoch 1重新开始训练
**问题**加载epoch_19.pth后训练从Epoch 1开始而不是Epoch 20继续
---
## 🔍 根本原因
### 模型架构不匹配
**epoch_19.pth训练时使用**`BEVSegmentationHead`(原始简单版本)
**当前训练使用**`EnhancedBEVSegmentationHead`(增强复杂版本)
这两个分割头的**网络结构完全不同**,导致权重无法对应。
---
## 📊 架构对比
### 原始 BEVSegmentationHeadepoch_19.pth中
```python
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当前使用
```python
# 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继续训练相同架构
```python
# 如果使用原始BEVSegmentationHead
--load_from epoch_19.pth
结果:
✅ 所有权重完全匹配
✅ 从Epoch 20继续训练
✅ 只需训练剩余4个epochs (20→23)
✅ 约14小时完成
```
### 场景2迁移学习架构改变- 当前情况
```python
# 使用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`
```bash
# 继续训练(相同架构)
--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包含
```python
{
'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时
```python
# PyTorch加载逻辑
checkpoint = torch.load('epoch_19.pth')
model.load_state_dict(checkpoint['state_dict'], strict=False)
# strict=False: 允许部分匹配
# 只加载state_dict忽略optimizer和meta
```
### 匹配结果:
```python
✅ 匹配: 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天后