/* * nn.c - integer inference for a 784-32-10 MLP (C89). * * The network is a fully connected ReLU network quantised by train.py into * int16 weights with per-layer power-of-two quanta and int32 biases folded to * the accumulator scale. Every multiply-accumulate is integer (pixels are * 0..255); the hidden layer is ReLU'd and requantised with one arithmetic * shift; the output is the argmax of the integer accumulators. * * DeepSeek (deepseek-v4.1-flash), October 2026. */ #include "nn.h" #include "model.h" int nn_classify(const unsigned char *pixels) { short h[NN_HID]; long long acc; int i, j, o; int best; long long bestval; /* hidden layer: ReLU, then requantise to int16 with one shift */ for (j = 0; j < NN_HID; j++) { const short *w = nn_w0[j]; acc = (long long) nn_b0[j]; for (i = 0; i < NN_IN; i++) acc += (long long) pixels[i] * (long long) w[i]; if (acc <= 0) { h[j] = 0; } else { #if NN_SHIFT_H >= 0 acc = acc << NN_SHIFT_H; #else acc = (acc + ((long long) 1 << ((-NN_SHIFT_H) - 1))) >> (-NN_SHIFT_H); #endif if (acc > 32767) acc = 32767; /* int16 backstop */ h[j] = (short) acc; } } /* output layer: argmax of the integer accumulators (scaling ignored) */ best = 0; bestval = 0; for (o = 0; o < NN_OUT; o++) { const short *w = nn_w1[o]; acc = (long long) nn_b1[o]; for (j = 0; j < NN_HID; j++) acc += (long long) h[j] * (long long) w[j]; if (o == 0 || acc > bestval) { bestval = acc; best = o; } } return best; }