guanghulab/server/training-agent/watch-training-output.sh

84 lines
3.3 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════
# 训练输出解析器 · watch-training-output.sh
# ═══════════════════════════════════════════════════════════
# 签发: 铸渊 · ICE-GL-ZY001 · 国作登字-2026-A-00037559
#
# 把 train.py 的 stdout 流式解析,每识别到一行
# ZY_PROGRESS step=N total=M loss=X lr=Y epoch=E total_epochs=TE
# 就调用 progress-reporter.sh 上报。
#
# 同时把所有原文透传到 stdout保留训练日志
#
# 用法:
# python train.py | watch-training-output.sh
# ═══════════════════════════════════════════════════════════
set -uo pipefail
ROOT="${ZY_TRAIN_ROOT:-/opt/guanghu/training}"
REPORTER="$ROOT/progress-reporter.sh"
ENV_FILE="$ROOT/.env"
if [[ -f "$ENV_FILE" ]]; then
# shellcheck disable=SC1090
set -a; source "$ENV_FILE"; set +a
fi
REPORT_INTERVAL_STEPS="${ZY_REPORT_EVERY_STEPS:-10}"
LAST_REPORT_STEP=-1
START_TS=$(date -u +%s)
while IFS= read -r line; do
# 透传
printf '%s\n' "$line"
# 只解析 ZY_PROGRESS 标记行
if [[ "$line" != *"ZY_PROGRESS"* ]]; then
continue
fi
# 提取 key=value
STEP=$(echo "$line" | grep -oE 'step=[0-9]+' | head -1 | cut -d= -f2 || true)
TOTAL=$(echo "$line" | grep -oE 'total=[0-9]+' | head -1 | cut -d= -f2 || true)
LOSS=$(echo "$line" | grep -oE 'loss=[0-9.eE+\-]+' | head -1 | cut -d= -f2 || true)
LR=$(echo "$line" | grep -oE 'lr=[0-9.eE+\-]+' | head -1 | cut -d= -f2 || true)
EPOCH=$(echo "$line" | grep -oE 'epoch=[0-9]+' | head -1 | cut -d= -f2 || true)
TE=$(echo "$line" | grep -oE 'total_epochs=[0-9]+' | head -1 | cut -d= -f2 || true)
THR=$(echo "$line" | grep -oE 'thr=[0-9.eE+\-]+' | head -1 | cut -d= -f2 || true)
STEP=${STEP:-0}; TOTAL=${TOTAL:-0}; EPOCH=${EPOCH:-0}; TE=${TE:-0}
LOSS=${LOSS:-null}; LR=${LR:-null}; THR=${THR:-null}
# 节流:每 N 步上报一次(最后一步=训练完成必报;首步必报)
IS_LAST=0
if [[ "$TOTAL" -gt 0 && "$STEP" -ge "$TOTAL" ]]; then
IS_LAST=1
fi
if (( IS_LAST == 0 )) && (( LAST_REPORT_STEP >= 0 )) && (( STEP - LAST_REPORT_STEP < REPORT_INTERVAL_STEPS )); then
continue
fi
LAST_REPORT_STEP=$STEP
NOW=$(date -u +%s)
ELAPSED=$(( NOW - START_TS ))
ETA="null"
if [[ "$STEP" -gt 0 && "$TOTAL" -gt 0 && "$STEP" -lt "$TOTAL" ]]; then
PER=$(awk "BEGIN{print $ELAPSED/$STEP}")
LEFT=$(( TOTAL - STEP ))
ETA=$(awk "BEGIN{printf \"%d\",$PER*$LEFT}")
fi
PROG=$(printf '{"step":%s,"total_steps":%s,"epoch":%s,"total_epochs":%s,"loss":%s,"learning_rate":%s,"throughput_samples_per_sec":%s,"eta_seconds":%s,"elapsed_seconds":%s}' \
"$STEP" "$TOTAL" "$EPOCH" "$TE" "$LOSS" "$LR" "$THR" "$ETA" "$ELAPSED")
LABEL="训练第 ${STEP}/${TOTAL}"
MSG="step=${STEP}/${TOTAL} loss=${LOSS} lr=${LR}"
"$REPORTER" "training" "$LABEL" "$PROG" "$MSG" >/dev/null 2>&1 || true
done
# 训练循环结束 → done
"$REPORTER" "done" "训练完成" "" "train.py exited cleanly on $(hostname)" || true
echo "done" > "$ROOT/.phase" 2>/dev/null || true