Master of Science Thesis
2026-09-08
Source: thesis (Coming soon)
Summary
My master's thesis, done at Linköping University during spring 2026, builds a quantized neural network (QNN) that estimates blood perfusion from multi-exposure laser speckle contrast imaging (MELSCI) measurements, deployed on an FPGA for real-time, bedside inference. MELSCI is a camera-based technique that measures blood flow across a whole area of tissue at once — useful for burn assessment, wound care, and monitoring tissue viability in reconstructive surgery — and prior research had shown that a neural network estimates perfusion from MELSCI more accurately than the older, model-based approach. Getting that network to run at the bedside in real time favors an FPGA over a CPU or GPU, for its lower power draw and deterministic latency, which in turn means the network has to be quantized down to low-precision integer arithmetic instead of running in floating point.
The thesis builds two things end to end: a quantization-aware training (QAT) flow around Brevitas for a small fully-connected regression network, and a purpose-built export and hardware pipeline that turns a trained model directly into a bit-exact Verilog implementation. Fourteen model configurations were trained across four bit-widths (2, 4, 8, 16), with and without a bias term, and, at 8 bits, four synthetic noise levels. The 8-bit design was carried all the way through export and verified to reproduce its own Python simulation bit-exactly against the RTL, sample by sample, on both the full synthetic test set and a real hand-perfusion image. On synthetic data, 8-bit accuracy is close to the 16-bit ceiling and matches prior published floating-point results; on the real hand image, accuracy is much lower over the whole frame (mostly due to the model never having been trained on background pixels) but recovers to close to the synthetic figures once restricted to the hand's own silhouette. So the design flow itself is sound and hardware-verified end to end, with a real, on-subject accuracy gap left as the main open problem.
Everything in the project — training, editing, exporting, simulating, and rendering results — is reachable from a single command-line tool, qnn.sh, rather than a collection of standalone scripts.
Brevitas
Brevitas is a PyTorch library for quantization-aware training: it provides drop-in quantized replacements for standard torch.nn layers, together with a wide selection of interchangeable quantizers for weights, biases, and activations. Each quantized layer inserts fake-quantization and a straight-through estimator directly into the PyTorch autograd graph, so a QAT model trains with an ordinary optimizer and loss function without any custom backward-pass code.
The network itself is a stack of QuantIdentity (quantizes the seven-channel input), QuantLinear (quantized fully-connected layers, with an optional bias), and QuantTanh (quantized activation) — a 7-10-1 linear-tanh-linear topology. All reported models use Int8WeightPerTensorFixedPointMSE for weights and Int8ActPerTensorFixedPointMSE for activations: per-tensor, power-of-two-scale quantizers whose scale is calibrated by minimizing quantization error in an MSE sense. Restricting the scale to a power of two is what later lets rescaling between layers on the FPGA reduce to a bit shift instead of a multiplication. Where a bias is used, it is quantized separately at a fixed 16 bits regardless of the rest of the network's bit-width — a per-layer bias at the same low bit-width as the surrounding weights turned out to leave too little resolution to correct the tanh non-linearity's floor near zero perfusion.
Bit-width is configurable per layer, but every model in the thesis shares one bit-width across all layers, so it can be treated as a single independent variable. The whole layer specification (types, dimensions, quantizer choices, bit-widths) lives in one portable JSON format that is the single source of truth for both training and FPGA export.
Network editor and training TUI
Rather than hand-editing that JSON, a network is normally built through an interactive terminal UI (built with Textual) that wraps layer creation, training, evaluation, and export in one screen.
Layer types offered are restricted to what the export path actually supports, and every field (dimensions, quantizer, bit-width) is a typed input or dropdown rather than free text, so invalid configurations are rejected immediately, in place, instead of surfacing later as a training-time exception. As a network is assembled, the editor runs the same worst-case-bit-width calculation the FPGA export uses to size the hardware accumulator, live, and annotates each layer with its resulting bit-width and whether the network as a whole still fits the target FPGA's limits — catching an infeasible configuration before a training run is spent on it, rather than after.
Training itself uses the Adam optimizer with MSE loss and a cosine-annealing learning-rate schedule, with learning rate, epoch count, batch size, noise modality, and seed all exposed as editable fields. A single training request can also train several sequential-seed copies of the same configuration in one batch, for later variance estimation. Once trained, a model can be evaluated against the synthetic test set or a real hand image, have its loss history plotted, and be exported to FPGA memory files, all without leaving the tool.
FPGA
The exported network architecture is deliberately independent of how much hardware is spent computing it. A trained model is exported as a set of .mem files (per-layer weights, per-layer tanh lookup tables, and an architecture-description file) in Verilog $readmemh hex format, and the same architecture can be run by many different physical engines, trading FPGA resources against per-pixel latency. That trade is governed by three build-time constants — P (multipliers per accumulator), A (parallel output accumulators), and T (parallel tanh LUT readers) — fixed at synthesis time. A single synthesized engine can then run any exported model that fits its structural ceiling on layer count and width, without resynthesis, since only latency (not correctness) depends on (P, A, T).
A standalone resource solver enumerates every (P, A, T) combination against a target device's DSP48/BRAM36/LUT budget (a Xilinx Zynq UltraScale+ XCZU7EG, by default), discards anything over budget, and picks the lowest-latency feasible point. For the network used in this thesis, the committed build (P=A=16, T=1) achieves 36 cycles of per-pixel latency (200 ns at 180 MHz, 5.00 Mpx/s) at 14.8% DSP and 23.4% BRAM usage; letting the solver pick T freely instead selects T=4, cutting latency to 22 cycles (8.18 Mpx/s, a 64% throughput increase) at the cost of raising BRAM usage to 92.6%.
The Verilog top-level module interprets the exported model at runtime rather than having it baked in at compile time: it loads the architecture-description file into an on-chip register file and the weight/tanh files into ROM at time zero, so a change to the trained model's shape only needs a re-export, not a resynthesis, as long as it fits the structural ceiling. An AXI4-Lite slave alongside the top-level module additionally exposes these regions as memory-mapped, letting a host on the FPGA's Zynq processing system swap in an entirely new model — architecture, weights, bias, and tanh tables — without resynthesizing at all, gated by a two-register handshake so no inference can start against a partially-written model mid-reload.
Correctness is checked with a dedicated equivalence testbench that drives the RTL with the same test vectors used to generate the exported reference values, and compares every intermediate pipeline stage (quantized input, pre-tanh accumulator, post-tanh activation, final integer and float output) bit-exactly against Brevitas's own Python simulation. For the representative 8-bit models, every one of the testbench's checked values — 573,440 quantized inputs, 819,200 accumulator and tanh values, 81,920 final outputs — matched bit-exactly against the hand-perfusion image, and the same held on the full 50,000-sample synthetic test set.
Results
On the held-out synthetic test set, relative RMS error falls from about 0.60 at 2-bit to 0.25–0.29 at 4-bit and 0.07–0.20 at 8-bit; 16-bit is within roughly 0.005–0.010 of the 8-bit figure at every noise level, meaning little further accuracy is available by going wider than 8 bits. Qualitatively, the 2-bit model's output is nearly flat regardless of input, the 4-bit model tracks the true value with a real but imprecise relationship, and 8-bit and 16-bit both track the diagonal closely.
Adding the 16-bit bias term helps most at low bit-widths — RelRMS drops from 0.60 to 0.39 at 2-bit and from 0.26 to 0.19 at 4-bit — and has little effect by 8 bits (0.094 → 0.082). At 8-bit, the same models reach a Pearson correlation of 0.988–0.991 against ground truth, within 0.002–0.005 of the 0.993 reported by prior work for a comparable full-precision floating-point network.
The same training workflow was used, unmodified, to train separate models on four synthetic noise levels (none, low, normal, high). All four generalize reasonably to every test condition (RelRMS within a 0.06–0.20 band), each doing best on test conditions closest to its own training noise level.
On a real hand-perfusion image, the RTL-simulated 8-bit model's whole-image correlation with the reference perfusion map is much lower (0.20–0.48) than on synthetic data — mostly because roughly half of the image is untrained-on background rather than hand. Restricted to the hand's own silhouette, correlation rises to 0.93–0.94, still 0.05–0.06 short of the synthetic-test figures, but confirming that the export/hardware pipeline itself reproduces the trained model's real accuracy rather than introducing its own error.
Conclusion
The thesis met its aim: a QAT flow built around Brevitas, and a portable model description exported directly to a bit-exact Verilog implementation, verified sample-by-sample against its own Python reference on both synthetic data and a real image. Eight bits is the practical answer to the accuracy/bit-width tradeoff — close enough to the 16-bit ceiling to leave little on the table, and the finest granularity actually carried through hardware verification. The training/export workflow is modality-agnostic, needing no per-modality code changes.
What the thesis does not yet establish is bedside-ready real-world accuracy: the gap between synthetic and real (on-subject) performance, and the model's undefined behavior on background pixels, are the main open problems, alongside estimating training variance across seeds, widening the accumulator to support the 16-bit models in hardware, and comparing against post-training quantization as a cheaper alternative to QAT.