Skip to content

darkyfoxy/AI_on_STM32

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Nov 30, 2021
e5c767a · Nov 30, 2021

History

19 Commits
Nov 23, 2021
Nov 13, 2021
Nov 30, 2021
Oct 22, 2021
Oct 22, 2021
Nov 23, 2021
Nov 23, 2021
Nov 9, 2021
Nov 22, 2021
Nov 8, 2021
Nov 13, 2021
Oct 26, 2021
Nov 22, 2021
Oct 22, 2021
Oct 22, 2021
Nov 23, 2021
Nov 22, 2021
Nov 22, 2021
Oct 22, 2021
Nov 30, 2021
Nov 13, 2021
Oct 22, 2021

Repository files navigation

AI on STM32

Neural network implementation on STM32 with Cube-AI.

Проект для демонстрации работы нейронных сетей на микроконтроллерах STM32.

Hardware

Для экспериментов был собран стенд:

board

Изображение снимается на камеру OV2640 (Разрешение матрицы датчика изображения: 1600x1200).

Камера через плату-коннектор подключена к отладочной плате от DevEBox c микроконтроллером STM32H743VIT6 (480 MHz, 2M Flash, 1M RAM).

Изображение и результат выводиться на TFT LCD дисплей 1.44 дюйма с контроллером ili9163 (Разрешение матрицы 128x128).

STMicroelectronics.X-CUBE-AI

Для всех экспериментов использовался пакет программного обеспечения 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.

Example with MNIST Dataset

Демонстрация работы нейронной сети для распознавания рукописных цифр на микроконтроллере STM32H743.

Код для обучения нейронной сети, нейронная сеть и выборки для валидации доступны в репозитории.

С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.

mnist

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-средних и применима только к полносвязным слоям.

Видео демонстрации работы youtube.

Example with CIFAR10 Dataset

Демонстрация работы нейронной сети для распознавания образов на микроконтроллере 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.

cifar10

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 оптимизации.

Видео демонстрации работы youtube.

Example with CIFAR100 SuperClasses Dataset

Демонстрация работы нейронной сети для распознавания образов на микроконтроллере 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.

Cifar100h

Нейронная сеть была обучена с помощью TensorFlow на датасете CIFAR100.

Optimization methods

TensorFlow Lite without optimization

Нейронная сеть сохранена в формате 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)

TensorFlow Lite with Cube-AI compression (x4/x8)

Нейронная сеть сохранена в формате TFLite без каких-либо оптимизаций. После этого в среде CubeMX применена компрессия весов посносвязных слоёв на основе метода k-средних.

Quantization

Квантованные модели - это модели, в которых мы используем параметры с более низкой точностью, такие как 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'

Parameters pruning

Метод отсечение веса постепенно обнуляет веса модели в процессе обучения для достижения разреженности модели. Разреженные модели легче сжимать, и мы можем пропустить нули во время выполнения для уменьшения задержки.

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)

Weight clustering

Кластеризация веса уменьшает количество уникальных значений веса в модели, что дает преимущества при сжатия модели.

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 methods comparison

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);

Example with MobileNet

Демонстрация работы нейронной сети для распознавания образов на микроконтроллере STM32H743. Классификация производиться на 1001 класс.

Нейронные сети доступны на сайте TensorFlow или на TensorFlow Hub или на GitHub.

С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.

mobilenet


Models comparison

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

Example with person detection

Демонстрация работы нейронной сети для распознавания образа человека на микроконтроллере STM32H743.

Нейронная сеть доступна на GitHub.

С помощью генератора кода STM32CubeMX и расширения X-CUBE-AI нейронная сеть была реализована на микроконтроллер STM32H743.

Model parameters:

Flash - 213.79 KB;

RAM - 46.35 KB;

Time on target - 33.737 ms.

Copyright

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.

About

Neural network implementation on STM32

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages