"""V4.1 random-initialized LM/chat/conversion pretraining, with exact epoch quotas.""" import copy import hashlib import json import math from pathlib import Path import random import signal import time import torch from vimeml.training.data import SentenceWindowDataset,file_sha,write_json from vimeml.training.data_mixture import mixture_dataset from vimeml.training.v41_data import IndexedConversionWindows,conversion_quota from vimeml.training.model_v4 import TinyGPTV4,GPTV4Config from vimeml.training.train import optimizer_for,learning_rate,atomic_checkpoint,rng_state,restore_rng,resolve_precision from vimeml.posttraining.data import ConversionDataset,ConversionCodec,TOKENIZER_SHA from vimeml.posttraining.streams import ConversionWindows,CommittedStream,loader_for from vimeml.posttraining.runtime import update,restore_checkpoint,TASK_CONTRACT from vimeml.posttraining.train import validate,checkpoint_payload from vimeml.posttraining.infer import read_snapshot,fp32_evaluation,evaluate_snapshot ROOT=Path(__file__).resolve().parents[3] def read(path): return json.loads(Path(path).read_text('utf-8')) def build_plan(config,root=ROOT,verify_files=True): root=Path(root) if config.get('stage')!='v4.1' or config.get('architecture')!='tiny_gpt_v4' or config.get('initialization')!={'mode':'random'}: raise ValueError('V4.1 requires explicit random initialization; no source checkpoint') GPTV4Config(**config['model']) s=dict(config['training']) if s['epochs']!=16 or s['lm_weight']!=.8 or s['conversion_weight']!=.2: raise ValueError('Use the frozen 16-epoch V4.1 joint objective') for key in ('lm_batch','conversion_batch','lm_microbatch','conversion_microbatch','epochs','num_workers', 'cpu_threads','log_every','eval_every','checkpoint_every','validation_batch','bucket_multiplier','warmup_steps'): if type(s[key]) is not int or s[key]<1: raise ValueError(f'Invalid training integer: {key}') if type(s['seed']) is not int or not 0<=s['seed']<2**63: raise ValueError('Invalid random seed') for key in ('learning_rate','min_learning_rate','lm_weight','conversion_weight','beta1','beta2','weight_decay','grad_clip'): if not math.isfinite(s[key]) or s[key]<=0: raise ValueError(f'Invalid training scalar: {key}') if (s['device']!='cuda' or s['precision']!='bf16' or not config['runtime']['compile_backbone'] or not 0=1 or s['beta2']>=1 or not 0<=s['prefix_crop_probability']<1 or s['conversion_microbatch']s['lm_batch']): raise ValueError('V4.1 CUDA/precision/optimizer/microbatch contract mismatch') mix_dir=root/config['data_mixture']['directory'] mix=read(mix_dir/'manifest.json') request=mix['request'] if (mix['status']!='complete' or request['epochs']!=16 or len(mix['epochs'])!=16 or request['seed']!=s['seed'] or request['crop_probability']!=s['prefix_crop_probability'] or request['crop_minimum']!=8 or request['chat_fraction']!=.05): raise ValueError('Frozen mixture differs from training configuration') if file_sha(mix_dir/'manifest.json')!=config['dataset_sha256']: raise ValueError('V4.1 dataset manifest changed') required={} signatures={'dataset':config['dataset_sha256']} for bank in request['joined_stores']: folder,index=root/bank['token_dir'],root/bank['index_dir'] manifest=read(folder/'manifest.json') if file_sha(folder/'manifest.json')!=bank['token_manifest_sha256'] or file_sha(index/'manifest.json')!=bank['window_manifest_sha256']: raise ValueError('Changed LM/chat bank manifest') if manifest['tokenizer_model_sha256']!=TOKENIZER_SHA or manifest['vocab_size']!=8192: raise ValueError('LM/chat vocabulary mismatch') windows=read(index/'manifest.json') for split in ('train','validation'): for suffix in ('tokens.bin','offsets.bin','sources.bin'): required[folder/f'{split}.{suffix}']=manifest['output_sha256'][f'{split}.{suffix}'] required[index/f'{split}.windows.npz']=windows['splits'][split]['index_sha256'] required[folder/'manifest.json']=bank['token_manifest_sha256'] required[index/'manifest.json']=bank['window_manifest_sha256'] primary=request['joined_stores'][0] if config['token_dir']!=primary['token_dir'] or config['index_dir']!=primary['index_dir']: raise ValueError('Primary model tokenizer/LM bank mismatch') signatures.update(tokens=primary['token_manifest_sha256'],windows=primary['window_manifest_sha256']) conversion=root/config['conversion_dir'] cv=read(conversion/'manifest.json') acceptance=read(conversion/'acceptance.json') signatures['conversion']=file_sha(conversion/'manifest.json') if signatures['conversion']!=request['conversion_manifest_sha256'] or cv['contract']['tokenizer_sha256']!=TOKENIZER_SHA or acceptance['manifest_sha256']!=signatures['conversion'] or acceptance['status']!='pass': raise ValueError('Conversion bank identity mismatch') for name,digest in cv['output_sha256'].items(): if name.endswith('.bin'): required[conversion/name]=digest required[conversion/'manifest.json']=signatures['conversion'] required[conversion/'acceptance.json']=file_sha(conversion/'acceptance.json') for name in ('old_keep','conversion_keep'): required[root/request[name]]=request[name+'_sha256'] for record in mix['epochs']: required[mix_dir/record['file']]=record['sha256'] required[mix_dir/'manifest.json']=config['dataset_sha256'] required[mix_dir/'request.json']=file_sha(mix_dir/'request.json') tokenizer=root/config['tokenizer_dir']/'tokenizer.model' if file_sha(tokenizer)!=TOKENIZER_SHA: raise ValueError('Tokenizer changed') required[tokenizer]=TOKENIZER_SHA standard=read_snapshot('standard-development',root) required[root/'outputs/evaluation-v4-20261015/inputs/standard-development.json']=file_sha(root/'outputs/evaluation-v4-20261015/inputs/standard-development.json') code={p.relative_to(root).as_posix():file_sha(p) for p in sorted((root/'src/vimeml').rglob('*.py'))} code['scripts/training/train_v41.py']=file_sha(root/'scripts/training/train_v41.py') signatures['code']=hashlib.sha256(json.dumps(code,sort_keys=True).encode()).hexdigest() if verify_files: for path,digest in required.items(): if file_sha(path)!=digest: raise ValueError('Changed training dependency: '+str(path)) s['updates_per_epoch']=[math.ceil(r['windows']/s['lm_batch']) for r in mix['epochs']] s['max_steps']=sum(s['updates_per_epoch']) if request['conversion_examples']updates[e]): raise ValueError('Invalid committed LM epoch cursor') if state['step']!=sum(updates[:e])+b: raise ValueError('Step does not match committed epoch updates') expected=e*plan['conversion_examples']+(b*plan['conversion_examples']//updates[e] if estate['best_rerank_key']: state['best_rerank_key']=key;roles+=['best-rerank','best'] ordinary_key=[ordinary['correct'],-lv['bpc'],-state['step']] if state['best_ordinary_key'] is None or ordinary_key>state['best_ordinary_key']: state['best_ordinary_key']=ordinary_key;roles.append('best-ordinary') state['epoch_results'].append(report) write_json(destination/'summary.json',report) write_json(output/'full-validation.json',report) for tag,value in [('conversion/full_validation_loss',cv['loss']),('lm/full_validation_bpc',lv['bpc']), ('chat/full_validation_bpc',chat['bpc']),('ime/conditional_correct',ime['correct']),('ime/ordinary_correct',ordinary['correct'])]: writer.add_scalar(tag,value,state['step']) for role in roles: save(role) save(f'epoch-{e+1}') print(f"[epoch {e+1}] LM BPC={lv['bpc']:.6f} chat BPC={chat['bpc']:.6f} conv={cv['loss']:.6f} conditional={ime['correct']}/728 ordinary={ordinary['correct']}/728",flush=True) state['epoch']+=1 state['epoch_batch']=0 state['lm_cursor']={'epoch':state['epoch'],'batch':0,'offset':0} validate_state(state,plan) save() terminal='complete' if state['epoch']==s['epochs'] else 'interrupted' save() result=progress(state,plan,terminal) result['checkpoints']={n:str(output/f'{n}.pt') for n in ('best','best-rerank','best-ordinary','best-lm','best-conversion','last') if (output/f'{n}.pt').exists()} result['peak_allocated_MiB']=torch.cuda.max_memory_allocated(device)/2**20 result['peak_reserved_MiB']=torch.cuda.max_memory_reserved(device)/2**20 write_json(output/'summary.json',result) write_json(output/'progress.json',result) return result except BaseException as error: write_json(output/'failure.json',{'status':'failed','active':True,'error_type':type(error).__name__, 'error':str(error),'last_committed_in_memory_step':state['step'],'safe_resume_checkpoint':str(output/'last.pt')}) raise finally: writer.close();metrics.close() for sig,handler in handlers.items(): signal.signal(sig,handler) lm_train.close() for data in (conv_train.data,conv_val.data,lm_val,chat_val): data.close()