"""Learn a byte BPE using training text only; tokenize complete supervised records.""" import argparse import hashlib import json from pathlib import Path import numpy as np from tokenizers import Tokenizer, models, trainers, pre_tokenizers, decoders SPECIAL=['<|pad|>','<|context|>','<|user|>','<|assistant|>','<|end|>'] def file_sha256(path): h=hashlib.sha256() with Path(path).open('rb') as stream: for block in iter(lambda:stream.read(4*1024*1024),b''):h.update(block) return h.hexdigest() def rows(path): with open(path) as stream: for line in stream: yield json.loads(line) def main(): p=argparse.ArgumentParser(); p.add_argument('--data',default='data/tinyquery') p.add_argument('--vocab',type=int,default=24000); p.add_argument('--context',type=int,default=2048) p.add_argument('--reuse-tokenizer',action='store_true') args=p.parse_args(); base=Path(args.data) if args.reuse_tokenizer: tokenizer=Tokenizer.from_file(str(base/'tokenizer.json')) else: tokenizer=Tokenizer(models.BPE()) tokenizer.pre_tokenizer=pre_tokenizers.ByteLevel(add_prefix_space=False) tokenizer.decoder=decoders.ByteLevel() trainer=trainers.BpeTrainer(vocab_size=args.vocab,min_frequency=2,special_tokens=SPECIAL, initial_alphabet=pre_tokenizers.ByteLevel.alphabet()) tokenizer.train_from_iterator((r['prompt']+r['response']+'<|end|>' for r in rows(base/'train.jsonl')),trainer) tokenizer.save(str(base/'tokenizer.json')) stats={'vocab_size':tokenizer.get_vocab_size(),'context':args.context,'special_tokens':{s:tokenizer.token_to_id(s) for s in SPECIAL}} for split in ['train','validation','test']: data=list(rows(base/(split+'.jsonl'))) encoded=[]; boundaries=[]; lengths=[]; actions=[]; kept=[]; rejected=[] for i,r in enumerate(data): prefix=tokenizer.encode(r['prompt']).ids suffix=tokenizer.encode(r['response']+'<|end|>').ids ids=prefix+suffix if len(ids)>args.context+1: rejected.append({'id':r['id'],'tokens':len(ids)}); continue encoded.append(ids); boundaries.append(len(prefix)-1); lengths.append(len(ids)) actions.append({'call':0,'clarify':1,'answer':2}[r['target']['action']]); kept.append(i) width=max(lengths) array=np.zeros((len(encoded),width),dtype=np.uint16) for i,ids in enumerate(encoded): array[i,:len(ids)]=ids np.save(base/(split+'-ids.npy'),array) np.savez(base/(split+'-meta.npz'),boundaries=np.array(boundaries,dtype=np.int32), lengths=np.array(lengths,dtype=np.int32),actions=np.array(actions,dtype=np.int32), kept=np.array(kept,dtype=np.int32),sample_weights=np.array([data[i].get('sample_weight',1.0) for i in kept],dtype=np.float32)) stats[split]={'examples':len(encoded),'tokens':sum(lengths),'response_tokens':sum(l-b-1 for l,b in zip(lengths,boundaries)), 'ids_sha256':file_sha256(base/(split+'-ids.npy')), 'length_p50':float(np.median(lengths)),'length_p95':float(np.quantile(lengths,.95)), 'max_length':max(lengths),'rejected':rejected} print(split,stats[split],flush=True) (base/'tokenization.json').write_text(json.dumps(stats,indent=2)) if __name__=='__main__': main()