Skip to main content

Victoria Harbour, Hongkong

Github Repository

See also:

Tensorflow Transfer Learning

Model Scaling

  1. Pretraining EfficientNetB0 (10% of Dataset with 10 Classes)
  2. Fine-Tuning (100% of Dataset with 10 Classes)
  3. Scaling the Model to all 101 Classes (10% of Dataset with 101 Classes)
import datetime
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import random
from sklearn.metrics import accuracy_score, classification_report
import tensorflow as tf
from tensorflow.keras.utils import image_dataset_from_directory
# export helper functions from above into helper script
from helper import (create_tensorboard_callback,
create_checkpoint_callback,
plot_accuracy_curves,
combine_training_curves,
data_augmentation_layer_no_rescaling,
plot_confusion_matrix)
# global variables
SEED = 42
BATCH_SIZE = 32
IMG_SHAPE = (224, 224)

Get Data

# preparing datasets
# wget https://storage.googleapis.com/ztm_tf_course/food_vision/101_food_classes_10_percent.zip

training_directory = "../datasets/101_food_classes_10_percent/train"
testing_directory = "../datasets/101_food_classes_10_percent/test"

training_data_101_10 = image_dataset_from_directory(training_directory,
labels='inferred',
label_mode='categorical',
seed=SEED,
shuffle=True,
image_size=IMG_SHAPE,
batch_size=BATCH_SIZE)

testing_data_101_10 = image_dataset_from_directory(testing_directory,
labels='inferred',
label_mode='categorical',
seed=SEED,
shuffle=False,
image_size=IMG_SHAPE,
batch_size=BATCH_SIZE)

# get class names
class_names_101_10 = training_data_101_10.class_names
len(class_names_101_10), class_names_101_10

# Found 7575 files belonging to 101 classes.
# Found 25250 files belonging to 101 classes.

# (101,
# ['apple_pie',
# 'baby_back_ribs',
# 'baklava',
# 'beef_carpaccio',
# 'beef_tartare',
# 'beet_salad',
# 'beignets',
# 'bibimbap',
# 'bread_pudding',
# 'breakfast_burrito',
# 'bruschetta',
# 'caesar_salad',
# 'cannoli',
# 'caprese_salad',
# 'carrot_cake',
# 'ceviche',
# 'cheese_plate',
# 'cheesecake',
# 'chicken_curry',
# 'chicken_quesadilla',
# 'chicken_wings',
# 'chocolate_cake',
# 'chocolate_mousse',
# 'churros',
# 'clam_chowder',
# 'club_sandwich',
# 'crab_cakes',
# 'creme_brulee',
# 'croque_madame',
# 'cup_cakes',
# 'deviled_eggs',
# 'donuts',
# 'dumplings',
# 'edamame',
# 'eggs_benedict',
# 'escargots',
# 'falafel',
# 'filet_mignon',
# 'fish_and_chips',
# 'foie_gras',
# 'french_fries',
# 'french_onion_soup',
# 'french_toast',
# 'fried_calamari',
# 'fried_rice',
# 'frozen_yogurt',
# 'garlic_bread',
# 'gnocchi',
# 'greek_salad',
# 'grilled_cheese_sandwich',
# 'grilled_salmon',
# 'guacamole',
# 'gyoza',
# 'hamburger',
# 'hot_and_sour_soup',
# 'hot_dog',
# 'huevos_rancheros',
# 'hummus',
# 'ice_cream',
# 'lasagna',
# 'lobster_bisque',
# 'lobster_roll_sandwich',
# 'macaroni_and_cheese',
# 'macarons',
# 'miso_soup',
# 'mussels',
# 'nachos',
# 'omelette',
# 'onion_rings',
# 'oysters',
# 'pad_thai',
# 'paella',
# 'pancakes',
# 'panna_cotta',
# 'peking_duck',
# 'pho',
# 'pizza',
# 'pork_chop',
# 'poutine',
# 'prime_rib',
# 'pulled_pork_sandwich',
# 'ramen',
# 'ravioli',
# 'red_velvet_cake',
# 'risotto',
# 'samosa',
# 'sashimi',
# 'scallops',
# 'seaweed_salad',
# 'shrimp_and_grits',
# 'spaghetti_bolognese',
# 'spaghetti_carbonara',
# 'spring_rolls',
# 'steak',
# 'strawberry_shortcake',
# 'sushi',
# 'tacos',
# 'takoyaki',
# 'tiramisu',
# 'tuna_tartare',
# 'waffles'])

Model Building and Training

# create callbacks from helper.py
## checkpoint callback
checkpoint_callback = create_checkpoint_callback(
dir_name='../checkpoints/transfer_learning_scaling',
experiment_name='101_classes_10_percent_dataset')

## tensorboard callback
tensorboard_callback = create_tensorboard_callback(
dir_name='../tensorboard/transfer_learning_scaling',
experiment_name='101_classes_10_percent_dataset')
# get base model from keras applications
base_model = tf.keras.applications.efficientnet_v2.EfficientNetV2B0(
include_top=False
)
base_model.trainable = False

# build model using keras functional API
input_layer = tf.keras.layers.Input(shape=IMG_SHAPE+(3,), name='input_layer')
# use image augmentation layer from helper.py
data = data_augmentation_layer_no_rescaling(input_layer)
# run in inference mode so batchnorm statistics don't get updated
# even after unfreezing the base model for fine-tuning
data = base_model(data, training=False)
data = tf.keras.layers.GlobalAveragePooling2D(name="global_average_pooling_layer")(data)
output_layer = tf.keras.layers.Dense(len(class_names_101_10), activation="softmax", name="output_layer")(data)

model = tf.keras.Model(input_layer, output_layer)

# compile the model
model.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])
# fit the model
tf.random.set_seed(SEED)
## training epochs before fine-tuning
pretraining_epochs = 5

history_model = model.fit(
training_data_101_10,
epochs=pretraining_epochs,
steps_per_epoch=len(training_data_101_10),
validation_data=testing_data_101_10,
# evaluate performance on 15% of the testing dataset
validation_steps=int(0.15 * len(testing_data_101_10)),
callbacks=[tensorboard_callback,
checkpoint_callback])

# Epoch 5/5
# 73s 307ms/step - loss: 1.5557 - accuracy: 0.6176 - val_loss: 1.8671 - val_accuracy: 0.5246
# evaluate performance on whole dataset
pre_training_results = model.evaluate(testing_data_101_10)
print(pre_training_results)

# [1.5907552242279053, 0.5817425847053528]

Fine-tuning the Model

# unfreeze entire model
base_model.trainable = True

# keep only the last 5 layers trainable
for layer in base_model.layers[:-5]:
layer.trainable = False
# list all layers in model
for layer in model.layers:
print(layer, layer.trainable)

# <keras.engine.input_layer.InputLayer object at 0x7f695c39b370> True
# <keras.engine.sequential.Sequential object at 0x7f695c39a260> True
# <keras.engine.functional.Functional object at 0x7f6943f0d300> True
# <keras.layers.pooling.global_average_pooling2d.GlobalAveragePooling2D object at 0x7f6943f4e830> True
# <keras.layers.core.dense.Dense object at 0x7f69400c0520> True

# layer 2 is the now only partly unfrozen imported model (efficientnetb0)
for layer_number, layer in enumerate(model.layers[2].layers):
print(layer_number, layer.name, layer.trainable)

# 0 input_1 False
# 1 rescaling False
# 2 normalization False
# ...
# 262 block6h_se_excite False
# 263 block6h_project_conv False
# 264 block6h_project_bn False
# 265 block6h_drop True
# 266 block6h_add True
# 267 top_conv True
# 268 top_bn True
# 269 top_activation True
# recompile the model with the new basemodel
### to prevent overfitting / to better hold on to pre-training
### the learning rate during fine-tuning should be lowered 10x
### default Adam(lr)=1e-3 => 1e-4
model.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
metrics=['accuracy'])
# continue training
tf.random.set_seed(SEED)
fine_tuning_epochs = pretraining_epochs + 5

history_fine_tuning_model = model.fit(
training_data_101_10,
epochs=fine_tuning_epochs,
# start from last pre-training checkpoint
# training from epoch 6 - 10
initial_epoch = history_model.epoch[-1],
steps_per_epoch=len(training_data_101_10),
validation_data=testing_data_101_10,
# evaluate performance on 15% of the testing dataset
validation_steps=int(0.15 * len(testing_data_101_10)),
callbacks=[tensorboard_callback,
checkpoint_callback])

# Epoch 10/10
# 70s 294ms/step - loss: 1.3161 - accuracy: 0.6547 - val_loss: 1.7494 - val_accuracy: 0.5469
# evaluate performance on whole dataset
fine_tuning_results = model.evaluate(testing_data_101_10)
print(fine_tuning_results)

# pre_training_results
# [1.5907552242279053, 0.5817425847053528]

# fine_tuning_results
# [1.513363242149353, 0.5931881070137024]
# print accuracy curves
plot_accuracy_curves(history_model, "Pre-Training", history_fine_tuning_model, "Fine-Tuning")

Transfer Learning

# the validation accuracy increase keeps slowing while training
# accuracy goes up this points to an overfitting problem
combine_training_curves(history_model, history_fine_tuning_model, pretraining_epochs=5)

Transfer Learning

The model keeps improving but there is a growing gap between training and validation. This means that the model is overfitting the test data. Leading to worse results during the validation run.

Saving and Restoring the Trained Model

# saving the model
models_path = "../saved_models/model_101_classes_10_percent_training_data"
# model.save(models_path)
# INFO:tensorflow:Assets written to: ../saved_models/model_101_classes_10_percent_training_data/assets
# load the model
loaded_model = tf.keras.models.load_model(models_path)

# verify the model was loaded correctly
loaded_model_results = loaded_model.evaluate(testing_data_101_10)
print(loaded_model_results)

# fine_tuning_results to compare
# [1.513363242149353, 0.5931881070137024]

# loaded_model_results are the same - it worked!
# [1.513363242149353, 0.5931881070137024]

Evaluating Predictions

# making predictions on all 25250 validation images for 101 classes
test_prediction_probabilities = loaded_model.predict(testing_data_101_10, verbose=1)
print(test_prediction_probabilities.shape)
# (25250, 101)
# display prediction probabilities for the first image
print(test_prediction_probabilities[:1])

# [[2.03596894e-03 3.65167543e-05 2.17581794e-01 5.17010115e-08
# 3.32512741e-06 2.29585039e-05 2.71960180e-05 1.80260338e-06
# 5.30003861e-04 2.73975573e-04 1.06404877e-05 6.97153791e-06
# 1.50283886e-04 2.78270818e-06 3.42393899e-03 2.25132797e-02
# 2.40503272e-04 2.13146021e-04 1.64284266e-03 9.58332021e-05
# 1.32550078e-03 2.55020423e-05 7.39933093e-05 1.92245971e-05
# 8.13204853e-04 1.93982956e-03 8.89056770e-04 6.18012882e-06
# 7.59714108e-04 8.36205290e-05 3.92342417e-06 2.00494527e-04
# 2.51588156e-03 6.87084466e-05 7.40043120e-04 4.45897895e-05
# 4.05072642e-04 4.21193661e-04 1.59948692e-02 2.43602030e-04
# 1.10967096e-03 6.28277950e-04 5.56461528e-05 6.36936966e-05
# 4.16825242e-05 8.82518361e-05 1.96498135e-04 4.21540433e-04
# 1.76622216e-05 1.82788055e-02 4.08739754e-04 2.77833984e-04
# 6.94064796e-02 7.92958029e-03 2.63221909e-06 3.67652555e-03
# 1.11585250e-04 1.25307284e-04 5.01443865e-03 2.33431001e-05
# 9.54853363e-07 6.14999968e-04 1.00202730e-03 6.56907330e-04
# 1.59870205e-03 9.99520998e-05 5.11349281e-05 3.93474428e-03
# 1.97406291e-04 3.40633960e-05 8.98707913e-06 1.06544203e-05
# 5.15696120e-05 1.98621456e-05 5.94679965e-04 3.69085114e-06
# 1.09817571e-04 2.08929856e-03 1.73596389e-04 2.66812931e-05
# 1.44478225e-03 1.20244418e-04 1.30806561e-03 2.10159646e-06
# 2.99385400e-04 5.72092354e-01 2.51285546e-02 1.11975627e-04
# 4.98035988e-05 2.09982645e-05 3.76860640e-08 3.77362909e-07
# 1.97288580e-03 1.36366225e-05 2.58001910e-05 1.10756594e-03
# 6.64085674e-04 9.06130299e-05 4.01897114e-06 8.89552932e-04
# 1.07155865e-04]]

# the highest probabilty is `5.72092354e-01`
# get position of the class with the highest probability
arr = np.array(test_prediction_probabilities[:1])
predicted_class = np.argmax(arr)
print(f"INFO :: The predicted Class Number is: {predicted_class}")
# INFO :: The predicted Class Number is: 85
print(f"INFO :: The predicted Class is: {class_names_101_10[predicted_class]}")
# INFO :: The predicted Class is: samosa
# get all predicted classes
predicted_classes = test_prediction_probabilities.argmax(axis=1)

# there are predicted classes for all validation images
print(predicted_classes.shape)
# (25250,)

# print the predicted classes for the first 10 images
print(predicted_classes[:10])
# [85 0 0 8 8 78 29 46 0 0]
# get all true labels from batched dataset
print(testing_data_101_10)
# <BatchDataset element_spec=(TensorSpec(shape=(None, 224, 224, 3), dtype=tf.float32,name=None),
# TensorSpec(shape=(None, 101), dtype=tf.float32, name=None))>

# get class_names index value from unbatched dataset
y_labels = []
for images, labels in testing_data_101_10.unbatch():
y_labels.append(labels.numpy().argmax())

# display first result
print(y_labels[:10])

# these are the true labels and should be identical to `predicted_classes[:10]` above
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

# training dataset was not shuffled -> all 10 belong to the same class:
# print(class_names_101_10[0])
# apple_pie

Accuracy Score

Compare the predicted classes to the true classes to get the accuracy score for the model.

# use sklearn to determin the accuracy
sk_accuracy = accuracy_score(y_true=y_labels,
y_pred=predicted_classes)
print(sk_accuracy)
# 0.5931881188118812 => same as validation_accuracy from tensorflow

Confusion Matrix

plot_confusion_matrix(y_pred=y_labels, y_true=predicted_classes, classes=class_names_101_10)

Transfer Learning Transfer Learning

plot_confusion_matrix(y_pred=y_labels,
y_true=predicted_classes,
classes=class_names_101_10,
figsize = (88, 88),
text_size=8)

Transfer Learning

open in new tab to zoom in

We can see that the results are overall impressive. The 59.3% accuracy is mostly based on a couple of classes that might be hard to distinguish in general:

  • spaghetti_carbonara <-> spaghetti_bolongnese
  • prime_rib <-> steak
  • steak <-> filet_mignon
  • chicken_quesadilla <-> breakfast_burrito
  • hamburger <-> pulled_pork_sandwich
  • club_sandwich <-> grilled_cheese_sandwich

...

SciKit Learn Classification Report

print(classification_report(y_true=y_labels,
y_pred=predicted_classes))
class #classnameprecisionrecallf1-scoresupport
0apple_pie0.360.250.29250
1baby_back_ribs0.610.640.62250
2baklava0.670.550.60250
3beef_carpaccio0.690.570.62250
4beef_tartare0.650.310.42250
5beet_salad0.650.320.43250
6beignets0.860.710.78250
7bibimbap0.850.700.76250
8bread_pudding0.300.520.38250
9breakfast_burrito0.320.750.45250
10bruschetta0.480.520.50250
11caesar_salad0.630.620.62250
12cannoli0.580.660.62250
13caprese_salad0.530.600.56250
14carrot_cake0.500.540.52250
15ceviche0.360.310.33250
16cheese_plate0.670.490.56250
17cheesecake0.360.420.39250
18chicken_curry0.650.350.46250
19chicken_quesadilla0.700.380.49250
20chicken_wings0.710.700.70250
21chocolate_cake0.610.520.56250
22chocolate_mousse0.440.350.39250
23churros0.820.640.72250
24clam_chowder0.710.780.74250
25club_sandwich0.650.560.60250
26crab_cakes0.360.550.43250
27creme_brulee0.750.720.73250
28croque_madame0.580.690.63250
29cup_cakes0.660.820.73250
30deviled_eggs0.890.550.68250
31donuts0.740.740.74250
32dumplings0.750.860.80250
33edamame0.940.980.96250
34eggs_benedict0.560.760.65250
35escargots0.690.640.66250
36falafel0.530.430.47250
37filet_mignon0.270.510.35250
38fish_and_chips0.600.750.66250
39foie_gras0.420.160.23250
40french_fries0.800.740.77250
41french_onion_soup0.650.700.68250
42french_toast0.540.500.52250
43fried_calamari0.790.550.65250
44fried_rice0.730.550.63250
45frozen_yogurt0.830.820.82250
46garlic_bread0.530.620.57250
47gnocchi0.300.560.39250
48greek_salad0.560.660.61250
49grilled_cheese_sandwich0.370.450.41250
50grilled_salmon0.530.340.42250
51guacamole0.760.930.83250
52gyoza0.620.580.60250
53hamburger0.490.790.61250
54hot_and_sour_soup0.720.860.78250
55hot_dog0.650.880.75250
56huevos_rancheros0.400.340.37250
57hummus0.700.360.48250
58ice_cream0.660.660.66250
59lasagna0.480.490.49250
60lobster_bisque0.770.670.71250
61lobster_roll_sandwich0.690.640.66250
62macaroni_and_cheese0.660.500.57250
63macarons0.970.800.88250
64miso_soup0.740.870.80250
65mussels0.880.810.84250
66nachos0.750.230.35250
67omelette0.410.430.42250
68onion_rings0.730.880.80250
69oysters0.830.850.84250
70pad_thai0.740.800.77250
71paella0.860.450.59250
72pancakes0.660.620.64250
73panna_cotta0.560.440.49250
74peking_duck0.580.590.59250
75pho0.830.880.85250
76pizza0.640.900.75250
77pork_chop0.320.300.31250
78poutine0.610.680.65250
79prime_rib0.450.820.58250
80pulled_pork_sandwich0.620.520.56250
81ramen0.480.770.59250
82ravioli0.360.290.32250
83red_velvet_cake0.690.650.67250
84risotto0.430.370.40250
85samosa0.480.660.55250
86sashimi0.880.770.82250
87scallops0.370.330.35250
88seaweed_salad0.910.770.83250
89shrimp_and_grits0.440.430.43250
90spaghetti_bolognese0.820.560.66250
91spaghetti_carbonara0.660.960.78250
92spring_rolls0.610.640.63250
93steak0.320.290.31250
94strawberry_shortcake0.540.580.56250
95sushi0.740.510.61250
96tacos0.690.320.44250
97takoyaki0.750.570.65250
98tiramisu0.420.470.45250
99tuna_tartare0.360.350.35250
100waffles0.790.650.71250
___________________________________________________________
accuracy0.5925250
macro avg0.620.590.5925250
weighted avg0.620.590.5925250
# visualizing the F1 scores per class
classification_report_dict = classification_report(y_true=y_labels,
y_pred=predicted_classes,
output_dict=True)
# {'0': {'precision': 0.36257309941520466,
# 'recall': 0.248,
# 'f1-score': 0.29453681710213775,
# 'support': 250},
# '1': {'precision': 0.6060606060606061,
# 'recall': 0.64,
# 'f1-score': 0.622568093385214,
# 'support': 250},
# ...


# extract f1-scores from dictionary
class_f1_scores = {}

## loop through classification report
for k, v in classification_report_dict.items():
# stop when you reach end of table => class# = accuracy
if k == "accuracy":
break
else:
# get class name and f1 score for class #
class_f1_scores[class_names_101_10[int(k)]] = v["f1-score"]

# print(class_f1_scores)

# {'apple_pie': 0.29453681710213775,
# 'baby_back_ribs': 0.622568093385214,
# 'baklava': 0.6008771929824562,
# ...

# write it into a dataframe
f1_scores = pd.DataFrame({"classname": list(class_f1_scores.keys()),
"f1-score": list(class_f1_scores.values())}).sort_values("f1-score", ascending=False)

print(f1_scores)
class #classnamef1-score
33edamame0.958904
63macarons0.877729
75pho0.854369
65mussels0.844075
69oysters0.837945
........
82ravioli0.320713
77pork_chop0.309623
93steak0.307368
0apple_pie0.294537
39foie_gras0.228070
f1_bar_chart = f1_scores.plot.bar(x='classname',
y='f1-score',
title="F1 Scores vs Class Names",
rot=70, legend=True,
figsize=(42,12))

Transfer Learning

f1_scores_inverse = f1_scores.sort_values(by=['f1-score'])
f1_bar_chart = f1_scores_inverse.plot.barh(x='classname',
y='f1-score', fontsize=16,
title="F1 Scores vs Class Names",
rot=0, legend=True,
figsize=(12,36))

Transfer Learning

Run Predictions

# load and preprocess custom images
def load_and_preprocess_image(filename, img_shape=224, normalize=True):
# load image
image = tf.io.read_file(filename)

# decode image into tensor
image = tf.io.decode_image(image)

# print(image.shape)
# resize image
# image = tf.image.resize(image, [img_shape[0], img_shape[1]])
image = tf.image.resize(image, [img_shape, img_shape])
# print(image.shape)
# models like efficientnet don't
# need normalization -> make it optional
if normalize:
return image/255
else:
return image
# test prediction
file_path = "../datasets/101_food_classes_10_percent/train/caesar_salad/621878.jpg"
# load and preprocess images
test_image = load_and_preprocess_image(file_path, img_shape=224, normalize=False)
# test image is (224, 224, 3) but model expects batch shape (None, 224, 224, 3)
test_image_expanded = tf.expand_dims(test_image, axis=0)
# get probabilities over all classes
prediction_probabilities = model.predict(test_image_expanded)
print(prediction_probabilities)
# get classname for highest probability
predicted_class = class_names_101_10[prediction_probabilities.argmax()]
print(prediction_probabilities.argmax())
print(predicted_class)

