nx_f32_conv2d_backward_gate.nx source
↩ module page · 78 lines · 4638 B
1// nx_f32_conv2d_backward_gate.nx -- EXACT gradient check of nx_f32_conv2d_backward. Integer-valued inputs/weights
2// make every gradient an exact small integer, so the analytic backward is checked BIT-EXACT (nx_i32_to_f32 canonical
3// f32, ==) with NO numerical tolerance -- a stronger proof than finite-difference. Toy conv: C_in=1 H=3 W=3
4// input=[1..9], C_out=1 KH=KW=2 stride=1 pad=0 (OH=OW=2), weight=[10,20,30,40].
5// dOut=all-ones -> dW=[12,16,24,28], dBias=4, dIn(0,0)=10, dIn(1,1 center)=100 (verified by hand)
6// dOut=[1,2,3,4] -> dW[0,0]=37, dBias=10 (general dOut weighting)
7// stride=0 -> ERR_BAD_ARGS (guard)
8// expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_f32_cvt.nx"
11import "nx_f32_conv2d_backward.nx"
12
13func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return sys_write(1, s, n) }
14func gn(v: i64) -> i64 {
15 let bb: *u8 = sys_mmap(28); var m: i64 = v
16 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
17 let t: *u8 = sys_mmap(28); var k: i64 = 0
18 if m == 0 { t[0] = 48 as u8; k = 1 }
19 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
20 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
21 return sys_write(1, bb, k)
22}
23
24func main(argc: i64, argv: *i64) -> i64 {
25 var pass: i64 = 0
26 let input: *i64 = sys_mmap(8 * 16) as *i64
27 let weight: *i64 = sys_mmap(8 * 16) as *i64
28 let dout: *i64 = sys_mmap(8 * 16) as *i64
29 let dinput: *i64 = sys_mmap(8 * 16) as *i64
30 let dweight: *i64 = sys_mmap(8 * 16) as *i64
31 let dbias: *i64 = sys_mmap(8) as *i64
32
33 // input = 1..9 (row-major 3x3)
34 var i: i64 = 0; while i < 9 { input[i] = nx_i32_to_f32(i + 1); i = i + 1 }
35 // weight 2x2 = [10,20,30,40]
36 weight[0] = nx_i32_to_f32(10); weight[1] = nx_i32_to_f32(20)
37 weight[2] = nx_i32_to_f32(30); weight[3] = nx_i32_to_f32(40)
38
39 // ===== run 1: dOut = all ones =====
40 i = 0; while i < 4 { dout[i] = nx_i32_to_f32(1); i = i + 1 }
41 var r1: i64 = nx_f32_conv2d_backward(input, 1, 1, 3, 3, weight, 1, 2, 2, 1, 0, dout, dinput, dweight, dbias)
42
43 // CB1 dWeight exact = [12,16,24,28]
44 if dweight[0] == nx_i32_to_f32(12) { if dweight[1] == nx_i32_to_f32(16) { if dweight[2] == nx_i32_to_f32(24) { if dweight[3] == nx_i32_to_f32(28) {
45 pass = pass + 1; gp("CB1 dWeight=[12,16,24,28] exact OK\n" as *u8)
46 } } } }
47 if dweight[0] != nx_i32_to_f32(12) { gp("CB1 FAIL dW0 bits=" as *u8); gn(dweight[0]); gp(" want=" as *u8); gn(nx_i32_to_f32(12)); gp("\n" as *u8) }
48
49 // CB2 dBias exact = 4 (OH*OW)
50 if dbias[0] == nx_i32_to_f32(4) { pass = pass + 1; gp("CB2 dBias=4 exact OK\n" as *u8) }
51 if dbias[0] != nx_i32_to_f32(4) { gp("CB2 FAIL dBias bits=" as *u8); gn(dbias[0]); gp("\n" as *u8) }
52
53 // CB3 dInput exact: corner (0,0)=10, center (1,1)=input idx 4 =100
54 if dinput[0] == nx_i32_to_f32(10) { if dinput[4] == nx_i32_to_f32(100) {
55 pass = pass + 1; gp("CB3 dInput corner=10, center=100 exact OK\n" as *u8)
56 } }
57 if dinput[4] != nx_i32_to_f32(100) { gp("CB3 FAIL dIn0=" as *u8); gn(dinput[0]); gp(" dIn4=" as *u8); gn(dinput[4]); gp(" want100=" as *u8); gn(nx_i32_to_f32(100)); gp("\n" as *u8) }
58
59 // ===== run 2: dOut = [1,2,3,4] (general weighting) =====
60 dout[0] = nx_i32_to_f32(1); dout[1] = nx_i32_to_f32(2); dout[2] = nx_i32_to_f32(3); dout[3] = nx_i32_to_f32(4)
61 var r2: i64 = nx_f32_conv2d_backward(input, 1, 1, 3, 3, weight, 1, 2, 2, 1, 0, dout, dinput, dweight, dbias)
62
63 // CB4 dW[0,0] = 1*1+2*2+3*4+4*5 = 37 ; dBias = 1+2+3+4 = 10
64 if dweight[0] == nx_i32_to_f32(37) { if dbias[0] == nx_i32_to_f32(10) {
65 pass = pass + 1; gp("CB4 general dOut -> dW[0,0]=37, dBias=10 exact OK\n" as *u8)
66 } }
67 if dweight[0] != nx_i32_to_f32(37) { gp("CB4 FAIL dW0=" as *u8); gn(dweight[0]); gp(" want37=" as *u8); gn(nx_i32_to_f32(37)); gp("\n" as *u8) }
68
69 // CB5 guard: stride 0 -> ERR_BAD_ARGS (4). (r1,r2 should have been 0.)
70 var r3: i64 = nx_f32_conv2d_backward(input, 1, 1, 3, 3, weight, 1, 2, 2, 0, 0, dout, dinput, dweight, dbias)
71 if r1 == 0 { if r2 == 0 { if r3 == 4 { pass = pass + 1; gp("CB5 rc ok (fwd=0,0; stride0=ERR) OK\n" as *u8) } } }
72 if r3 != 4 { gp("CB5 FAIL r1=" as *u8); gn(r1); gp(" r2=" as *u8); gn(r2); gp(" r3=" as *u8); gn(r3); gp("\n" as *u8) }
73
74 gp("CONV-BWD-GATE pass=" as *u8); gn(pass); gp("/5\n" as *u8)
75 if pass == 5 { gp("CONV-BWD-GATE GREEN 5/5 (dWeight + dBias + dInput exact + general dOut + guard) -- conv is now trainable\n" as *u8); sys_exit(0) }
76 sys_exit(1)
77 return 0
78}