Training model that can be run on Raspberry Pi 4 (tflite)

Hi,

I have being trying to train model on google Colab. The aim is to identify between different plant leaves such as strawberry, apple and Soybean etc.

Below is the code that I have so far.

from google.colab import drive
drive.mount('/content/drive/')

!ls -l "/content/drive/Othercomputers/My_MacBook_Pro/"

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

#  path to 'train' and 'valid' directories
train_dir = '/content/drive/Othercomputers/My_MacBook_Pro/Strawberry/train'
validation_dir = '/content/drive/Othercomputers/My_MacBook_Pro/Strawberry/valid'

# Set up data generators without augmentation
train_datagen = ImageDataGenerator(rescale=1./255)
validation_datagen = ImageDataGenerator(rescale=1./255)

# The 'Strawberry__healthy' is the name of the subdirectory within 'train' and 'valid' and it represents the class name
train_generator = train_datagen.flow_from_directory(
    train_dir,
    target_size=(224, 224),
    batch_size=32,
    class_mode='categorical')

validation_generator = validation_datagen.flow_from_directory(
    validation_dir,
    target_size=(224, 224),
    batch_size=32,
    class_mode='categorical')

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import regularizers

# Load MobileNetV2 pre-trained model
base_model = MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights='imagenet')

# Freeze the base model weights
base_model.trainable = False

# Define the number of classes
num_classes = 2  # Update this based on your dataset

# Create the model
model = Sequential([
    base_model,
    GlobalAveragePooling2D(),
    Dense(256, activation='relu', kernel_regularizer=regularizers.l2(0.01)),  # L2 regularization
    Dropout(0.5),  # Dropout layer
    Dense(num_classes, activation='softmax')
])

