第五轮:锁/续跑/超时/自动分支/R4 探针与 encode-once/控制台加固;阶段内断点续跑(gen 按条、prep 按 shard、训练与 APG 定时原子存档+体面停止);分歧以实现方意见为准

This commit is contained in:
Hark
2026-10-17 18:57:48 +00:00
parent e3bb78388d
commit a5e779c515
36 changed files with 1199 additions and 285 deletions
+44
View File
@@ -0,0 +1,44 @@
"""断点存档与"体面停止"(2026-10-18 第五轮补充:用户要求长训练意外中断后能接着跑,不从头来)。
- atomic_save:先写 <名>.tmp 再 os.replace —— 断电/被杀发生在写盘中途时,旧的 last.pt 仍完整可用。
- Saver.due():按墙钟每 every_min 分钟存一次 last.pt(不再只在每 2000 步验证时存,断电最多丢 10 分钟)。
- Saver.stop():收到 SIGTERM/SIGINT,或看到停止标记文件 ml/runs/STOP 时返回 True;
训练循环据此先存 last.pt 再以 143 退出。Windows 上 taskkill /F 是硬杀、收不到信号,
所以控制台「停止」先写 STOP 文件、等进程自己存档退出,超时才硬杀(见 monitor.Job.stop)。
"""
import os, signal, time
import torch
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
STOP = os.path.join(ROOT, 'ml', 'runs', 'STOP')
def atomic_save(obj, path):
tmp = path + '.tmp'
torch.save(obj, tmp)
os.replace(tmp, path)
class Saver:
def __init__(self, every_min=10.0):
self.every = every_min * 60; self.last = time.time(); self._sig = False; self._chk = 0.0
for s in ('SIGTERM', 'SIGINT', 'SIGBREAK', 'SIGHUP'):
if hasattr(signal, s):
try: signal.signal(getattr(signal, s), self._on)
except (ValueError, OSError): pass # 非主线程等情况:只靠 STOP 文件
def _on(self, *_): self._sig = True
def stop(self):
if self._sig: return True
now = time.time()
if now - self._chk > 2: # 文件每 2 s 查一次,不拖慢训练步
self._chk = now; self._sig = os.path.exists(STOP)
return self._sig
def due(self):
if time.time() - self.last >= self.every:
self.last = time.time(); return True
return False
def mark(self): self.last = time.time()