code wiki / (root) / nx_f32_conv_train_gate.nx

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}