Paper · 2025-01-01 · cs.LG, cs.AI
AI-ANNE: (A) (N)eural (N)et for (E)xploration: Transferring Deep Learning Models onto Microcontrollers and Embedded Systems
Working paper on AI-ANNE, a MicroPython implementation of dense neural networks (neurons, layers, activations) that runs weights exported from TensorFlow/Keras on a Raspberry Pi Pico. Two pre-trained nets (8 and 6 neurons) classify Iris Versicolor vs Virginica at 90% and 95% accuracy on 20 samples. No latency or energy measurements.
On-device inferenceMulti-layer perceptron (MLP)Deep learningtinymlmicrocontrollerinference
arXivPDFCodeMarkdown9 min read
AI-compiled by claude-haiku-5-5 on 2026-10-25, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Small neural networks are usually trained and run on powerful computers with large numerical libraries. A microcontroller, the tiny chip inside a sensor or a small gadget, has far less memory and no such libraries. This working paper shows how to train a small network on a normal computer, copy its learned numbers (the weights and biases) onto a Raspberry Pi Pico, and run it there using MicroPython, a lightweight version of Python.
The paper builds the network from its basic parts, which are neurons, layers, and activation functions, written as plain Python lists and loops. It then runs two small example networks on an Iris flower classification task.
The paper reports only accuracy for these two examples and gives no speed or energy measurements. Treat the claims about efficiency as the authors' framing.
TL;DR
- Ports a pre-trained Keras/TensorFlow dense network to MicroPython on a Raspberry Pi Pico, implementing neurons, layers, and activations by hand §I app. A app. C.
- Two example nets: 8 neurons (2-3-2-1) at 90% and 6 neurons (3-2-1) at 95% accuracy on a binary Iris task §IV.
- Accuracy is the only metric. No baseline, no timing, no memory numbers.
- The public code differs from the paper in several activation choices and in how weights are laid out; see Paper vs code before implementing.
Problem
Frameworks such as TensorFlow and Keras need resources a microcontroller does not have, so trained networks cannot run there as they are §I. Training on the device is described as a complex challenge §I. The Raspberry Pi Pico (RP2040: 133 MHz, 264 KB SRAM, 2 MB flash) and the Pico 2 (RP2350: 150 MHz, 520 KB SRAM, 4 MB flash) are the target boards §II. The paper assumes the networks are small enough to be written as explicit MicroPython loops, and that training happens on a larger system beforehand §III.d §IV.
Method
The workflow is: pre-train in Python, export the parameters, then rebuild the forward pass on the board.
- Pre-train a small dense network in TensorFlow and Keras on a computer. The paper names the activations used per layer for each example §IV.
- Export each layer's weights and biases in a fixed order into MicroPython lists. The paper shows the 8-neuron and 6-neuron values tab. 2 tab. 4.
- Implement the building blocks in MicroPython: zero and add helpers, transpose, matrix creation, a
denselayer that loops over units, and aneuronthat sums weighted inputs, adds the bias and applies an activation app. A app. C. - Implement the activation functions Sigmoid, ReLU, Leaky ReLU, Tanh and Softmax app. B. The formulas are in §III.d.
- Feed the standardized Iris data through the layers in order. The example transposes the test matrix so each row of the input is one feature across all samples tab. 3 tab. 5.
- Compute accuracy from the confusion matrix of predicted against true classes §III tab. 1.
A single neuron computes a weighted sum of its inputs, adds a bias, and applies an activation §III.a:
y = f\left(\sum_i w_i x_i + b\right) §III.a
The activation functions are defined as follows. The paper does not number these equations §III.d:
\mathrm{Sigmoid}(x) = \frac{1}{1 + e^{-x}} §III.d
\mathrm{ReLU}(x) = \begin{cases} x & x \geq 0 \\ 0 & x < 0 \end{cases} §III.d
\mathrm{LeakyReLU}(x) = \begin{cases} x & x \geq 0 \\ \alpha x & x < 0 \end{cases} §III.d
\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} §III.d
\mathrm{Softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}} §III.d
Dense layer, written in the paper's batch form. Weights are passed as (units, inputs), which is the transpose of the tables; see Pitfalls.
# Dense layer over a batch, as the paper's code does [tab. A] [tab. C]
# inputs: (n_in, batch); weights: (n_units, n_in); biases: (n_units,)
# output: (n_units, batch)
def dense(n_units, inputs, weights, biases, activation):
# one neuron per output unit [tab. A]
outputs = []
for unit in range(n_units):
# weighted sum over the inputs, for every sample [§III.a] [tab. C]
total = zeros(batch)
for i in range(n_in):
total = total + weights[unit][i] * inputs[i]
# add the bias before the activation [§III.a] [tab. C]
total = total + biases[unit]
# apply the activation to the vector over samples [tab. C]
outputs.append(activate(total, activation))
return outputsForward pass of the 8-neuron example as the paper writes it. The values of w1 to w4 and b1 to b4 are in tab. 2. The paper's code transposes them first ki-enna-b.py.
# Forward pass of the 8-neuron example [tab. 3]
# x: standardized features, shape (4, batch)
def predict_8_neurons(x):
# input layer: 2 units, ReLU [tab. 3]
hidden_1 = dense(2, x, w1, b1, 'relu')
# first hidden layer: 3 units, tanh [tab. 3]
hidden_2 = dense(3, hidden_1, w2, b2, 'tanh')
# second hidden layer: 2 units, softmax [tab. 3]
hidden_3 = dense(2, hidden_2, w3, b3, 'softmax')
# output layer: 1 unit, sigmoid [tab. 3]
return dense(1, hidden_3, w4, b4, 'sigmoid')The class decision uses a threshold of 0.5. The paper does not state it; the code does ki-enna-b.py.
# Binary decision from the sigmoid output [code: ki-enna-b.py#L1112]
# probs: output of the single output unit, one value per sample
def classify(probs):
# the paper does not state this threshold
return [1 if p > 0.5 else 0 for p in probs]Hyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| Training framework | TensorFlow and Keras, Python | §IV | pre-training happens off-device |
| Target device | Raspberry Pi Pico or Pico 2, MicroPython, Thonny | §II | Pico 2 recommended for practical use §II |
| Task | Binary: Iris Versicolor vs Virginica | §IV | which class is coded as 1 is not stated |
| Input features | 4: sepal length, sepal width, petal length, petal width | §IV | standardization parameters not stated |
| 8-neuron net, input layer | 2 units, ReLU | tab. 3 | |
| 8-neuron net, hidden layer 1 | 3 units, tanh | tab. 3 | |
| 8-neuron net, hidden layer 2 | 2 units, softmax | tab. 3 | softmax behaviour: see Pitfalls |
| 8-neuron net, output | 1 unit, sigmoid | tab. 3 | |
| 8-neuron net, weight shapes | w1 4x2, w2 2x3, w3 3x2, w4 2x1; b1 2, b2 3, b3 2, b4 1 | tab. 2 | written as inputs x units |
| 6-neuron net, input layer | 3 units, ReLU | tab. 5 | |
| 6-neuron net, hidden layer | 2 units, sigmoid | tab. 5 | |
| 6-neuron net, output | 1 unit, sigmoid | tab. 5 | |
| 6-neuron net, weight shapes | w1 4x3, w2 3x2, w3 2x1; b1 3, b2 2, b3 1 | tab. 4 | written as inputs x units |
| Decision threshold | 0.5 | not stated §IV | in code: ki-enna-b.py |
| Leaky ReLU slope alpha | small negative slope; 0.01 or 0.1 given as examples | §III.d app. B | neither example net uses Leaky ReLU |
| Epochs, learning rate, optimizer, loss, batch size | not stated | device screen text says "500 EPOs @ 0.035 LR" ki-enna-b.py; training notebooks are not in the repo clone | |
| Standardization parameters, Iris nets | not stated | the test matrix is pre-standardized in code ki-enna-b.py | |
| Input scaling, third net in ai-anne-b.py | min/max bounds per feature: 4.9-7.9, 2.0-3.8, 3.0-6.9, 1.0-2.5 | ai-anne-b.py | not described in the paper |
Results
Author-reported. Setup: binary Iris task (Versicolor vs Virginica), a test set of 20 samples implied by tab. 1, pre-trained TensorFlow/Keras nets exported to MicroPython. No baseline is reported. The paper names AIfES, another on-device framework, as a comparable approach §I but gives no comparison numbers.
| Setting | Metric | Baseline | This paper | Source |
|---|---|---|---|---|
| 8-neuron net (2-3-2-1 units) | accuracy | not stated | 90% | §IV |
| 6-neuron net (3-2-1 units) | accuracy | not stated | 95% | §IV tab. 1 |
| 6-neuron net, confusion counts | TP / FN / FP / TN | not stated | 9 / 1 / 0 / 10 | tab. 1 |
| Latency, memory, energy on the Pico | any | not stated | not measured | none in the paper |
The paper's confusion matrix is an illustrative example for the 6-neuron net tab. 1. The paper prints the counts with leading zeros.
Paper vs code
Checked against the repository statistical-thinking/KI.ENNA at
code_commit (MIT, LICENSE). The README calls this version 2.0.1 [README.md#L131],
so it is later than the paper.
- Weight values match. The 8-neuron and 6-neuron weights and biases in tab. 2 and tab. 4 match the values in the code on the entries I compared. ki-enna-b.py ki-enna-b.py
- 8-neuron activations differ. The paper uses ReLU, tanh, softmax and sigmoid tab. 3. The code uses tanh, sigmoid, ReLU and sigmoid ki-enna-b.py.
- 6-neuron activations differ. The paper uses ReLU, sigmoid and sigmoid tab. 5. The code uses ReLU in all three layers ki-enna-b.py.
- Weights are transposed in the code. The paper's tables store weights as
inputs x units, but its
denseindexes them per unit tab. 2 app. A. The code transposes them before use ki-enna-b.py ki-enna-b.py. The paper does not mention this step. - The Leaky ReLU branch calls ReLU. The
neuronfunction sends theleaky_relubranch toreluand ignores alpha tab. C ki-enna-b.py. - The softmax branch calls tanh. The
neuronfunction sendssoftmaxtotanhtab. C ki-enna-b.py. The softmax helper in the code is a separate function ki-enna-b.py that is not used by the example nets. - Accuracy and loss on the device are typed in. The device screen shows fixed strings for accuracy, loss, epochs and learning rate ki-enna-b.py ki-enna-b.py. They are not computed at run time.
- Confusion matrix order differs. The paper's table lists actual positive
first tab. 1. The code builds
[[TN, FP], [FN, TP]], with actual negative first ki-enna-b.py. - A third network exists in the repository.
ai-anne-b.pycontains a 4-4-2-1 net with leaky ReLU, sigmoid and sigmoid, input min/max scaling, and a different weight layout (no transpose) ai-anne-b.py ai-anne-b.py. The paper does not describe it. - The license statement differs. Appendix D names the German Free Software
License app. D. The repository's
LICENSEis MIT, and the README says the 2021 version used the German Free Software License while 2.0.1 is MIT [README.md#L62] [README.md#L130-L132] [LICENSE]. The card records the repo license as MIT. - Training code is not in the clone. The paper says the repository holds
Jupyter notebooks for pre-training §Open Source Code. The clone at
code_commithas no.ipynbfiles, so the training recipe cannot be checked.
Pitfalls
- Weight layout. The tables are inputs x units tab. 2 tab. 4. The
densein the paper indexes weights per unit app. A. Transpose before use, as the repository does ki-enna-b.py.ai-anne-b.pytakes the other layout ai-anne-b.py. - Softmax normalises over samples, not units. The
neuronfunction applies the activation to the vector over samples for one unit tab. C. As written, softmax therefore normalises across the batch for each unit. The paper's text describes normalising across classes §III.d. An implementer who follows the code gets the batch behaviour; one who follows the text gets the class behaviour. The paper does not say which it intends. - The paper's softmax code is wrong. The code in tab. B takes
max(x[val])and exponentiatesval - max_xover indices, not values. Use the formula in §III.d with the maximum subtracted per sample. - Leaky ReLU is not implemented. tab. C and the code send the
leaky_reluname to ReLU. Implement alpha yourself if you need it. - Overflow in sigmoid and tanh. The formulas in tab. B call
expon the raw input. Large negative inputs overflow the float range.ai-anne-b.pybranches on the sign of the input to avoid this ai-anne-b.py. - Accuracy rests on 20 samples. One wrong sample moves accuracy by 5 percentage points tab. 1. The paper does not describe a split or cross-validation.
- Label mapping and threshold are not stated. Which class is 1 and the 0.5 cut-off are not in the paper. The code uses 0.5 ki-enna-b.py.
- Preprocessing is not stated. The Iris input is pre-standardized in the code ki-enna-b.py. The paper gives no means or scales.
- The two example nets do not match the code. Activations differ from the paper (see Paper vs code). Results from the paper's stated setup should not be assumed to hold for the code without rerunning.
- Cost is not measured. The paper claims low latency and low energy use but reports no timing, memory or power data. Memory use on the 264 KB Pico is not measured either §II.
- Interpreter cost. The layers are Python loops over lists. The paper does not measure the cost of this.
Lineage
- Builds on: none stated. The paper cites TinyML and MicroPython background only §I §II.
- Compared against: AIfES (Wulfert et al., 2024; no arXiv id in the paper) §I. The paper says AIfES outperforms similar tools in speed and memory for certain tasks, without giving the data.
- Superseded by: not known from this wiki.
Source and license
Adapted from “AI-ANNE: (A) (N)eural (N)et for (E)xploration: Transferring Deep Learning Models onto Microcontrollers and Embedded Systems” by Dennis Klinkhammer (arXiv:2501.03256), used under CC BY-SA 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. This page is licensed under CC BY-SA 4.0. Code citations refer to https://github.com/statistical-thinking/KI.ENNA at commit f21ae0a926f1 (MIT). Any quoted code is shown under that license.