Upload scripts/model_dna.py with huggingface_hub
Browse files- scripts/model_dna.py +100 -0
scripts/model_dna.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# model_dna.py - DNA-DiskChat-0.5b: DiskChat backbone + PKM table stored as CODON rows.
|
| 2 |
+
# v14 scheme: rows persist as codons (uint8) + shared learned codebook; training keeps an
|
| 3 |
+
# fp32 shadow (STE) and re-encodes touched rows each step ("mutation").
|
| 4 |
+
import math, torch
|
| 5 |
+
from torch import nn
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
|
| 8 |
+
G=4; KCODE=256 # codon group + codewords: 512 dims -> 128 codons -> 128 B/row (75% off int8)
|
| 9 |
+
|
| 10 |
+
def diag_scan(g,u,C=16):
|
| 11 |
+
B,T,D=g.shape; pad=(C-T%C)%C
|
| 12 |
+
if pad: g=F.pad(g,(0,0,0,pad),value=1.0); u=F.pad(u,(0,0,0,pad),value=0.0)
|
| 13 |
+
Tp=T+pad; NC=Tp//C; g=g.view(B,NC,C,D); u=u.view(B,NC,C,D)
|
| 14 |
+
s=torch.zeros(B,NC,D,device=g.device,dtype=g.dtype); P=torch.ones_like(s); loc=[]; Ps=[]
|
| 15 |
+
for t in range(C):
|
| 16 |
+
s=g[:,:,t,:]*s+u[:,:,t,:]; loc.append(s); P=P*g[:,:,t,:]; Ps.append(P)
|
| 17 |
+
s_local=torch.stack(loc,2); Pcum=torch.stack(Ps,2); Gc=Pcum[:,:,-1,:]; sl=s_local[:,:,-1,:]
|
| 18 |
+
carry=torch.zeros(B,D,device=g.device,dtype=g.dtype); car=[]
|
| 19 |
+
for c in range(NC):
|
| 20 |
+
car.append(carry); carry=Gc[:,c,:]*carry+sl[:,c,:]
|
| 21 |
+
car=torch.stack(car,1); return (s_local+Pcum*car[:,:,None,:]).view(B,Tp,D)[:,:T,:]
|
| 22 |
+
|
| 23 |
+
class FastBlock(nn.Module):
|
| 24 |
+
def __init__(self,d,ff):
|
| 25 |
+
super().__init__(); self.n1=nn.LayerNorm(d); self.proj=nn.Linear(d,4*d,bias=False); self.o=nn.Linear(d,d,bias=False)
|
| 26 |
+
self.n2=nn.LayerNorm(d); self.up=nn.Linear(d,2*ff,bias=False); self.down=nn.Linear(ff,d,bias=False); self.decay=nn.Parameter(torch.full((d,),2.0))
|
| 27 |
+
def forward(self,x,C=16):
|
| 28 |
+
k,v,r,g=self.proj(self.n1(x)).chunk(4,-1); g=torch.sigmoid(g+self.decay); u=(1-g)*torch.tanh(k)
|
| 29 |
+
s=diag_scan(g,u,C); x=x+self.o(torch.sigmoid(r)*s*torch.sigmoid(v))
|
| 30 |
+
a,b=self.up(self.n2(x)).chunk(2,-1); return x+self.down(F.silu(b)*a)
|
| 31 |
+
|
| 32 |
+
class STEDecode(torch.autograd.Function):
|
| 33 |
+
"""fwd: decoded codon rows. bwd: pass grad straight through to shadow rows (STE)."""
|
| 34 |
+
@staticmethod
|
| 35 |
+
def forward(ctx,shadow_rows,decoded_rows):
|
| 36 |
+
return decoded_rows
|
| 37 |
+
@staticmethod
|
| 38 |
+
def backward(ctx,gout):
|
| 39 |
+
return gout,gout # STE into shadow AND through decode into the codebook
|
| 40 |
+
|
| 41 |
+
class DnaChat(nn.Module):
|
| 42 |
+
def __init__(self,vocab=32000,d=512,layers=16,ff=1536,a=2048,b=2048,chunk=16):
|
| 43 |
+
super().__init__(); self.vocab=vocab; self.d=d; self.layers=layers; self.ff=ff; self.a=a; self.b=b; self.chunk=chunk
|
| 44 |
+
self.ncod=d//G; self.nrows=a*b
|
| 45 |
+
self.embed=nn.Embedding(vocab,d); nn.init.normal_(self.embed.weight,std=.02)
|
| 46 |
+
self.blocks=nn.ModuleList([FastBlock(d,ff) for _ in range(layers)]); self.norm=nn.LayerNorm(d)
|
| 47 |
+
self.ra=nn.Linear(d,a,bias=False); self.rb=nn.Linear(d,b,bias=False)
|
| 48 |
+
self.ca=nn.Parameter(torch.randn(a,d)*.02); self.cb=nn.Parameter(torch.randn(b,d)*.02)
|
| 49 |
+
self.head2=nn.Linear(d,vocab,bias=False); nn.init.normal_(self.head2.weight,std=.02)
|
| 50 |
+
# DNA table (v16 ZERO-SHADOW + v17): genotype only; POSITIONAL codebooks (reading frames):
|
| 51 |
+
# 4 codebooks cycled by codon position -> 4x decode capacity at SAME 128 B/row rate.
|
| 52 |
+
self.codebook=nn.Parameter(torch.randn(4,KCODE,G)*.05)
|
| 53 |
+
self.register_buffer('codons',torch.randint(0,KCODE,(self.nrows,self.ncod),dtype=torch.uint8))
|
| 54 |
+
frames=torch.arange(self.ncod)%4
|
| 55 |
+
self.register_buffer('frames',frames) # [ncod]
|
| 56 |
+
# v17 UltraMem-style implicit expansion: stored 128 B -> effective 2x value dim
|
| 57 |
+
self.expand=nn.Linear(d,2*d,bias=False); self.contract=nn.Linear(2*d,d,bias=False)
|
| 58 |
+
@torch.no_grad()
|
| 59 |
+
def mutate_rows(self,idx,new_rows,chunk=65536):
|
| 60 |
+
"""v17 mutation, CHUNKED to bound cdist memory (fixes OOM at large cadences)."""
|
| 61 |
+
for s0 in range(0,idx.size(0),chunk):
|
| 62 |
+
sl=slice(s0,min(s0+chunk,idx.size(0)))
|
| 63 |
+
x=new_rows[sl].view(-1,self.ncod,G)
|
| 64 |
+
out=torch.empty(x.size(0),self.ncod,dtype=torch.uint8,device=x.device)
|
| 65 |
+
for fr in range(4):
|
| 66 |
+
mask=(self.frames==fr)
|
| 67 |
+
xf=x[:,mask,:].reshape(-1,G)
|
| 68 |
+
d2=torch.cdist(xf.bfloat16(),self.codebook[fr].bfloat16())
|
| 69 |
+
out[:,mask]=d2.argmin(-1).view(x.size(0),-1).to(torch.uint8)
|
| 70 |
+
self.codons[idx[sl]]=out
|
| 71 |
+
def decode_rows(self,idx):
|
| 72 |
+
cod=self.codons[idx].long() # [n,ncod]
|
| 73 |
+
fr=self.frames.unsqueeze(0).expand_as(cod) # [n,ncod]
|
| 74 |
+
dec=self.codebook[fr,cod].view(-1,self.d).to(getattr(self,'_hdtype',self.codebook.dtype)) # dtype-safe positional decode
|
| 75 |
+
leaf=dec.detach().clone().requires_grad_(True)
|
| 76 |
+
self._leaf=leaf; self._leaf_idx=idx
|
| 77 |
+
return dec + (leaf - leaf.detach())
|
| 78 |
+
def route(self,h):
|
| 79 |
+
self._hdtype=h.dtype
|
| 80 |
+
xa,xb=self.ra(h),self.rb(h); va,ia=xa.topk(2,-1); vb,ib=xb.topk(2,-1)
|
| 81 |
+
s0=va[:,0]+vb[:,0]; sa=va[:,1]+vb[:,0]; sb=va[:,0]+vb[:,1]; use=sa>=sb
|
| 82 |
+
k0=ia[:,0]*self.b+ib[:,0]; k1=torch.where(use,ia[:,1]*self.b+ib[:,0],ia[:,0]*self.b+ib[:,1]); s1=torch.where(use,sa,sb)
|
| 83 |
+
alpha=torch.softmax(torch.stack((s0,s1),1),1).unsqueeze(-1)
|
| 84 |
+
rows=torch.stack((k0,k1),1) # [N,2]
|
| 85 |
+
dec=self.decode_rows(rows.reshape(-1)).view(-1,2,self.d)
|
| 86 |
+
ext=(dec*alpha.to(dec.dtype)).sum(1)
|
| 87 |
+
ext=self.contract(F.silu(self.expand(ext)).view(ext.size(0),2,self.d).flatten(1)) # v17 implicit expansion
|
| 88 |
+
code=(self.ca[ia[:,0]]+self.cb[ib[:,0]]).to(xa.dtype)
|
| 89 |
+
pa,pb=xa.softmax(-1).mean(0),xb.softmax(-1).mean(0); balance=.5*(self.a*pa.square().sum()+self.b*pb.square().sum())
|
| 90 |
+
return ext+code,balance,rows
|
| 91 |
+
def features(self,ids):
|
| 92 |
+
B,T=ids.shape; x=self.embed(ids)
|
| 93 |
+
for blk in self.blocks: x=blk(x,self.chunk)
|
| 94 |
+
mem,bal,rows=self.route(x.reshape(B*T,self.d)); return self.norm(x+mem.reshape(B,T,self.d)),bal,rows
|
| 95 |
+
def config(self): return {k:getattr(self,k) for k in ('vocab','d','layers','ff','a','b')}
|
| 96 |
+
|
| 97 |
+
def counts(m):
|
| 98 |
+
ctrl=sum(p.numel() for n,p in m.named_parameters())
|
| 99 |
+
tbl_bytes=m.nrows*m.ncod # codon bytes on disk
|
| 100 |
+
return ctrl,tbl_bytes
|