Application
L’idée est de définir un modèle sur le PC, puis de faire tourner ce modèle sur un microcontrôleur, afin de faire correspondre une caractéristique à une mesure d’un capteur.
Dans notre application, on fera correspondre une étiquette (label) ‘jour’/’nuit’/‘intermédiaire’ aux mesures d’un capteur de luminosité.
Là encore on écrase une mouche avec un marteau ( le problème aurait été résolu avec un if then else ), le but étant d’étudier les étapes de mise en oeuvre de tensorflow lite dans un cas simple.
Dataset :
import pandas as pd
import numpy as np
import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler, LabelEncoder
# Charger le dataset
df = pd.read_csv("light_dataset.csv")
# Normalisation des valeurs de lux
scaler = MinMaxScaler()
df["Lux"] = scaler.fit_transform(df[["Lux"]])
# Conversion des labels en nombres
label_encoder = LabelEncoder()
df["Category"] = label_encoder.fit_transform(df["Category"])
df
df.plot()
# Séparer les données en train/test
X = df[["Lux"]].values
y = df["Category"].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Définir le modèle (MLP simple)
model = tf.keras.Sequential([
tf.keras.layers.Dense(8, activation='relu', input_shape=(1,)),
tf.keras.layers.Dense(8, activation='relu'),
tf.keras.layers.Dense(3, activation='softmax') # 3 classes
])
# Compiler le modèle
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
# Entraîner le modèle
model.fit(X_train, y_train, epochs=50, batch_size=16, validation_data=(X_test, y_test))
# Évaluer le modèle
loss, accuracy = model.evaluate(X_test, y_test)
print(f'Précision du modèle : {accuracy * 100:.2f}%')
# Sauvegarder le modèle
model.save("light_classifier.h5")
Epoch 1/50
/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)
[1m75/75[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.5355 - loss: 1.0415 - val_accuracy: 0.6967 - val_loss: 1.0063
Epoch 2/50
[1m75/75[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1ms/step - accuracy: 0.5094 - loss: 1.0007 - val_accuracy: 0.6867 - val_loss: 0.9444
Epoch 3/50
[1m75/75[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1ms/step - accuracy: 0.6561 - loss: 0.9399 - val_accuracy: 0.6800 - val_loss: 0.8699
Epoch 4/50
[1m75/75[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1ms/step - accuracy: 0.6279 - loss: 0.8723 - val_accuracy: 0.6667 - val_loss: 0.8030
...
Précision du modèle : 95.67%
# Conversion en TensorFlow Lite
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
# Sauvegarde du modèle TensorFlow Lite
with open("light_classifier.tflite", "wb") as f:
f.write(tflite_model)
print("Modèle converti en TensorFlow Lite et sauvegardé sous 'light_classifier.tflite'")
from tensorflow.lite.python.util import convert_bytes_to_c_source
source_text, header_text = convert_bytes_to_c_source(tflite_model,
"light_classifier_model",
include_path="light_classifier_model.h")
with open('light_classifier_model.h', 'w') as file:
file.write(header_text)
with open('light_classifier_model.cpp', 'w') as file:
file.write(source_text)
WORKSPACE_F411_DATA_LABELING.zip
#include "main.h"
#include "light_classifier_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>
#include <time.h>
#define MAX 100000
#define MIN 0
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;
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 search_max(float* tab, int nb_val, int *idx)
{
float max_value = tab[0];
for (int i = 1; i < nb_val; i++)
{
if (tab[i] > max_value)
{
max_value =tab[i]; // Mettre à jour la plus grande valeur
*idx = i; // Enregistrer l'index de la meilleure classe
}
}
return 0;
}
int val_test[20] = { 0 , 100, 150, 400, 500 , 700 , 850, 1000, 5000, 10000, 50000, 99999, 30, 2000, 7000, 23, 177, 20000, 50, 1250};
//==============================================================================================
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(light_classifier_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;
float tab_log[3];
int lux;
static int i=0;
static int max = 0;
while (1)
{
lux = val_test[(i++)%20];
float normalized_lux = lux / 100000.0f;
//-------------------------------------------------------------------------
model_input->data.f[0] = normalized_lux;
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;
}
//-------------------------------------------------------------------------
int ix = 0;
search_max(model_output->data.f, 3, &ix);
tab_log[0] = model_output->data.f[0]; // Probabilité de "Intermédiaire"
tab_log[1] = model_output->data.f[1]; // Probabilité de "Jour"
tab_log[2] = model_output->data.f[2]; // Probabilité de "Nuit"
cout << "Luminosité : " << lux << " lux | probas : " << tab_log[0] <<" "<<tab_log[1] << " "<<tab_log[2];
const char* categories[] = {"Intermédiaire", "Jour", "Nuit"};
cout << " -> Classe : " << categories[ix] << endl << '\r';
HAL_Delay(500);
}
}
//==============================================================================================
Terminal Série
Luminosité : 0 lux | probas : 0.189 0.001 0.811 -> Classe : Nuit
Luminosité : 100 lux | probas : 0.231 0.001 0.768 -> Classe : Nuit
Luminosité : 150 lux | probas : 0.254 0.001 0.745 -> Classe : Nuit
Luminosité : 400 lux | probas : 0.392 0.001 0.606 -> Classe : Nuit
Luminosité : 500 lux | probas : 0.454 0.002 0.544 -> Classe : Nuit
Luminosité : 700 lux | probas : 0.580 0.003 0.417 -> Classe : Intermédiaire
Luminosité : 850 lux | probas : 0.668 0.004 0.328 -> Classe : Intermédiaire
Luminosité : 1000 lux | probas : 0.746 0.004 0.250 -> Classe : Intermédiaire
Luminosité : 5000 lux | probas : 0.826 0.174 0.000 -> Classe : Intermédiaire
Luminosité : 10000 lux | probas : 0.027 0.973 0.000 -> Classe : Jour
Luminosité : 50000 lux | probas : 0.000 1.000 0.000 -> Classe : Jour
Luminosité : 99999 lux | probas : 0.000 1.000 0.000 -> Classe : Jour
Luminosité : 30 lux | probas : 0.201 0.001 0.799 -> Classe : Nuit
Luminosité : 2000 lux | probas : 0.962 0.013 0.025 -> Classe : Intermédiaire
Luminosité : 7000 lux | probas : 0.378 0.622 0.000 -> Classe : Jour
Luminosité : 23 lux | probas : 0.198 0.001 0.801 -> Classe : Nuit
Luminosité : 177 lux | probas : 0.268 0.001 0.731 -> Classe : Nuit
Luminosité : 20000 lux | probas : 0.000 1.000 0.000 -> Classe : Jour
Luminosité : 50 lux | probas : 0.209 0.001 0.790 -> Classe : Nuit
Luminosité : 1250 lux | probas : 0.844 0.006 0.149 -> Classe : Intermédiaire