TRANSFER LEARNING FOR IMAGE CLASSIFICATION IN QUALITY CONTROL APP

For your application it may be more interesting to train an already trained machine learning model, for a different but related problem. We exploit what has been learned in one task to improve the performance in another. This is useful if for example we do not have much labeled data. Other advantages are reduced training time and better performance. 

Here we will guide you through the steps required to train with transfer learning the model using a dataset as an example that could be useful for a Quality Control task in manufacturing, and the steps required to visualize, check if the model works and to deploy it for edge devices for on-premise inference.

Imports.

import itertools

import os

import matplotlib.pylab as plt

import numpy as np

import os

import tensorflow as tf

import tensorflow_hub as hub

Download model 

If you want to do transfer learning, your starting point is an already trained model, you can obtain these from different repositories, we will use TensorFlow Hub.

Choose a SavedModel from the tensorflow hub, there are many models with different structures and amount of parameters and trained on different datasets. Get a link to use that particular model in your Python code. We will be using efficientnetv2 trained on the imagenet-21k dataset. 

model_name = "efficientnetv2-xl-21k"

model_handle_map = {

    "efficientnetv2-xl-21k": "https://tfhub.dev/google/imagenet/efficientnet_v2_imagenet21k_xl/feature_vector/2"

}

model_handle = model_handle_map.get(model_name)

#Define the size according to the model specifications. 

pixels = 512

IMAGE_SIZE = (pixels, pixels)

print(f"Input size {IMAGE_SIZE}")

#If you have gpu you can use a larger Batch size

BATCH_SIZE = 16

#Build the dataset. 

We will use a dataset from kaggle for food inspection, containing two classes, one with fresh meat and the other with spoiled meat. This is just one example of the many applications we could use deep learning for quality assurance in manufacturing.

O.Ulucan , D.Karakaya and M.Turkan.(2019) Meat quality assessment based on deep learning. In Conf. Innovations Intell. Syst. Appli. (ASYU)

myFile= "Path/to/dataset.zip"

fullPath = os.path.abspath(myFile)

print(fullPath)

#We will train a model to assess meat quality.

data_for_processing =tf.keras.utils.get_file(origin='file://'+fullPath, extract=True)

def build_dataset(subset):

  return tf.keras.preprocessing.image_dataset_from_directory(

   "archive",

   validation_split=.20,

   subset=subset,

   label_mode="categorical",

   # Seed needs to provided when using validation_split and shuffle = True.

   # A fixed seed is used so that the validation set is stable across runs.

   seed=123,

   image_size=IMAGE_SIZE,

   batch_size=1)

train_ds = build_dataset("training")

class_names = tuple(train_ds.class_names)

train_size = train_ds.cardinality().numpy()

train_ds = train_ds.unbatch().batch(BATCH_SIZE)

train_ds = train_ds.repeat()

normalization_layer = tf.keras.layers.Rescaling(1. / 255)

preprocessing_model = tf.keras.Sequential([normalization_layer])

#You could add data_augmentation to obtain a larger dataset, #and the model will perform better overall 

#Example:   preprocessing_model.add(

# tf.keras.layers.RandomTranslation(0.2, 0))

train_ds = train_ds.map(lambda images, labels:

                     (preprocessing_model(images), labels))

#Validation dataset

val_ds = build_dataset("validation")

valid_size = val_ds.cardinality().numpy()

val_ds = val_ds.unbatch().batch(BATCH_SIZE)

val_ds = val_ds.map(lambda images, labels:

                 (normalization_layer(images), labels))

Building the model. 

Next, we will build the model layers, because we will be using an already training model we will freeze the parameters of the model and work on the output layer. 

model = tf.keras.Sequential([

# Explicitly define the input shape so the model can be 

# properly loaded by the TFLiteConverter

tf.keras.layers.InputLayer(input_shape=IMAGE_SIZE + (3,)),

hub.KerasLayer(model_handle, trainable=False),

tf.keras.layers.Dropout(rate=0.2),

tf.keras.layers.Dense(len(class_names),                      kernel_regularizer=tf.keras.regularizers.l2(0.0001))

])

model.build((None,)+IMAGE_SIZE+(3,))

model.summary()

model.compile(

  optimizer=tf.keras.optimizers.SGD(learning_rate=0.005, momentum=0.9),

  loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True, label_smoothing=0.1),

  metrics=['accuracy'])

Training

Adjust the number of epochs according to the duration of the training, this will take you the longest, it is important to correctly choose the number of epochs to obtain a good performance but make sure not to get into overfitting territory. 

steps_per_epoch = train_size // BATCH_SIZE

validation_steps = valid_size // BATCH_SIZE

hist = model.fit(

    train_ds,

    epochs=5, steps_per_epoch=steps_per_epoch,

    validation_data=val_ds,

    validation_steps=validation_steps).history

Statistics. 

When training has finished, we would like to check how well we can expect our model to perform, the next plots can help us look at this. We will look at the values of our model’s loss and precision.

plt.figure()

plt.ylabel("Loss (training and validation)")

plt.xlabel("Training Steps")

plt.plot(hist["loss"])

plt.plot(hist["val_loss"])

plt.figure()

plt.ylabel("Accuracy (training and validation)")

plt.xlabel("Training Steps")

plt.ylim([0,1])

plt.plot(hist["accuracy"])

plt.plot(hist["val_accuracy"])

plt.savefig("res1.png")

Improvement of the accuracy (number of correct guesses) and loss (difference between model output and expected values) is not linear. But the usual trend is that the larger the training steps the more fitting are the model predictions to our dataset, but be careful with overfitting.

Test on validation set.

We can perform  inference on an image in the validation set. The model will return if the image provided is from the fresh or spoiled category. 

