Una pequeña placa de microcontrolador junto a una moneda como escala, con una pequeña red neuronal dibujada en luz sobre el chip
Contenido

Inferencia en un Cortex-M: qué limita la velocidad

Inferencia en un Cortex-M explicada: SIMD y Helium, memoria e im2col, kernels y runtimes, y cómo estimar si un modelo va a caber y a correr a tiempo en tu MCU.

Un modelo de detección de palabras clave corre en 20 ms en una tarjeta de desarrollo y en 400 ms en otra, y las dos dicen “Arm Cortex-M”. El modelo y el runtime son idénticos. La diferencia está en el conjunto de instrucciones del núcleo, en la memoria donde viven las activaciones y en si se enlazaron los kernels optimizados. Correr inferencia en un Cortex-M es una pregunta de hardware tanto como de modelo, y unos cuantos números (multiplicaciones-acumulaciones por inferencia, memoria pico de activaciones y el rendimiento de MAC del núcleo) predicen casi todo el resultado. Aquí te explico qué determina la velocidad y si el modelo cabe en un microcontrolador Cortex-M, con los resultados publicados detrás de los principales kernels y runtimes.

Puntos clave

  • Cuenta las multiplicaciones-acumulaciones (MAC) por inferencia y la memoria pico de activaciones antes que nada; predicen la latencia y si cabe.
  • Los Cortex-M son muy distintos entre sí. Los núcleos con extensiones DSP hacen dos MAC de 16 bits en una sola instrucción (SMLAD); los núcleos con Helium (del Cortex-M55 en adelante) tienen vectores de 128 bits con dieciséis carriles de 8 bits.
  • Los kernels optimizados importan: CMSIS-NN reportó 4.6× más rendimiento y 4.9× más eficiencia energética que una base de comparación en un Cortex-M7.
  • La RAM suele ser el primer límite. En el ejemplo CIFAR-10 de CMSIS-NN, el im2col parcial bajó la memoria pico de unos 332 KB a unos 133 KB.
  • Planear la memoria en toda la red puede bajarla más: TinyEngine, de MCUNet, reportó 4.8× menos uso de memoria que TF Lite Micro y CMSIS-NN.

El problema: “Cortex-M” es una familia, no una cifra de rendimiento

Cortex-M abarca núcleos con capacidades muy distintas. Algunos están diseñados para el área y el consumo mínimos, sin instrucciones DSP ni unidad de punto flotante. Otros agregan instrucciones SIMD de DSP y unidad de punto flotante. Los más nuevos agregan procesamiento vectorial. Encima del núcleo, cada microcontrolador pone su propio sistema de memoria: flash con estados de espera, SRAM dividida en bancos y a veces memoria fuertemente acoplada (TCM) o cachés.

Un ingeniero que elige una pieza para una función de IA casi siempre se pregunta dos cosas: ¿va a caber el modelo? y ¿va a correr a tiempo? Ni el nombre del núcleo ni su frecuencia de reloj responden por sí solos.

Qué determina la velocidad y si cabe

Las multiplicaciones-acumulaciones son la carga de trabajo

Casi todo el trabajo de una red convolucional o totalmente conectada son operaciones de multiplicación-acumulación. Cuéntalas por capa: para una convolución, alto de salida × ancho de salida × alto del kernel × ancho del kernel × canales de entrada × canales de salida.

Una capa ilustrativa: una convolución de 3 × 3 con 32 canales de entrada y 32 de salida sobre un mapa de 25 × 5 necesita 25 × 5 × 3 × 3 × 32 × 32 = 1,152,000 MAC. A un ideal de dos MAC por ciclo, son 576,000 ciclos, unos 7.2 ms a 80 MHz, sin contar la carga de datos, la recuantización de las salidas ni el control de los ciclos. Los kernels reales no llegan al ideal, pero el conteo te da el orden de magnitud y te dice qué capas dominan.

Las instrucciones SIMD y vectoriales fijan el ritmo de MAC

El artículo de CMSIS-NN de Lai, Suda y Chandra explica cómo los kernels optimizados de Arm aprovechan las instrucciones DSP de los Cortex-M con soporte SIMD. Las entradas int8 se expanden a 16 bits y se alimentan a SMLAD, que hace dos multiplicaciones-acumulaciones de 16 bits en una sola instrucción, con acumuladores de 32 bits. Sin esas instrucciones, cada MAC cuesta al menos una multiplicación y una suma.

La tecnología Helium de Arm (la M-Profile Vector Extension) va más lejos. La guía Introduction to Helium de Arm describe registros vectoriales de 128 bits que pueden guardar dieciséis elementos de 8 bits, y señala al Cortex-M55 como el primer procesador que la soporta. Las instrucciones vectoriales sobre datos int8 son lo que vuelve prácticos modelos más grandes en los Cortex-M recientes, y algunos microcontroladores acompañan al núcleo con una unidad de procesamiento neuronal dedicada para ganar todavía más.

Los kernels y los runtimes convierten instrucciones en rendimiento

