Je reprends ici une sorte de ‘hello world’ du deep learning, les explications détaillées ( de la mise en oeuvre uniquement ) se trouvent dans les articles suivants :
Application
L’objectif est de concevoir un modèle s’exécutant sur un microcontrôleur STM32, permettant de prédire une sortie y de type y=sin(x), x étant l’entrée du modèle.
# REFERENCE :
# https://colab.research.google.com/drive/1OCEPVSfMK9Jk2JbdNPpcYP2_-9tOvdQU?usp=sharing#scrollTo=McqBIP4KpuH6
import numpy as np
import tensorflow as tf
from tensorflow import keras # Keras is TensorFlow's high-level API for deep learning
import matplotlib.pyplot as plt
import math
# Number of sample datapoints
SAMPLES = 1500
# Set a "seed" value, so we get the same random numbers each time we run this
# notebook for reproducible results. Any number can be used here.
np.random.seed(786)
tf.random.set_seed(786)
# Generate a uniformly distributed set of random numbers in the range from
# 0 to 2π, which covers a complete sine wave oscillation
x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES)
# Let's visualize x_values to see if the x_values are random or
# concentrated in one area
plt.plot(x_values)
plt.show()
# You can also shuffle the values to guarantee they're not in order
np.random.shuffle(x_values)
# Calculate the corresponding sine values
y_values = np.sin(x_values)
# Plot our data
plt.plot(x_values, y_values, 'b.')
plt.show()
# Add a small random number to each y value
y_values += 0.1 * np.random.randn(*y_values.shape)
# Plot our data
plt.plot(x_values, y_values, 'b.')
plt.show()
''' Training: 60%
Validation: 20%
Testing: 20%
'''
# We'll use 60% of our data for training and 20% for validation. The remaining 20%
# will be used for testing. Calculate the indices of each section.
TRAIN_SPLIT = int(0.6 * SAMPLES)
TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
# Use np.split to chop our data into three parts.
# The second argument to np.split is an array of indices where the data will be
# split. We provide two indices, so the data will be divided into three chunks.
x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])
# Double check that our splits add up correctly
assert (x_train.size + x_validate.size + x_test.size) == SAMPLES
# Plot the data in each partition.
# We have to make sure that each set, train, validation and test, has the full
# range of x values, 0 to 2pi
plt.plot(x_train, y_train, 'b.', label="Train")
plt.legend()
plt.show()
plt.plot(x_validate, y_validate, 'y.', label="Validate")
plt.legend()
plt.show()
plt.plot(x_test, y_test, 'r.', label="Test")
plt.legend()
<matplotlib.legend.Legend at 0x7d1cb2de4920>
# We'll use Keras to create a simple model architecture
model = tf.keras.Sequential()
# First layer takes a scalar input and feeds it through 16 "neurons". The
# neurons decide whether to activate based on the 'relu' activation function.
model.add(keras.layers.Dense(16, activation='relu', input_shape=(1,)))
# The second layer of 16 neurons. Note input connected to the first layer.
# As this is a sequential model, all the first layer neuron will be connected
# to this second layer.
model.add(keras.layers.Dense(16, activation='relu'))
# Final layer is a single neuron, since we want to output a single value
model.add(keras.layers.Dense(1))
# Compile the model using a standard optimizer and loss function for regression
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
training_info = model.fit(x_train, y_train, epochs=350, batch_size=64, validation_data=(x_validate, y_validate))
Epoch 1/350
/home/kerhoas/anaconda3/lib/python3.12/site-packages/keras/src/layers/core/dense.py:87: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
super().__init__(activity_regularizer=activity_regularizer, kwargs)
[1m15/15[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - loss: 0.3873 - mae: 0.5188 - val_loss: 0.4357 - val_mae: 0.5751
Epoch 2/350
[1m15/15[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 3ms/step - loss: 0.3517 - mae: 0.5189 - val_loss: 0.3981 - val_mae: 0.5468
Epoch 3/350
...
# Draw a graph of the loss, which is the distance between
# the predicted and actual values during training and validation.
loss = training_info.history['loss']
validation_loss = training_info.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'g.', label='Training loss')
plt.plot(epochs, validation_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
# Exclude the first few epochs so the graph is easier to read
SKIP = 50
plt.plot(epochs[SKIP:], loss[SKIP:], 'g.', label='Training loss')
plt.plot(epochs[SKIP:], validation_loss[SKIP:], 'b.', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
# Calculate and print the loss on our test dataset
loss = model.evaluate(x_test, y_test)
# Make predictions based on our test dataset
predictions = model.predict(x_test)
# Graph the predictions against the actual values
plt.clf()
plt.title('Comparison of predictions and actual values')
plt.plot(x_test, y_test, 'b.', label='Actual')
plt.plot(x_test, predictions, 'r.', label='Predicted')
plt.legend()
plt.show()
[1m10/10[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1ms/step - loss: 0.0114 - mae: 0.0843
[1m10/10[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 3ms/step
# Convert the model to the TensorFlow Lite format with quantization
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# Set the optimization flag.
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
# Save the model to disk
open("sinewave_model.tflite", "wb").write(tflite_model)
from tensorflow.lite.python.util import convert_bytes_to_c_source
source_text, header_text = convert_bytes_to_c_source(tflite_model,
"sine_model",
include_path="sine_model.h")
with open('sine_model.h', 'w') as file:
file.write(header_text)
with open('sine_model.cpp', 'w') as file:
file.write(source_text)
Je rappelle à nouveau qu’il s’agit d’un test de mise en oeuvre d’un modèle tensorflow lite.
L’objectif est de passer en revue les différentes étapes pour générer puis impléter un modèle dans un microcontrôleur.
Bien évidemment pour cet exemple nous aurions pu fournir directement un tableau contenant les points d’une période d’une
sinusoide, et nous aurions eu le même résultat.
#include "main.h"
#include "sine_model.h"
#include "tensorflow/lite/micro/kernels/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/version.h"
#include <iostream>
#include <iomanip>
using namespace std;
namespace
{
tflite::ErrorReporter* error_reporter = nullptr;
const tflite::Model* model = nullptr;
tflite::MicroInterpreter* interpreter = nullptr;
TfLiteTensor* model_input = nullptr;
TfLiteTensor* model_output = nullptr;
// Create an area of memory to use for input, output, and intermediate arrays.
// Finding the minimum value for your model may require some trial and error.
constexpr uint32_t kTensorArenaSize = 2 * 1024;
uint8_t tensor_arena[kTensorArenaSize];
}
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim5;
UART_HandleTypeDef huart2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_I2C1_Init(void);
static void MX_TIM5_Init(void);
//================================================================================
// redirection du fprintf vers uart2
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
//================================================================================
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_I2C1_Init();
MX_TIM5_Init();
//-------------------------------------------------------------------------------
static tflite::MicroErrorReporter micro_error_reporter;
error_reporter = µ_error_reporter;
TF_LITE_REPORT_ERROR(error_reporter,"test uart \n\r");
model = tflite::GetModel(sine_model);
static tflite::ops::micro::AllOpsResolver resolver;
static tflite::MicroInterpreter static_interpreter(model, resolver, tensor_arena, kTensorArenaSize, error_reporter);
interpreter = &static_interpreter;
TfLiteStatus allocate_status = interpreter->AllocateTensors();
if (allocate_status != kTfLiteOk)
{
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
return 0;
}
model_input = interpreter->input(0);
model_output = interpreter->output(0);
//-------------------------------------------------------------------------------
static float x_val = 0;
std::cout << std::setprecision(3) << std::fixed;
while (1)
{
// Calculate an x value to feed into the model
x_val = x_val + (2*M_PI)/20;
if ( x_val > 2*M_PI ) { x_val = x_val - 2*M_PI; }
model_input->data.f[0] = x_val;
//-------------------------------------------------------------------------------
TfLiteStatus invoke_status = interpreter->Invoke();
if (invoke_status != kTfLiteOk)
{
TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed on x_val: %f\n", static_cast<float>(x_val));
return 0;
}
//-------------------------------------------------------------------------------
float y_val = model_output->data.f[0];
cout << "x_value : "<< x_val << " | y_value : "<< y_val << endl << '\r';
HAL_Delay(100);
}
}
Terminal Série
x_value : 1.571 | y_value : 0.995
x_value : 1.885 | y_value : 0.940
x_value : 2.199 | y_value : 0.821
x_value : 2.513 | y_value : 0.586
x_value : 2.827 | y_value : 0.300
x_value : 3.142 | y_value : 0.015
x_value : 3.456 | y_value : -0.271
x_value : 3.770 | y_value : -0.557
x_value : 4.084 | y_value : -0.820
x_value : 4.398 | y_value : -0.933
x_value : 4.712 | y_value : -0.984
x_value : 5.027 | y_value : -0.928
x_value : 5.341 | y_value : -0.782
x_value : 5.655 | y_value : -0.556
x_value : 5.969 | y_value : -0.280
x_value : 0.000 | y_value : 0.069
x_value : 0.314 | y_value : 0.318
x_value : 0.628 | y_value : 0.606
x_value : 0.942 | y_value : 0.799
x_value : 1.257 | y_value : 0.993