code wiki / _hdl_build / nx_nofloat_distill_gate.nx
nx_nofloat_distill_gate.nx source
↩ module page · 91 lines · 5326 B
1// nx_nofloat_distill_gate.nx -- R3 (cheap quality) from the sovereign-researcher roadmap
2// (knowledge/research/2026-06-23-nofloat-affordable-land-roadmap.md, grounded in nfs_hinton_distill.raw,
3// arXiv 1503.02531): DISTILLATION -- a student trained on a teacher's SOFT output distribution inherits the
4// full distribution ("dark knowledge": relative probabilities of the non-top classes), which one-hot HARD
5// labels throw away. Pure integer Q16; trains student logits W via softmax_rows(W) -> mse(target) (existing
6// gradcheck-verified ops, NO lib change -> zero regression risk to the other gates).
7// T1 the distilled student MATCHES the teacher's soft distribution (low total L1).
8// T2 (the point) distilled L1-to-teacher << hard-label student L1-to-teacher (soft targets > hard labels).
9// expect_exit: 0 Sovereign: nx_nofloat_autograd + nx_syscalls.
10import "nx_nofloat_autograd.nx"
11import "nx_syscalls.nx"
12import "nx_gate_emit_lib.nx"
13const Q16: i64 = 65536
14
15
16func g_abs(v: i64) -> i64 { if v<0 { return 0-v } return v }
17
18// train student logits Wp[C*V] so softmax_rows(Wp) matches target[C*V]; returns nothing (Wp updated in place).
19func train_student(tape: *i64, vals: *i64, grads: *i64, st: *i64, Wp: *i64, target: *i64, C: i64, V: i64, gb: *i64, steps: i64) -> i64 {
20 var ep: i64=0
21 while ep < steps {
22 st[0]=0; st[1]=0
23 let nW: i64 = nfa_leaf(tape,vals,st,C,V,Wp,0)
24 let nP: i64 = nfa_softmax_rows(tape,vals,st,nW,0) // causal=0 -> full per-row softmax
25 let nT: i64 = nfa_leaf(tape,vals,st,C,V,target,0)
26 let loss: i64 = nfa_mse(tape,vals,st,nP,nT)
27 nfa_backward(tape,vals,grads,st[0],loss)
28 var i: i64=0; while i<C*V { gb[i]=nfa_grad(tape,grads,nW,i); i=i+1 }
29 nfa_sgd(Wp, gb, C*V, 8192)
30 ep=ep+1
31 }
32 return 0
33}
34// total L1 distance between softmax_rows(Wp) and the teacher T (both [C,V], Q16). Lower = closer to teacher.
35func l1_to_teacher(tape: *i64, vals: *i64, st: *i64, Wp: *i64, T: *i64, C: i64, V: i64) -> i64 {
36 st[0]=0; st[1]=0
37 let nW: i64 = nfa_leaf(tape,vals,st,C,V,Wp,0)
38 let nP: i64 = nfa_softmax_rows(tape,vals,st,nW,0)
39 let off: i64 = tape[7*nP+5]
40 var s: i64=0; var i: i64=0
41 while i<C*V { s = s + g_abs(vals[off+i]-T[i]); i=i+1 }
42 return s
43}
44
45func main() -> i64 {
46 g_puts("nx_nofloat_distill gate (DISTILLATION: soft targets transfer the full distribution vs hard labels)\n" as *u8)
47 let C: i64=4; let V: i64=4
48 let tape: *i64 = sys_mmap(256*7*8) as *i64
49 let vals: *i64 = sys_mmap(4096*8) as *i64
50 let grads: *i64 = sys_mmap(4096*8) as *i64
51 let st: *i64 = sys_mmap(2*8) as *i64
52 let gb: *i64 = sys_mmap(64*8) as *i64
53
54 // teacher T: soft per-context distributions (each row sums to Q16). Row 3 is UNIFORM = pure dark knowledge.
55 let T: *i64 = sys_mmap(C*V*8) as *i64
56 T[0]=45875; T[1]=13107; T[2]=6554; T[3]=0 // 0.70 0.20 0.10 0.00
57 T[4]=6554; T[5]=39322; T[6]=13107; T[7]=6553 // 0.10 0.60 0.20 0.10
58 T[8]=0; T[9]=6554; T[10]=52428; T[11]=6554 // 0.00 0.10 0.80 0.10
59 T[12]=16384;T[13]=16384;T[14]=16384;T[15]=16384 // 0.25 0.25 0.25 0.25 (uniform)
60 // hard labels H = one-hot(argmax row of T)
61 let H: *i64 = sys_mmap(C*V*8) as *i64
62 var z: i64=0; while z<C*V { H[z]=0; z=z+1 }
63 H[0]=Q16; H[4+1]=Q16; H[8+2]=Q16; H[12+0]=Q16 // argmax rows: 0,1,2,0
64
65 // distilled student (soft targets = T)
66 let Wd: *i64 = sys_mmap(C*V*8) as *i64; var i: i64=0; while i<C*V { Wd[i]=((i%5)-2)*4096; i=i+1 }
67 train_student(tape,vals,grads,st,Wd,T,C,V,gb,5000)
68 let ld: i64 = l1_to_teacher(tape,vals,st,Wd,T,C,V)
69
70 // hard-label student (one-hot targets = H), measured against the SAME soft teacher T
71 let Wh: *i64 = sys_mmap(C*V*8) as *i64; i=0; while i<C*V { Wh[i]=((i%5)-2)*4096; i=i+1 }
72 train_student(tape,vals,grads,st,Wh,H,C,V,gb,5000)
73 let lh: i64 = l1_to_teacher(tape,vals,st,Wh,T,C,V)
74
75 g_puts(" [measure] L1(student softmax vs soft teacher T): distilled=" as *u8); g_pn(ld); g_puts(" hard-label=" as *u8); g_pn(lh); g_puts(" (Q16; lower=closer; total mass=" as *u8); g_pn(C*Q16); g_puts(")\n" as *u8)
76
77 var pass: i64=0; var total: i64=0
78 var t1: i64=0; if ld < 26214 { t1=1 } // distilled total L1 < 0.4 (Q16) across all C rows = matches T
79 pass=pass+g_check("T1: distilled student MATCHES the teacher's soft distribution (low L1)" as *u8, t1); total=total+1
80 var t2: i64=0; if ld*2 < lh { t2=1 } // distilled at least 2x closer than hard-label
81 pass=pass+g_check("T2: soft targets >> hard labels -- distilled L1 << hard L1 (transfers the dark knowledge)" as *u8, t2); total=total+1
82
83 var okall: i64=0; if pass==total { okall=1 }
84 if okall==1 {
85 let logf: i64 = sys_openat_append("knowledge/status/nofloat_distill.log" as *u8, 420)
86 if logf >= 0 { let x0: i64=sys_write(logf,"NOFLOATDISTILL soft-vs-hard target distillation measured\n" as *u8,56); sys_close(logf) }
87 }
88 g_puts("---- distill gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
89 if okall==1 { g_puts("verdict=GREEN (distillation: soft targets transfer the teacher's full distribution; R3 cheap-quality win)\n" as *u8); sys_exit(0); return 0 }
90 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
91}