Tener las instrucciones no basta; el software tiene que usarlas. El benchmark de CMSIS-NN corrió una red pequeña de CIFAR-10 (tres capas convolucionales y una totalmente conectada, cuantizadas a 8 bits) en un Cortex-M7 a 216 MHz. Tardó unos 99 ms por imagen, unas 10 imágenes por segundo, con 79.9 % de exactitud después de cuantizar contra 80.3 % antes. El artículo reporta que los kernels optimizados dieron 4.6× más rendimiento y 4.9× más eficiencia energética que una base construida con funciones DSP genéricas.

Los runtimes como TensorFlow Lite Micro están encima de los kernels. Sus autores describen un intérprete diseñado para sistemas sin asignación dinámica de memoria, con implementaciones de operadores que se pueden reemplazar por versiones optimizadas para cada núcleo. Si esas versiones optimizadas quedaron enlazadas en tu compilación es una de las razones más comunes por las que dos despliegues “idénticos” corren a velocidades muy distintas.

La memoria suele ser el primer límite

Los pesos viven en la flash; las activaciones viven en la RAM, y la RAM necesaria la fija el par más grande de tensores de entrada y salida que tienen que existir al mismo tiempo, más los buffers de trabajo. En el ejemplo CIFAR-10 de CMSIS-NN, los pesos ocupaban unos 87 KB y las activaciones unos 55 KB. Una transformación im2col completa, que reorganiza los parches de entrada en una matriz para multiplicar rápido, habría llevado la memoria pico a unos 332 KB, demasiado para la tarjeta. Expandir solo un par de columnas a la vez (im2col parcial) mantuvo la memoria pico en unos 133 KB y conservó casi toda la velocidad.

El trabajo MCUNet de Lin y sus colegas en NeurIPS 2020 atacó el mismo límite por los dos lados. Señalaron que la memoria de un microcontrolador es de dos a tres órdenes de magnitud menor que la de un teléfono, diseñaron arquitecturas de red que caben en presupuestos de memoria concretos (TinyNAS) y construyeron un motor de inferencia que planea la memoria en toda la red y no capa por capa (TinyEngine). Reportaron 4.8× menos uso de memoria y una inferencia de 1.7 a 3.3× más rápida que TF Lite Micro y CMSIS-NN, y la primera exactitud top-1 en ImageNet arriba de 70 % en un microcontrolador comercial.

La cuantización lo hace posible

Todo esto supone aritmética entera. El esquema de inferencia solo con enteros de Jacob y sus colegas, que guarda pesos y activaciones como enteros de 8 bits con escalas y puntos cero, es el que usan casi todos los runtimes actuales. El artículo original de CMSIS-NN usó un escalamiento más sencillo, por potencias de dos, para no necesitar decuantización en punto flotante entre capas, lo que importa en núcleos sin unidad de punto flotante. De cualquier forma, los pesos int8 ocupan la cuarta parte de la flash que en float32 y corren con instrucciones SIMD enteras.

Dónde viven los datos cambia la velocidad

El código y los pesos que se ejecutan desde la flash pueden detenerse por estados de espera a frecuencias altas, según el acelerador de flash o la caché de la pieza. Las activaciones en SRAM fuertemente acoplada o sin estados de espera corren más rápido que en bancos más lentos. Estos efectos son propios de cada microcontrolador, así que lee el manual de referencia de la pieza y mide; el mismo núcleo al mismo reloj puede correr un modelo a velocidades bastante distintas en dos piezas diferentes.

Factores de la inferencia en Cortex-M, comparados

Factor Qué afecta Impacto típico Cómo revisarlo
MAC por inferencia Latencia, energía Fija la carga; las capas dominantes saltan a la vista Conteo por capa a partir del modelo
Conjunto de instrucciones (sin SIMD / SIMD DSP / Helium) MAC por ciclo Grande; SIMD da 2 MAC de 16 bits por SMLAD, Helium tiene 16 carriles int8 por vector Manual técnico de referencia del núcleo
Kernels optimizados enlazados Rendimiento, energía CMSIS-NN reportó 4.6× de rendimiento y 4.9× de eficiencia energética Configuración de la compilación, perfilado
Memoria pico de activaciones Si cabe o no Muchas veces la restricción que manda; im2col parcial 332 → 133 KB en un ejemplo Reporte de la arena del runtime, tamaños de tensores por capa
Planeación de memoria en toda la red RAM pico TinyEngine reportó 4.8× menos uso de memoria Documentación del runtime, medición
Estados de espera de flash, cachés, ubicación en TCM Velocidad real a un reloj dado Depende de la pieza Manual de referencia; mide al reloj de producción
Esquema de cuantización Exactitud, necesidad de FPU Int8 es 4× más pequeño que float32 Exactitud del modelo cuantizado en el conjunto de prueba

Qué hacer

Cuenta primero las MAC y la memoria pico

Antes de elegir pieza, calcula las MAC por capa y la memoria pico de activaciones del modelo que piensas correr. Suma a la RAM los buffers de muestras y de features.

Empata el núcleo con la carga

Divide el número de MAC entre un ritmo de MAC realista para la clase de núcleo y el reloj, y compáralo con tu fecha límite. Si no cabe con margen, cambia el modelo, la pieza o la fecha límite, en ese orden de costo.

