Vai al contenuto
Ingegneria per la prossima generazione di AI applicataScopri di più

La Capra Engineering

Eseguire una rete neurale su un microcontrollore

Un microcontrollore con 256 KB può eseguire un modello utile, ma non quello addestrato su una workstation. L’inferenza embedded è soprattutto un problema di budget della memoria travestito da ML.

Un microcontrollore con 256 KB di RAM può eseguire una rete neurale utile. Non può eseguire quella che hai addestrato su una workstation, e lo spazio fra queste due frasi è dove molti progetti di embedded ML muoiono in silenzio.

Il vincolo è la memoria, non l’aritmetica

Chi è nuovo all’inferenza embedded tende a preoccuparsi della frequenza di clock. Un Cortex-M4 a 80 MHz dispone di una MAC a ciclo singolo, quindi sostiene nell’ordine di decine di milioni di operazioni multiply-accumulate al secondo, e per keyword spotting o rilevamento di anomalie su un sensore di vibrazioni è sufficiente. Ciò che ti ferma è la memoria, e lo fa in due punti distinti.

La flash contiene i pesi. Di solito è il numero che viene citato, ed è quello facile: i pesi sono statici e la quantizzazione li riduce in modo prevedibile.

La RAM contiene le attivazioni: i tensori intermedi che esistono soltanto mentre viene calcolato uno strato. È questo il numero che uccide i progetti, perché non si vede nel riepilogo del modello e non cresce insieme al numero di parametri. Un modello con 40 KB di pesi può richiedere 180 KB di memoria di lavoro se uno strato iniziale produce una feature map molto larga.

FLASH 512Kpesi 120KfirmwareRAM 256Kstack + apptensor arena 180K — dimensionata dallo strato PIÙ LARGOimpronta delle attivazioni per stratoil picco fissa la dimensioneconv1 → conv2 → conv3 → pool → dense → out
I pesi vivono in flash e si riducono in modo prevedibile. La RAM della tensor arena è dimensionata dallo strato singolo più largo, non dal totale.

La conseguenza pratica è che dimensioni la tensor arena sul picco, quindi un solo strato iniziale molto largo tassa l’intero modello. Restringere quello strato spesso libera più margine che fare pruning di tutto ciò che viene dopo.

Quanto costa davvero la quantizzazione

Convertire i pesi float32 in int8 divide per quattro la memoria dei pesi e, su un core con estensioni DSP, rende l’aritmetica sensibilmente più veloce. Questa parte è ben pubblicizzata. Due aspetti lo sono molto meno.

Primo, la quantizzazione non è uniformemente distruttiva. Gli strati differiscono moltissimo nella tolleranza e la perdita di accuratezza tende a concentrarsi in un paio di strati sensibili anziché distribuirsi in modo uniforme. Quantizzazione per-channel e un dataset di calibrazione rappresentativo recuperano gran parte di ciò che una conversione ingenua perde.

Secondo, un modello “quantizzato” spesso non è interamente quantizzato. Se un operatore non ha un kernel integer, il runtime inserisce conversioni attorno a esso e paghi lo storage float, la conversione e la perdita di velocità. Guarda il report degli operatori invece della dimensione dichiarata: un modello int8 al 95% può comportarsi come un modello float a runtime.

L’elaborazione del segnale è gran parte del lavoro

In un progetto audio o di vibrazioni, la rete è spesso la parte più piccola della pipeline. Prima dell’inferenza applichi una finestra al segnale, esegui una FFT, lo trasformi su scala mel, prendi i logaritmi e impili i frame nella forma attesa dal modello. Quel front end gira per ogni singolo frame, nel percorso degli interrupt, indipendentemente dal fatto che il modello si attivi o meno.

Sbaglialo e nulla a valle può salvarti. Errori comuni: un window hop diverso da quello usato in training, una filterbank mel costruita con convenzioni diverse ai bordi oppure statistiche di normalizzazione calcolate sul training set e mai applicate sul dispositivo. Ognuno di questi casi produce un modello che ottiene risultati eccellenti offline e si comporta casualmente sul campo, perché riceve feature che non ha mai visto.

Lo strumento di debug più utile qui è noiosamente semplice: scarica via seriale i vettori di feature prodotti dal dispositivo e confrontali numericamente con la pipeline host usando lo stesso input. Nove volte su dieci la divergenza è visibile già nel primo frame.

Misura sul dispositivo

I benchmark sull’host mentono in entrambe le direzioni. Sottostimano le prestazioni ignorando le istruzioni DSP disponibili sul target e le sovrastimano enormemente ignorando comportamento della cache, wait state della flash e il fatto che l’inferenza compete con un campionatore pilotato da interrupt.

Strumenta l’hardware reale. Porta un GPIO alto all’inizio dell’inferenza e basso alla fine e osserva il segnale con un oscilloscopio: ottieni il vero tempo wall-clock praticamente senza overhead né bias di strumentazione. Traccia il worst case, non la media: su un dispositivo che non può permettersi di perdere un buffer audio, la coda della distribuzione è l’unico numero che conta.

Che cosa portarsi a casa

L’inferenza embedded è un esercizio di budgeting della memoria travestito da machine learning. Decidi prima la RAM della tensor arena, scegli un’architettura che ci stia, quantizza con veri dati di calibrazione e assicurati in modo assoluto che la pipeline delle feature sul dispositivo sia bit-per-bit quella contro cui hai addestrato il modello. La rete raramente è la parte difficile.

Porta la parte difficile. Costruiamo il prossimo passo.

Parliamone