x, y = next(iter(val_ds))

image = x[0, :, :, :]

true_index = np.argmax(y[0])

# Expand the validation image to (1, 224, 224, 3) before predicting the label

prediction_scores = model.predict(np.expand_dims(image, axis=0))

predicted_index = np.argmax(prediction_scores)

print("True label: " + class_names[true_index])

print("Predicted label: " + class_names[predicted_index])
True label: Fresh
Predicted label: Fresh

Save the model. 

Once the training has finished you can save your model with the parameters to be used on later applications. 

tf.saved_model.save(model, saved_model_path)

Export 

Export model to tflite for use on Edge devices, for example raspberry, mobile phones or Google Coral. 

It is possible to perform optimizations to the model for better performance on mobile devices, like quantizations. 

lite_model_content = converter.convert()

with open(f"/tmp/lite_flowers_model_{model_name}.tflite", "wb") as f:

  f.write(lite_model_content)

print("Wrote %sTFLite model of %d bytes." %

   ("optimized " if optimize_lite_model else "", len(lite_model_content)))

interpreter = tf.lite.Interpreter(model_content=lite_model_content)

# This little helper wraps the TFLite Interpreter as a numpy-to-numpy function.

def lite_model(images):

  interpreter.allocate_tensors()

  interpreter.set_tensor(interpreter.get_input_details()[0]['index'], images)

  interpreter.invoke()

  return interpreter.get_tensor(interpreter.get_output_details()[0]['index'])

num_eval_examples = 50

eval_dataset = ((image, label)  # TFLite expects batch size 1.

                for batch in train_ds

                for (image, label) in zip(*batch))

count = 0

count_lite_tf_agree = 0

count_lite_correct = 0

for image, label in eval_dataset:

  probs_lite = lite_model(image[None, ...])[0]

  probs_tf = model(image[None, ...]).numpy()[0]

  y_lite = np.argmax(probs_lite)

  y_tf = np.argmax(probs_tf)

  y_true = np.argmax(label)

  count +=1

  if y_lite == y_tf: count_lite_tf_agree += 1

  if y_lite == y_true: count_lite_correct += 1

  if count >= num_eval_examples: break

print("TFLite model agrees with original model on %d of %d examples (%g%%)." %

      (count_lite_tf_agree, count, 100.0 * count_lite_tf_agree / count))

print("TFLite model is accurate on %d of %d examples (%g%%)." %

      (count_lite_correct, count, 100.0 * count_lite_correct / count))

When you have downloaded the model and have your label files ready, you can perform as many inferences as you want. Without the need to train your model again.  

If you would like to use your model on Google Coral, you would need to copy your files to your coral sd image. You could do it with the help of a bash script to mount a Google Coral image on your Linux machine. 

losetup -f /Path/to/image

//Look for the loop device where the virtual disk has been installed

if mount /dev/loopXX /media; then

   echo "Mounted"

else

   echo "Doesn't exist, losetup failed"

fi

cp -i  $modelo $labels /media/home/mendel/

umount -d /media/image-coral/

Now you only need to have an inference script inside your device to use your trained model, you could make use of the following script.

Inference

Finally, once we have our tflite model, you can perform the inference on your device with the following script, such as Coral or Raspberry. If you have a TPU make sure to enable it for better performance. 

from tflite_runtime.interpreter import Interpreter

import numpy as np

import tensorflow as tf

from PIL import Image as ImagePIL

import time

from numpy import asarray, transpose, tri

from PIL import ImageTk

import tflite_runtime.interpreter as tflite

import pprint

import cv2

pp = pprint.PrettyPrinter(indent=4) # Set Pretty Print Indentation

model = "/path/to/.tflite"

labels_path = "/path/to/labels.txt"

img = "/path/to/image"

#load labels from ffile.

def load_labels(filename):

  with open(filename, 'r') as f:

return [line.strip() for line in f.readlines()]

labels = load_labels(labels_path)

#Model

global interpreter, input_details, output_details

interpreter = tflite.Interpreter(model)

interpreter.allocate_tensors()

#experimental_delegates=[tflite.load_delegate('libedgetpu.so.1')])

input_details = interpreter.get_input_details()

output_details = interpreter.get_output_details()

_, height, width, _ = interpreter.get_input_details()[0]['shape']

# print('tensor input', input_details)

global size

size = [width, height]

print(size)

inicio=time.time()

#Open the image

img = cv2.imread(img)

img_resized = cv2.resize(img, (224, 224))

input_data = np.array(asarray(img_resized), dtype=np.float32)

input_data = np.expand_dims(input_data , axis=0)

# check the type of the input tensor

floating_model = input_details[0]['dtype'] == np.float32

#floating_model = input_details[0]['dtype'] == np.float32

if floating_model:

  input_data = (np.float32(input_data) - 127.5) / 127.5

print("-> Input data:", input_data)

interpreter.set_tensor(input_details[0]['index'], input_data)

interpreter.invoke()

tensor_result= interpreter.get_tensor(output_details[0]['index'])[0]

print(tensor_result)

results = np.squeeze(tensor_result)

#[::-1] is a trick in python to obtain a list in the opposite order.

top_k= results.argsort()[-5:][::-1]

#We get the top 5 results

result=''

#print(top_k)

for i in top_k:

result+=('{:08.6f}: {}'.format(float(tensor_result[i]), labels[i]))+"\n"

result=result+"Inference time: "+str(time.time()-inicio)

#print(resultado, '\n')

resultado_max= result.partition('\n')[0]+'\n'+ result.split('\n')[-1]

#Get the time it took for the inference

fin = time.time()

print("Inference time:" , fin-inicio)

print(resultado_max)

print("\n",result)

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top