Asegúrate de que se usen los kernels optimizados

Revisa la compilación: las implementaciones optimizadas de los operadores del runtime para tu núcleo deben estar habilitadas y enlazadas. Perfila por capa; una capa que corre mucho más lento de lo que sugiere su conteo de MAC muchas veces está usando un kernel de referencia.

Ubica la memoria a propósito

Pon la arena de tensores en la RAM más rápida disponible y revisa los estados de espera de la flash a tu reloj. Mide antes y después.

Qué no hacer

No compares piezas Cortex-M solo por frecuencia de reloj. Y no supongas que un modelo que cabe en la flash va a correr; la RAM de las activaciones suele ser la primera pared.

Cómo ponerlo en práctica

Si tienes una hora

Toma tu modelo, enlista las MAC y el tamaño del tensor de salida de cada capa, y encuentra la capa con más MAC y el punto de memoria pico. Esas dos capas son donde optimizar paga.

Si tienes un sprint

Perfila el modelo capa por capa en la tarjeta de destino, con y sin los kernels optimizados, y registra la latencia de cada capa. Compárala con los conteos de MAC para encontrar las capas que rinden menos de lo esperado.

Si quieres que dure toda la vida del producto

Guarda un perfil por capa como parte de cada versión del modelo, con el reloj y la configuración de memoria de producción, para que una actualización del modelo o un cambio de pieza muestre su costo antes de llegar a campo.

La pregunta que debes hacerte: “¿Sé cuántas MAC y cuánta RAM necesita mi modelo, y cuánto puede entregar de verdad el núcleo que elegí al reloj con el que voy a producir?”

Preguntas frecuentes

¿Un Cortex-M puede correr una red neuronal?

Sí. Redes cuantizadas pequeñas para tareas como detección de palabras clave, reconocimiento de gestos y detección de anomalías corren en muchos microcontroladores Cortex-M con runtimes como TensorFlow Lite Micro y kernels como CMSIS-NN. Los límites son la RAM para las activaciones y el rendimiento de multiplicación-acumulación del núcleo.

¿Qué Cortex-M es mejor para aprendizaje automático?

Los núcleos con instrucciones SIMD de DSP corren la inferencia int8 mucho más rápido que los que no las tienen, y los núcleos con instrucciones vectoriales Helium, empezando por el Cortex-M55, son todavía más rápidos. La mejor opción depende del número de MAC del modelo, de su memoria y del presupuesto de energía.

¿Qué es CMSIS-NN?

CMSIS-NN es la biblioteca de Arm de kernels optimizados de redes neuronales para procesadores Cortex-M. Su artículo original reportó 4.6× más rendimiento y 4.9× más eficiencia energética que una implementación base en un Cortex-M7, usando instrucciones SIMD de multiplicación-acumulación y técnicas para ahorrar memoria como el im2col parcial.

¿Cómo sé si mi modelo va a caber en un microcontrolador?

Revisa la flash para el código, el runtime y los pesos int8, y la RAM para la memoria pico de activaciones más los buffers de muestras y de features. El requisito de la arena de tensores del runtime, medido en el dispositivo, es la cifra confiable para las activaciones.

¿Por qué mi modelo es más lento en una tarjeta Cortex-M que en otra?

Las razones comunes son un núcleo distinto (con o sin SIMD o Helium), kernels optimizados que no se enlazaron en una de las compilaciones, otro reloj u otros estados de espera de la flash, y la arena de tensores puesta en una memoria más lenta.

Lecturas relacionadas: del sensor a la inferencia embebida, cuantización int8 para microcontroladores y cómo medir un demo de IA embebida.

Fuentes

  1. Lai, L., Suda, N., y Chandra, V. (2018). “CMSIS-NN: Efficient Neural Network Kernels for Arm Cortex-M CPUs.” arXiv:1801.06601. https://arxiv.org/abs/1801.06601
  2. Lin, J., Chen, W.-M., Lin, Y., Cohn, J., Gan, C., y Han, S. (2020). “MCUNet: Tiny Deep Learning on IoT Devices.” Advances in Neural Information Processing Systems 33 (NeurIPS 2020). arXiv:2007.10319. https://arxiv.org/abs/2007.10319
  3. David, R., Duke, J., Jain, A., Reddi, V. J., et al. (2021). “TensorFlow Lite Micro: Embedded Machine Learning on TinyML Systems.” Proceedings of Machine Learning and Systems (MLSys). arXiv:2010.08678. https://arxiv.org/abs/2010.08678
  4. Jacob, B., Kligys, S., Chen, B., et al. (2018). “Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference.” 2018 IEEE/CVF Conference on Computer Vision and Pattern Recognition, 2704–2713. https://doi.org/10.1109/CVPR.2018.00286
  5. Arm Ltd. (2024). Introduction to Helium, versión 1.3 (documento 102102). https://documentation-service.arm.com/static/65969ed3159ca73387227541
Escrito porAlphaCrew Studio

Creamos contenido técnico y animación, desarrollamos apps y plataformas a la medida y configuramos flujos de trabajo con IA para equipos de ingeniería y tecnología.