nx_f32_conv_train_gate.nx source
↩ module page · 108 lines · 5677 B
1// nx_f32_conv_train_gate.nx -- proves the SOVEREIGN conv TRAINING LOOP converges end-to-end: forward
2// (nx_f32_conv2d_forward) -> MSE loss/grad (nx_f32_train_ops) -> backward (nx_f32_conv2d_backward) -> SGD step,
3// iterated. A 2x2 conv (C_in=1 H=2 W=2, C_out=1, 1x1 out) is trained from ZERO weights to fit a target produced by
4// a known "true" weight; the loss must never increase and must converge to ~0 -- the first mechanical evidence that
5// "build 2" (training our own pose net) works, reducing the arc to SCALE + real labeled data. Also unit-checks the
6// ReLU backward and the MSE gradient (needed for the multi-layer pose net). This is a CONVERGENCE proof, NOT a
7// trained pose net. expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_f32_cvt.nx"
10import "nx_f32.nx"
11import "nx_f32_conv2d.nx"
12import "nx_f32_conv2d_backward.nx"
13import "nx_f32_train_ops.nx"
14
15func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return sys_write(1, s, n) }
16func gn(v: i64) -> i64 {
17 let bb: *u8 = sys_mmap(28); var m: i64 = v
18 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
19 let t: *u8 = sys_mmap(28); var k: i64 = 0
20 if m == 0 { t[0] = 48 as u8; k = 1 }
21 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
22 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
23 return sys_write(1, bb, k)
24}
25
26func main(argc: i64, argv: *i64) -> i64 {
27 var pass: i64 = 0
28 let input: *i64 = sys_mmap(8 * 8) as *i64
29 let tw: *i64 = sys_mmap(8 * 8) as *i64
30 let tb: *i64 = sys_mmap(8) as *i64
31 let target: *i64 = sys_mmap(8) as *i64
32 let w: *i64 = sys_mmap(8 * 8) as *i64
33 let b: *i64 = sys_mmap(8) as *i64
34 let pred: *i64 = sys_mmap(8) as *i64
35 let dpred: *i64 = sys_mmap(8) as *i64
36 let dinput: *i64 = sys_mmap(8 * 8) as *i64
37 let dweight: *i64 = sys_mmap(8 * 8) as *i64
38 let dbias: *i64 = sys_mmap(8) as *i64
39
40 // input 2x2 = [1,2,3,4]; true weight = [3,1,4,1], true bias = 2 -> target = 3+2+12+4+2 = 23
41 input[0] = nx_i32_to_f32(1); input[1] = nx_i32_to_f32(2); input[2] = nx_i32_to_f32(3); input[3] = nx_i32_to_f32(4)
42 tw[0] = nx_i32_to_f32(3); tw[1] = nx_i32_to_f32(1); tw[2] = nx_i32_to_f32(4); tw[3] = nx_i32_to_f32(1)
43 tb[0] = nx_i32_to_f32(2)
44 nx_f32_conv2d_forward(input, 1, 1, 2, 2, tw, 1, 2, 2, 1, 0, tb, target)
45
46 // model starts at zero
47 var i: i64 = 0; while i < 4 { w[i] = 0; i = i + 1 }
48 b[0] = 0
49 let lr: i64 = nx_q14_to_f32(512) // 0.03125, safely below 2/(||x||^2+1)=2/31
50
51 // initial loss (w=0)
52 nx_f32_conv2d_forward(input, 1, 1, 2, 2, w, 1, 2, 2, 1, 0, b, pred)
53 let initL: i64 = f32_mse_loss(pred, target, 1)
54
55 // train
56 var step: i64 = 0
57 var prevL: i64 = initL
58 var mono: i64 = 1
59 while step < 100 {
60 nx_f32_conv2d_forward(input, 1, 1, 2, 2, w, 1, 2, 2, 1, 0, b, pred)
61 let L: i64 = f32_mse_loss(pred, target, 1)
62 if step < 12 { if nx_f32_gt(L, prevL) == 1 { mono = 0 } } // loss must never INCREASE
63 prevL = L
64 f32_mse_grad(pred, target, dpred, 1)
65 nx_f32_conv2d_backward(input, 1, 1, 2, 2, w, 1, 2, 2, 1, 0, dpred, dinput, dweight, dbias)
66 f32_sgd_step(w, dweight, lr, 4)
67 f32_sgd_step(b, dbias, lr, 1)
68 step = step + 1
69 }
70 nx_f32_conv2d_forward(input, 1, 1, 2, 2, w, 1, 2, 2, 1, 0, b, pred)
71 let finalL: i64 = f32_mse_loss(pred, target, 1)
72
73 // T1 loss monotone non-increasing over the first steps
74 if mono == 1 { pass = pass + 1; gp("T1 loss never increased (gradient-descent direction correct) OK\n" as *u8) }
75 if mono == 0 { gp("T1 FAIL loss increased at some early step\n" as *u8) }
76
77 // T2 converged >= 100x below initial: finalL*100 < initL
78 if nx_f32_lt(nx_f32_mul(finalL, nx_i32_to_f32(100)), initL) == 1 { pass = pass + 1; gp("T2 converged >=100x below initial OK\n" as *u8) }
79 if nx_f32_lt(nx_f32_mul(finalL, nx_i32_to_f32(100)), initL) == 0 { gp("T2 FAIL not converged\n" as *u8) }
80
81 // T3 final loss small absolute (< ~0.01)
82 if nx_f32_lt(finalL, nx_q14_to_f32(164)) == 1 { pass = pass + 1; gp("T3 final loss < 0.01 OK\n" as *u8) }
83 if nx_f32_lt(finalL, nx_q14_to_f32(164)) == 0 { gp("T3 FAIL final loss not small\n" as *u8) }
84
85 // T4 relu backward: post=[5,0,3,0], dpost=[1,1,1,1] -> dpre=[1,0,1,0]
86 let post: *i64 = sys_mmap(8 * 4) as *i64
87 let dpost: *i64 = sys_mmap(8 * 4) as *i64
88 let dpre: *i64 = sys_mmap(8 * 4) as *i64
89 post[0] = nx_i32_to_f32(5); post[1] = 0; post[2] = nx_i32_to_f32(3); post[3] = 0
90 i = 0; while i < 4 { dpost[i] = nx_i32_to_f32(1); i = i + 1 }
91 f32_relu_bwd(post, dpost, dpre, 4)
92 if dpre[0] == nx_i32_to_f32(1) { if dpre[1] == 0 { if dpre[2] == nx_i32_to_f32(1) { if dpre[3] == 0 {
93 pass = pass + 1; gp("T4 relu_bwd gates gradient by activation OK\n" as *u8)
94 } } } }
95 if dpre[1] != 0 { gp("T4 FAIL dpre=[" as *u8); gn(dpre[0]); gp("," as *u8); gn(dpre[1]); gp("]\n" as *u8) }
96
97 // T5 mse_grad exact: pred=10, target=7 -> 3
98 let p2: *i64 = sys_mmap(8) as *i64; let t2: *i64 = sys_mmap(8) as *i64; let g2: *i64 = sys_mmap(8) as *i64
99 p2[0] = nx_i32_to_f32(10); t2[0] = nx_i32_to_f32(7)
100 f32_mse_grad(p2, t2, g2, 1)
101 if g2[0] == nx_i32_to_f32(3) { pass = pass + 1; gp("T5 mse_grad (pred-target) exact OK\n" as *u8) }
102 if g2[0] != nx_i32_to_f32(3) { gp("T5 FAIL g=" as *u8); gn(g2[0]); gp("\n" as *u8) }
103
104 gp("CONV-TRAIN-GATE pass=" as *u8); gn(pass); gp("/5\n" as *u8)
105 if pass == 5 { gp("CONV-TRAIN-GATE GREEN 5/5 (conv training loop CONVERGES + relu_bwd + mse_grad) -- build-2 loop is mechanically sound\n" as *u8); sys_exit(0) }
106 sys_exit(1)
107 return 0
108}