# 1/1 [==============================] - 2s 2s/step
# [[1.50316203e-06 6.54247488e-05 3.67346947e-05 2.69303378e-03
# 6.89498498e-04 1.87405187e-03 5.24590746e-07 9.33061761e-04
# 8.65595939e-06 3.25173722e-04 5.10892831e-04 3.65509897e-01
# 5.99349778e-06 7.83709735e-02 2.39862379e-06 5.81788830e-03
# 9.84990475e-05 3.48087779e-05 1.90171588e-04 1.23677682e-02
# 6.14877135e-05 2.40283384e-08 2.12790405e-06 1.13999391e-07
# 5.72356976e-06 7.92978518e-03 9.47913341e-03 7.58256647e-07
# 1.09906327e-02 7.71022357e-08 2.06695331e-05 5.95029064e-07
# 2.49674427e-04 6.92231743e-06 2.87168048e-04 3.15096986e-05
# 4.69330046e-03 6.78624609e-04 7.83884199e-04 1.68355810e-03
# 1.82755193e-05 9.03009493e-07 2.82402652e-05 1.71108084e-04
# 8.81860105e-05 3.58489638e-06 3.93012015e-05 4.95287916e-03
# 4.10611063e-01 2.75604805e-04 5.55841299e-03 1.80046377e-03
# 6.12967357e-04 1.32623117e-03 3.72738782e-07 7.42003205e-04
# 6.33771438e-03 1.89700077e-04 1.42778049e-06 1.78189657e-04
# 2.83671682e-08 5.77349402e-03 1.16270230e-05 1.74992886e-06
# 2.15548107e-06 1.77174807e-05 2.03449815e-03 7.82472896e-04
# 3.88388798e-07 1.51169850e-04 2.83787904e-05 9.07634239e-06
# 3.15053308e-06 2.30283586e-05 1.42191842e-04 3.49449765e-05
# 2.96340950e-05 3.22835840e-05 3.79087487e-06 3.55910415e-05
# 6.37637422e-05 1.73983644e-04 5.40133740e-04 6.14976784e-07
# 1.03683116e-04 9.38189216e-04 1.80834774e-02 7.08847656e-04
# 3.19155771e-03 3.94216222e-05 6.57606563e-07 1.21063601e-06
# 9.14987270e-03 7.52260457e-05 2.20976843e-04 2.03661504e-03
# 1.35101350e-02 1.15356571e-03 1.52835700e-08 1.37943309e-03
# 1.31967667e-04]]
# 48
# greek_salad

# run prediction on random test images
location = testing_directory

plt.figure(figsize=(17, 15))
# pick random test images in random class
for i in range(9):
# get file paths
class_name = random.choice(class_names_101_10)
file_name = random.choice(os.listdir(location + "/" + class_name))
file_path = location + "/" + class_name + "/" + file_name

# load and preprocess images
test_image = load_and_preprocess_image(file_path, img_shape=224, normalize=False)
# test image is (224, 224, 3) but model expects batch shape (None, 224, 224, 3)
test_image_expanded = tf.expand_dims(test_image, axis=0)
# get probabilities over all classes
prediction_probabilities = model.predict(test_image_expanded)
# get classname for highest probability
predicted_class = class_names_101_10[prediction_probabilities.argmax()]
plt.subplot(3, 3, i+1)
# show normalized image
plt.imshow(test_image/255.)
if class_name == predicted_class:
title_color = 'green'
else:
title_color = 'red'
plt.title(f"Pred: {predicted_class} ({prediction_probabilities.max()*100:.2f} %), True: {class_name}",
c=title_color)
plt.axis(False)

Transfer Learning

Find most Wrong Predictions

# get all images in the test dataset
test_files = testing_directory + "/*/*.jpg"
test_file_paths = []
for file_path in testing_data_101_10.list_files(test_files, shuffle=False):
test_file_paths.append(file_path.numpy())

print(test_file_paths[:1])
# [b'../datasets/101_food_classes_10_percent/test/apple_pie/1011328.jpg']
# create dataframe with filepaths and pred evals
prediction_quality = pd.DataFrame({"img_path": test_file_paths,
"y_true": y_labels,
"y_pred": predicted_classes,
"pred_conf": test_prediction_probabilities.max(axis=1),
"y_true_classname": [class_names_101_10[i] for i in y_labels],
"y_pred_classname": [class_names_101_10[i] for i in predicted_classes]})

prediction_quality

