dna-diskchat-2b-peer-v1 / scripts /train_sft_peer.py
jaivial's picture
Upload scripts/train_sft_peer.py with huggingface_hub
f1fb716 verified
Raw
History Blame Contribute Delete
3.92 kB
# train_sft_peer.py - SFT the ternary+PEER DNA model for instruction following.
# Assistant-only loss on the same 200k UltraChat data; codon table frozen.
import argparse, json, math, os, time
from pathlib import Path
import numpy as np, torch, torch.nn.functional as F
import torch.utils.checkpoint as cp
import sys; sys.path.insert(0, '/root/dna')
from model_dna_peer import DnaPeer, peer_counts
ap = argparse.ArgumentParser()
ap.add_argument('--base', default='/root/dna/ckpt-peer/base.pt')
ap.add_argument('--sft', default='/root/dna/sft'); ap.add_argument('--out', default='/root/dna/ckpt-peer-sft')
ap.add_argument('--batch', type=int, default=16); ap.add_argument('--maxlen', type=int, default=768)
ap.add_argument('--steps', type=int, default=6000); ap.add_argument('--lr', type=float, default=2e-5); ap.add_argument('--warmup', type=int, default=150)
ap.add_argument('--hf-repo', default='jaivial/dna-diskchat-2b-peer-v1')
a = ap.parse_args(); dev = 'cuda'; Path(a.out).mkdir(parents=True, exist_ok=True); torch.manual_seed(3)
torch.backends.cuda.matmul.allow_tf32 = True
ck = torch.load(a.base, map_location=dev, weights_only=False)
m = DnaPeer(**ck['config']).to(dev)
m.load_state_dict({k.replace('_orig_mod.', ''): v for k, v in ck['model'].items()}, strict=True); m.train()
for i in range(len(m.blocks)): m.blocks[i] = torch.compile(m.blocks[i])
flat = np.memmap(f'{a.sft}/sft_tokens.u16', np.uint16, 'r'); mflat = np.memmap(f'{a.sft}/sft_mask.u8', np.uint8, 'r')
lens = np.fromfile(f'{a.sft}/sft_lens.i32', np.int32); off = np.zeros(len(lens) + 1, np.int64); off[1:] = np.cumsum(lens)
print('SFT_CONFIG', json.dumps({'records': len(lens), 'ctrl_params': peer_counts(m)[0]}), flush=True)
opt = torch.optim.AdamW(m.parameters(), lr=a.lr, betas=(.9, .95), weight_decay=0.0)
def batch():
idx = np.random.randint(0, len(lens), size=a.batch); L = min(a.maxlen, max(int(lens[i]) for i in idx))
ids = np.zeros((a.batch, L), np.int64); msk = np.zeros((a.batch, L), np.float32)
for j, i in enumerate(idx):
n = min(int(lens[i]), L); s = off[i]; ids[j, :n] = flat[s:s+n]; msk[j, :n] = mflat[s:s+n]
return torch.from_numpy(ids).to(dev), torch.from_numpy(msk).to(dev)
def fwd(ids):
B, T = ids.shape; x = m.embed(ids)
for blk in m.blocks: x = cp.checkpoint(blk, x, m.chunk, use_reentrant=False)
mem, bal, _ = m.route(x.reshape(B * T, m.d)); return m.norm(x + mem.reshape(B, T, m.d)), bal
def lrf(s): return s/a.warmup if s < a.warmup else max(0.1, 0.5*(1+math.cos(math.pi*(s-a.warmup)/max(1, a.steps-a.warmup))))
t0 = time.time()
for step in range(1, a.steps + 1):
for g in opt.param_groups: g['lr'] = a.lr * lrf(step)
ids, msk = batch(); opt.zero_grad(set_to_none=True)
with torch.autocast('cuda', dtype=torch.bfloat16):
feat, bal = fwd(ids); lo = F.linear(feat, m.embed.weight)
tgt = ids[:, 1:]; mm = msk[:, 1:]
ce = F.cross_entropy(lo[:, :-1].reshape(-1, m.vocab), tgt.reshape(-1), reduction='none').view_as(tgt)
loss = (ce * mm).sum() / mm.sum().clamp_min(1) + 1e-4 * bal
loss.backward(); torch.nn.utils.clip_grad_norm_(m.parameters(), 1.0); opt.step()
if step % 50 == 0:
print(f'sft step={step}/{a.steps} loss={loss.item():.4f} ppl={math.exp(min(20, loss.item())):.1f} rec_s={step*a.batch/(time.time()-t0):,.1f}', flush=True)
if step % 1000 == 0: torch.save({'model': m.state_dict(), 'config': m.config(), 'step': step}, Path(a.out)/'sft.pt')
torch.save({'model': m.state_dict(), 'config': m.config(), 'step': a.steps}, Path(a.out)/'sft.pt')
print('PEER_SFT_DONE', flush=True)
try:
from huggingface_hub import HfApi
api = HfApi(token=os.environ.get('HUGGING_FACE'))
api.upload_file(path_or_fileobj=str(Path(a.out)/'sft.pt'), path_in_repo='sft/sft.pt', repo_id=a.hf_repo)
print('PEER_SFT_HF_PUSH ok', flush=True)
except Exception as e: print('PEER_SFT_HF_PUSH_FAIL', str(e)[:160], flush=True)