Upload folder using huggingface_hub
Browse files- README.md +18 -0
- code/diag2_body.py +110 -0
- code/export_onnx_body.py +127 -0
- code/ncm_chat_train.py +102 -0
- code/ncm_export_kernel.py +138 -0
- code/ncmchat.py +76 -0
- generator_chat.pt +3 -0
- training_report.json +91 -0
README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- next-concept-modeling
|
| 5 |
+
- ncm
|
| 6 |
+
- experimental
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
# NCM chat generator (experimental, rough)
|
| 10 |
+
|
| 11 |
+
Fine-tuned generator weights (fp16, 177 MB) for a small chat demo built on **Next-Concept Modeling** (Stanford Hazy Research, arXiv 2610.14162, https://github.com/HazyResearch/ncm).
|
| 12 |
+
|
| 13 |
+
- Base: the released XSum checkpoint at https://huggingface.co/hazyresearch/ncm-checkpoints (frozen multiscale VQ tokenizer plus prefix-conditioned generator). Only the generator was fine-tuned, for 4000 steps on short public dialogue and instruction data (everyday-conversations, dolly, alpaca; 21.6k examples). Held-out code loss 6.31 to 1.47.
|
| 14 |
+
- Load it with the base repo's config, tokenizer and code (`code/ncmchat.py` shows how). `generator_chat.pt` holds a `model` state dict.
|
| 15 |
+
- Prefix window 128 tokens, reply up to 48 tokens, levels [4,6,10,14,20,26,32,40,48].
|
| 16 |
+
- `code/` has the training, ONNX export and parity scripts.
|
| 17 |
+
- Quality is rough: replies are short and often repetitive or wrong. This is a research demo, not an assistant.
|
| 18 |
+
- Browser demo: https://huggingface.co/spaces/danielharkin21/ncm-chat-experimental
|
code/diag2_body.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Teacher-forced logit parity: identical prefix + identical codes through torch and ONNX (fp32, fp32 no-opt, int8).
|
| 2 |
+
import os, sys, json, types, re, time
|
| 3 |
+
os.environ["NCM_ROOT"]="/kaggle/working/w/ncm"
|
| 4 |
+
import torch, torch.nn as nn, numpy as np, ncmchat as N
|
| 5 |
+
import torch.nn.functional as _F
|
| 6 |
+
_orig=_F.interpolate
|
| 7 |
+
def _am(L,o):
|
| 8 |
+
m=np.zeros((o,L),dtype=np.float32)
|
| 9 |
+
for i in range(o):
|
| 10 |
+
a=(i*L)//o; b=-((-(i+1)*L)//o); m[i,a:b]=1.0/(b-a)
|
| 11 |
+
return torch.from_numpy(m)
|
| 12 |
+
def _lm(L,o):
|
| 13 |
+
m=np.zeros((o,L),dtype=np.float32)
|
| 14 |
+
for i in range(o):
|
| 15 |
+
s=max((i+0.5)*L/o-0.5,0.0); i0=min(int(s),L-1); i1=min(i0+1,L-1); w=s-i0
|
| 16 |
+
m[i,i0]+=1-w; m[i,i1]+=w
|
| 17 |
+
return torch.from_numpy(m)
|
| 18 |
+
def _interp(x,size=None,scale_factor=None,mode="nearest",**kw):
|
| 19 |
+
if mode=="area" and x.dim()==3 and size is not None:
|
| 20 |
+
o=size[0] if isinstance(size,(tuple,list)) else size
|
| 21 |
+
return torch.matmul(x,_am(x.shape[-1],o).t())
|
| 22 |
+
if mode=="linear" and x.dim()==3 and size is not None and not kw.get("align_corners"):
|
| 23 |
+
o=size[0] if isinstance(size,(tuple,list)) else size
|
| 24 |
+
return torch.matmul(x,_lm(x.shape[-1],o).t())
|
| 25 |
+
return _orig(x,size=size,scale_factor=scale_factor,mode=mode,**kw)
|
| 26 |
+
gp=[os.path.join(r,"generator_chat.pt") for r,_,f in os.walk("/kaggle/input") if "generator_chat.pt" in f][0]
|
| 27 |
+
P=128
|
| 28 |
+
c=open("ck/config.yaml").read(); c=re.sub(r"prefix_len: 512","prefix_len: %d"%P,c); open("ck/config_p.yaml","w").write(c)
|
| 29 |
+
CFG,tk,net=N.load("ck",cfg_path="ck/config_p.yaml",dev="cpu",load_gen=False); net.eval()
|
| 30 |
+
sd=torch.load(gp,map_location="cpu",weights_only=False)["model"]; own=net.state_dict()
|
| 31 |
+
net.load_state_dict({k:v.float() for k,v in sd.items() if k in own and own[k].shape==v.shape},strict=False)
|
| 32 |
+
codec=N.Codec(CFG,tk,net); offs=[0]
|
| 33 |
+
for l in net.levels: offs.append(offs[-1]+l)
|
| 34 |
+
levels=list(net.levels)
|
| 35 |
+
class GTF(nn.Module): # generate-path with forced codes; returns logits of every level
|
| 36 |
+
def __init__(s):
|
| 37 |
+
super().__init__(); s.net=net
|
| 38 |
+
def forward(s,prefix,codes):
|
| 39 |
+
st={"i":0}; L=[]
|
| 40 |
+
def samp(self_,logits,temperature,top_k,top_p):
|
| 41 |
+
i=st["i"]; st["i"]+=1; L.append(logits)
|
| 42 |
+
return codes[:,offs[i]:offs[i+1]]
|
| 43 |
+
s.net._sample_level=types.MethodType(samp,s.net)
|
| 44 |
+
s.net.generate(None,prefix,temperature=1.0,top_k=20,top_p=0.9)
|
| 45 |
+
return torch.cat(L,1)
|
| 46 |
+
m=GTF().eval()
|
| 47 |
+
tests=["Hi, how are you?","What is the capital of France?","Tell me a joke.","What do you like to do for fun?","Can you recommend a good book?","How do I boil an egg?","I'm feeling sad today.","What is the meaning of life?"]
|
| 48 |
+
prompts=[N.fmt_prompt([("user",t)]) for t in tests]
|
| 49 |
+
pre=torch.tensor([codec.prefix_ids(p) for p in prompts])
|
| 50 |
+
# codes: use the REAL data codes of plausible replies (teacher forcing), via tokenizer encode
|
| 51 |
+
reps=["Hello! How can I help you today?","The capital of France is Paris.","Why did the chicken cross the road? To get to the other side.","I like to read books and go hiking.","I recommend The Hobbit, it is a fun story.","Put it in boiling water for ten minutes.","I am sorry to hear that. I hope you feel better.","Life is about finding what makes you happy."]
|
| 52 |
+
tgt=torch.tensor([codec.target_ids(r) for r in reps])
|
| 53 |
+
with torch.no_grad(): codes=tk.vqvae.encode(tgt)
|
| 54 |
+
# (1) training-path forward logits vs generate-path logits (torch vs torch)
|
| 55 |
+
with torch.no_grad():
|
| 56 |
+
lg_gen=torch.cat([m(pre[i:i+1],codes[i:i+1]) for i in range(len(tests))],0)
|
| 57 |
+
x=N.build_inputs(codec,pre,codes,0.0); pm=pre==codec.ppad
|
| 58 |
+
lg_tr=net(x,prefix_pad_mask=pm)
|
| 59 |
+
d_tr=(lg_gen-lg_tr).abs()
|
| 60 |
+
print("torch generate-path vs training-forward logits: max|d|",float(d_tr.max()),"mean|d|",float(d_tr.mean()),"argmax mismatch",int((lg_gen.argmax(-1)!=lg_tr.argmax(-1)).sum()),"/",lg_gen.shape[0]*lg_gen.shape[1],flush=True)
|
| 61 |
+
os.makedirs("/kaggle/working/out",exist_ok=True)
|
| 62 |
+
_F.interpolate=_interp
|
| 63 |
+
torch.onnx.export(m,(pre[:1],codes[:1]),"/kaggle/working/tf.onnx",input_names=["prefix","codes"],output_names=["logits"],opset_version=17,dynamo=False)
|
| 64 |
+
import onnxruntime as ort, onnx
|
| 65 |
+
from onnx import numpy_helper, helper
|
| 66 |
+
def clean_quant(src,dst):
|
| 67 |
+
mo=onnx.load(src); g=mo.graph
|
| 68 |
+
keep=[]
|
| 69 |
+
for n in g.node: # Constant nodes -> initializers so the quantizer sees weights
|
| 70 |
+
if n.op_type=="Constant" and len(n.attribute)==1 and n.attribute[0].name=="value":
|
| 71 |
+
t=n.attribute[0].t; t.name=n.output[0]; g.initializer.append(t)
|
| 72 |
+
else: keep.append(n)
|
| 73 |
+
del g.node[:]; g.node.extend(keep)
|
| 74 |
+
inits={i.name for i in g.initializer}; alias={}; final=[]
|
| 75 |
+
for n in g.node:
|
| 76 |
+
if n.op_type=="Identity" and (n.input[0] in inits or n.input[0] in alias): alias[n.output[0]]=n.input[0]
|
| 77 |
+
else: final.append(n)
|
| 78 |
+
def r(x):
|
| 79 |
+
while x in alias: x=alias[x]
|
| 80 |
+
return x
|
| 81 |
+
for n in final:
|
| 82 |
+
for j,xx in enumerate(n.input): n.input[j]=r(xx)
|
| 83 |
+
del g.node[:]; g.node.extend(final); onnx.save(mo,"/kaggle/working/tf_clean.onnx")
|
| 84 |
+
from onnxruntime.quantization import quantize_dynamic, QuantType
|
| 85 |
+
quantize_dynamic("/kaggle/working/tf_clean.onnx",dst,weight_type=QuantType.QInt8)
|
| 86 |
+
HAVE_I8=True
|
| 87 |
+
try:
|
| 88 |
+
clean_quant("/kaggle/working/tf.onnx","/kaggle/working/tf_i8.onnx"); print("int8 MB",os.path.getsize("/kaggle/working/tf_i8.onnx")/1e6,flush=True)
|
| 89 |
+
except Exception as e:
|
| 90 |
+
HAVE_I8=False; print("INT8 QUANT FAILED",repr(e)[:300],flush=True)
|
| 91 |
+
def run(path,opt):
|
| 92 |
+
so=ort.SessionOptions(); so.graph_optimization_level=opt
|
| 93 |
+
s=ort.InferenceSession(path,so,providers=["CPUExecutionProvider"])
|
| 94 |
+
return torch.cat([torch.from_numpy(s.run(None,{"prefix":pre[i:i+1].numpy(),"codes":codes[i:i+1].numpy()})[0]) for i in range(len(tests))],0)
|
| 95 |
+
rep={}
|
| 96 |
+
for name,path,opt in [("onnx_fp32_default","/kaggle/working/tf.onnx",ort.GraphOptimizationLevel.ORT_ENABLE_ALL),("onnx_fp32_noopt","/kaggle/working/tf.onnx",ort.GraphOptimizationLevel.ORT_DISABLE_ALL),("onnx_int8","/kaggle/working/tf_i8.onnx",ort.GraphOptimizationLevel.ORT_ENABLE_ALL)]:
|
| 97 |
+
if name=="onnx_int8" and not HAVE_I8: continue
|
| 98 |
+
lg=run(path,opt); d=(lg-lg_gen).abs(); top2=lg_gen.topk(2,-1).values; gap=top2[...,0]-top2[...,1]
|
| 99 |
+
mism=(lg.argmax(-1)!=lg_gen.argmax(-1))
|
| 100 |
+
per=[]
|
| 101 |
+
for k in range(len(levels)):
|
| 102 |
+
a,b=offs[k],offs[k+1]
|
| 103 |
+
per.append({"level":k,"max_abs":float(d[:,a:b].max()),"mean_abs":float(d[:,a:b].mean()),"argmax_mismatch":int(mism[:,a:b].sum()),"of":int(mism[:,a:b].numel())})
|
| 104 |
+
rep[name]={"max_abs":float(d.max()),"mean_abs":float(d.mean()),"logit_std":float(lg_gen.std()),"argmax_mismatch":int(mism.sum()),"of":int(mism.numel()),
|
| 105 |
+
"gap_at_mismatches_max":float(gap[mism].max()) if mism.any() else None,"gap_at_mismatches_median":float(gap[mism].median()) if mism.any() else None,"per_level":per}
|
| 106 |
+
print(name,"max|d|",rep[name]["max_abs"],"mean|d|",rep[name]["mean_abs"],"logit std",rep[name]["logit_std"],"argmax mism",rep[name]["argmax_mismatch"],"/",rep[name]["of"],"gap@mism max/med",rep[name]["gap_at_mismatches_max"],rep[name]["gap_at_mismatches_median"],flush=True)
|
| 107 |
+
for p_ in per: print(" ",p_,flush=True)
|
| 108 |
+
rep["torch_gen_vs_train_forward"]={"max_abs":float(d_tr.max()),"mean_abs":float(d_tr.mean())}
|
| 109 |
+
json.dump(rep,open("/kaggle/working/out/parity.json","w"),indent=1)
|
| 110 |
+
print("done",flush=True)
|
code/export_onnx_body.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Runs inside a Kaggle kernel (CPU ok). Inputs: /kaggle/input/<SRC>/generator_chat.pt ; outputs ncm_fp16w.onnx + report.
|
| 2 |
+
import os, sys, json, types, time
|
| 3 |
+
os.environ["NCM_ROOT"]="/kaggle/working/w/ncm"
|
| 4 |
+
import torch, torch.nn as nn, numpy as np, ncmchat as N
|
| 5 |
+
import torch.nn.functional as _F
|
| 6 |
+
_orig_interp=_F.interpolate
|
| 7 |
+
def _area_mat(L,o):
|
| 8 |
+
m=np.zeros((o,L),dtype=np.float32)
|
| 9 |
+
for i in range(o):
|
| 10 |
+
a=(i*L)//o; b=-((-(i+1)*L)//o); m[i,a:b]=1.0/(b-a)
|
| 11 |
+
return torch.from_numpy(m)
|
| 12 |
+
def _lm(L,o):
|
| 13 |
+
m=np.zeros((o,L),dtype=np.float32)
|
| 14 |
+
for i in range(o):
|
| 15 |
+
s=max((i+0.5)*L/o-0.5,0.0); i0=min(int(s),L-1); i1=min(i0+1,L-1); w=s-i0
|
| 16 |
+
m[i,i0]+=1-w; m[i,i1]+=w
|
| 17 |
+
return torch.from_numpy(m)
|
| 18 |
+
def _interp(x,size=None,scale_factor=None,mode="nearest",**kw):
|
| 19 |
+
if mode=="area" and x.dim()==3 and size is not None:
|
| 20 |
+
o=size[0] if isinstance(size,(tuple,list)) else size
|
| 21 |
+
return torch.matmul(x,_area_mat(x.shape[-1],o).t())
|
| 22 |
+
if mode=="linear" and x.dim()==3 and size is not None and not kw.get("align_corners"):
|
| 23 |
+
o=size[0] if isinstance(size,(tuple,list)) else size
|
| 24 |
+
return torch.matmul(x,_lm(x.shape[-1],o).t())
|
| 25 |
+
return _orig_interp(x,size=size,scale_factor=scale_factor,mode=mode,**kw)
|
| 26 |
+
_chk=torch.randn(2,5,48); assert torch.allclose(_orig_interp(_chk,size=(26,),mode="area"),_interp(_chk,size=(26,),mode="area"),atol=1e-5)
|
| 27 |
+
_F.interpolate=_interp
|
| 28 |
+
SRC=os.environ.get("SRC","ncm-chat-smoke")
|
| 29 |
+
cands=[f"/kaggle/input/{SRC}/generator_chat.pt"]+[os.path.join(r,"generator_chat.pt") for r,_,f in os.walk("/kaggle/input") if "generator_chat.pt" in f]
|
| 30 |
+
gp=[c for c in cands if os.path.exists(c)][0]; print("using",gp,flush=True)
|
| 31 |
+
PEFF=int(os.environ.get("PEFF","128"))
|
| 32 |
+
import re
|
| 33 |
+
_c=open("ck/config.yaml").read(); _c=re.sub(r"prefix_len: 512","prefix_len: %d"%PEFF,_c); open("ck/config_p.yaml","w").write(_c)
|
| 34 |
+
CFG,tk,net=N.load("ck",cfg_path="ck/config_p.yaml",dev="cpu",load_gen=False); net.eval()
|
| 35 |
+
sd=torch.load(gp,map_location="cpu",weights_only=False)["model"]
|
| 36 |
+
own=net.state_dict(); sdf={k:v.float() for k,v in sd.items() if k in own and own[k].shape==v.shape}
|
| 37 |
+
print("skipped keys:",[k for k in sd if k not in sdf],flush=True)
|
| 38 |
+
net.load_state_dict(sdf,strict=False); assert all(k in sdf for k in own if "attn_mask" not in k and not k.startswith("rope")), "missing weights"
|
| 39 |
+
codec=N.Codec(CFG,tk,net); offs=[0]
|
| 40 |
+
for l in net.levels: offs.append(offs[-1]+l)
|
| 41 |
+
class G(nn.Module):
|
| 42 |
+
def __init__(s):
|
| 43 |
+
super().__init__(); s.net=net; s.vq=tk.vqvae.vq; s.dec=tk.vqvae.decoder; s.enc=tk.vqvae.encoder
|
| 44 |
+
def forward(s, prefix, noise, temp):
|
| 45 |
+
st={"i":0}
|
| 46 |
+
def samp(self_, logits, temperature, top_k, top_p):
|
| 47 |
+
i=st["i"]; st["i"]+=1
|
| 48 |
+
nz=noise[:, offs[i]:offs[i+1], :]
|
| 49 |
+
v,_=torch.topk(logits,20,dim=-1)
|
| 50 |
+
lg=torch.where(logits<v[...,-1:],torch.full_like(logits,-1e9),logits)
|
| 51 |
+
return (lg+temp*nz).argmax(-1)
|
| 52 |
+
s.net._sample_level=types.MethodType(samp,s.net)
|
| 53 |
+
g=s.net.generate(None,prefix,temperature=1.0,top_k=20,top_p=0.9)
|
| 54 |
+
_,zl=s.vq.idx_to_zhats(g,return_zlevels=True)
|
| 55 |
+
return s.dec(zl[-1]).argmax(-1)
|
| 56 |
+
m=G().eval()
|
| 57 |
+
tests=["Hi, how are you?","What is the capital of France?","Tell me a joke.","What do you like to do for fun?","Can you recommend a good book?","How do I boil an egg?","I'm feeling sad today.","What is the meaning of life?"]
|
| 58 |
+
prompts=[N.fmt_prompt([("user",t)]) for t in tests]
|
| 59 |
+
pre=torch.tensor([codec.prefix_ids(p) for p in prompts])
|
| 60 |
+
Z=torch.zeros(1,offs[-1],4096); T0=torch.tensor(0.0)
|
| 61 |
+
def txt(ids):
|
| 62 |
+
ids=list(ids)
|
| 63 |
+
if codec.EOS in ids: ids=ids[:ids.index(codec.EOS)]
|
| 64 |
+
return codec.pre.decode([t for t in ids if t not in (codec.EOS,codec.PAD)],skip_special_tokens=True).strip()
|
| 65 |
+
with torch.no_grad(): ref=[m(pre[i:i+1],Z,T0)[0].tolist() for i in range(len(tests))]
|
| 66 |
+
print("torch greedy:",[txt(r) for r in ref],flush=True)
|
| 67 |
+
os.makedirs("/kaggle/working/out",exist_ok=True)
|
| 68 |
+
f32="/kaggle/working/ncm_fp32.onnx"
|
| 69 |
+
with torch.no_grad(): torch.onnx.export(m,(pre[:1],Z,T0),f32,input_names=["prefix","noise","temp"],output_names=["ids"],opset_version=17,dynamo=False)
|
| 70 |
+
print("fp32 onnx MB",os.path.getsize(f32)/1e6,flush=True)
|
| 71 |
+
import onnxruntime as ort
|
| 72 |
+
from onnxruntime.quantization import quantize_dynamic, QuantType
|
| 73 |
+
import onnx
|
| 74 |
+
from onnx import numpy_helper, helper
|
| 75 |
+
mo=onnx.load(f32); g=mo.graph; inits={i.name:i for i in g.initializer}; newn=[]; ng=0
|
| 76 |
+
for n in g.node:
|
| 77 |
+
if n.op_type=="Gemm" and n.input[1] in inits and (len(n.input)<3 or n.input[2] in inits):
|
| 78 |
+
at={a.name:helper.get_attribute_value(a) for a in n.attribute}
|
| 79 |
+
W=numpy_helper.to_array(inits[n.input[1]]); W=(W.T if at.get("transB",0) else W)*at.get("alpha",1.0)
|
| 80 |
+
wn=n.input[1]+"_mm"; g.initializer.append(numpy_helper.from_array(W.astype("float32"),wn))
|
| 81 |
+
mm=n.output[0]+"_mmout" if len(n.input)>=3 else n.output[0]
|
| 82 |
+
newn.append(helper.make_node("MatMul",[n.input[0],wn],[mm],name=n.name+"_mm"))
|
| 83 |
+
if len(n.input)>=3: newn.append(helper.make_node("Add",[mm,n.input[2]],[n.output[0]],name=n.name+"_add"))
|
| 84 |
+
ng+=1
|
| 85 |
+
else: newn.append(n)
|
| 86 |
+
# remove Identity nodes fed by initializers (they hid the weights from the int8 quantizer)
|
| 87 |
+
inits_all={i.name for i in g.initializer}
|
| 88 |
+
alias={}
|
| 89 |
+
final=[]
|
| 90 |
+
for n in newn:
|
| 91 |
+
if n.op_type=="Identity" and (n.input[0] in inits_all or n.input[0] in alias):
|
| 92 |
+
alias[n.output[0]]=n.input[0]
|
| 93 |
+
else: final.append(n)
|
| 94 |
+
def res(x):
|
| 95 |
+
while x in alias: x=alias[x]
|
| 96 |
+
return x
|
| 97 |
+
for n in final:
|
| 98 |
+
for j,x in enumerate(n.input): n.input[j]=res(x)
|
| 99 |
+
for o in g.output:
|
| 100 |
+
if o.name in alias: raise SystemExit("identity feeds graph output")
|
| 101 |
+
del g.node[:]; g.node.extend(final); print("gemm->matmul",ng,"identity removed",len(alias),flush=True)
|
| 102 |
+
f32m="/kaggle/working/ncm_fp32_mm.onnx"; onnx.save(mo,f32m)
|
| 103 |
+
i8="/kaggle/working/out/ncm_fp16w.onnx" # weights stored fp16 + Cast->fp32 at load (compute stays fp32)
|
| 104 |
+
mo=onnx.load(f32m); g=mo.graph; casts=[]; nconv=0
|
| 105 |
+
for init in list(g.initializer):
|
| 106 |
+
if init.data_type==onnx.TensorProto.FLOAT and int(np.prod(init.dims))>=1024:
|
| 107 |
+
arr=numpy_helper.to_array(init); h=numpy_helper.from_array(arr.astype(np.float16),init.name+"_h")
|
| 108 |
+
casts.append(helper.make_node("Cast",[init.name+"_h"],[init.name],to=onnx.TensorProto.FLOAT,name=init.name+"_cast"))
|
| 109 |
+
g.initializer.remove(init); g.initializer.append(h); nconv+=1
|
| 110 |
+
nodes=list(g.node); del g.node[:]; g.node.extend(casts+nodes)
|
| 111 |
+
onnx.save(mo,i8); print("fp16-weights onnx MB",os.path.getsize(i8)/1e6,"converted",nconv,flush=True)
|
| 112 |
+
res={"files_MB":{"fp32":os.path.getsize(f32)/1e6,"fp16w":os.path.getsize(i8)/1e6},"cases":[]}
|
| 113 |
+
for name,path in [("fp32",f32m),("fp16w",i8)]:
|
| 114 |
+
s=ort.InferenceSession(path,providers=["CPUExecutionProvider"]); outs=[]
|
| 115 |
+
t=time.time()
|
| 116 |
+
for i in range(len(tests)):
|
| 117 |
+
o=s.run(None,{"prefix":pre[i:i+1].numpy(),"noise":Z.numpy(),"temp":np.array(0.0,dtype=np.float32)})[0][0].tolist()
|
| 118 |
+
outs.append(o)
|
| 119 |
+
dt=(time.time()-t)/len(tests); res[name]={"sec_per_reply_cpu":dt,"exact_match_vs_torch":sum(o==r for o,r in zip(outs,ref)),"replies":[txt(o) for o in outs]}
|
| 120 |
+
print(name,"exact-match",res[name]["exact_match_vs_torch"],"/",len(tests),"s/reply",round(dt,2),flush=True)
|
| 121 |
+
for t_,o in zip(tests,outs): print(" ",t_,"->",txt(o),flush=True)
|
| 122 |
+
res["torch_replies"]=[txt(r) for r in ref]
|
| 123 |
+
# tokenizer parity vectors for the browser
|
| 124 |
+
res["tok_vectors"]=[{"prompt":p,"ids":codec.enc(p),"prefix_ids":codec.prefix_ids(p)[:len(codec.enc(p))]} for p in prompts[:4]]
|
| 125 |
+
res["EOS"]=codec.EOS; res["PAD"]=codec.PAD; res["ppad"]=codec.ppad; res["P"]=codec.P; res["offs"]=offs
|
| 126 |
+
json.dump(res,open("/kaggle/working/out/report.json","w"),indent=1)
|
| 127 |
+
print("done",flush=True)
|
code/ncm_chat_train.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os, subprocess, sys, json, random, time, math
|
| 2 |
+
STEPS=int(os.environ.get("STEPS","300")); BS=int(os.environ.get("BS","24")); LR=float(os.environ.get("LR","1e-4"))
|
| 3 |
+
def sh(c): print("$",c,flush=True); subprocess.run(c,shell=True,check=True)
|
| 4 |
+
os.makedirs("/kaggle/working/w",exist_ok=True); os.chdir("/kaggle/working/w")
|
| 5 |
+
if not os.path.exists("ncm"): sh("git clone -q https://github.com/HazyResearch/ncm.git")
|
| 6 |
+
sh("pip install -q omegaconf hydra-core regex 2>&1 | tail -1")
|
| 7 |
+
os.makedirs("ck",exist_ok=True)
|
| 8 |
+
for f in ["config.yaml","generator.pt","tokenizer.pt"]:
|
| 9 |
+
if not os.path.exists("ck/"+f): sh(f"curl -sL -o ck/{f} https://huggingface.co/hazyresearch/ncm-checkpoints/resolve/main/xsum/{f}")
|
| 10 |
+
open('ncmchat.py','w').write('"""NCM chat fine-tune: reuse released XSum NCM (frozen multiscale VQ tokenizer + prefix-conditioned generator),\nfine-tune the generator on (conversation prefix -> short reply). Needs HazyResearch/ncm on NCM_ROOT."""\nimport os, sys, random, torch, torch.nn.functional as F\nfrom omegaconf import OmegaConf\nNCM_ROOT = os.environ.get("NCM_ROOT", "/tmp/ncm"); sys.path.insert(0, NCM_ROOT)\nfrom models.next_concept import NextConceptTransformer\nfrom tokenization.vqvae_tokenizer import VQVAETokenizer\n\ndef load(ck, cfg_path=None, dev="cpu", load_gen=True):\n CFG = OmegaConf.load(cfg_path or os.path.join(ck, "config.yaml"))\n CFG.tokenizer.vqvae.checkpoint_path = os.path.join(ck, CFG.checkpoints.tokenizer)\n tk = VQVAETokenizer.from_config(CFG.tokenizer)\n tk.vqvae = tk.vqvae.to(dev).eval()\n for p in tk.vqvae.parameters(): p.requires_grad = False\n net = NextConceptTransformer(vqvae_local=tk.vqvae, config=CFG.model).to(dev)\n if load_gen:\n sd = torch.load(os.path.join(ck, CFG.checkpoints.generator), map_location="cpu", weights_only=False)\n net.load_state_dict(sd["model"], strict=True)\n return CFG, tk, net\n\ndef fmt_prompt(turns):\n """turns: list of (role, text) ending with a user turn -> prompt string"""\n s = "".join(f"{\'User\' if r==\'user\' else \'Assistant\'}: {t}\\n" for r, t in turns)\n return s + "Assistant:"\n\nclass Codec:\n def __init__(self, CFG, tk, net):\n self.tk, self.net, self.P = tk, net, int(CFG.model.prefix_len)\n self.pre = tk.pre_tokenizer\n self.EOS, self.PAD = self.pre.eos_token_id, self.pre.pad_token_id\n self.ppad = int(CFG.model.prefix_pad_token_id) # pad id used in the generator prefix\n self.T = tk.vqvae.context_length # target length (48)\n self.hf = self.pre.tokenizer if hasattr(self.pre, "tokenizer") else None\n def enc(self, text): return self.pre.encode(text)\n def prefix_ids(self, prompt):\n ids = self.enc(prompt)[-self.P:] # keep most recent context\n return ids + [self.ppad] * (self.P - len(ids))\n def target_ids(self, reply):\n ids = self.enc(" " + reply.strip())[: self.T - 1] + [self.EOS]\n return ids + [self.EOS] * (self.T - len(ids))\n\n@torch.no_grad()\ndef target_codes(codec, tgt): # tgt: LongTensor (B,T) -> idx (B, sum levels)\n return codec.tk.vqvae.encode(tgt)\n\ndef build_inputs(codec, prefix, idx, corrupt=0.1):\n net, vq = codec.net, codec.tk.vqvae.vq\n with torch.no_grad():\n idc = idx.clone()\n if corrupt > 0: # robustness: corrupt conditioning codes (as in paper)\n m = torch.rand_like(idc, dtype=torch.float) < corrupt\n idc[m] = torch.randint(0, 4096, (int(m.sum()),), device=idc.device)\n nc = vq.idx_to_ncm_input(idc) # cumulative recon fed to levels 1..K-1\n zp = codec.tk.vqvae.encoder.wte(prefix)[:, :codec.P, :]\n q0 = net.build_l0_query_input(prefix.shape[0])\n return torch.cat([zp, q0, nc], dim=1)\n\ndef loss_fn(codec, prefix, tgt, corrupt=0.1):\n idx = target_codes(codec, tgt)\n x = build_inputs(codec, prefix, idx, corrupt)\n pad_mask = prefix == codec.ppad\n logits = codec.net(x, prefix_pad_mask=None if not hasattr(codec.net, "_prefix_pad_bias") else pad_mask)\n return F.cross_entropy(logits.reshape(-1, logits.shape[-1]).float(), idx.reshape(-1)), logits, idx\n\n@torch.no_grad()\ndef reply(codec, prompts, temperature=0.8, top_k=20, top_p=0.9, dev="cpu"):\n net, tk = codec.net, codec.tk\n pre = torch.tensor([codec.prefix_ids(p) for p in prompts], device=dev)\n g = net.generate(None, pre, temperature=temperature, top_k=top_k, top_p=top_p)\n _, zl = tk.vqvae.vq.idx_to_zhats(g, return_zlevels=True)\n ids = tk.vqvae.decoder(zl[-1]).argmax(-1).tolist()\n out = []\n for r in ids:\n if codec.EOS in r: r = r[: r.index(codec.EOS)]\n out.append(codec.pre.decode([t for t in r if t not in (codec.EOS, codec.PAD)], skip_special_tokens=True).strip())\n return out\n')
|
| 11 |
+
sys.path.insert(0,os.getcwd())
|
| 12 |
+
os.environ["NCM_ROOT"]="/kaggle/working/w/ncm"
|
| 13 |
+
import torch, ncmchat as N
|
| 14 |
+
dev="cuda"
|
| 15 |
+
CFG,tk,net=N.load("ck",dev=dev); codec=N.Codec(CFG,tk,net)
|
| 16 |
+
# ---------------- data ----------------
|
| 17 |
+
random.seed(0)
|
| 18 |
+
from datasets import load_dataset
|
| 19 |
+
ex=[] # (turns list[(role,text)], reply)
|
| 20 |
+
def add_dialog(utts, maxn=6):
|
| 21 |
+
# utts alternating, first is "user"
|
| 22 |
+
for i in range(1,len(utts)):
|
| 23 |
+
if i%2==1:
|
| 24 |
+
hist=[("user" if j%2==0 else "assistant",utts[j]) for j in range(max(0,i-3),i)]
|
| 25 |
+
if hist[0][0]=="assistant": hist=hist[1:]
|
| 26 |
+
ex.append((hist,utts[i]))
|
| 27 |
+
src={}
|
| 28 |
+
def tryload(name,fn):
|
| 29 |
+
try:
|
| 30 |
+
n0=len(ex); fn(); src[name]=len(ex)-n0; print("loaded",name,src[name],flush=True)
|
| 31 |
+
except Exception as e: print("FAILED",name,repr(e)[:200],flush=True)
|
| 32 |
+
def dd():
|
| 33 |
+
d=load_dataset("roskoN/dailydialog",split="train")
|
| 34 |
+
for r in d:
|
| 35 |
+
u=r.get("utterances") or r.get("dialog")
|
| 36 |
+
add_dialog([x.strip() for x in u])
|
| 37 |
+
def ev():
|
| 38 |
+
d=load_dataset("HuggingFaceTB/everyday-conversations-llama3.1-2k",split="train_sft")
|
| 39 |
+
for r in d:
|
| 40 |
+
m=r["messages"]; add_dialog([x["content"].strip() for x in m if x["role"]!="system"])
|
| 41 |
+
def dolly():
|
| 42 |
+
d=load_dataset("databricks/databricks-dolly-15k",split="train")
|
| 43 |
+
for r in d:
|
| 44 |
+
if not r["context"] and r["response"]: ex.append(([("user",r["instruction"].strip())],r["response"].strip()))
|
| 45 |
+
def alp():
|
| 46 |
+
d=load_dataset("tatsu-lab/alpaca",split="train")
|
| 47 |
+
for r in d:
|
| 48 |
+
if not r["input"]: ex.append(([("user",r["instruction"].strip())],r["output"].strip()))
|
| 49 |
+
tryload("dailydialog",dd); tryload("everyday",ev); tryload("dolly",dolly); tryload("alpaca",alp)
|
| 50 |
+
print("raw examples",len(ex),flush=True)
|
| 51 |
+
def ok(e):
|
| 52 |
+
h,r=e
|
| 53 |
+
return 0<len(codec.enc(r))<=40 and "\n" not in r and len(r)>1
|
| 54 |
+
ex=[e for e in ex if ok(e)]
|
| 55 |
+
random.shuffle(ex); print("kept short-reply examples",len(ex),flush=True)
|
| 56 |
+
val=ex[:500]; tr=ex[500:]
|
| 57 |
+
def batch(items):
|
| 58 |
+
pre=torch.tensor([codec.prefix_ids(N.fmt_prompt(h)) for h,_ in items],device=dev)
|
| 59 |
+
tgt=torch.tensor([codec.target_ids(r) for _,r in items],device=dev)
|
| 60 |
+
return pre,tgt
|
| 61 |
+
# ---------------- train ----------------
|
| 62 |
+
opt=torch.optim.AdamW([p for p in net.parameters() if p.requires_grad],lr=LR,weight_decay=0.01,betas=(0.9,0.95))
|
| 63 |
+
scaler=torch.cuda.amp.GradScaler()
|
| 64 |
+
def evalval():
|
| 65 |
+
net.eval(); tot=0;n=0
|
| 66 |
+
with torch.no_grad():
|
| 67 |
+
for i in range(0,len(val),50):
|
| 68 |
+
pre,tgt=batch(val[i:i+50])
|
| 69 |
+
with torch.autocast("cuda",dtype=torch.float16): l,_,_=N.loss_fn(codec,pre,tgt,0.0)
|
| 70 |
+
tot+=l.item()*len(pre); n+=len(pre)
|
| 71 |
+
net.train(); return tot/n
|
| 72 |
+
net.train(); print("val loss before",evalval(),flush=True)
|
| 73 |
+
t0=time.time(); ptr=0; ema=None
|
| 74 |
+
for step in range(1,STEPS+1):
|
| 75 |
+
lr=LR*min(1,step/50)*(0.1+0.9*0.5*(1+math.cos(math.pi*step/STEPS)))
|
| 76 |
+
for g in opt.param_groups: g["lr"]=lr
|
| 77 |
+
if ptr+BS>len(tr): random.shuffle(tr); ptr=0
|
| 78 |
+
pre,tgt=batch(tr[ptr:ptr+BS]); ptr+=BS
|
| 79 |
+
with torch.autocast("cuda",dtype=torch.float16): loss,_,_=N.loss_fn(codec,pre,tgt,0.1)
|
| 80 |
+
opt.zero_grad(set_to_none=True); scaler.scale(loss).backward(); scaler.unscale_(opt)
|
| 81 |
+
torch.nn.utils.clip_grad_norm_(net.parameters(),1.0); scaler.step(opt); scaler.update()
|
| 82 |
+
ema=loss.item() if ema is None else 0.98*ema+0.02*loss.item()
|
| 83 |
+
if step%50==0: print(f"step {step} loss {ema:.4f} lr {lr:.2e} {time.time()-t0:.0f}s",flush=True)
|
| 84 |
+
if step%1000==0: print("val loss",evalval(),flush=True)
|
| 85 |
+
print("val loss after",evalval(),flush=True)
|
| 86 |
+
net.eval()
|
| 87 |
+
tests=["Hi, how are you?","What is the capital of France?","Tell me a joke.","What do you like to do for fun?","Can you recommend a good book?","How do I boil an egg?","I'm feeling sad today.","What is the meaning of life?"]
|
| 88 |
+
prompts=[N.fmt_prompt([("user",t)]) for t in tests]
|
| 89 |
+
res=[]
|
| 90 |
+
for T,K in [(0.7,20),(0.0,1)]:
|
| 91 |
+
with torch.autocast("cuda",dtype=torch.float16): outs=N.reply(codec,prompts,temperature=T,top_k=K,top_p=0.9,dev=dev)
|
| 92 |
+
for t,o in zip(tests,outs): res.append({"temp":T,"user":t,"ncm_reply":o}); print(f"[T={T}] User: {t}\n NCM: {o}",flush=True)
|
| 93 |
+
# speed: one batch of 1 and 8
|
| 94 |
+
import time as _t
|
| 95 |
+
for B in (1,8):
|
| 96 |
+
torch.cuda.synchronize(); a=_t.time()
|
| 97 |
+
with torch.autocast("cuda",dtype=torch.float16): N.reply(codec,prompts[:B]*1,dev=dev)
|
| 98 |
+
torch.cuda.synchronize(); print(f"gen time batch {B}: {_t.time()-a:.3f}s",flush=True)
|
| 99 |
+
json.dump({"src":src,"n_train":len(tr),"steps":STEPS,"samples":res},open("/kaggle/working/report.json","w"),indent=1)
|
| 100 |
+
sd={"model":{k:v.detach().cpu().half() for k,v in net.state_dict().items()}}
|
| 101 |
+
torch.save(sd,"/kaggle/working/generator_chat.pt")
|
| 102 |
+
print("saved",flush=True)
|
code/ncm_export_kernel.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os, subprocess, sys, json, random, time, math
|
| 2 |
+
|
| 3 |
+
def sh(c): print("$",c,flush=True); subprocess.run(c,shell=True,check=True)
|
| 4 |
+
os.makedirs("/kaggle/working/w",exist_ok=True); os.chdir("/kaggle/working/w")
|
| 5 |
+
if not os.path.exists("ncm"): sh("git clone -q https://github.com/HazyResearch/ncm.git")
|
| 6 |
+
sh("pip install -q omegaconf hydra-core regex onnx onnxruntime onnxscript 2>&1 | tail -1")
|
| 7 |
+
os.makedirs("ck",exist_ok=True)
|
| 8 |
+
for f in ["config.yaml","generator.pt","tokenizer.pt"]:
|
| 9 |
+
if not os.path.exists("ck/"+f): sh(f"curl -sL -o ck/{f} https://huggingface.co/hazyresearch/ncm-checkpoints/resolve/main/xsum/{f}")
|
| 10 |
+
open('ncmchat.py','w').write('"""NCM chat fine-tune: reuse released XSum NCM (frozen multiscale VQ tokenizer + prefix-conditioned generator),\nfine-tune the generator on (conversation prefix -> short reply). Needs HazyResearch/ncm on NCM_ROOT."""\nimport os, sys, random, torch, torch.nn.functional as F\nfrom omegaconf import OmegaConf\nNCM_ROOT = os.environ.get("NCM_ROOT", "/tmp/ncm"); sys.path.insert(0, NCM_ROOT)\nfrom models.next_concept import NextConceptTransformer\nfrom tokenization.vqvae_tokenizer import VQVAETokenizer\n\ndef load(ck, cfg_path=None, dev="cpu", load_gen=True):\n CFG = OmegaConf.load(cfg_path or os.path.join(ck, "config.yaml"))\n CFG.tokenizer.vqvae.checkpoint_path = os.path.join(ck, CFG.checkpoints.tokenizer)\n tk = VQVAETokenizer.from_config(CFG.tokenizer)\n tk.vqvae = tk.vqvae.to(dev).eval()\n for p in tk.vqvae.parameters(): p.requires_grad = False\n net = NextConceptTransformer(vqvae_local=tk.vqvae, config=CFG.model).to(dev)\n if load_gen:\n sd = torch.load(os.path.join(ck, CFG.checkpoints.generator), map_location="cpu", weights_only=False)\n net.load_state_dict(sd["model"], strict=True)\n return CFG, tk, net\n\ndef fmt_prompt(turns):\n """turns: list of (role, text) ending with a user turn -> prompt string"""\n s = "".join(f"{\'User\' if r==\'user\' else \'Assistant\'}: {t}\\n" for r, t in turns)\n return s + "Assistant:"\n\nclass Codec:\n def __init__(self, CFG, tk, net):\n self.tk, self.net, self.P = tk, net, int(CFG.model.prefix_len)\n self.pre = tk.pre_tokenizer\n self.EOS, self.PAD = self.pre.eos_token_id, self.pre.pad_token_id\n self.ppad = int(CFG.model.prefix_pad_token_id) # pad id used in the generator prefix\n self.T = tk.vqvae.context_length # target length (48)\n self.hf = self.pre.tokenizer if hasattr(self.pre, "tokenizer") else None\n def enc(self, text): return self.pre.encode(text)\n def prefix_ids(self, prompt):\n ids = self.enc(prompt)[-self.P:] # keep most recent context\n return ids + [self.ppad] * (self.P - len(ids))\n def target_ids(self, reply):\n ids = self.enc(" " + reply.strip())[: self.T - 1] + [self.EOS]\n return ids + [self.EOS] * (self.T - len(ids))\n\n@torch.no_grad()\ndef target_codes(codec, tgt): # tgt: LongTensor (B,T) -> idx (B, sum levels)\n return codec.tk.vqvae.encode(tgt)\n\ndef build_inputs(codec, prefix, idx, corrupt=0.1):\n net, vq = codec.net, codec.tk.vqvae.vq\n with torch.no_grad():\n idc = idx.clone()\n if corrupt > 0: # robustness: corrupt conditioning codes (as in paper)\n m = torch.rand_like(idc, dtype=torch.float) < corrupt\n idc[m] = torch.randint(0, 4096, (int(m.sum()),), device=idc.device)\n nc = vq.idx_to_ncm_input(idc) # cumulative recon fed to levels 1..K-1\n zp = codec.tk.vqvae.encoder.wte(prefix)[:, :codec.P, :]\n q0 = net.build_l0_query_input(prefix.shape[0])\n return torch.cat([zp, q0, nc], dim=1)\n\ndef loss_fn(codec, prefix, tgt, corrupt=0.1):\n idx = target_codes(codec, tgt)\n x = build_inputs(codec, prefix, idx, corrupt)\n pad_mask = prefix == codec.ppad\n logits = codec.net(x, prefix_pad_mask=None if not hasattr(codec.net, "_prefix_pad_bias") else pad_mask)\n return F.cross_entropy(logits.reshape(-1, logits.shape[-1]).float(), idx.reshape(-1)), logits, idx\n\n@torch.no_grad()\ndef reply(codec, prompts, temperature=0.8, top_k=20, top_p=0.9, dev="cpu"):\n net, tk = codec.net, codec.tk\n pre = torch.tensor([codec.prefix_ids(p) for p in prompts], device=dev)\n g = net.generate(None, pre, temperature=temperature, top_k=top_k, top_p=top_p)\n _, zl = tk.vqvae.vq.idx_to_zhats(g, return_zlevels=True)\n ids = tk.vqvae.decoder(zl[-1]).argmax(-1).tolist()\n out = []\n for r in ids:\n if codec.EOS in r: r = r[: r.index(codec.EOS)]\n out.append(codec.pre.decode([t for t in r if t not in (codec.EOS, codec.PAD)], skip_special_tokens=True).strip())\n return out\n')
|
| 11 |
+
sys.path.insert(0,os.getcwd())
|
| 12 |
+
# Runs inside a Kaggle kernel (CPU ok). Inputs: /kaggle/input/<SRC>/generator_chat.pt ; outputs ncm_fp16w.onnx + report.
|
| 13 |
+
import os, sys, json, types, time
|
| 14 |
+
os.environ["NCM_ROOT"]="/kaggle/working/w/ncm"
|
| 15 |
+
import torch, torch.nn as nn, numpy as np, ncmchat as N
|
| 16 |
+
import torch.nn.functional as _F
|
| 17 |
+
_orig_interp=_F.interpolate
|
| 18 |
+
def _area_mat(L,o):
|
| 19 |
+
m=np.zeros((o,L),dtype=np.float32)
|
| 20 |
+
for i in range(o):
|
| 21 |
+
a=(i*L)//o; b=-((-(i+1)*L)//o); m[i,a:b]=1.0/(b-a)
|
| 22 |
+
return torch.from_numpy(m)
|
| 23 |
+
def _lm(L,o):
|
| 24 |
+
m=np.zeros((o,L),dtype=np.float32)
|
| 25 |
+
for i in range(o):
|
| 26 |
+
s=max((i+0.5)*L/o-0.5,0.0); i0=min(int(s),L-1); i1=min(i0+1,L-1); w=s-i0
|
| 27 |
+
m[i,i0]+=1-w; m[i,i1]+=w
|
| 28 |
+
return torch.from_numpy(m)
|
| 29 |
+
def _interp(x,size=None,scale_factor=None,mode="nearest",**kw):
|
| 30 |
+
if mode=="area" and x.dim()==3 and size is not None:
|
| 31 |
+
o=size[0] if isinstance(size,(tuple,list)) else size
|
| 32 |
+
return torch.matmul(x,_area_mat(x.shape[-1],o).t())
|
| 33 |
+
if mode=="linear" and x.dim()==3 and size is not None and not kw.get("align_corners"):
|
| 34 |
+
o=size[0] if isinstance(size,(tuple,list)) else size
|
| 35 |
+
return torch.matmul(x,_lm(x.shape[-1],o).t())
|
| 36 |
+
return _orig_interp(x,size=size,scale_factor=scale_factor,mode=mode,**kw)
|
| 37 |
+
_chk=torch.randn(2,5,48); assert torch.allclose(_orig_interp(_chk,size=(26,),mode="area"),_interp(_chk,size=(26,),mode="area"),atol=1e-5)
|
| 38 |
+
_F.interpolate=_interp
|
| 39 |
+
SRC=os.environ.get("SRC","ncm-chat-smoke")
|
| 40 |
+
cands=[f"/kaggle/input/{SRC}/generator_chat.pt"]+[os.path.join(r,"generator_chat.pt") for r,_,f in os.walk("/kaggle/input") if "generator_chat.pt" in f]
|
| 41 |
+
gp=[c for c in cands if os.path.exists(c)][0]; print("using",gp,flush=True)
|
| 42 |
+
PEFF=int(os.environ.get("PEFF","128"))
|
| 43 |
+
import re
|
| 44 |
+
_c=open("ck/config.yaml").read(); _c=re.sub(r"prefix_len: 512","prefix_len: %d"%PEFF,_c); open("ck/config_p.yaml","w").write(_c)
|
| 45 |
+
CFG,tk,net=N.load("ck",cfg_path="ck/config_p.yaml",dev="cpu",load_gen=False); net.eval()
|
| 46 |
+
sd=torch.load(gp,map_location="cpu",weights_only=False)["model"]
|
| 47 |
+
own=net.state_dict(); sdf={k:v.float() for k,v in sd.items() if k in own and own[k].shape==v.shape}
|
| 48 |
+
print("skipped keys:",[k for k in sd if k not in sdf],flush=True)
|
| 49 |
+
net.load_state_dict(sdf,strict=False); assert all(k in sdf for k in own if "attn_mask" not in k and not k.startswith("rope")), "missing weights"
|
| 50 |
+
codec=N.Codec(CFG,tk,net); offs=[0]
|
| 51 |
+
for l in net.levels: offs.append(offs[-1]+l)
|
| 52 |
+
class G(nn.Module):
|
| 53 |
+
def __init__(s):
|
| 54 |
+
super().__init__(); s.net=net; s.vq=tk.vqvae.vq; s.dec=tk.vqvae.decoder; s.enc=tk.vqvae.encoder
|
| 55 |
+
def forward(s, prefix, noise, temp):
|
| 56 |
+
st={"i":0}
|
| 57 |
+
def samp(self_, logits, temperature, top_k, top_p):
|
| 58 |
+
i=st["i"]; st["i"]+=1
|
| 59 |
+
nz=noise[:, offs[i]:offs[i+1], :]
|
| 60 |
+
v,_=torch.topk(logits,20,dim=-1)
|
| 61 |
+
lg=torch.where(logits<v[...,-1:],torch.full_like(logits,-1e9),logits)
|
| 62 |
+
return (lg+temp*nz).argmax(-1)
|
| 63 |
+
s.net._sample_level=types.MethodType(samp,s.net)
|
| 64 |
+
g=s.net.generate(None,prefix,temperature=1.0,top_k=20,top_p=0.9)
|
| 65 |
+
_,zl=s.vq.idx_to_zhats(g,return_zlevels=True)
|
| 66 |
+
return s.dec(zl[-1]).argmax(-1)
|
| 67 |
+
m=G().eval()
|
| 68 |
+
tests=["Hi, how are you?","What is the capital of France?","Tell me a joke.","What do you like to do for fun?","Can you recommend a good book?","How do I boil an egg?","I'm feeling sad today.","What is the meaning of life?"]
|
| 69 |
+
prompts=[N.fmt_prompt([("user",t)]) for t in tests]
|
| 70 |
+
pre=torch.tensor([codec.prefix_ids(p) for p in prompts])
|
| 71 |
+
Z=torch.zeros(1,offs[-1],4096); T0=torch.tensor(0.0)
|
| 72 |
+
def txt(ids):
|
| 73 |
+
ids=list(ids)
|
| 74 |
+
if codec.EOS in ids: ids=ids[:ids.index(codec.EOS)]
|
| 75 |
+
return codec.pre.decode([t for t in ids if t not in (codec.EOS,codec.PAD)],skip_special_tokens=True).strip()
|
| 76 |
+
with torch.no_grad(): ref=[m(pre[i:i+1],Z,T0)[0].tolist() for i in range(len(tests))]
|
| 77 |
+
print("torch greedy:",[txt(r) for r in ref],flush=True)
|
| 78 |
+
os.makedirs("/kaggle/working/out",exist_ok=True)
|
| 79 |
+
f32="/kaggle/working/ncm_fp32.onnx"
|
| 80 |
+
with torch.no_grad(): torch.onnx.export(m,(pre[:1],Z,T0),f32,input_names=["prefix","noise","temp"],output_names=["ids"],opset_version=17,dynamo=False)
|
| 81 |
+
print("fp32 onnx MB",os.path.getsize(f32)/1e6,flush=True)
|
| 82 |
+
import onnxruntime as ort
|
| 83 |
+
from onnxruntime.quantization import quantize_dynamic, QuantType
|
| 84 |
+
import onnx
|
| 85 |
+
from onnx import numpy_helper, helper
|
| 86 |
+
mo=onnx.load(f32); g=mo.graph; inits={i.name:i for i in g.initializer}; newn=[]; ng=0
|
| 87 |
+
for n in g.node:
|
| 88 |
+
if n.op_type=="Gemm" and n.input[1] in inits and (len(n.input)<3 or n.input[2] in inits):
|
| 89 |
+
at={a.name:helper.get_attribute_value(a) for a in n.attribute}
|
| 90 |
+
W=numpy_helper.to_array(inits[n.input[1]]); W=(W.T if at.get("transB",0) else W)*at.get("alpha",1.0)
|
| 91 |
+
wn=n.input[1]+"_mm"; g.initializer.append(numpy_helper.from_array(W.astype("float32"),wn))
|
| 92 |
+
mm=n.output[0]+"_mmout" if len(n.input)>=3 else n.output[0]
|
| 93 |
+
newn.append(helper.make_node("MatMul",[n.input[0],wn],[mm],name=n.name+"_mm"))
|
| 94 |
+
if len(n.input)>=3: newn.append(helper.make_node("Add",[mm,n.input[2]],[n.output[0]],name=n.name+"_add"))
|
| 95 |
+
ng+=1
|
| 96 |
+
else: newn.append(n)
|
| 97 |
+
# remove Identity nodes fed by initializers (they hid the weights from the int8 quantizer)
|
| 98 |
+
inits_all={i.name for i in g.initializer}
|
| 99 |
+
alias={}
|
| 100 |
+
final=[]
|
| 101 |
+
for n in newn:
|
| 102 |
+
if n.op_type=="Identity" and (n.input[0] in inits_all or n.input[0] in alias):
|
| 103 |
+
alias[n.output[0]]=n.input[0]
|
| 104 |
+
else: final.append(n)
|
| 105 |
+
def res(x):
|
| 106 |
+
while x in alias: x=alias[x]
|
| 107 |
+
return x
|
| 108 |
+
for n in final:
|
| 109 |
+
for j,x in enumerate(n.input): n.input[j]=res(x)
|
| 110 |
+
for o in g.output:
|
| 111 |
+
if o.name in alias: raise SystemExit("identity feeds graph output")
|
| 112 |
+
del g.node[:]; g.node.extend(final); print("gemm->matmul",ng,"identity removed",len(alias),flush=True)
|
| 113 |
+
f32m="/kaggle/working/ncm_fp32_mm.onnx"; onnx.save(mo,f32m)
|
| 114 |
+
i8="/kaggle/working/out/ncm_fp16w.onnx" # weights stored fp16 + Cast->fp32 at load (compute stays fp32)
|
| 115 |
+
mo=onnx.load(f32m); g=mo.graph; casts=[]; nconv=0
|
| 116 |
+
for init in list(g.initializer):
|
| 117 |
+
if init.data_type==onnx.TensorProto.FLOAT and int(np.prod(init.dims))>=1024:
|
| 118 |
+
arr=numpy_helper.to_array(init); h=numpy_helper.from_array(arr.astype(np.float16),init.name+"_h")
|
| 119 |
+
casts.append(helper.make_node("Cast",[init.name+"_h"],[init.name],to=onnx.TensorProto.FLOAT,name=init.name+"_cast"))
|
| 120 |
+
g.initializer.remove(init); g.initializer.append(h); nconv+=1
|
| 121 |
+
nodes=list(g.node); del g.node[:]; g.node.extend(casts+nodes)
|
| 122 |
+
onnx.save(mo,i8); print("fp16-weights onnx MB",os.path.getsize(i8)/1e6,"converted",nconv,flush=True)
|
| 123 |
+
res={"files_MB":{"fp32":os.path.getsize(f32)/1e6,"fp16w":os.path.getsize(i8)/1e6},"cases":[]}
|
| 124 |
+
for name,path in [("fp32",f32m),("fp16w",i8)]:
|
| 125 |
+
s=ort.InferenceSession(path,providers=["CPUExecutionProvider"]); outs=[]
|
| 126 |
+
t=time.time()
|
| 127 |
+
for i in range(len(tests)):
|
| 128 |
+
o=s.run(None,{"prefix":pre[i:i+1].numpy(),"noise":Z.numpy(),"temp":np.array(0.0,dtype=np.float32)})[0][0].tolist()
|
| 129 |
+
outs.append(o)
|
| 130 |
+
dt=(time.time()-t)/len(tests); res[name]={"sec_per_reply_cpu":dt,"exact_match_vs_torch":sum(o==r for o,r in zip(outs,ref)),"replies":[txt(o) for o in outs]}
|
| 131 |
+
print(name,"exact-match",res[name]["exact_match_vs_torch"],"/",len(tests),"s/reply",round(dt,2),flush=True)
|
| 132 |
+
for t_,o in zip(tests,outs): print(" ",t_,"->",txt(o),flush=True)
|
| 133 |
+
res["torch_replies"]=[txt(r) for r in ref]
|
| 134 |
+
# tokenizer parity vectors for the browser
|
| 135 |
+
res["tok_vectors"]=[{"prompt":p,"ids":codec.enc(p),"prefix_ids":codec.prefix_ids(p)[:len(codec.enc(p))]} for p in prompts[:4]]
|
| 136 |
+
res["EOS"]=codec.EOS; res["PAD"]=codec.PAD; res["ppad"]=codec.ppad; res["P"]=codec.P; res["offs"]=offs
|
| 137 |
+
json.dump(res,open("/kaggle/working/out/report.json","w"),indent=1)
|
| 138 |
+
print("done",flush=True)
|
code/ncmchat.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""NCM chat fine-tune: reuse released XSum NCM (frozen multiscale VQ tokenizer + prefix-conditioned generator),
|
| 2 |
+
fine-tune the generator on (conversation prefix -> short reply). Needs HazyResearch/ncm on NCM_ROOT."""
|
| 3 |
+
import os, sys, random, torch, torch.nn.functional as F
|
| 4 |
+
from omegaconf import OmegaConf
|
| 5 |
+
NCM_ROOT = os.environ.get("NCM_ROOT", "/tmp/ncm"); sys.path.insert(0, NCM_ROOT)
|
| 6 |
+
from models.next_concept import NextConceptTransformer
|
| 7 |
+
from tokenization.vqvae_tokenizer import VQVAETokenizer
|
| 8 |
+
|
| 9 |
+
def load(ck, cfg_path=None, dev="cpu", load_gen=True):
|
| 10 |
+
CFG = OmegaConf.load(cfg_path or os.path.join(ck, "config.yaml"))
|
| 11 |
+
CFG.tokenizer.vqvae.checkpoint_path = os.path.join(ck, CFG.checkpoints.tokenizer)
|
| 12 |
+
tk = VQVAETokenizer.from_config(CFG.tokenizer)
|
| 13 |
+
tk.vqvae = tk.vqvae.to(dev).eval()
|
| 14 |
+
for p in tk.vqvae.parameters(): p.requires_grad = False
|
| 15 |
+
net = NextConceptTransformer(vqvae_local=tk.vqvae, config=CFG.model).to(dev)
|
| 16 |
+
if load_gen:
|
| 17 |
+
sd = torch.load(os.path.join(ck, CFG.checkpoints.generator), map_location="cpu", weights_only=False)
|
| 18 |
+
net.load_state_dict(sd["model"], strict=True)
|
| 19 |
+
return CFG, tk, net
|
| 20 |
+
|
| 21 |
+
def fmt_prompt(turns):
|
| 22 |
+
"""turns: list of (role, text) ending with a user turn -> prompt string"""
|
| 23 |
+
s = "".join(f"{'User' if r=='user' else 'Assistant'}: {t}\n" for r, t in turns)
|
| 24 |
+
return s + "Assistant:"
|
| 25 |
+
|
| 26 |
+
class Codec:
|
| 27 |
+
def __init__(self, CFG, tk, net):
|
| 28 |
+
self.tk, self.net, self.P = tk, net, int(CFG.model.prefix_len)
|
| 29 |
+
self.pre = tk.pre_tokenizer
|
| 30 |
+
self.EOS, self.PAD = self.pre.eos_token_id, self.pre.pad_token_id
|
| 31 |
+
self.ppad = int(CFG.model.prefix_pad_token_id) # pad id used in the generator prefix
|
| 32 |
+
self.T = tk.vqvae.context_length # target length (48)
|
| 33 |
+
self.hf = self.pre.tokenizer if hasattr(self.pre, "tokenizer") else None
|
| 34 |
+
def enc(self, text): return self.pre.encode(text)
|
| 35 |
+
def prefix_ids(self, prompt):
|
| 36 |
+
ids = self.enc(prompt)[-self.P:] # keep most recent context
|
| 37 |
+
return ids + [self.ppad] * (self.P - len(ids))
|
| 38 |
+
def target_ids(self, reply):
|
| 39 |
+
ids = self.enc(" " + reply.strip())[: self.T - 1] + [self.EOS]
|
| 40 |
+
return ids + [self.EOS] * (self.T - len(ids))
|
| 41 |
+
|
| 42 |
+
@torch.no_grad()
|
| 43 |
+
def target_codes(codec, tgt): # tgt: LongTensor (B,T) -> idx (B, sum levels)
|
| 44 |
+
return codec.tk.vqvae.encode(tgt)
|
| 45 |
+
|
| 46 |
+
def build_inputs(codec, prefix, idx, corrupt=0.1):
|
| 47 |
+
net, vq = codec.net, codec.tk.vqvae.vq
|
| 48 |
+
with torch.no_grad():
|
| 49 |
+
idc = idx.clone()
|
| 50 |
+
if corrupt > 0: # robustness: corrupt conditioning codes (as in paper)
|
| 51 |
+
m = torch.rand_like(idc, dtype=torch.float) < corrupt
|
| 52 |
+
idc[m] = torch.randint(0, 4096, (int(m.sum()),), device=idc.device)
|
| 53 |
+
nc = vq.idx_to_ncm_input(idc) # cumulative recon fed to levels 1..K-1
|
| 54 |
+
zp = codec.tk.vqvae.encoder.wte(prefix)[:, :codec.P, :]
|
| 55 |
+
q0 = net.build_l0_query_input(prefix.shape[0])
|
| 56 |
+
return torch.cat([zp, q0, nc], dim=1)
|
| 57 |
+
|
| 58 |
+
def loss_fn(codec, prefix, tgt, corrupt=0.1):
|
| 59 |
+
idx = target_codes(codec, tgt)
|
| 60 |
+
x = build_inputs(codec, prefix, idx, corrupt)
|
| 61 |
+
pad_mask = prefix == codec.ppad
|
| 62 |
+
logits = codec.net(x, prefix_pad_mask=None if not hasattr(codec.net, "_prefix_pad_bias") else pad_mask)
|
| 63 |
+
return F.cross_entropy(logits.reshape(-1, logits.shape[-1]).float(), idx.reshape(-1)), logits, idx
|
| 64 |
+
|
| 65 |
+
@torch.no_grad()
|
| 66 |
+
def reply(codec, prompts, temperature=0.8, top_k=20, top_p=0.9, dev="cpu"):
|
| 67 |
+
net, tk = codec.net, codec.tk
|
| 68 |
+
pre = torch.tensor([codec.prefix_ids(p) for p in prompts], device=dev)
|
| 69 |
+
g = net.generate(None, pre, temperature=temperature, top_k=top_k, top_p=top_p)
|
| 70 |
+
_, zl = tk.vqvae.vq.idx_to_zhats(g, return_zlevels=True)
|
| 71 |
+
ids = tk.vqvae.decoder(zl[-1]).argmax(-1).tolist()
|
| 72 |
+
out = []
|
| 73 |
+
for r in ids:
|
| 74 |
+
if codec.EOS in r: r = r[: r.index(codec.EOS)]
|
| 75 |
+
out.append(codec.pre.decode([t for t in r if t not in (codec.EOS, codec.PAD)], skip_special_tokens=True).strip())
|
| 76 |
+
return out
|
generator_chat.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b21acb483deb0814dbaee271f054fd7ebb43019b18321dd28d2de5dd75a87d6
|
| 3 |
+
size 177107827
|
training_report.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"src": {
|
| 3 |
+
"everyday": 8625,
|
| 4 |
+
"dolly": 10544,
|
| 5 |
+
"alpaca": 31323
|
| 6 |
+
},
|
| 7 |
+
"n_train": 21084,
|
| 8 |
+
"steps": 4000,
|
| 9 |
+
"samples": [
|
| 10 |
+
{
|
| 11 |
+
"temp": 0.7,
|
| 12 |
+
"user": "Hi, how are you?",
|
| 13 |
+
"ncm_reply": "Hello! How can I help you today?"
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"temp": 0.7,
|
| 17 |
+
"user": "What is the capital of France?",
|
| 18 |
+
"ncm_reply": "The capital capital of Paris is Paris."
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"temp": 0.7,
|
| 22 |
+
"user": "Tell me a joke.",
|
| 23 |
+
"ncm_reply": "Hello! a weather??"
|
| 24 |
+
},
|
| 25 |
+
{
|
| 26 |
+
"temp": 0.7,
|
| 27 |
+
"user": "What do you like to do for fun?",
|
| 28 |
+
"ncm_reply": "Enough to make making fun about wishing your fun, and love books and making you look fun!"
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"temp": 0.7,
|
| 32 |
+
"user": "Can you recommend a good book?",
|
| 33 |
+
"ncm_reply": "A book is a good book."
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"temp": 0.7,
|
| 37 |
+
"user": "How do I boil an egg?",
|
| 38 |
+
"ncm_reply": "When you boil an egg, you might try."
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"temp": 0.7,
|
| 42 |
+
"user": "I'm feeling sad today.",
|
| 43 |
+
"ncm_reply": "I am very sorry."
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"temp": 0.7,
|
| 47 |
+
"user": "What is the meaning of life?",
|
| 48 |
+
"ncm_reply": "The meaning meaning of life means that."
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"temp": 0.0,
|
| 52 |
+
"user": "Hi, how are you?",
|
| 53 |
+
"ncm_reply": "Hello! How can I help you today?"
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"temp": 0.0,
|
| 57 |
+
"user": "What is the capital of France?",
|
| 58 |
+
"ncm_reply": "Paris is a capital capital."
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"temp": 0.0,
|
| 62 |
+
"user": "Tell me a joke.",
|
| 63 |
+
"ncm_reply": "you don't help you help you today?"
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"temp": 0.0,
|
| 67 |
+
"user": "What do you like to do for fun?",
|
| 68 |
+
"ncm_reply": "What fun items do you like like fun, cake, butter, butter, or.."
|
| 69 |
+
},
|
| 70 |
+
{
|
| 71 |
+
"temp": 0.0,
|
| 72 |
+
"user": "Can you recommend a good book?",
|
| 73 |
+
"ncm_reply": "Stars is a good book."
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"temp": 0.0,
|
| 77 |
+
"user": "How do I boil an egg?",
|
| 78 |
+
"ncm_reply": "You can tap a fruit cake egg."
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"temp": 0.0,
|
| 82 |
+
"user": "I'm feeling sad today.",
|
| 83 |
+
"ncm_reply": "I'm feeling sorry.."
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"temp": 0.0,
|
| 87 |
+
"user": "What is the meaning of life?",
|
| 88 |
+
"ncm_reply": "Life is a daily meaning of life."
|
| 89 |
+
}
|
| 90 |
+
]
|
| 91 |
+
}
|