# the "most wrong" predictions are those where the the predicted and
# true classname does not match but the prediction confidence is high:
#img_pathy_truey_predpred_confy_true_classnamey_pred_classname
0b'../datasets/101_food_classes_10_percent/test...0850.498493apple_piesamosa
1b'../datasets/101_food_classes_10_percent/test...000.821453apple_pieapple_pie
2b'../datasets/101_food_classes_10_percent/test...000.413805apple_pieapple_pie
3b'../datasets/101_food_classes_10_percent/test...000.242082apple_pieapple_pie
4b'../datasets/101_food_classes_10_percent/test...080.625819apple_piebread_pudding
.....................
25245b'../datasets/101_food_classes_10_percent/test...1001000.860823waffleswaffles
25246b'../datasets/101_food_classes_10_percent/test...1001000.969415waffleswaffles
25247b'../datasets/101_food_classes_10_percent/test...100740.259333wafflespeking_duck
25248b'../datasets/101_food_classes_10_percent/test...1001000.266893waffleswaffles
25249b'../datasets/101_food_classes_10_percent/test...1001000.248541waffleswaffles
# add bool comlumn for correct predictions
prediction_quality["pred_correct"] = prediction_quality["y_true"] == prediction_quality["y_pred"]
# create new dataframe with the 100 most wrong predictions
top_100_wrong = prediction_quality[prediction_quality["pred_correct"] == False].sort_values("pred_conf", ascending=False)[:100]
top_100_wrong
#img_pathy_truey_predpred_confy_true_classnamey_pred_classnamepred_correct
23797b'../datasets/101_food_classes_10_percent/test...95860.997811sushisashimiFalse
10880b'../datasets/101_food_classes_10_percent/test...43680.997761fried_calamarionion_ringsFalse
14482b'../datasets/101_food_classes_10_percent/test...57510.997528hummusguacamoleFalse
17897b'../datasets/101_food_classes_10_percent/test...71650.996058paellamusselsFalse
18001b'../datasets/101_food_classes_10_percent/test...72670.995309pancakesomeletteFalse
........................
13199b'../datasets/101_food_classes_10_percent/test...5290.947564gyozabreakfast_burritoFalse
20551b'../datasets/101_food_classes_10_percent/test...82830.947554raviolired_velvet_cakeFalse
5114b'../datasets/101_food_classes_10_percent/test...20380.947265chicken_wingsfish_and_chipsFalse
548b'../datasets/101_food_classes_10_percent/test...2670.947076baklavaomeletteFalse
15750b'../datasets/101_food_classes_10_percent/test...63290.946876macaronscup_cakesFalse

# what predictions are most often wrong
grouped_top_100_wrong_pred = top_100_wrong.groupby(['y_pred', 'y_pred_classname']).agg(', '.join).reset_index()
grouped_top_100_wrong_pred
#y_predy_pred_classnamey_true_classname
02baklavagarlic_bread
15beet_saladseaweed_salad
29breakfast_burritochicken_quesadilla, pulled_pork_sandwich, chic...
312cannolibaklava
416cheese_platecheesecake
520chicken_wingsfish_and_chips, french_fries
626crab_cakesfalafel
727creme_bruleehummus
828croque_madamepoutine
929cup_cakesmacarons, carrot_cake, macarons
1031donutsescargots
1132dumplingsgyoza, macaroni_and_cheese
1233edamamepho
1338fish_and_chipschicken_wings
1440french_friesmacaroni_and_cheese, poutine
1542french_toastwaffles
1645frozen_yogurtcreme_brulee
1746garlic_breadgrilled_cheese_sandwich, grilled_cheese_sandwich
1847gnocchirisotto, hummus
1948greek_saladceviche
2051guacamolehummus, fried_rice, tuna_tartare, risotto, hum...
2153hamburgerpulled_pork_sandwich, eggs_benedict
2254hot_and_sour_soupwaffles, french_onion_soup, french_onion_soup
2358ice_creamfrozen_yogurt
2464miso_soupcheesecake, scallops, lobster_bisque
2565musselspaella, paella
2667omelettepancakes, baklava
2768onion_ringsfried_calamari, fried_calamari, fried_calamari...
2874peking_duckbeef_carpaccio
2975phoramen, hot_and_sour_soup
3076pizzachicken_quesadilla, french_onion_soup, hummus,...
3178poutinepeking_duck, takoyaki, spaghetti_bolognese, fr...
3279prime_ribfilet_mignon
3381ramenmiso_soup, chicken_curry
3483red_velvet_cakecup_cakes, ravioli
3585samosapeking_duck, spring_rolls, baklava
3686sashimisushi , sushi
3791spaghetti_carbonaraspaghetti_bolognese, spaghetti_bolognese, ravi...
3892spring_rollssamosa

