Download train.py from cccat6/FlowMo-WM: direct link, hf CLI and curl.
- Browser
- Download file 6.25 kB
-
https://huggingface.co/cccat6/FlowMo-WM/resolve/main/train.py
- Command line
-
hf download hf://cccat6/FlowMo-WM/train.py
-
curl -L -o train.py https://huggingface.co/cccat6/FlowMo-WM/resolve/main/train.py
6.25 kB
| """Train the published FlowMo configurations without overwriting supplied weights.""" | |
| from pathlib import Path | |
| import argparse | |
| import json | |
| import os | |
| import subprocess | |
| import sys | |
| import time | |
| CORE = ['flowmo', 'history32', 'history8', 'flowmo_additive', 'flowmo_film'] | |
| INITIAL = ['flowmo', 'leworldmodel', 'planet', 'tdmpc2'] | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--package', type=Path, default=Path(__file__).resolve().parent) | |
| parser.add_argument('--output', type=Path, required=True) | |
| parser.add_argument('--groups', nargs='+', choices=['initial', 'core', 'ood', 'scaling', 'all'], default=['core']) | |
| parser.add_argument('--methods', nargs='+', choices=sorted(set(CORE + INITIAL))) | |
| parser.add_argument('--seeds', nargs='+', type=int, default=list(range(1, 9))) | |
| parser.add_argument('--device', default='cuda') | |
| parser.add_argument('--smoke', action='store_true', help='One optimizer step; never formal paper statistics') | |
| parser.add_argument('--dry-run', action='store_true') | |
| args = parser.parse_args() | |
| root, out = args.package.resolve(), args.output.resolve() | |
| if out == root or out in root.parents or any(out == root / d or root / d in out.parents for d in ['data', 'experiments', 'paper', 'assets', 'provenance']): | |
| raise ValueError('Choose a separate training output directory') | |
| if any(s not in range(1, 9) for s in args.seeds): | |
| raise ValueError('Formal seeds are 1 through 8') | |
| cfg = json.loads((root / 'experiments/shared/config/rebuttal_v1.json').read_text()) | |
| groups = ['initial', 'core', 'ood', 'scaling'] if 'all' in args.groups else args.groups | |
| jobs = [] | |
| for group in groups: | |
| methods = INITIAL if group == 'initial' else (['flowmo'] if group == 'scaling' else CORE) | |
| if args.methods: | |
| methods = [m for m in methods if m in args.methods] | |
| for method in methods: | |
| for seed in ([19] if group == 'initial' else args.seeds): | |
| for count in ([300, 600, 1200] if group == 'scaling' else [2400]): | |
| checkpoint = ('paper.pt' if group == 'initial' else | |
| f'rebuttal_scale_e{count:04d}_seed_{seed:04d}.pt' if group == 'scaling' else | |
| f'rebuttal_{group}_seed_{seed:04d}.pt') | |
| train_windows = cfg['training']['train_windows'] * count // 2400 | |
| job = dict(group=group, method=method, seed=seed, episodes=count, checkpoint=checkpoint) | |
| cmd = [sys.executable, '-m', 'experiments.train_image_world_models', '--methods', method, | |
| '--train-source', str(root / cfg['data']['train_source']), '--test-source', str(root / cfg['data']['test_source']), | |
| '--train-episodes', str(64 if args.smoke else count), '--test-episodes', str(64 if args.smoke else 480), | |
| '--history-len', '32', '--horizon', '60', '--train-windows', str(512 if args.smoke else train_windows), | |
| '--test-windows', str(64 if args.smoke else 24576), '--batch-size', '256', | |
| '--microbatch-size', '64', '--eval-batch-size', '64', | |
| '--steps', '1' if args.smoke else '20000', '--lr', '0.0003', '--log-every', '1' if args.smoke else '200', | |
| '--seed', str(seed), '--seed-mode', 'method_offset' if group == 'initial' else 'paired', | |
| '--device', args.device, '--num-workers', '4', '--image-size', '160', '--visual-scale', '2.5', | |
| '--render-mode', 'device', '--precision', 'bf16', '--target-mode', 'absolute_normalized', | |
| '--heading-weight', '2.0', '--current-pose-weight', '1.0', '--motion-weight', '0.5', | |
| '--checkpoint-name', checkpoint, '--checkpoint-interval', '0'] | |
| if group == 'ood': | |
| families = [f for f in cfg['flow_families'] if f not in cfg['heldout_flow_families']] | |
| cmd += ['--train-flow-families', *families, '--test-flow-families', *cfg['heldout_flow_families']] | |
| if group == 'initial': | |
| for flag in ['--microbatch-size', '--eval-batch-size', '--seed-mode']: | |
| index = cmd.index(flag) | |
| del cmd[index:index + 2] | |
| job['source_tree'] = 'protocols/initial/src' if group == 'initial' else '.' | |
| jobs.append(dict(job, command=cmd)) | |
| if not jobs: | |
| raise ValueError('No configurations selected') | |
| if args.dry_run: | |
| print(json.dumps({'training_jobs': len(jobs), 'smoke': args.smoke, 'jobs': jobs}, indent=2)) | |
| return | |
| out.mkdir(parents=True, exist_ok=True) | |
| env = os.environ.copy() | |
| env['PYTHONPATH'] = str(root) + os.pathsep + env.get('PYTHONPATH', '') | |
| env['OMP_NUM_THREADS'] = '1' | |
| env['OPENBLAS_NUM_THREADS'] = '1' | |
| env['PYTHONPYCACHEPREFIX'] = str(out / 'pycache') | |
| (out / 'training_plan.json').write_text(json.dumps({'smoke': args.smoke, 'jobs': jobs}, indent=2)) | |
| records = [] | |
| for job in jobs: | |
| env['PYTHONPATH'] = str(root / job['source_tree']) + os.pathsep + str(root) | |
| endpoint = out / 'experiments' / job['method'] / 'checkpoint' / job['checkpoint'] | |
| if endpoint.exists(): | |
| raise FileExistsError(f'Checkpoint already exists: {endpoint}; choose another run directory') | |
| started = time.monotonic() | |
| log = out / f'{job["group"]}_{job["method"]}_{job["seed"]}_{job["episodes"]}.log' | |
| with log.open('w') as f: | |
| subprocess.run(job['command'], cwd=out, env=env, stdout=f, stderr=subprocess.STDOUT, check=True) | |
| assert endpoint.is_file() | |
| records.append({k: v for k, v in job.items() if k != 'command'} | {'elapsed_seconds': time.monotonic() - started}) | |
| (out / 'training_receipt.json').write_text(json.dumps({'smoke': args.smoke, 'completed': len(records), 'planned': len(jobs), 'jobs': records}, indent=2)) | |
| print(json.dumps({'completed': len(records), 'planned': len(jobs), 'configuration': records[-1]}), flush=True) | |
| if __name__ == '__main__': | |
| main() | |