训练监控与可观测性
有效的监控和可观测性是保障训练任务稳定运行的基础。通过实时监控训练进度、资源使用和性能指标,可以及时发现问题、优化资源利用,并为后续的训练调优提供数据支撑。
为什么需要监控
AI 训练任务具有以下特点,使得监控尤为重要:
| 特点 | 监控需求 |
|---|---|
| 运行时间长(小时到天级) | 需要持续追踪进度,避免长时间无响应 |
| 资源消耗大(GPU、显存) | 需要监控资源利用率,优化成本 |
| 故障影响大(重新训练) | 需要及时发现异常,快速恢复 |
| 参数调优复杂 | 需要记录训练曲线,支持实验对比 |
一个完善的监控系统应该回答以下问题:
- 训练进度如何?预计何时完成?
- GPU 利用率和显存使用是否正常?
- 损失曲线是否收敛?
- 是否存在异常或错误?
- 与历史实验相比性能如何?
监控体系架构
一个典型的训练监控体系包含多个层次:
┌─────────────────────────────────────────────────────────────┐
│ 可视化层 │
│ Grafana │ TensorBoard │ WANDB │
├─────────────────────────────────────────────────────────────┤
│ 存储层 │
│ Prometheus │ InfluxDB │ Elasticsearch │
├─────────────────────────────────────────────────────────────┤
│ 采集层 │
│ DCGM Exporter │ Node Exporter │ 自定义指标采集 │
├─────────────────────────────────────────────────────────────┤
│ 数据源 │
│ GPU 硬件 │ 训练框架 │ 操作系统 │ 应用日志 │
└─────────────────────────────────────────────────────────────┘
GPU 监控
GPU 是 AI 训练的核心资源,需要重点监控。
关键监控指标
| 指标类别 | 具体指标 | 说明 |
|---|---|---|
| 计算资源 | GPU 利用率 | 计算单元的使用比例 |
| Tensor Core 利用率 | 矩阵运算单元的使用比例 | |
| 内存资源 | 显存使用量 | 已分配和预留的显存 |
| 显存带宽利用率 | 内存传输带宽使用比例 | |
| 热管理 | GPU 温度 | 核心温度,影响稳定性 |
| 功耗 | 实时功耗,影响成本 | |
| 互联 | NVLink 流量 | GPU 间通信量 |
| PCIe 流量 | GPU 与 CPU/内存的数据传输 |
DCGM Exporter
NVIDIA DCGM(Data Center GPU Manager)Exporter 是收集 GPU 指标的标准工具。
安装部署:
# 使用 Docker 部署
docker run -d --name dcgm-exporter \
--gpus all \
-p 9400:9400 \
nvcr.io/nvidia/k8s/dcgm-exporter:3.3.0-3.4.0-ubuntu22.04
# 验证指标
curl http://localhost:9400/metrics
Kubernetes 部署:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dcgm-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: dcgm-exporter
template:
metadata:
labels:
app: dcgm-exporter
spec:
containers:
- name: dcgm-exporter
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.0-3.4.0-ubuntu22.04
ports:
- containerPort: 9400
name: metrics
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: gpu-resources
mountPath: /var/lib/kubelet/pod-resources
volumes:
- name: gpu-resources
hostPath:
path: /var/lib/kubelet/pod-resources
关键指标说明:
# GPU 计算利用率(0-100)
DCGM_FI_DEV_GPU_UTIL
# GPU 显存使用量(字节)
DCGM_FI_DEV_FB_USED
# GPU 显存总量
DCGM_FI_DEV_FB_FREE
# GPU 温度(摄氏度)
DCGM_FI_DEV_GPU_TEMP
# GPU 功耗(瓦特)
DCGM_FI_DEV_POWER_USAGE
# NVLink 吞吐量
DCGM_FI_NVLINK_THROUGHPUT_TX
DCGM_FI_NVLINK_THROUGHPUT_RX
nvidia-smi 监控
基础的 GPU 监控可以通过 nvidia-smi 命令实现:
# 查看所有 GPU 状态
nvidia-smi
# 持续监控(每秒刷新)
watch -n 1 nvidia-smi
# 查看详细信息
nvidia-smi -q
# 只看计算模式和功耗
nvidia-smi --query-gpu=index,utilization.gpu,power.draw --format=csv
# 导出历史数据
nvidia-smi --query-gpu=timestamp,index,utilization.gpu,memory.used --format=csv -l 1 > gpu_log.csv
Python 获取 GPU 信息:
import pynvml
pynvml.nvmlInit()
device_count = pynvml.nvmlDeviceGetCount()
for i in range(device_count):
handle = pynvml.nvmlDeviceGetHandleByIndex(i)
# 基本信息
name = pynvml.nvmlDeviceGetName(handle)
memory_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
utilization = pynvml.nvmlDeviceGetUtilizationRates(handle)
temperature = pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU)
power = pynvml.nvmlDeviceGetPowerUsage(handle) / 1000 # 转换为瓦特
print(f"GPU {i}: {name.decode()}")
print(f" 显存: {memory_info.used / 1024**3:.1f}GB / {memory_info.total / 1024**3:.1f}GB")
print(f" 利用率: {utilization.gpu}%")
print(f" 温度: {temperature}°C")
print(f" 功耗: {power:.1f}W")
pynvml.nvmlShutdown()
训练指标监控
除了硬件指标,训练过程中的软件指标同样重要。
核心训练指标
| 指标 | 说明 | 健康状态判断 |
|---|---|---|
| Loss | 训练损失 | 持续下降,无异常波动 |
| Learning Rate | 学习率 | 按计划变化 |
| Gradient Norm | 梯度范数 | 不爆炸、不消失 |
| Throughput | 吞吐量(samples/s) | 稳定或提升 |
| Epoch Progress | Epoch 进度 | 正常推进 |
| Validation Metrics | 验证集指标 | 与训练集同步改善 |
TensorBoard
TensorBoard 是 PyTorch 和 TensorFlow 内置的可视化工具。
PyTorch 集成:
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(log_dir="runs/experiment_1")
for epoch in range(num_epochs):
for batch_idx, (data, target) in enumerate(train_loader):
# 训练步骤
loss = train_step(model, data, target)
# 记录训练损失
global_step = epoch * len(train_loader) + batch_idx
writer.add_scalar('Loss/train', loss, global_step)
writer.add_scalar('Learning_Rate', optimizer.param_groups[0]['lr'], global_step)
# 验证
val_loss, val_acc = validate(model, val_loader)
writer.add_scalar('Loss/validation', val_loss, epoch)
writer.add_scalar('Accuracy/validation', val_acc, epoch)
# 记录模型参数分布
for name, param in model.named_parameters():
writer.add_histogram(f'Parameters/{name}', param, epoch)
if param.grad is not None:
writer.add_histogram(f'Gradients/{name}', param.grad, epoch)
writer.close()
启动 TensorBoard:
tensorboard --logdir=runs --port=6006
高级可视化:
# 记录嵌入向量
writer.add_embedding(
features,
metadata=labels,
label_img=images,
global_step=epoch,
tag='embeddings'
)
# 记录模型图
writer.add_graph(model, input_sample)
# 记录文本
writer.add_text('hyperparameters', str(config), 0)
# 记录图像
writer.add_image('input', image_tensor, global_step)
# 自定义图表
from tensorboardX import SummaryWriter
writer.add_custom_scalars_multiline_chart(
'losses',
['Loss/train', 'Loss/validation']
)
Weights & Biases (W&B)
W&B 是专业的机器学习实验跟踪平台,提供更丰富的功能。
基本使用:
import wandb
# 初始化实验
wandb.init(
project="my-project",
name="experiment-1",
config={
"learning_rate": 1e-4,
"batch_size": 32,
"model": "llama-7b"
}
)
# 训练循环
for epoch in range(num_epochs):
for batch in train_loader:
loss = train_step(model, batch)
# 记录指标
wandb.log({
"loss": loss,
"learning_rate": optimizer.param_groups[0]['lr'],
"epoch": epoch
})
# 保存模型
wandb.save("model.pt")
# 结束实验
wandb.finish()
记录模型和数据:
# 记录模型检查点
artifact = wandb.Artifact('model', type='model')
artifact.add_file('model.pt')
wandb.log_artifact(artifact)
# 记录数据集
dataset_artifact = wandb.Artifact('dataset', type='dataset')
dataset_artifact.add_dir('data/')
wandb.log_artifact(dataset_artifact)
# 可视化预测
table = wandb.Table(columns=["image", "prediction", "label"])
for img, pred, label in samples:
table.add_data(wandb.Image(img), pred, label)
wandb.log({"predictions": table})
实验对比:
W&B 提供了强大的实验对比功能,可以:
- 并排比较多次实验的指标曲线
- 分析超参数对结果的影响
- 自动生成实验报告
MLflow
MLflow 是开源的机器学习生命周期管理平台。
import mlflow
# 开始实验
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("my-experiment")
with mlflow.start_run():
# 记录参数
mlflow.log_param("learning_rate", 1e-4)
mlflow.log_param("batch_size", 32)
# 训练
for epoch in range(num_epochs):
loss = train_epoch(model, train_loader)
val_loss = validate(model, val_loader)
# 记录指标
mlflow.log_metric("train_loss", loss, step=epoch)
mlflow.log_metric("val_loss", val_loss, step=epoch)
# 保存模型
mlflow.pytorch.log_model(model, "model")
Prometheus + Grafana
Prometheus 和 Grafana 是监控领域的黄金组合,适合生产环境。
Prometheus 配置
prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# DCGM Exporter(GPU 指标)
- job_name: 'dcgm-exporter'
static_configs:
- targets: ['dcgm-exporter:9400']
# Node Exporter(系统指标)
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
# 训练任务指标
- job_name: 'training-metrics'
static_configs:
- targets: ['training-server:8000']
自定义训练指标导出:
from prometheus_client import Counter, Gauge, Histogram, start_http_server
import time
# 定义指标
training_samples = Counter(
'training_samples_total',
'Total training samples processed'
)
training_loss = Gauge(
'training_loss',
'Current training loss'
)
training_duration = Histogram(
'training_step_duration_seconds',
'Time spent on each training step'
)
gpu_memory_used = Gauge(
'gpu_memory_used_bytes',
'GPU memory used',
['gpu_id']
)
# 启动 HTTP 服务器
start_http_server(8000)
# 训练循环
for batch in train_loader:
start_time = time.time()
loss = train_step(model, batch)
# 更新指标
training_samples.inc(len(batch))
training_loss.set(loss)
training_duration.observe(time.time() - start_time)
# GPU 显存
for i in range(torch.cuda.device_count()):
mem = torch.cuda.memory_allocated(i)
gpu_memory_used.labels(gpu_id=str(i)).set(mem)
Grafana Dashboard
创建 GPU 监控 Dashboard:
{
"dashboard": {
"title": "GPU Training Monitor",
"panels": [
{
"title": "GPU Utilization",
"type": "graph",
"targets": [
{
"expr": "avg(DCGM_FI_DEV_GPU_UTIL)",
"legendFormat": "Average"
},
{
"expr": "DCGM_FI_DEV_GPU_UTIL",
"legendFormat": "GPU {{gpu}}"
}
]
},
{
"title": "GPU Memory",
"type": "graph",
"targets": [
{
"expr": "DCGM_FI_DEV_FB_USED / 1024 / 1024 / 1024",
"legendFormat": "GPU {{gpu}}"
}
]
},
{
"title": "Training Loss",
"type": "graph",
"targets": [
{
"expr": "training_loss",
"legendFormat": "Loss"
}
]
}
]
}
}
常用 Grafana 查询:
# GPU 平均利用率
avg(DCGM_FI_DEV_GPU_UTIL)
# GPU 显存使用率
DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) * 100
# 训练吞吐量(样本/秒)
rate(training_samples_total[1m])
# P95 训练步骤延迟
histogram_quantile(0.95, rate(training_step_duration_seconds_bucket[5m]))
# GPU 温度告警
DCGM_FI_DEV_GPU_TEMP > 85
日志管理
日志是故障排查和性能分析的重要数据源。
结构化日志
使用结构化日志格式便于后续分析:
import structlog
logger = structlog.get_logger()
# 配置日志
structlog.configure(
processors=[
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.add_log_level,
structlog.processors.JSONRenderer()
]
)
# 使用日志
logger.info(
"training_step_completed",
step=global_step,
loss=loss,
learning_rate=lr,
gpu_memory=torch.cuda.memory_allocated() / 1e9
)
logger.error(
"cuda_error",
error_code=error.code,
gpu_id=gpu_id,
details=str(error)
)
日志收集
使用 Filebeat 收集日志:
# filebeat.yml
filebeat.inputs:
- type: log
paths:
- /var/log/training/*.log
fields:
type: training
json.keys_under_root: true
output.elasticsearch:
hosts: ["elasticsearch:9200"]
index: "training-logs-%{+yyyy.MM.dd}"
使用 Fluentd:
<source>
@type tail
path /var/log/training/*.log
tag training
<parse>
@type json
</parse>
</source>
<match training>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix training
</match>
日志分析
使用 Elasticsearch 和 Kibana 进行日志分析:
# 搜索错误日志
from elasticsearch import Elasticsearch
es = Elasticsearch(['http://elasticsearch:9200'])
# 查询最近的 CUDA 错误
result = es.search(
index="training-logs-*",
body={
"query": {
"bool": {
"must": [
{"match": {"level": "ERROR"}},
{"match": {"message": "CUDA"}}
]
}
},
"sort": [{"@timestamp": "desc"}],
"size": 100
}
)
告警配置
及时发现异常是监控的核心目的。
Prometheus 告警规则
groups:
- name: training-alerts
rules:
# GPU 温度过高
- alert: HighGPUTemperature
expr: DCGM_FI_DEV_GPU_TEMP > 85
for: 5m
labels:
severity: warning
annotations:
summary: "GPU 温度过高"
description: "GPU {{ $labels.gpu }} 温度 {{ $value }}°C 超过 85°C"
# GPU 利用率过低
- alert: LowGPUUtilization
expr: avg(DCGM_FI_DEV_GPU_UTIL) < 30
for: 10m
labels:
severity: warning
annotations:
summary: "GPU 利用率过低"
description: "平均 GPU 利用率 {{ $value }}% 低于 30%"
# 训练损失异常
- alert: TrainingLossSpike
expr: training_loss > 10 * training_loss offset 5m
for: 1m
labels:
severity: critical
annotations:
summary: "训练损失异常上升"
description: "训练损失突然上升超过 10 倍"
# 显存不足
- alert: GPUOOMWarning
expr: (DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)) > 0.95
for: 2m
labels:
severity: warning
annotations:
summary: "GPU 显存即将耗尽"
description: "GPU {{ $labels.gpu }} 显存使用率超过 95%"
# 训练卡住
- alert: TrainingStuck
expr: increase(training_samples_total[10m]) == 0
labels:
severity: critical
annotations:
summary: "训练可能已卡住"
description: "过去 10 分钟没有训练任何样本"
Alertmanager 配置
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.example.com:587'
smtp_from: '[email protected]'
route:
group_by: ['alertname', 'severity']
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
receiver: 'team-email'
routes:
- match:
severity: critical
receiver: 'team-pagerduty'
receivers:
- name: 'team-email'
email_configs:
- to: '[email protected]'
send_resolved: true
- name: 'team-pagerduty'
pagerduty_configs:
- service_key: 'your-service-key'
监控最佳实践
指标选择原则
监控指标应该遵循 USE 和 RED 方法:
USE 方法(资源指标):
- Utilization:资源使用率(如 GPU 利用率)
- Saturation:饱和度(如显存使用率)
- Errors:错误数(如 CUDA 错误)
RED 方法(服务指标):
- Rate:请求速率(如训练样本/秒)
- Errors:错误率
- Duration:延迟(如训练步骤耗时)
Dashboard 设计原则
- 层次分明:从概览到详细,逐层深入
- 重点突出:关键指标放在显眼位置
- 关联展示:相关指标放在一起便于分析
- 时间范围:支持灵活的时间范围选择
性能考虑
监控系统本身也会消耗资源,需要平衡监控粒度和开销:
| 监控频率 | 适用场景 | 开销 |
|---|---|---|
| 1s | 实时调试、故障排查 | 较高 |
| 10s | 日常监控 | 中等 |
| 1m | 长期趋势分析 | 较低 |
减少开销的方法:
- 使用聚合指标而非原始指标
- 适当增加采集间隔
- 使用 downsampling 保留长期数据
小结
完善的监控体系是保障训练稳定运行的基础。本章介绍了:
- GPU 监控:DCGM Exporter 和 nvidia-smi 的使用
- 训练指标监控:TensorBoard、W&B、MLflow 的集成
- Prometheus + Grafana:生产级监控方案
- 日志管理:结构化日志和日志收集
- 告警配置:及时发现和响应异常
选择合适的工具组合,建立完善的监控告警体系,可以显著提升训练任务的可靠性和运维效率。
参考资料
官方文档
- NVIDIA DCGM 文档 - GPU 数据中心管理
- TensorBoard 文档 - 训练可视化
- Weights & Biases 文档 - 实验跟踪平台
- Prometheus 文档 - 监控系统
- Grafana 文档 - 可视化平台
相关论文
- PagedAttention 论文 - vLLM 核心技术