1. 项目概述
作为一名计算机视觉方向的实践者,我在Windows平台上复现PIDNet语义分割模型时遇到了不少坑。PIDNet作为2022年提出的高效实时语义分割网络,在CamVid和Cityscapes等数据集上表现优异。但原代码主要针对Linux环境开发,在Windows系统下复现时出现了数据集路径、训练参数、多进程处理等一系列问题。本文将详细记录这些问题的排查过程和解决方案,希望能帮助同样在Windows平台进行计算机视觉研究的同行们少走弯路。
2. 环境准备与基础配置
2.1 硬件与系统环境
我的实验环境是一台配备NVIDIA GeForce RTX 4060显卡的Windows 11主机。虽然原论文作者推荐使用Ubuntu系统,但通过以下配置仍可在Windows上获得不错的训练效果:
- Python 3.8.10 (建议使用Anaconda管理环境)
- PyTorch 1.12.1 + CUDA 11.6
- torchvision 0.13.1
- OpenCV 4.6.0
注意:Windows下的路径分隔符使用反斜杠(),而代码中多为正斜杠(/),这在大多数情况下可以兼容,但在某些文件操作中仍可能引发问题。建议在代码中统一使用os.path.join()处理路径。
2.2 数据集准备要点
对于CamVid数据集,需要特别注意其目录结构。正确的目录组织应该是:
code复制CamVid/
├── train/
├── val/
├── test/
└── list/
├── camvid/
├── train.lst
├── val.lst
└── test.lst
数据集列表文件(.lst)的格式应为:
code复制path/to/image1.png path/to/label1.png
path/to/image2.png path/to/label2.png
...
3. CamVid数据集训练问题排查
3.1 mIoU指标异常问题
现象描述:
训练过程中验证集mIoU达到异常高的值(如98%),但实际测试时性能正常。
原因分析:
这是典型的数据泄露(data leakage)问题。检查配置文件发现:
yaml复制# pidnet_small_camvid.yaml
DATASET:
TRAIN_SET: 'list/camvid/trainval.lst'
TEST_SET: 'list/camvid/val.lst' # 错误配置
训练时使用了trainval集(训练+验证),而测试时又使用了val集,导致模型在训练阶段已经"见过"测试数据。
解决方案:
修改配置文件中测试集路径为独立测试集:
yaml复制TEST_SET: 'list/camvid/test.lst'
3.2 训练epoch异常终止
现象描述:
配置文件中设置END_EPOCH=200,但实际训练120轮后停止。
代码分析:
检查train.py发现epoch控制逻辑:
python复制# train.py
max_epoch = 120 # 硬编码值覆盖了配置文件
for epoch in range(max_epoch):
...
解决方案:
有两种修复方式:
- 直接修改train.py中的max_epoch值
- 更优雅的方式是修改代码读取配置文件:
python复制max_epoch = config.TRAIN.END_EPOCH if hasattr(config.TRAIN, 'END_EPOCH') else 120
3.3 模型加载问题
现象描述:
评估时出现与ImageNet预训练相关的错误。
问题定位:
eval.py中模型加载逻辑不完善:
python复制model = models.pidnet.get_seg_model(config, imgnet_pretrained=True)
解决方案:
根据配置动态决定是否使用ImageNet预训练:
python复制imgnet = 'imagenet' in config.MODEL.PRETRAINED
model = models.pidnet.get_seg_model(config, imgnet_pretrained=imgnet)
4. Cityscapes数据集训练问题
4.1 Loss值为零问题
现象描述:
训练时Semantic loss和SB loss始终为0,且一个epoch后崩溃。
错误现象:
code复制RuntimeError: CUDA error: device-side assert triggered
原因分析:
这是典型的多进程数据加载问题。Windows下Python的多进程实现与Linux不同,在数据加载时容易引发CUDA错误。
解决方案1:
禁用多进程(简单但训练速度慢):
yaml复制DATASET:
WORKERS: 0
解决方案2:
完整修复方案需要三处修改:
- 配置文件中添加类别权重:
yaml复制DATASET:
CLASS_WEIGHTS: [0.8373, 0.918, 0.866, ..., 1.0507]
- 修改cityscapes.py中的权重处理:
python复制# 原代码
self.class_weights = torch.FloatTensor([...]).cuda()
# 修改为
self.class_weights = torch.FloatTensor([...]) # 不立即转移到GPU
# 或
self.class_weight = None
- 在criterion.py中添加权重处理逻辑:
python复制def _ce_forward(self, score, target):
if config.DATASET.CLASS_WEIGHTS is not None:
weight = torch.FloatTensor(config.DATASET.CLASS_WEIGHTS).to(score.device)
self.criterion.weight = weight
...
5. Windows平台特有优化建议
5.1 内存管理技巧
Windows的显存管理不如Linux高效,建议:
- 调整DataLoader参数:
python复制DataLoader(
batch_size=4, # 适当减小
shuffle=True,
num_workers=0 if os.name == 'nt' else 4, # Windows下禁用多进程
pin_memory=True,
drop_last=True
)
- 定期清空缓存:
python复制import torch
torch.cuda.empty_cache()
5.2 性能监控工具
推荐使用Windows自带的性能监视器:
-
添加GPU计数器:
- GPU引擎使用情况
- GPU专用内存
-
设置Python计数器:
- 进程/处理器时间百分比
- 进程/工作集
6. 训练调参经验分享
6.1 学习率策略
PIDNet对学习率敏感,建议采用warmup策略:
yaml复制TRAIN:
LR: 0.01
LR_SCHEDULER: 'poly'
WARMUP_EPOCHS: 5
WARMUP_START_LR: 0.001
6.2 数据增强配置
针对小显存设备优化:
yaml复制AUG:
SCALES: [0.5, 0.75, 1.0] # 多尺度训练
FLIP: True
CROP_SIZE: [480, 960] # 适当减小
7. 模型评估与结果分析
7.1 指标解读技巧
- mIoU计算时注意忽略index=255的像素(通常为边界或忽略区域)
- 实时性测试应包括前处理和后处理时间
7.2 可视化工具推荐
- 使用OpenCV叠加显示:
python复制def overlay_mask(image, mask, alpha=0.5):
color_mask = np.zeros_like(image)
# 为每个类别应用不同颜色
...
return cv2.addWeighted(image, 1-alpha, color_mask, alpha, 0)
- 使用TensorBoard记录训练过程:
python复制from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()
writer.add_scalar('Train/loss', loss.item(), global_step)
8. 进阶优化方向
对于希望进一步提升性能的开发者:
- 自定义损失函数:
python复制class CustomLoss(nn.Module):
def __init__(self):
super().__init__()
self.ce = nn.CrossEntropyLoss()
self.dice = DiceLoss()
def forward(self, output, target):
return 0.7*self.ce(output, target) + 0.3*self.dice(output, target)
- 混合精度训练:
python复制scaler = torch.cuda.amp.GradScaler()
with torch.cuda.amp.autocast():
output = model(input)
loss = criterion(output, target)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
在Windows平台复现复杂模型确实会遇到各种环境问题,关键是要理解错误背后的原理,而不是盲目尝试解决方案。通过这次PIDNet的复现经历,我总结了三点重要经验:
- 数据集配置要仔细检查,特别是train/val/test的划分
- Windows下的多进程处理需要特殊注意
- 模型代码中的硬编码参数应该尽量改为可配置项
最后分享一个小技巧:在修改代码前先做好版本标记,可以使用git tag保存关键节点,方便出现问题后快速回退。