# what classes cause the most wrong predictions
grouped_top_100_wrong_cause = top_100_wrong.groupby(['y_true', 'y_true_classname']).agg(', '.join).reset_index()
grouped_top_100_wrong_cause
#y_truey_true_classnamey_pred_classname
02baklavacannoli, samosa, omelette
13beef_carpacciopeking_duck
214carrot_cakecup_cakes
315cevichegreek_salad
417cheesecakemiso_soup, cheese_plate
518chicken_curryramen
619chicken_quesadillabreakfast_burrito, breakfast_burrito, pizza, g...
720chicken_wingsfish_and_chips
823churrosonion_rings, onion_rings, onion_rings
926crab_cakesspaghetti_carbonara
1027creme_bruleefrozen_yogurt
1129cup_cakesred_velvet_cake
1234eggs_benedicthamburger
1335escargotsdonuts
1436falafelcrab_cakes, breakfast_burrito
1537filet_mignonprime_rib
1638fish_and_chipschicken_wings, breakfast_burrito
1740french_frieschicken_wings, poutine
1841french_onion_souphot_and_sour_soup, pizza, hot_and_sour_soup
1943fried_calamarionion_rings, onion_rings, onion_rings, onion_r...
2044fried_riceguacamole
2145frozen_yogurtice_cream
2246garlic_breadbaklava
2349grilled_cheese_sandwichgarlic_bread, garlic_bread
2452gyozadumplings, breakfast_burrito, breakfast_burrito
2554hot_and_sour_souppho
2656huevos_rancherosbreakfast_burrito, breakfast_burrito
2757hummusguacamole, creme_brulee, guacamole, pizza, gno...
2859lasagnapizza
2960lobster_bisquemiso_soup
3062macaroni_and_cheesefrench_fries, dumplings
3163macaronscup_cakes, cup_cakes
3264miso_soupramen
3367omelettespaghetti_carbonara
3471paellamussels, mussels
3572pancakesomelette
3674peking_ducksamosa, poutine
3775phoedamame
3878poutinecroque_madame, french_fries
3980pulled_pork_sandwichbreakfast_burrito, hamburger
4081ramenpho
4182raviolispaghetti_carbonara, spaghetti_carbonara, spag...
4284risottoguacamole, gnocchi
4385samosaspring_rolls
4487scallopsmiso_soup
4588seaweed_saladbeet_salad
4690spaghetti_bolognesespaghetti_carbonara, spaghetti_carbonara, spag...
4792spring_rollssamosa
4895sushisashimi, sashimi
4996tacosbreakfast_burrito
5097takoyakipoutine
5199tuna_tartareguacamole
52100wafflesfrench_toast, hot_and_sour_soup
# visualize test images
images_displayed = 12
start_index = 0
plt.figure(figsize=(15, 15))
# create tuples from each df row
for i, row in enumerate(top_100_wrong[start_index:start_index+images_displayed].itertuples()):
plt.subplot(4, 3, i+1)
# row[1] => 2nd cell in row = image path
img = load_and_preprocess_image(row[1], normalize=False)
# extract confidence and labels from row
_, _, _, _, pred_conf, y_true_classname, y_pred_classname, _ = row
plt.imshow(img/255.)
plt.title(f"Pred: {y_pred_classname} ({pred_conf*100:.2f} %),\nTrue: {y_true_classname}")
plt.axis(False)

Transfer Learning

Predict Custom Images

# get list of custom image file paths
custom_images_path = "../datasets/custom_images/"
custom_images = [ custom_images_path + img_path for img_path in os.listdir(custom_images_path)]
custom_images

# ['../datasets/custom_images/cheesecake.jpg',
# '../datasets/custom_images/crema_catalana.jpg',
# '../datasets/custom_images/fish_and_chips.jpg',
# '../datasets/custom_images/jiaozi.jpg',
# '../datasets/custom_images/paella.jpg',
# '../datasets/custom_images/pho.jpg',
# '../datasets/custom_images/quesadilla.jpg',
# '../datasets/custom_images/ravioli.jpg',
# '../datasets/custom_images/waffles.jpg']
# run prediction on custom images
for image in custom_images:
image = load_and_preprocess_image(image, normalize=False)
# test image is (224, 224, 3) but model expects batch shape (None, 224, 224, 3)
image_expanded = tf.expand_dims(image, axis=0)
# get probabilities over all classes
prediction_probabilities = model.predict(image_expanded)
# get classname for highest probability
predicted_class = class_names_101_10[prediction_probabilities.argmax()]
# plot normalized image
plt.figure()
plt.imshow(image/255.)
plt.title(f"Pred: {predicted_class} ({prediction_probabilities.max()*100:.2f} %)")
plt.axis(False)

Transfer Learning