ms-swift训练监控:日志查看与进度跟踪技巧
在大模型微调实践中,训练过程如同一场精密的航行——模型参数是船体,数据集是海图,而日志与进度监控则是驾驶舱里的仪表盘。没有清晰的监控,再好的模型也可能在无声中偏离航向:显存悄然溢出、梯度悄然消失、学习率悄然失效,甚至训练早已停滞却无人察觉。ms-swift作为覆盖600+文本模型与300+多模态模型的轻量级微调基础设施,其强大不仅在于支持LoRA、GRPO、Megatron等前沿技术,更在于它为工程师提供了可观察、可干预、可回溯的全链路训练视图。
本文不讲如何启动一次训练,而是聚焦你按下swift sft后最常被忽略却最关键的环节:如何真正“看见”训练在发生什么。我们将避开抽象术语,用真实命令、可复制的日志片段、终端截图式描述和即插即用的脚本,带你掌握四类核心监控能力:实时日志流解析、结构化指标提取、训练曲线可视化、异常模式快速诊断。无论你是刚跑通第一个LoRA实验的新手,还是管理百卡集群的SRE,这些技巧都能帮你把训练从“黑盒等待”变成“透明掌控”。
1. 实时日志流:读懂终端里滚动的每一行信息
ms-swift默认将训练日志输出到标准输出(stdout),这是你与训练进程最直接的对话窗口。但海量滚动文字中,哪些是关键信号?哪些是干扰噪音?我们来逐层解码。
1.1 日志层级与关键字段识别
运行以下典型命令后,终端会持续输出类似内容:
CUDA_VISIBLE_DEVICES=0 swift sft \ --model Qwen/Qwen2.5-7B-Instruct \ --dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \ --train_type lora \ --output_dir output \ --logging_steps 5 \ --save_steps 50你会看到如下结构化日志(已精简):
[2024-09-15 14:22:03,872] [INFO] [trainer.py:1245] ***** Running training ***** [2024-09-15 14:22:03,872] [INFO] [trainer.py:1246] Num examples = 1000 [2024-09-15 14:22:03,872] [INFO] [trainer.py:1247] Num Epochs = 1 [2024-09-15 14:22:03,872] [INFO] [trainer.py:1248] Instantaneous batch size per device = 1 [2024-09-15 14:22:03,872] [INFO] [trainer.py:1249] Total train batch size (w. parallel, distributed & accumulation) = 16 [2024-09-15 14:22:03,872] [INFO] [trainer.py:1250] Gradient Accumulation steps = 16 [2024-09-15 14:22:03,872] [INFO] [trainer.py:1251] Total optimization steps = 63 [2024-09-15 14:22:03,872] [INFO] [trainer.py:1252] Number of trainable parameters = 5,242,880关键字段速查表:
| 字段 | 含义 | 为什么重要 | 健康值示例 |
|---|---|---|---|
Num examples | 训练样本总数 | 确认数据集加载无误,避免因路径错误导致样本数为0 | 1000(与#500×2一致) |
Total train batch size | 全局有效批次大小 | 决定梯度更新频率,直接影响收敛速度与稳定性 | 16(per_device=1×grad_acc=16) |
Total optimization steps | 总优化步数 | 预估训练完成时间,也是--max_steps的依据 | 63(1000÷16≈62.5→63) |
Number of trainable parameters | 可训练参数量 | 验证LoRA配置是否生效(全参训练应为数十亿) | 5,242,880(约524万,符合LoRA rank=8预期) |
实操技巧:用
grep过滤关键信息,避免眼花缭乱# 实时监控总步数与当前步数 swift sft ... 2>&1 | grep -E "(optimization steps|step\ [0-9]+\/)" # 监控GPU显存占用(需nvidia-smi) watch -n 1 'nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits'
1.2 进度条与时间戳:判断训练是否“活着”
每--logging_steps步(默认5步),ms-swift会输出一行带进度的指标日志:
[2024-09-15 14:23:12,105] [INFO] [trainer.py:2341] Step 5/63: loss=2.1428, learning_rate=9.92e-05, epoch=0.005, grad_norm=12.34, speed=0.82s/it逐项解读:
Step 5/63:当前第5步,共63步 →确认训练在推进,非卡死loss=2.1428:当前批次损失值 →初期应明显下降,若连续10步不降需警惕learning_rate=9.92e-05:当前学习率 →验证warmup是否生效(初始应接近0,逐步上升)speed=0.82s/it:每步耗时 →单卡A100上LoRA训练通常0.5~2s/it,若>5s需检查I/O或CPU瓶颈
新手易错点:
看到loss=nan或loss=inf→ 检查--learning_rate是否过大(LoRA建议1e-4起)、--gradient_clip是否缺失speed突然飙升至10s+ → 检查--dataloader_num_workers是否过高导致CPU争抢,或数据集路径错误触发重试
2. 结构化日志提取:从文本中自动抓取关键指标
终端日志适合实时盯屏,但长期训练(如多日预训练)需要自动化记录与分析。ms-swift将完整日志写入output_dir下的run.log文件,我们可通过脚本将其转为结构化数据。
2.1 日志格式解析与CSV导出
output/run.log中,所有指标行均符合统一正则模式:
Step (\d+)/(\d+): loss=([\d.]+), learning_rate=([\d.e-]+), epoch=([\d.]+), grad_norm=([\d.]+), speed=([\d.]+)s/it以下Python脚本可自动提取并生成CSV:
# extract_metrics.py import re import csv import sys def parse_log_file(log_path, output_csv): pattern = r"Step (\d+)/(\d+): loss=([\d.]+), learning_rate=([\d.e-]+), epoch=([\d.]+), grad_norm=([\d.]+), speed=([\d.]+)s/it" metrics = [] with open(log_path, 'r', encoding='utf-8') as f: for line in f: match = re.search(pattern, line) if match: step, total_steps, loss, lr, epoch, grad_norm, speed = match.groups() metrics.append({ 'step': int(step), 'total_steps': int(total_steps), 'loss': float(loss), 'learning_rate': float(lr), 'epoch': float(epoch), 'grad_norm': float(grad_norm), 'speed_s_per_it': float(speed) }) # 写入CSV if metrics: with open(output_csv, 'w', newline='', encoding='utf-8') as f: writer = csv.DictWriter(f, fieldnames=metrics[0].keys()) writer.writeheader() writer.writerows(metrics) print(f" 成功提取 {len(metrics)} 条指标,保存至 {output_csv}") else: print(" 未匹配到任何指标行,请检查log路径或日志格式") if __name__ == "__main__": if len(sys.argv) != 3: print("用法: python extract_metrics.py <log_path> <output_csv>") sys.exit(1) parse_log_file(sys.argv[1], sys.argv[2])使用方式:
# 在训练目录下执行 python extract_metrics.py ./output/run.log ./output/metrics.csv生成的metrics.csv可直接导入Excel或Pandas进行分析,例如快速计算损失下降率:
import pandas as pd df = pd.read_csv('./output/metrics.csv') print(f"初始loss: {df['loss'].iloc[0]:.4f}, 最终loss: {df['loss'].iloc[-1]:.4f}") print(f"总下降率: {(df['loss'].iloc[0]-df['loss'].iloc[-1])/df['loss'].iloc[0]*100:.1f}%")2.2 关键指标阈值告警脚本
将监控自动化:当指标异常时自动发送通知(邮件/钉钉)。以下为轻量级告警逻辑:
# alert_on_anomaly.py import pandas as pd import time def check_anomalies(csv_path, window_size=10): df = pd.read_csv(csv_path) if len(df) < window_size: return # 检查loss是否连续上升 recent_loss = df['loss'].tail(window_size).values if all(recent_loss[i] <= recent_loss[i+1] for i in range(len(recent_loss)-1)): print(f"🚨 WARNING: Loss has increased for {window_size} consecutive steps!") # 此处可添加钉钉Webhook发送逻辑 # requests.post(webhook_url, json={"msg": "Loss anomaly detected!"}) # 检查梯度范数是否爆炸(>100) if df['grad_norm'].max() > 100: print("💥 CRITICAL: Gradient norm exceeded 100! Possible gradient explosion.") if __name__ == "__main__": while True: try: check_anomalies('./output/metrics.csv') except FileNotFoundError: print("⏳ Log file not ready yet...") time.sleep(60) # 每分钟检查一次3. 训练曲线可视化:用图表代替数字直觉
数字需要图表赋予意义。我们将用Matplotlib将metrics.csv转化为直观曲线,重点关注三个维度:收敛性、稳定性、效率。
3.1 三图合一可视化脚本
# plot_training_curve.py import pandas as pd import matplotlib.pyplot as plt import numpy as np def plot_training_curves(csv_path): df = pd.read_csv(csv_path) fig, axes = plt.subplots(1, 3, figsize=(18, 5)) # 图1:Loss曲线(对数坐标,突出早期下降) axes[0].semilogy(df['step'], df['loss'], 'b-', linewidth=2, label='Training Loss') axes[0].set_xlabel('Step') axes[0].set_ylabel('Loss (log scale)') axes[0].set_title('Loss Convergence') axes[0].grid(True, alpha=0.3) axes[0].legend() # 图2:学习率曲线(验证warmup与decay) axes[1].plot(df['step'], df['learning_rate'], 'g-', linewidth=2, label='Learning Rate') axes[1].set_xlabel('Step') axes[1].set_ylabel('Learning Rate') axes[1].set_title('Learning Rate Schedule') axes[1].grid(True, alpha=0.3) axes[1].legend() # 图3:梯度范数与速度(稳定性双指标) ax1 = axes[2] ax2 = ax1.twinx() ax1.plot(df['step'], df['grad_norm'], 'r-', linewidth=2, label='Grad Norm') ax2.plot(df['step'], df['speed_s_per_it'], 'c--', linewidth=2, label='Speed (s/it)') ax1.set_xlabel('Step') ax1.set_ylabel('Gradient Norm', color='r') ax2.set_ylabel('Speed (s/it)', color='c') ax1.set_title('Stability & Efficiency') ax1.grid(True, alpha=0.3) ax1.legend(loc='upper left') ax2.legend(loc='upper right') plt.tight_layout() plt.savefig('./output/training_curves.png', dpi=300, bbox_inches='tight') print(" Training curves saved to ./output/training_curves.png") plt.show() if __name__ == "__main__": plot_training_curves('./output/metrics.csv')关键洞察点:
- Loss曲线:理想形态是快速下降后平缓收敛。若出现锯齿状剧烈波动,可能
--per_device_train_batch_size过小;若后期完全水平,可能已收敛或陷入局部最优。 - 学习率曲线:应呈现先升后降的平滑倒U形(warmup+cosine decay)。若为直线,检查
--warmup_ratio是否设为0。 - 梯度范数:健康范围通常在1~50之间。若持续>100,需启用
--gradient_clip 1.0;若趋近于0,可能学习率过低或模型已饱和。
进阶提示:在Web-UI中,ms-swift已集成TensorBoard支持。启动训练时添加
--report_to tensorboard,然后运行tensorboard --logdir output/runs即可获得交互式仪表盘。
4. 异常模式诊断:从日志中定位常见故障
即使有完美监控,训练仍可能失败。以下是ms-swift中最常出现的5类异常及其日志特征与解决方案。
4.1 显存溢出(OOM):最频繁的“猝死”原因
日志特征:
torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 GiB...或终端突然中断,无错误信息(静默OOM)。
根因与对策:
| 场景 | 检查点 | 解决方案 |
|---|---|---|
| 单卡OOM | --per_device_train_batch_size=1仍失败 | ↓--max_length(如从2048→1024),↑--gradient_accumulation_steps(保持全局batch不变) |
| 多卡OOM | --deepspeed zero2下仍报错 | 改用zero3或--fp16 true(而非bfloat16,后者显存更高) |
| 多模态OOM | 使用Qwen2-VL等视觉模型 | ↑--max_pixels(如518400→259200),或↓--vision_tower_lr降低视觉编码器更新频率 |
4.2 损失NaN/Inf:训练“中毒”
日志特征:
Step 12/63: loss=nan, learning_rate=9.23e-05, ...根因与对策:
- 学习率过高:LoRA微调时
--learning_rate=1e-3极易导致NaN →立即降至1e-4 - 数据问题:数据集中存在空字符串、非法Unicode字符 →用
--dataset后加#100小样本测试 - 数值不稳定:混合精度训练中梯度爆炸 →添加
--gradient_clip 1.0
4.3 进度停滞:训练“假死”
日志特征:Step 100/63(步数超过总数)或长时间无新日志输出(>10分钟)。
根因与对策:
- 数据集路径错误:
--dataset指向空目录 →检查ls -l /path/to/dataset - 分布式通信失败:多卡训练时NCCL超时 →设置环境变量
export NCCL_ASYNC_ERROR_HANDLING=0 - I/O瓶颈:数据集在慢速NAS上 →将数据集复制到本地SSD,用
--dataset /local/path
4.4 模型加载失败:启动即崩
日志特征:
OSError: Can't load config for 'Qwen/Qwen2.5-7B-Instruct'.根因与对策:
- 网络问题:ModelScope/HF下载中断 →添加
--use_hf true切换源,或提前modelscope download --model Qwen/Qwen2.5-7B-Instruct - 权限问题:Docker容器内无写权限 →启动容器时加
-u $(id -u):$(id -g)
4.5 Web-UI无法访问:界面“隐身”
现象:swift web-ui命令无报错,但浏览器打不开http://localhost:7860
根因与对策:
- 端口被占:
lsof -i :7860查进程并kill -9 - Docker网络:容器内Web-UI绑定
127.0.0.1→改用swift web-ui --server-name 0.0.0.0
5. 工程化监控实践:构建你的训练看板
将上述技巧整合为可持续的工作流,是专业工程师的标志。我们推荐一个最小可行监控栈:
5.1 三层次监控体系
| 层级 | 工具 | 频率 | 作用 |
|---|---|---|---|
| 实时层 | tail -f output/run.log | grep "Step"+nvidia-smi | 秒级 | 快速确认训练存活与显存 |
| 分析层 | extract_metrics.py+plot_training_curve.py | 训练结束后 | 生成报告,归档对比 |
| 告警层 | alert_on_anomaly.py+ 钉钉Webhook | 分钟级 | 主动发现异常,减少人工盯屏 |
5.2 一键监控启动脚本
创建monitor.sh,让监控像启动训练一样简单:
#!/bin/bash # monitor.sh - 一键启动ms-swift训练监控 LOG_DIR="./output" echo " 启动ms-swift训练监控..." echo "├─ 实时日志流监控..." tail -f "$LOG_DIR/run.log" | grep --line-buffered "Step" & echo "├─ GPU显存监控..." nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits -l 2 & echo "├─ 自动指标提取(每5分钟)..." while true; do python extract_metrics.py "$LOG_DIR/run.log" "$LOG_DIR/metrics.csv" 2>/dev/null sleep 300 done & echo "├─ 异常告警服务..." python alert_on_anomaly.py & echo " 监控服务已就绪!按Ctrl+C停止" wait赋予执行权限并运行:
chmod +x monitor.sh ./monitor.sh获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。