#!/usr/bin/env python3 """光湖蒸馏实时监控 v1.0 · 在AutoDL终端直接运行 运行方式: python3 watch_distill.py 功能: 实时滚动展示训练数据,不用手动刷新 主权: 冰朔 TCS-0002∞ · 铸渊 ICE-GL-ZY001 守护 """ import os, sys, time, json, subprocess from datetime import datetime LOG = '/root/autodl-tmp/distill_mother.log' STATUS = '/root/autodl-tmp/training_status.json' WATCHDOG = '/root/autodl-tmp/watchdog.log' def get_gpu_info(): try: r = subprocess.run( ['nvidia-smi', '--query-gpu=temperature.gpu,utilization.gpu,memory.used,memory.total', '--format=csv,noheader'], capture_output=True, text=True, timeout=5) parts = r.stdout.strip().split(', ') return {'temp': parts[0], 'util': parts[1], 'mem_used': parts[2], 'mem_total': parts[3]} except: return None def get_last_losses(n=10): if not os.path.isfile(LOG): return [] losses = [] with open(LOG) as f: for line in f: if 'loss=' in line: losses.append(line.strip()) return losses[-n:] def get_current_progress(): if not os.path.isfile(LOG): return None with open(LOG) as f: for line in f: pass last = line.strip() if line else '' return last def get_watchdog(): if not os.path.isfile(WATCHDOG): return None with open(WATCHDOG) as f: for line in f: pass return line.strip() if line else None def get_status(): if not os.path.isfile(STATUS): return None with open(STATUS) as f: return json.load(f) def draw_progress_bar(pct, width=30): filled = int(pct * width / 100) bar = '█' * filled + '░' * (width - filled) return f'[{bar}] {pct:.0f}%' def main(): print('\033[2J\033[H', end='') # clear print('═══ 光湖蒸馏实时监控 v1.0 ═══') print('═══ 主权:冰朔 TCS-0002∞ · 铸渊守护 ═══') print('(每5秒自动刷新 · Ctrl+C 退出)') print() last_loss_count = 0 loss_history = [] try: while True: # 移动光标到顶部 print('\033[H', end='') gpu = get_gpu_info() progress = get_current_progress() wd = get_watchdog() st = get_status() losses = get_last_losses(20) now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # === GPU状态 === if gpu: temp = int(gpu['temp']) temp_warn = ' ⚠ 偏高' if temp > 80 else ' ✓' mem_u = int(gpu['mem_used'].replace(' MiB', '')) mem_t = int(gpu['mem_total'].replace(' MiB', '')) mem_pct = mem_u * 100 // mem_t if mem_t > 0 else 0 mem_warn = ' ⚠ 高负载' if mem_pct > 95 else ' ✓' print(f'┌─ GPU 状态 ─────────────────────────────┐') print(f'│ 温度: {temp}°C{temp_warn}') print(f'│ 利用率: {gpu["util"]}') print(f'│ 显存: {mem_u}MB / {mem_t}MB ({mem_pct}%){mem_warn}') print(f'└────────────────────────────────────────┘') else: print('┌─ GPU ──────────────────────────────────┐') print('│ ❌ 无法获取GPU信息') print('└────────────────────────────────────────┘') print() # === 蒸馏进度 === print('┌─ 蒸馏进度 ─────────────────────────────┐') if progress: # 解析 Ep 和步数 if 'Ep' in progress and '/' in progress: ep_part = progress.split('|')[0].strip() if '|' in progress else progress.split()[0] step_part = progress.split('|')[1].strip().split()[0] if '|' in progress else '' print(f'│ 当前: {ep_part}') if '/' in step_part: cur, total = step_part.split('/') cur, total = int(cur), int(total) pct = cur * 100 / total if total > 0 else 0 print(f'│ 步数: {cur}/{total}') print(f'│ 进度: {draw_progress_bar(pct)}') # 最近loss行 if losses: last_loss = losses[-1] print(f'│ Loss: {last_loss}') else: print('│ ⏳ 等待蒸馏开始...') print('└────────────────────────────────────────┘') print() # === Loss趋势 === if losses: print('┌─ 最近Loss趋势 ─────────────────────────┐') # 找出loss值的范围 loss_vals = [] for l in losses: try: # 提取 loss= 后面的数字 idx = l.index('loss=') end = l.index(' ', idx) if ' ' in l[idx:] else len(l) val = float(l[idx+5:end]) loss_vals.append(val) except: pass if loss_vals: min_l, max_l = min(loss_vals), max(loss_vals) rng = max_l - min_l if max_l > min_l else 1 # 打印mini图表 for i, v in enumerate(loss_vals): rel = (v - min_l) / rng bar_len = int((1 - rel) * 20) # loss越低越长 marker = '↓' if i > 0 and v < loss_vals[i-1] else ('↑' if i > 0 and v > loss_vals[i-1] else '→') print(f'│ {marker} loss={v:.4f} {"█"*bar_len}') print('└────────────────────────────────────────┘') print() # === 看门狗 === if wd: print(f'┌─ 看门狗 ───────────────────────────────┐') print(f'│ {wd}') print(f'└────────────────────────────────────────┘') print() # === 状态摘要 === if st: mm = st.get('mother_model', {}) gs = mm.get('gpu', {}) print(f'┌─ 系统状态 ─────────────────────────────┐') print(f'│ 时间: {now}') print(f'│ 母模型: {mm.get("status_label", "?")}') print(f'│ 蒸馏状态: {"🟢 运行中" if mm.get("status") == "training" else "⚪ 待开始/已完成"}') print(f'│ GPU温度: {gs.get("temp_c", "?")}°C') print(f'│ 显存: {gs.get("mem_used_mb", 0)//1024}GB/{gs.get("mem_total_mb", 0)//1024}GB') print(f'└────────────────────────────────────────┘') # 刷新间隔 time.sleep(5) except KeyboardInterrupt: print('\n\n监控已退出。蒸馏继续在后台运行。') print('重新查看: tail -f /root/autodl-tmp/distill_mother.log') sys.exit(0) if __name__ == '__main__': main()