Data Labeling sur microcontrôleur avec Tensorflow lite
Data Labeling sur microcontrôleur avec Tensorflow lite
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.
Préparation du modèle sur le PC
Dataset :
Préparation du 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()
Découpage du Dataset
# 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)
Génération du Modèle
# 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))
Test du Modèle
# É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 du Modèle
# 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)
Exécution du modèle sur le Microcontrôleur
WORKSPACE_F411_DATA_LABELING.zip
main.cpp
#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