cv_resnet18训练日志解读:workdirs输出文件全解析
1. 为什么需要读懂workdirs里的内容
你刚跑完一次cv_resnet18_ocr-detection模型的微调训练,终端上跳过一串“Epoch 1/5”“Loss: 0.421”的滚动日志,最后停在“Training finished.”——但心里没底:模型到底学得怎么样?权重真的变好了吗?下次该调哪个参数?别急,答案不在控制台里,而在那个叫workdirs/的文件夹中。
这个目录是整个训练过程的“数字黑匣子”,它不声不响地记录了每一次前向传播的细节、每一轮验证的精度变化、每一组超参的实际影响。很多人只盯着最终生成的.pth权重文件,却忽略了里面那些带时间戳的log、json、png和csv——它们才是真正告诉你“模型是否健康”“训练是否稳定”“结果是否可信”的第一手证据。
本文不讲怎么安装PyTorch,也不重复抄写config.py里的参数名。我们直接打开workdirs/,一张图、一行日志、一个坐标点地拆解:每个文件从哪来、存什么、怎么看、怎么用。读完你能独立判断一次训练是否成功,能快速定位loss突增的原因,也能在团队协作中准确描述“第3轮验证F-score掉到0.72是因为学习率衰减过早”。
2. workdirs目录结构全景速览
训练启动后,系统自动创建以时间戳命名的子目录,例如:
workdirs/ └── 20260105_143022_resnet18_ocr_det/ ├── checkpoints/ # 权重快照 │ ├── best.pth # 验证指标最优的模型 │ ├── last.pth # 最后一轮保存的模型 │ └── epoch_3.pth # 第3轮中间检查点(可选) ├── logs/ # 文本日志 │ ├── train.log # 主训练日志(含loss、lr、time) │ └── eval.log # 验证阶段详细输出 ├── metrics/ # 结构化评估数据 │ ├── train_metrics.csv # 每轮训练指标(loss, lr, time) │ ├── eval_metrics.csv # 每轮验证指标(precision, recall, f_score) │ └── best_eval.json # 最优验证结果的完整统计 ├── visualizations/ # 可视化诊断 │ ├── loss_curve.png # 训练/验证loss曲线 │ ├── f_score_curve.png # F-score变化趋势 │ ├── sample_pred_001.jpg # 随机抽样的预测效果对比图 │ └── grad_norms.png # 梯度范数监控(防梯度爆炸) └── configs/ # 备份配置 ├── train_config.yaml # 实际运行的训练配置 └── model_config.yaml # 模型结构定义(含backbone、neck、head)关键提示:所有路径均基于训练脚本实际执行位置生成,
workdirs/是相对路径,不是硬编码绝对路径。如果你在/root/cv_resnet18_ocr-detection下运行训练,那完整路径就是/root/cv_resnet18_ocr-detection/workdirs/20260105_143022_resnet18_ocr_det/
3. checkpoints/:不只是权重,更是训练的“快照”
3.1 三个.pth文件的本质区别
last.pth是最“诚实”的文件:它不挑好坏,只忠实地保存最后一轮训练结束时的状态。哪怕第5轮loss突然飙升,它也照存不误。适合做断点续训(resume training),但绝不推荐直接用于推理部署。best.pth是最“挑剔”的文件:它只在验证集F-score(或其他你设定的metric)达到历史最高时才覆盖写入。它的存在意味着:“这个模型在未见过的数据上表现最好”。这是你应该放进WebUI加载路径、导出ONNX、甚至发给客户的版本。epoch_X.pth是最“透明”的文件:当你在训练配置中开启save_interval: 2,它就会每2轮保存一次。作用不是为了部署,而是为了回溯——比如发现第4轮开始loss震荡,你可以加载epoch_3.pth和epoch_4.pth,用同一张测试图跑infer,对比输出box坐标差异,精准定位问题发生在哪一轮。
3.2 如何验证.pth文件是否可用
别急着复制进WebUI,先做两件事:
检查文件大小:正常训练产出的
best.pth通常在30–50MB之间。如果只有2MB,大概率是保存失败或模型未初始化完成。用Python快速加载校验:
import torch model = torch.load("workdirs/20260105_143022_resnet18_ocr_det/checkpoints/best.pth", map_location="cpu") print("Model keys:", list(model.keys())[:3]) # 应看到 'state_dict', 'optimizer', 'epoch' print("State dict keys:", list(model['state_dict'].keys())[:2]) # 应看到 'backbone.conv1.weight', 'neck.fpn_convs.0.weight'如果报KeyError: 'state_dict',说明保存格式异常;如果len(model['state_dict']) < 100,模型可能未完整加载。
4. logs/:从滚动日志到可分析文本
4.1 train.log:逐行解读真实含义
打开logs/train.log,你会看到类似这样的片段:
[2026-01-05 14:30:22] INFO: Epoch [1/5], Iter [10/245], Loss: 0.821, LR: 0.0070, Time: 0.42s/iter [2026-01-05 14:31:05] INFO: Epoch [1/5], Iter [20/245], Loss: 0.613, LR: 0.0070, Time: 0.41s/iter [2026-01-05 14:32:28] INFO: Epoch [1/5], Iter [50/245], Loss: 0.392, LR: 0.0070, Time: 0.43s/iter [2026-01-05 14:35:12] INFO: Epoch [1/5], Iter [100/245], Loss: 0.287, LR: 0.0070, Time: 0.44s/iter [2026-01-05 14:38:55] INFO: Epoch [1/5], Iter [150/245], Loss: 0.215, LR: 0.0070, Time: 0.45s/iter [2026-01-05 14:42:38] INFO: Epoch [1/5], Iter [200/245], Loss: 0.189, LR: 0.0070, Time: 0.46s/iter [2026-01-05 14:44:22] INFO: Epoch [1/5] completed. Avg Loss: 0.241, Time: 142.3sLoss值下降是否健康?
看迭代间差值:从0.821→0.613→0.392是平滑下降,说明学习率合适;但如果出现0.215→0.302→0.289反复跳动,可能是batch size太小或数据增强过于激进。Time值是否异常?
正常单次iter耗时应在0.4–0.6秒(GTX 1060)。如果某次突然变成2.1s/iter,立刻查nvidia-smi——大概率是显存被其他进程抢占。Avg Loss与最后一轮Loss差异大?
Avg Loss: 0.241远高于Iter [200/245] Loss: 0.189,说明前期loss偏高。这时要结合eval.log看验证集表现:如果验证loss同步下降,说明模型正在有效学习;如果验证loss停滞,可能是过拟合前兆。
4.2 eval.log:验证阶段的“体检报告”
[2026-01-05 14:44:25] INFO: Start evaluation on test set (25 images)... [2026-01-05 14:44:38] INFO: Precision: 0.821, Recall: 0.763, F-score: 0.791 [2026-01-05 14:44:38] INFO: Per-class metrics: [2026-01-05 14:44:38] INFO: text: P=0.842, R=0.785, F=0.812 [2026-01-05 14:44:38] INFO: background: P=0.991, R=0.998, F=0.994 [2026-01-05 14:44:38] INFO: Best F-score updated: 0.791 → 0.791 (saved to best.pth)重点看三组数字:Precision(查准率)、Recall(查全率)、F-score(综合指标)。OCR检测任务中,F-score > 0.75算合格,> 0.82算优秀。
Per-class metrics揭示细节:
background类F-score高达0.994,说明模型几乎不会把空白区域误判为文字;而text类F-score只有0.812,说明漏检或误检主要发生在文字区域——这直接指向数据标注质量或anchor设计问题。
5. metrics/:让训练过程可量化、可对比
5.1 CSV文件:用Excel就能做深度分析
train_metrics.csv和eval_metrics.csv是逗号分隔的纯文本,用Excel或pandas打开即得表格:
| epoch | iter | loss | lr | time_per_iter | precision | recall | f_score |
|---|---|---|---|---|---|---|---|
| 1 | 245 | 0.241 | 0.007 | 0.45 | 0.821 | 0.763 | 0.791 |
| 2 | 245 | 0.198 | 0.0056 | 0.44 | 0.837 | 0.779 | 0.807 |
| 3 | 245 | 0.172 | 0.0045 | 0.43 | 0.845 | 0.786 | 0.814 |
| 4 | 245 | 0.158 | 0.0036 | 0.42 | 0.851 | 0.792 | 0.820 |
| 5 | 245 | 0.149 | 0.0029 | 0.41 | 0.856 | 0.795 | 0.824 |
一眼识别训练瓶颈:如果
f_score从第4轮起增长<0.002,说明模型已接近收敛,再训更多轮意义不大。交叉验证学习率策略:对比
lr列与loss列,若lr=0.0029时loss下降变缓,说明当前学习率已低于最优值,下次可尝试min_lr=0.001。导出对比图表:用Excel选中
epoch和f_score两列,插入折线图——你将得到一条清晰的性能提升曲线,比口头说“效果变好了”更有说服力。
5.2 best_eval.json:部署前的最终确认书
这个JSON文件包含完整评估细节,是交付给下游系统的权威依据:
{ "timestamp": "2026-01-05T14:44:38", "model_path": "workdirs/20260105_143022_resnet18_ocr_det/checkpoints/best.pth", "dataset": "ICDAR2015_test", "total_images": 25, "metrics": { "precision": 0.856, "recall": 0.795, "f_score": 0.824, "false_positive_rate": 0.012, "false_negative_rate": 0.031 }, "per_class": { "text": {"precision": 0.856, "recall": 0.795, "f_score": 0.824}, "background": {"precision": 0.991, "recall": 0.998, "f_score": 0.994} }, "inference_speed": { "gpu_ms_per_img": 42.3, "cpu_ms_per_img": 3150.7 } }false_positive_rate(误检率)和false_negative_rate(漏检率)是业务关键指标:电商场景容忍漏检(少识别几个商品名),但不能容忍误检(把“促销”识别成“诈骗”);而金融单据场景则相反。inference_speed字段直接决定WebUI响应体验:GPU单图42ms ≈ 23FPS,完全满足实时交互;CPU单图3150ms ≈ 0.3FPS,则必须提醒用户“仅限离线批量处理”。
6. visualizations/:用眼睛代替代码做诊断
6.1 loss_curve.png:第一眼看出训练是否“生病”
健康曲线:训练loss(蓝色)和验证loss(橙色)同步平滑下降,且验证loss始终略高于训练loss(gap < 0.05)。
过拟合信号:验证loss在第3轮后开始上升,而训练loss继续下降——此时应立即停止训练,并检查
train_config.yaml中的weight_decay是否过小。欠拟合信号:两条线都高位徘徊,无明显下降趋势——大概率是学习率太低或模型容量不足,需调高
lr或换更大backbone。
6.2 sample_pred_001.jpg:最直观的效果验证
这张图左侧是原图,右侧是叠加了预测框的结果。重点观察:
框是否贴合文字边缘:理想状态是框紧贴文字四角,不覆盖背景也不遗漏笔画。如果框明显偏大(覆盖整行)或偏小(只框住文字中部),说明NMS阈值或anchor尺寸需调整。
低置信度框是否合理:图中右下角有个半透明浅蓝框(置信度0.31),它没被最终结果采用,但存在说明模型对这部分有疑虑——对应到业务,就是你需要人工复核的“灰色地带”。
漏检区域是否有规律:如果所有漏检都集中在图片顶部,检查训练数据中是否有大量顶部文字缺失的样本,或是预处理时resize导致顶部信息丢失。
7. configs/:配置即文档,备份即承诺
configs/目录下的两个yaml文件,是你未来复现实验的唯一凭证:
train_config.yaml不只是参数列表,更是训练逻辑的说明书。例如:data: train: dataset: ICDAR2015Dataset img_prefix: /root/custom_data/train_images/ ann_file: /root/custom_data/train_list.txt val: dataset: ICDAR2015Dataset img_prefix: /root/custom_data/test_images/ ann_file: /root/custom_data/test_list.txt这段代码明确告诉你:训练用的是
train_images/下的图+train_list.txt里的路径映射,验证用的是test_images/+test_list.txt。如果某次训练效果差,第一步就该核对这两个路径是否真实存在、文件名是否大小写一致。model_config.yaml定义了模型骨架:backbone: type: ResNet depth: 18 init_cfg: torchvision://resnet18 neck: type: FPN in_channels: [64, 128, 256, 512] head: type: DBHead in_channels: 256当你发现
best.pth在WebUI中加载失败,90%概率是model_config.yaml里type: DBHead与WebUI期望的PSEHead不匹配——此时不要改权重,而应修改WebUI的模型加载逻辑。
重要原则:每次训练前,手动备份一份
configs/到Git仓库。这不是多此一举,而是确保“今天能跑通的模型,三个月后同事也能一键复现”。
8. 总结:workdirs不是终点,而是下一次训练的起点
读懂workdirs/,本质上是在建立一种工程化思维习惯:
- 不把训练当成“黑盒运行”,而是一次可控的实验;
- 不把日志当成“刷屏噪音”,而是带时间戳的诊断报告;
- 不把权重当成“最终答案”,而是当前配置下的最优近似解。
下次当你准备点击“开始训练”按钮时,试着问自己三个问题:
- 我是否已检查
configs/中数据路径的真实有效性? - 我是否预设了合理的
save_interval以便捕捉关键中间状态? - 我是否计划在训练结束后,第一时间打开
visualizations/loss_curve.png和metrics/eval_metrics.csv做交叉验证?
这些问题的答案,就藏在你即将生成的那个workdirs/20260105_143022_resnet18_ocr_det/目录里——它不说话,但事无巨细,全部记录。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。