"""V4.3 random-initialized LM/chat pretraining with the expanded web pool.""" 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.model_v4 import TinyGPTV4,GPTV4Config from vimeml.training.train import (optimizer_for,learning_rate,atomic_checkpoint,rng_state, restore_rng,resolve_precision,amp_context) from vimeml.posttraining.streams import CommittedStream,loader_for,microbatches from vimeml.posttraining.train import validate from vimeml.posttraining.data import TOKENIZER_SHA ROOT=Path(__file__).resolve().parents[3] STAGE='v4.3' TASK_CONTRACT={ 'objective':'single_task_lm_token_normalized', 'tasks':['old_web_lm','expansion_web_lm','chat_lm'], 'normalization':'total supervised tokens per update', 'lm':'ordinary_unmarked_LM_all_valid_targets', 'conversion':'none; V4.1 conversion pool removed', 'ime_validation':'none_in_training; deferred post-training over saved epoch checkpoints', 'context_length':128,'tokenizer_sha256':TOKENIZER_SHA, 'chat_fraction':0.05,'expansion':'nine additional FineWeb2-Edu shards, cross-pool deduplicated', } def read(path): return json.loads(Path(path).read_text('utf-8')) def chat_store_of(request): for bank in request['joined_stores']: if bank['token_dir'].rstrip('/').endswith('corpus-v41-chat-8k'): return bank raise ValueError('V4.3 mixture must contain the frozen chat bank') def expansion_store_of(request): for bank in request['joined_stores']: if 'corpus-v43-exp8k' in bank['token_dir']: return bank raise ValueError('V4.3 mixture must contain the expansion bank') def build_plan(config,root=ROOT,verify_files=True): root=Path(root) if config.get('stage')!=STAGE or config.get('architecture')!='tiny_gpt_v4' or config.get('initialization')!={'mode':'random'}: raise ValueError('V4.3 requires explicit random initialization; no source checkpoint') GPTV4Config(**config['model']) s=dict(config['training']) for key in ('lm_batch','lm_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.get('smoke_updates')) is not int or s['smoke_updates']<0: raise ValueError('Invalid smoke update budget') 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','beta1','beta2','weight_decay','grad_clip'): if not math.isfinite(s[key]) or s[key]<=0: raise ValueError(f'Invalid training scalar: {key}') compiled=config['runtime']['compile_backbone'] if (s['device']=='cuda' and s['precision']=='bf16' and compiled): pass elif (s['device']=='cpu' and s['precision']=='fp32' and not compiled and s['smoke_updates']>0): pass else: raise ValueError('V4.3 formal training is CUDA/BF16/compiled; only smoke runs are CPU/FP32') if (not 0=1 or s['beta2']>=1 or not 0<=s['prefix_crop_probability']<1 or s['lm_microbatch']>s['lm_batch']): raise ValueError('V4.3 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 len(mix['epochs'])!=request['epochs'] or request['seed']!=s['seed'] or request['crop_probability']!=s['prefix_crop_probability'] or request['crop_minimum']!=8 or request['chat_fraction']!=.05 or not 1<=s['epochs']<=request['epochs']): raise ValueError('Frozen V4.3 mixture differs from training configuration') if file_sha(mix_dir/'manifest.json')!=config['dataset_sha256']: raise ValueError('V4.3 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/expansion/chat bank manifest') if manifest['tokenizer_model_sha256']!=TOKENIZER_SHA or manifest['vocab_size']!=8192: raise ValueError('LM/expansion/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']) expansion=expansion_store_of(request) signatures['expansion_tokens']=expansion['token_manifest_sha256'] for bank in request['joined_stores'][:-1]: required[root/bank['keep']]=bank['keep_sha256'] for record in mix['epochs'][:s['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 code={p.relative_to(root).as_posix():file_sha(p) for p in sorted((root/'src/vimeml').rglob('*.py'))} code['scripts/training/train_v43.py']=file_sha(root/'scripts/training/train_v43.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['plan_epochs']=request['epochs'] s['updates_per_epoch']=[math.ceil(r['windows']/s['lm_batch']) for r in mix['epochs'][:s['epochs']]] s['windows_per_epoch']=[r['windows'] for r in mix['epochs'][:s['epochs']]] s['max_steps']=sum(s['updates_per_epoch']) return {'stage':STAGE,'parameters':15_070_848,'settings':s,'signatures':signatures,'code_sha256':code, 'required_files':{p.relative_to(root).as_posix():h for p,h in required.items()}, 'request':request,'task_contract':TASK_CONTRACT} def initial_state(): return {'epoch':0,'epoch_batch':0,'step':0,'lm_cursor':{'epoch':0,'batch':0,'offset':0}, 'epoch_results':[],'best_lm':None,'training_seconds':0., 'totals':{'lm':{k:0 for k in ('examples','input_tokens','supervised_tokens','padded_positions')}}} def consumed_examples(state,plan): s=plan['settings'] e,b=state['epoch'],state['epoch_batch'] updates=s['updates_per_epoch'] total=sum(s['windows_per_epoch'][:e]) if eupdates[e]): raise ValueError('Invalid committed LM epoch cursor') if state['step']!=sum(updates[:e])+b: raise ValueError('Step does not match committed epoch updates') if state['totals']['lm']['examples']!=consumed_examples(state,plan): raise ValueError('LM exposure does not match committed updates') if state['lm_cursor']!={'epoch':e,'batch':b,'offset':0}: raise ValueError('LM stream cursor mismatch') epochs=[r['epoch'] for r in state['epoch_results']] allowed=list(range(1,e+1)) if e=s['smoke_updates']: break if s['smoke_updates'] and state['step']>=s['smoke_updates']: break if requested: break if not any(r['epoch']==e+1 for r in state['epoch_results']): save() write_json(output/'progress.json',progress(state,plan,'validating')) print(f'[epoch {e+1}] full old LM, expansion and chat validation',flush=True) before=rng_state() with fp32_evaluation(model): old_stats=read(root/config['token_dir']/'manifest.json')['splits']['validation'] chat_stats=read(root/chat_bank['token_dir']/'manifest.json')['splits']['validation'] exp_stats=read(root/exp_bank['token_dir']/'manifest.json')['splits']['validation'] lv=validate(model,old_validation,device,old_stats['prediction_pairs'],old_stats['characters']) chat=validate(model,chat_validation,device,chat_stats['prediction_pairs'],chat_stats['characters']) ev=validate(model,exp_validation,device,exp_stats['prediction_pairs'],exp_stats['characters']) restore_rng(before) report={'epoch':e+1,'step':state['step'],'lm':lv,'chat':chat,'expansion':ev} roles=[] if state['best_lm'] is None or lv['bpc']=s['smoke_updates']: save() terminal='smoke_complete' else: 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-lm','last') if (output/f'{n}.pt').exists()} result['epoch_checkpoints']=sorted(p.name for p in output.glob('epoch-*.pt')) if device.type=='cuda': 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 (lm_val,chat_val,exp_val): data.close()