Neural network implementation on STM32 with Cube-AI.
Проект для демонстрации работы нейронных сетей на микроконтроллерах STM32.
- Hardware
- STMicroelectronics.X-CUBE-AI
- Example with MNIST Dataset
- Example with CIFAR10 Dataset
- Example with CIFAR100 SuperClasses Dataset
- Example with MobileNet
- Example with person detection
- Copyright
Для экспериментов был собран стенд:
Изображение снимается на камеру OV2640 (Разрешение матрицы датчика изображения: 1600x1200).
Камера через плату-коннектор подключена к отладочной плате от DevEBox c микроконтроллером STM32H743VIT6 (480 MHz, 2M Flash, 1M RAM).
Изображение и результат выводиться на TFT LCD дисплей 1.44 дюйма с контроллером ili9163 (Разрешение матрицы 128x128).
Для всех экспериментов использовался пакет программного обеспечения STMicroelectronics.X-CUBE-AI.6.0.0
Для использования примеров нужно выбрать нужную функцию в файле main.h
:
//MNIST_AI_block((uint16_t *)ai_buff_pointer);
//CIFAR_AI_block((uint16_t *)ai_buff_pointer);
//CIFAR100_AI_block((uint16_t *)ai_buff_pointer); //work with #define QUANT in computer_vision.h only
//MobileNet_AI_block((uint16_t *)ai_buff_pointer);
Person_detect_AI_block((uint16_t *)ai_buff_pointer);
Функции определены в файле computer_vision.c
. В файле computer_vision.c
определены GUI и QUANT.
Не забудьте указать нужную нейронную сеть в STM32CubeMX.
Демонстрация работы нейронной сети для распознавания рукописных цифр на микроконтроллере STM32H743.
Код для обучения нейронной сети, нейронная сеть и выборки для валидации доступны в репозитории.
С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.
PyTorch and ONNX
Нейронная сеть была обучена с помощью PyTorch на датасете MNIST. После этого экспортирована в граф вычислений в формате ONNX.
Keras
Нейронная сеть была обучена с помощью TensorFlow на датасете MNIST. После этого сохранена в формате Keras H5.
TensorFlow Lite
Нейронная сеть была обучена с помощью TensorFlow на датасете MNIST.
После этого сохранена в формате TF Lite без квантования и с квантованием весов до int8 (TF Lite (Quant)).
Конфигурация квантования:
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
converter.representative_dataset = representative_dataset
Flash comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 778.16 KB | 778.16 KB | 778.16 KB | 195.74 KB |
x4 | 203.60 KB | 203.60 KB | 203.60 KB | Not supported |
x8 | 105.63 KB | 105.63 KB | 105.63 KB | Not supported |
RAM comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 4.66 KB | 4.66 KB | 4.66 KB | 1.17 KB |
x4 | 4.66 KB | 4.66 KB | 4.66 KB | Not supported |
x8 | 4.66 KB | 4.66 KB | 4.66 KB | Not supported |
Time on target comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 2.702 ms | 2.702 ms | 2.702 ms | 0.873 ms |
x4 | 2.657 ms | 2.653 ms | 2.654 ms | Not supported |
x8 | 2.513 ms | 2.513 ms | 2.513 ms | Not supported |
Accuracy comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 91.6% (91.6%)** | 93.8% (93.8%)** | 93.8% (93.8%)** | 95.4% (95.4%)** |
x4 | 91.5% (91.5%)** | 93.9% (93.9%)** | 93.9% (93.9%)** | Not supported |
x8 | 91.7% (91.7%)** | 93.8% (93.8%)** | 93.8% (93.8%)** | Not supported |
*Во всех вариантах использовались одинаковые граф вычислений и дадасет для валидации. Веса моделей различны;
**Validation on target (Validation on desktop).
Из документации от STMicroelectronics (п. 6.1) компрессия размера весов и смещений основана на методе k-средних и применима только к полносвязным слоям.
Демонстрация работы нейронной сети для распознавания образов на микроконтроллере STM32H743. Классификация производиться на 10 классов: 0 - airplane; 1 - automobile; 2 - bird; 3 - cat; 4 - deer; 5 - dog; 6 - frog; 7 - horse; 8 - ship; 9 - truck.
Код для обучения нейронной сети, нейронная сеть и выборки для валидации доступны в репозитории.
С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.
PyTorch and ONNX
Нейронная сеть была обучена с помощью PyTorch на датасете CIFAR10. После этого экспортирована в граф вычислений в формате ONNX.
PyTorch экспортирует модель в ONNX таким образом, что идет оптимизация полностязного слоя в матричное умножение (GeMM). После этого X-CUBE-AI не может произвести компрессию размера весов и смещений. Как это исправить я не знаю. Если кто знает, сообщите мне)
Keras
Нейронная сеть была обучена с помощью TensorFlow на датасете CIFAR10. После этого сохранена в формате Keras H5.
TensorFlow Lite
Нейронная сеть была обучена с помощью TensorFlow на датасете CIFAR10. После этого сохранена в формате TF Lite без квантования и с квантованием весов до int8 (TF Lite (Quant)).
Конфигурация квантования:
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
converter.representative_dataset = representative_dataset
Flash comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 1.04 MB | 1.04 MB | 1.04 MB | 267.76 KB |
x4 | WARNING*** | 682.66 KB | 682.66 KB | Not supported |
x8 | WARNING*** | 617.73 KB | 617.73 KB | Not supported |
RAM comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 148.41 KB | 148.41 KB | 148.41 KB | 43.63 KB |
x4 | WARNING*** | 148.41 KB | 148.41 KB | Not supported |
x8 | WARNING*** | 148.41 KB | 148.41 KB | Not supported |
Time on target comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 285.562 ms | 285.169 ms | 285.171 ms | 106.633 ms |
x4 | WARNING*** | 285.120 ms | 285.133 ms | Not supported |
x8 | WARNING*** | 285.040 ms | 285.048 ms | Not supported |
Accuracy comparison*
Сompression | ONNX | Keras | TF Lite | TF Lite (Quant) |
---|---|---|---|---|
None | 83.0% (83.0%)** | 82.5% (82.5%)** | 82.5% (82.5%)** | 81.5% (81.5%)** |
x4 | WARNING*** | 82.5% (82.5%)** | 82.5% (82.5%)** | Not supported |
x8 | WARNING*** | 82.5% (82.5%)** | 82.5% (82.5%)** | Not supported |
*Во всех вариантах использовались одинаковые граф вычислений и дадасет для валидации. Веса моделей различны;
**Validation on target (Validation on desktop);
***X-CUBE-AI выдает предупреждение (WARNING: no weight was compressed) из-за GeMM оптимизации.
Демонстрация работы нейронной сети для распознавания образов на микроконтроллере STM32H743. Классификация производиться на 20 супер-классов: 0 - aquatic mammals; 1 - fish; 2 - flowers; 3 - food containers; 4 - fruit and vegetables; 5 - household electrical devices; 6 - household furniture; 7 - insects; 8 - large carnivores; 9 - large man-made outdoor things; 10 - large natural outdoor scenes; 11 - large omnivores and herbivores; 12 - medium-sized mammals; 13 - non-insect invertebrates; 14 - people; 15 - reptiles; 16 - small mammals; 17 - trees; 18 - vehicles 1; 19 - vehicles 2.
Код для обучения нейронной сети, нейронная сеть и выборки для валидации доступны в репозитории.
С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.
Нейронная сеть была обучена с помощью TensorFlow на датасете CIFAR100.
Нейронная сеть сохранена в формате TFLite без каких-либо оптимизаций.
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
model_no_quant_tflite = converter.convert()
open("network_without_optim.tflite", "wb").write(model_no_quant_tflite)
Нейронная сеть сохранена в формате TFLite без каких-либо оптимизаций. После этого в среде CubeMX применена компрессия весов посносвязных слоёв на основе метода k-средних.
Квантованные модели - это модели, в которых мы используем параметры с более низкой точностью, такие как 8-битные целые числа вместо от 32-битных чисел с плавающей точкой.
*Изображение переведено с сайта TensorFlow.
TensorFlow Lite with рost-training quantization
Квантование после обучения - это метод преобразования, который может уменьшить размер модели, а также уменьшить время выполнения. При этом точности модели незначительно уменьшается.
*Изображение переведено с сайта TensorFlow.
Dynamic range quantization (DRQ)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
model_tflite = converter.convert()
open("network_DRQ.tflite", "wb").write(model_tflite)
Cube-AI:
WARNING: Hybrid layer dense_55 not supported. - Mapping on float version
...
TOOL ERROR: Didn't find op for builtin opcode 'CONV_2D' version '5'
Full integer quantization (FIQ)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
model_tflite = converter.convert()
open("network_FIQ.tflite", "wb").write(model_tflite)
Integer only: 16-bit activations with 8-bit weights (INT16_INT8)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
model_tflite = converter.convert()
open("network_INT16_INT8.tflite", "wb").write(model_tflite)
Cube-AI:
NOT IMPLEMENTED: Unexpected numpy.dtype: int64 (dict_keys(['uint8', 'int8', 'uint16', 'int16', 'uint32', 'int32', 'float32', 'bool']))
Integer only: 16-bit activations with 8-bit weights with BUILTINS (INT16_INT8_BUILTINS)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
model_tflite = converter.convert()
open("network_INT16_INT8_BUILTINS.tflite", "wb").write(model_tflite)
Cube-AI:
NOT IMPLEMENTED: Unexpected numpy.dtype: int64 (dict_keys(['uint8', 'int8', 'uint16', 'int16', 'uint32', 'int32', 'float32', 'bool']))
Integer only: BUILTINS (BUILTINS)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
model_tflite = converter.convert()
open("network_BUILTINS.tflite", "wb").write(model_tflite)
Full integer quantization Integer only (FIQ_int_only)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
model_tflite = converter.convert()
open("network_FIQ_int_only.tflite", "wb").write(model_tflite)
Full integer quantization Integer only with inference input/output type (FIQ_int_only_IIOT)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
model_tflite = converter.convert()
open("network_FIQ_int_only_IIOT.tflite", "wb").write(model_tflite)
Float16 quantization (float16)
converter = tf.lite.TFLiteConverter.from_saved_model(network_dir_path)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
model_tflite = converter.convert()
open("network_float16.tflite", "wb").write(model_tflite)
Cube-AI:
INTERNAL ERROR: 'FLOAT16'
Метод отсечение веса постепенно обнуляет веса модели в процессе обучения для достижения разреженности модели. Разреженные модели легче сжимать, и мы можем пропустить нули во время выполнения для уменьшения задержки.
Training with pruning:
prune_low_magnitude = tfmot.sparsity.keras.prune_low_magnitude
batch_size = 64
epochs = 2
validation_split = 0.1
num_images = train_images.shape[0] * (1 - validation_split)
end_step = np.ceil(num_images / batch_size).astype(np.int32) * epochs
pruning_params = {
'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay(initial_sparsity=0.30,
final_sparsity=0.70,
begin_step=0,
end_step=end_step)}
model_for_pruning = prune_low_magnitude(model, **pruning_params)
opt = tf.optimizers.SGD(learning_rate=0.001, momentum=0.9)
model_for_pruning.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
callbacks = [
tfmot.sparsity.keras.UpdatePruningStep(),
]
model_for_pruning.fit(train_images, train_labels,
batch_size=batch_size, epochs=epochs, validation_split=validation_split,
callbacks=callbacks)
Pruning without optimization
model_for_export = tfmot.sparsity.keras.strip_pruning(model_for_pruning)
converter = tf.lite.TFLiteConverter.from_keras_model(model_for_export)
model_no_quant_tflite = converter.convert()
open("pruning_network_without_optim.tflite", "wb").write(model_no_quant_tflite)
Pruning without full integer quantization Integer only with inference input/output type (Pruning_FIQ_int_only_IIOT)
model_for_export = tfmot.sparsity.keras.strip_pruning(model_for_pruning)
converter = tf.lite.TFLiteConverter.from_keras_model(model_for_export)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
model_tflite = converter.convert()
open("pruning_network_FIQ_int_only_IIOT.tflite", "wb").write(model_tflite)
Кластеризация веса уменьшает количество уникальных значений веса в модели, что дает преимущества при сжатия модели.
Training with clustering:
cluster_weights = tfmot.clustering.keras.cluster_weights
CentroidInitialization = tfmot.clustering.keras.CentroidInitialization
clustering_params = {
'number_of_clusters': 16,
'cluster_centroids_init': CentroidInitialization.KMEANS_PLUS_PLUS
}
clustered_model = cluster_weights(model, **clustering_params)
opt = tf.optimizers.SGD(learning_rate=0.001, momentum=0.9)
clustered_model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
clustered_model.fit(train_images, train_labels, batch_size=500, epochs=3, validation_data=(test_images, test_labels))
Clustering without optimization
final_model = tfmot.clustering.keras.strip_clustering(clustered_model)
converter = tf.lite.TFLiteConverter.from_keras_model(final_model)
model_no_quant_tflite = converter.convert()
open("clustered_network_without_optim.tflite", "wb").write(model_no_quant_tflite)
Clustering without full integer quantization Integer only with inference input/output type (Clustering_FIQ_int_only_IIOT)
final_model = tfmot.clustering.keras.strip_clustering(clustered_model)
converter = tf.lite.TFLiteConverter.from_keras_model(final_model)
def representative_dataset():
for i in range(500):
yield([test_images[i].reshape(1, 32, 32, 3).astype(np.float32)])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
model_tflite = converter.convert()
open("clustered_network_FIQ_int_only_IIOT.tflite", "wb").write(model_tflite)
Optimization | Flash | RAM | Time on target | Accuracy |
---|---|---|---|---|
without optimization | 1.78 MB | 196.08 KB | 341.742 ms | 71.67% (71.67%)** |
Cube-AI x4 | 1.75 MB | 196.08 KB | 343.088 ms | 71.67% (71.67%)** |
Cube-AI x8 | 1.74 MB | 196.08 KB | 343.069 ms | 72.67% (72.67%)** |
DRQ* | - | - | - | - |
FIQ | 458.52 KB | 59.25 KB | 134.764 ms | 64.00% (64.00%)** |
INT16_INT8* | - | - | - | - |
INT16_INT8_BUILTINS* | - | - | - | - |
BUILTINS | 458.52 KB | 59.25 KB | 134.767 ms | 64.00% (64.00%)** |
FIQ_int_only | 458.52 KB | 59.25 KB | 134.775 ms | 64.00% (64.00%)** |
FIQ_int_only_IIOT | 458.52 KB | 49.92 KB | 134.938 ms | 64.00% (64.00%)** |
float16* | - | - | - | - |
Pruning without optimization | 1.78 MB | 196.08 KB | 341.716 ms | 65.33% (65.33%)** |
Pruning_FIQ_int_only_IIOT | 458.52 KB | 49.92 KB | 134.956 ms | 47.33% (47.33%)** |
Clustering without optimization | 1.78 MB | 196.08 KB | 341.727 ms | 68.00% (68.00%)** |
Clustering_FIQ_int_only_IIOT | 458.52 KB | 49.92 KB | 134.942 ms | 65.33% (65.33%)** |
*Cube-AI ERROR;
**Validation on target (Validation on desktop);
Демонстрация работы нейронной сети для распознавания образов на микроконтроллере STM32H743. Классификация производиться на 1001 класс.
Нейронные сети доступны на сайте TensorFlow или на TensorFlow Hub или на GitHub.
С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.
Name | Flash | RAM | Time on target | Top-1* | Top-5* |
---|---|---|---|---|---|
mobilenet_v1_0.25_128_quant | 467.58 KB | 114.54 KB | 57.410 ms | 39.5% | 64.4% |
mobilenet_v1_0.25_160_quant | 467.58 KB | 177.92 KB | 89.128 ms | 42.8% | 68.1% |
mobilenet_v1_0.25_192_quant | 467.58 KB | 255.29 KB | 127.995 ms | 45.7% | 70.8% |
mobilenet_v1_0.25_224_quant | 467.58 KB | 346.67 KB | 175.739 ms | 48.2% | 72.8% |
mobilenet_v1_0.50_128_quant | 1.28 MB | 180.10 KB | 153.405 ms | 54.9% | 78.1% |
mobilenet_v1_0.50_160_quant | 1.28 MB | 279.85 KB | 242.991 ms | 57.2% | 80.5% |
mobilenet_v1_0.50_192_quant | 1.28 MB | 401.60 KB | 334.688 ms | 59.9% | 82.1% |
mobilenet_v1_0.50_224_quant | 1.28 MB | 545.35 KB** | -- | 61.2% | 83.2% |
*From TensorFlow site.
**region RAM_D1 overflowed
Демонстрация работы нейронной сети для распознавания образа человека на микроконтроллере STM32H743.
Нейронная сеть доступна на GitHub.
С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.
Model parameters:
Flash - 213.79 KB;
RAM - 46.35 KB;
Time on target - 33.737 ms.
Some parts of this code were taken from other repositories.
For OV2640 driver from OpenMV repository.
For TFT ili9163 display driver from Spirit532 repository.
Other drivers without special instructions from STMicroelectronics.