# Compile the model
model.compile(optimizer=tf.keras.optimizers.Adam(),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint


epochs = 8

# Implement early stopping and model checkpointing
early_stopping = EarlyStopping(monitor='val_loss', patience=3, verbose=1, mode='min')
model_checkpoint = ModelCheckpoint('best_model.h5', monitor='val_accuracy', mode='max', save_best_only=True, verbose=1)

# Train the model without class weighting (since we removed data augmentation)
history = model.fit(
    train_generator,
    steps_per_epoch=train_generator.samples // train_generator.batch_size,
    validation_data=validation_generator,
    validation_steps=validation_generator.samples // validation_generator.batch_size,
    epochs=epochs,
    callbacks=[early_stopping, model_checkpoint]
)

# Load the best model after training
model.load_weights('best_model.h5')

# Evaluate model performance
print("Evaluating model...")
eval_result = model.evaluate(validation_generator)
print(f"Validation Loss: {eval_result[0]}, Validation Accuracy: {eval_result[1]}")

# Save the trained model in the recommended Keras format
model.save('my_strawberry_model.keras')

# Convert the model to TensorFlow Lite format for deployment on edge devices
converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()

# Save the TensorFlow Lite model to disk
with open('my_strawberry_model.tflite', 'wb') as f:
    f.write(tflite_model)

from google.colab import files
files.download('my_strawberry_model.keras')  # Download the Keras model file
files.download('my_strawberry_model.tflite') # Download the TensorFlow Lite model file

output :

Mounted at /content/drive/
total 8
drwx------ 2 root root 4096 Dec  3 21:12 Soybean
drwx------ 2 root root 4096 Dec 26 11:43 Strawberry

Found 3640 images belonging to 2 classes. Found 910 images belonging to 2 classes. Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224_no_top.h5 9406464/9406464 [==============================] - 0s 0us/step 
Epoch 1/8 113/113 [==============================] - ETA: 0s - loss: 1.6966 - accuracy: 0.9734 Epoch 1: val_accuracy improved from -inf to 0.99554, saving model to best_model.h5

/usr/local/lib/python3.10/dist-packages/keras/src/engine/training.py:3103: UserWarning: You are saving your model as an HDF5 file via `model.save()`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')`. saving_api.save_model(

113/113 [==============================] - 605s 5s/step - loss: 1.6966 - accuracy: 0.9734 - val_loss: 0.5985 - val_accuracy: 0.9955
Epoch 2/8 113/113 [==============================] - ETA: 0s - loss: 0.3761 - accuracy: 0.9958 Epoch 2: val_accuracy did not improve from 0.99554 113/113 [==============================] - 208s 2s/step - loss: 0.3761 - accuracy: 0.9958 - val_loss: 0.2549 - val_accuracy: 0.9810 
Epoch 3/8 113/113 [==============================] - ETA: 0s - loss: 0.1596 - accuracy: 0.9981 Epoch 3: val_accuracy did not improve from 0.99554 113/113 [==============================] - 258s 2s/step - loss: 0.1596 - accuracy: 0.9981 - val_loss: 0.1097 - val_accuracy: 0.9955 
Epoch 4/8 113/113 [==============================] - ETA: 0s - loss: 0.0999 - accuracy: 0.9950 Epoch 4: val_accuracy improved from 0.99554 to 0.99888, saving model to best_model.h5 113/113 [==============================] - 218s 2s/step - loss: 0.0999 - accuracy: 0.9950 - val_loss: 0.0873 - val_accuracy: 0.9989 
Epoch 5/8 113/113 [==============================] - ETA: 0s - loss: 0.0753 - accuracy: 0.9953 Epoch 5: val_accuracy did not improve from 0.99888 113/113 [==============================] - 252s 2s/step - loss: 0.0753 - accuracy: 0.9953 - val_loss: 0.0622 - val_accuracy: 0.9967 
Epoch 6/8 113/113 [==============================] - ETA: 0s - loss: 0.0585 - accuracy: 0.9972 Epoch 6: val_accuracy did not improve from 0.99888 113/113 [==============================] - 250s 2s/step - loss: 0.0585 - accuracy: 0.9972 - val_loss: 0.0397 - val_accuracy: 0.9989 
Epoch 7/8 113/113 [==============================] - ETA: 0s - loss: 0.0964 - accuracy: 0.9875 Epoch 7: val_accuracy did not improve from 0.99888 113/113 [==============================] - 222s 2s/step - loss: 0.0964 - accuracy: 0.9875 - val_loss: 0.0661 - val_accuracy: 0.9978 
Epoch 8/8 113/113 [==============================] - ETA: 0s - loss: 0.0482 - accuracy: 0.9978 Epoch 8: val_accuracy did not improve from 0.99888 113/113 [==============================] - 253s 2s/step - loss: 0.0482 - accuracy: 0.9978 - val_loss: 0.0471 - val_accuracy: 0.9944 Evaluating model... 29/29 [==============================] - 44s 1s/step - loss: 0.0873 - accuracy: 0.9989 Validation Loss: 0.08727286010980606, Validation Accuracy: 0.9989010691642761

WARNING:absl:`mobilenetv2_1.00_224_input` is not a valid tf.function parameter name. Sanitizing to `mobilenetv2_1_00_224_input`. WARNING:absl:`mobilenetv2_1.00_224_input` is not a valid tf.function parameter name. Sanitizing to `mobilenetv2_1_00_224_input`. WARNING:absl:`mobilenetv2_1.00_224_input` is not a valid tf.function parameter name. Sanitizing to `mobilenetv2_1_00_224_input`.


I didn’t need to use data augumatation code as the pictures are already diverse(rotated, scaled, flipped etc )
I haven’t trained the model on Soybean yet as I want to fix is issue first before I train it on multiple plant species.

I get no errors when training the model as shown in the output. When I transfer the .tflite file to raspberry pi, the model identifies the picture of an animal with white background to be as “Strawberry” .

below is my python script and the output
FYI: I have changed the name of the tflite on my Raspberry Pi (my_strawberry_model_3.tflite) becuase I have generated model with same name before. but I assure you it is the right file.

import os
import numpy as np
import tflite_runtime.interpreter as tflite
from PIL import Image

# Function to preprocess the image
def preprocess_image(image_path):
    img = Image.open(image_path).convert('RGB').resize((224, 224))
    img_array = np.expand_dims(np.array(img, dtype=np.float32) / 255.0, axis=0)
    return img_array

# Function to run inference
def run_inference(image_path, interpreter, input_details, output_details):
    input_data = preprocess_image(image_path)
    interpreter.set_tensor(input_details[0]['index'], input_data)
    interpreter.invoke()
    output_data = interpreter.get_tensor(output_details[0]['index'])
    return output_data

# Load the TFLite model and allocate tensors
interpreter = tflite.Interpreter(model_path='my_strawberry_model_3.tflite')
#interpreter = tflite.Interpreter(model_path='my_soybean_model.tflite')
interpreter.allocate_tensors()

# Get input and output tensors
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# Directory containing images to classify
directory_path = '/home/pi4/Desktop/fun_project/all_diff'  

# Class names
class_names = ['Strawberry__healthy', 'Not_Strawberry']
#class_names = ['Soybean__healthy', 'Not_Soybean']

# Iterate over each image in the directory
for filename in os.listdir(directory_path):
    if filename.lower().endswith(('.png', '.jpg', '.jpeg')):  # Check for image files
        image_path = os.path.join(directory_path, filename)
        result = run_inference(image_path, interpreter, input_details, output_details)
        
        predicted_class_index = np.argmax(result)
        probabilities = result[0]
        
        print(f"Image: {filename}")
        print(f"Predicted class index: {predicted_class_index}")
        print(f"Probabilities: {probabilities}")
        print(f"Predicted class name: {class_names[predicted_class_index]}")
        print("-" * 50)  # Separator for readability
pi4@raspberrypi:~/Desktop/FYP $ python3 loop_plant_script.py 
Image: donkey.jpg
Predicted class index: 0
Probabilities: [9.9951291e-01 4.8705883e-04]
Predicted class name: Strawberry__healthy
--------------------------------------------------
pi4@raspberrypi:~/Desktop/FYP $ 

Issue: it is obviously that a picture of animal should not be classed as “starwberry” . I am not sure what can I try out to make the model work.

Thanks in advance.

Hi @Hum10, Could you please pass that image to the tensorflow model instead of tflite model and share the result like whether it was predicted as Strawberry__healthy or Not_Strawberry. Thank You.

@Kiran_Sai_Ramineni below is the script (test.py) that i ran on my Mac machine.


import tensorflow as tf
from tensorflow.keras.preprocessing.image import load_img, img_to_array
import numpy as np

# Load the model
model = tf.keras.models.load_model('/Users/humzaali/Downloads/my_strawberry_model.keras')

# Define the class names in the order they were in during training
class_names = ['Strawberry__healthy', 'Not_Strawberry']

# File path of the image to test
image_path = '/Users/humzaali/Downloads/wb.jpg'

# Load an image file to test, resizing it to the required input size
img = load_img(image_path, target_size=(224, 224))

# Convert the image to an array and add a batch dimension
img_array = img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)  # model expects a batch of images

