realstyle2_anima / scripts /watch_training.py
ACCC1380's picture
Add 40-epoch Anima LoRA, PixAI captions and training artifacts (no dataset images)
f8bbfa7 verified
Raw History Blame Contribute Delete
3.73 kB
"""Durable monitor and automatic packaging/upload after successful training."""
import json
import os
import re
import shutil
import signal
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path('/data/realstyle2')
pid = int(sys.argv[1])
log = Path(sys.argv[2])
started = time.time()
target_epochs = 40
eta_reduced = False
while True:
data = log.read_text(errors='replace')
matches = re.findall(r'steps:\s+[^\r\n]*?\b(\d+)/(\d+)\s+\[([^\]]+)\](?:[^\r\n]*)', data)
epochs = re.findall(r'epoch (\d+)/(\d+)', data)
progress = matches[-1] if matches else None
projected_seconds = None
if progress and int(progress[0]) >= 100:
rate = re.search(r'([\d.]+)s/it', progress[2])
if rate:
projected_seconds = float(rate[1]) * int(progress[1]) * target_epochs / 40
if projected_seconds > 4 * 3600 and target_epochs > 30:
target_epochs = 35 if float(rate[1]) * int(progress[1]) * 35 / 40 <= 4 * 3600 else 30
eta_reduced = True
(ROOT / 'metadata/eta_reduction.json').write_text(json.dumps({
'target_epochs': target_epochs, 'estimated_full_40_seconds': float(rate[1]) * int(progress[1]),
'at_step': int(progress[0]), 'reason': 'User requested a reduction when projected training exceeds 4 hours.'}, indent=2))
stopped_at_limit = False
if eta_reduced and epochs and int(epochs[-1][0]) > target_epochs:
# Entering the next epoch proves the checkpoint and optimizer state were fully saved.
checkpoint = ROOT / 'output' / f'realstyle2_anima-{target_epochs:06d}.safetensors'
assert checkpoint.exists()
os.kill(pid, signal.SIGTERM)
for _ in range(30):
try:
if Path(f'/proc/{pid}/stat').read_text().split()[2] == 'Z':
break
except FileNotFoundError:
break
time.sleep(1)
shutil.copyfile(checkpoint, ROOT / 'output/realstyle2_anima.safetensors')
import toml
config_path = ROOT / 'configs/train.toml'
config = toml.load(config_path)
config['max_train_epochs'] = target_epochs
config_path.write_text(toml.dumps(config))
stopped_at_limit = True
gpu = subprocess.check_output(['nvidia-smi', '--query-gpu=memory.used,utilization.gpu', '--format=csv,noheader'], text=True).strip()
status = {'pid': pid, 'epoch': epochs[-1] if epochs else None, 'progress': progress,
'gpu': gpu, 'updated_at': time.time(), 'elapsed_monitor_seconds': time.time()-started,
'target_epochs': target_epochs, 'projected_training_seconds': projected_seconds,
'saved_checkpoints': len(list((ROOT / 'output').glob('*.safetensors')))}
(ROOT / 'status.json').write_text(json.dumps(status, indent=2))
print(json.dumps(status), flush=True)
try:
os.kill(pid, 0)
process_state = Path(f'/proc/{pid}/stat').read_text().split()[2]
alive = process_state != 'Z'
except (ProcessLookupError, FileNotFoundError):
alive = False
if stopped_at_limit:
break
if not alive:
if 'model saved.' not in data or not (ROOT / 'output/realstyle2_anima.safetensors').is_file():
raise RuntimeError('Training process exited before successful final save; inspect training log')
break
time.sleep(30)
print('Training complete. Validating, packaging, uploading.', flush=True)
with (ROOT / 'logs/upload.log').open('w') as out:
subprocess.run(['/data/venv-training/bin/python', str(ROOT / 'scripts/package_upload.py')], stdout=out, stderr=subprocess.STDOUT, check=True)
print('UPLOAD COMPLETE', flush=True)