# Make a prediction
predictions = model.predict(img_array)

# Output the prediction probabilities
print(f"Prediction probabilities: {predictions[0]}")

# Determine the predicted class index
predicted_class_index = np.argmax(predictions[0])
predicted_class_name = class_names[predicted_class_index]

# Output the file name and the predicted class
print(f"Processing file: {image_path.split('/')[-1]}")  # Extracts and prints the file name
print(f"Predicted class index: {predicted_class_index}")
print(f"Predicted class name: {predicted_class_name}")

Here is the output, I do get some warnings about versions but regardless the model processes given picture. The picture this time is wb.jpg which is a white plain picture and for some reason it is predicted as Strawberry__healthy.


(base) humzaali@Humzas-MBP ~ % python3 Desktop/project/test.py
2023-12-28 16:02:28.245869: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
/Users/humzaali/opt/anaconda3/lib/python3.9/site-packages/scipy/__init__.py:146: UserWarning: A NumPy version >=1.16.5 and <1.23.0 is required for this version of SciPy (detected version 1.23.5
  warnings.warn(f"A NumPy version >={np_minversion} and <{np_maxversion}"
1/1 [==============================] - 1s 833ms/step
Prediction probabilities: [0.9969977  0.00300225]
Processing file: wb.jpg
Predicted class index: 0
Predicted class name: Strawberry__healthy
(base) humzaali@Humzas-MBP ~ %