Transfer Learning With 101 Food Classes Notebook
Train and evaluate transfer learning models against a 101-class food image classification problem.
Summary
There is a paper called "Food 101 Mining Discriminative Components with Random Forest", whcih describes image classification with 1010 classes of food. That paper and that idea is the launching pad of this notebook. That paper dsicusses how the average accuracy in the model developed there is 50.76%. This notebook is going to try to surpa
Notebook Highlights
Transfer Learning: 101 Classes
There is a paper called "Food 101 Mining Discriminative Components with Random Forest", whcih describes image classification with 1010 classes of food. That paper and that idea is the launching pad of this notebook. T...
Helpers
Get some helper functions
Get The Data
Getting 10% of the data to start experimenting with modeling:
Make Predictions
Here, pass the testing dataset to the model to make predictions on each testing image
Table Of Contents
- Transfer-Learning: 101 Classes
- Notebook goals
- Prep
- Imports
- Helpers
- Get The Data
- Build Train & Test Vars
- Convert Images & Labels Into tensorflow datasets
- Build A Checkpoint Callback
- Build Data-Augmenter
- Model I
- Base Model: EfficientNetB0
- Build
- Compile & Fit
- Analysis
- Model II: Fine-Tuning Model I
- Un-Freeze Layers
- Re-Compile
- Fine-Tune & Fit
- Analysis
- Saving The Model
- Load The Model
- Make Predictions
- Get Labels From test data
Transfer-Learning: 101 Classes
There is a paper called "Food-101 - Mining Discriminative Components with Random Forest", whcih describes image-classification with 1010 classes of food.
That paper and that idea is the launching pad of this notebook.
That paper dsicusses how the average accuracy in the model developed there is 50.76%.
This notebook is going to try to surpass that accuracy score.
Here's another notebook to check out about dealing with the images from the food101 dataset.
Notebook goals
Build a model that surpasses the accuracy score found in the paper above:
- use the food101 dataset (can be found here)
- Experiment with building "feature-extraction" transfer-learning models
- Fine-Tune a feature-extraction model
- Evaluate The performance of the model
- Save the trained model
Prep
Imports
import tensorflow as tf
cifar = tf.keras.datasets.cifar100
(x_train, y_train), (x_test, y_test) = cifar.load_data()
model = tf.keras.applications.ResNet50(
include_top=True,
weights=None,
input_shape=(32, 32, 3),
classes=100,)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)
model.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"])
model.fit(x_train, y_train, epochs=5, batch_size=64)import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
tf.config.get_visible_devices()import gc
gc.collect()Helpers
Get some helper functions
# !wget https://raw.githubusercontent.com/mrdbourke/tensorflow-deep-learning/main/extras/helper_functions.py
from helper_functions import create_tensorboard_callback, plot_loss_curves, unzip_data, compare_historys, walk_through_dirGet The Data
Getting 10% of the data to start experimenting with modeling:
# Download data from Google Storage (already preformatted)
# !wget https://storage.googleapis.com/ztm_tf_course/food_vision/101_food_classes_10_percent.zip # unzip_data("101_food_classes_10_percent.zip")# to get a brief "inspection" of the data
# walk_through_dir("101_food_classes_10_percent")Build Train & Test Vars
imagesDirPath = "101_food_classes_10_percent/"
trainDirPath = imagesDirPath + 'train/'
testDirPath = imagesDirPath + 'test/'
print(f'TRAIN: {trainDirPath} \n TEST: {testDirPath}')Convert Images & Labels Into tensorflow datasets
OUTPUT_IMG_SIZE = (224,224)
trainingData10P = tf.keras.preprocessing.image_dataset_from_directory(trainDirPath,
label_mode="categorical",
image_size=OUTPUT_IMG_SIZE)
testingData10p = tf.keras.preprocessing.image_dataset_from_directory(testDirPath,
label_mode="categorical",
image_size=OUTPUT_IMG_SIZE,
shuffle=False) # don't shuffle test data for prediction analysisBuild A Checkpoint Callback
# Create checkpoint callback to save model for later use
checkpoint_path = "101_classes_10_percent_data_model_checkpoint"
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,
save_weights_only=True, # save only the model weights
monitor="val_accuracy", # save the model weights which score the best validation accuracy
save_best_only=True) # only keep the best model weights on file (delete the rest)Build Data-Augmenter
dataAugLayer = Sequential([
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.2),
layers.RandomZoom(0.2),
layers.RandomHeight(0.2),
layers.RandomWidth(0.2),
# preprocessing.Rescaling(1./255) # keep for ResNet50V2, remove for EfficientNetB0
], name ="data_augmentation")Model I
Base Model: EfficientNetB0
base_model = tf.keras.applications.efficientnet.EfficientNetB0(include_top=False)
base_model.trainable = FalsehowManyClasses = len(testingData10p.class_names)
howManyClassesBuild
m0Inputs = layers.Input(shape=(224, 224, 3), name="input_layer") # shape of input image
m0Applied = dataAugLayer(m0Inputs) # augment images (only happens during training)
m0Applied = base_model(m0Applied, training=False) # put the base model in inference mode so we can use it to extract features without updating the weights
m0Applied = layers.GlobalAveragePooling2D(name="global_average_pooling")(m0Applied) # pool the outputs of the base model
m0Outputs = layers.Dense(howManyClasses, activation="softmax", name="output_layer")(m0Applied) # same number of outputs as classes
m0 = tf.keras.Model(m0Inputs, m0Outputs)m0.summary()Compile & Fit
m0.compile(loss="categorical_crossentropy",
optimizer=tf.keras.optimizers.Adam(), # use Adam with default settings
metrics=["accuracy"])howManyValidationSteps = int(0.15 * len(testingData10p))
howManyValidationStepsm0History = m0.fit(trainingData10P,
epochs=5, # fit for 5 epochs to keep experiments quick
validation_data=testingData10p,
validation_steps=int(0.15 * len(testingData10p)), # evaluate on smaller portion of test data
callbacks=[checkpoint_callback])Analysis
plot_loss_curves(m0History)- the loss curves indicate there might be som "overfitting"
- when the loss decreases in the TRAINING data but does not decrease in the VALIDATION data, the model is getting "better" at the training data and not getting better at the validation data
Model II: Fine-Tuning Model I
Un-Freeze Layers
base_model.trainable = True# RE-Freeze all-but-5 layers
for layer in base_model.layers[:-5]:
layer.trainable = FalseRe-Compile
m0.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(1e-4), # 10x lower learning rate than default
metrics=['accuracy'])for idx, layer in enumerate(m0.layers):
if(layer.trainable == True):
print(f'layer idx {idx}: {layer.name} is trainable')for idx, layer in enumerate(base_model.layers):
if(layer.trainable == True):
print(f'layer idx {idx}: {layer.name} is trainable')Fine-Tune & Fit
fineTinedEpochCount = 10
m0FineTunedHistory = m0.fit(trainingData10P,
epochs=fineTinedEpochCount,
validation_data=testingData10p,
validation_steps=int(0.15 * len(testingData10p)), # validate on 15% of the test data
initial_epoch=m0History.epoch[-1]) # start from previous last epochAnalysis
compare_historys(original_history=m0History,
new_history=m0FineTunedHistory,
initial_epochs=5)m0Loss, m0Accuracy = m0.evaluate(testingData10p)m0Loss, m0AccuracySaving The Model
m0.save('food101-10p-transfer-EfficientNetB0')Load The Model
loadedModel = tf.keras.models.load_model('food101-10p-transfer-EfficientNetB0')# Check to see if loaded model is a trained model
loaded_loss, loaded_accuracy = loadedModel.evaluate(testingData10p)
loaded_loss, loaded_accuracyMake Predictions
Here, pass the testing dataset to the model to make predictions on each testing image
m0PredictionProbabilities = m0.predict(testingData10p, verbose=1)len(m0PredictionProbabilities)# should be (numberOfImages, numberOfClasses)
m0PredictionProbabilities.shape# We get one prediction probability per class
print(f"Number of prediction probabilities for sample 0: {len(m0PredictionProbabilities[0])}")
print(f"What prediction probability sample 0 looks like:\n {m0PredictionProbabilities[0]}")
print(f"The class with the highest predicted probability by the model for sample 0: {m0PredictionProbabilities[0].argmax()}")# Get JUST THE CLASS NUMBER (0-100) of each label of each prediction
pred_classes = m0PredictionProbabilities.argmax(axis=1)
# How do they look?
pred_classes[:10]Get Labels From test data
testDataLabels = []
for images, labels in testingData10p.unbatch(): # unbatch the test data and get images and labels
testDataLabels.append(labels.numpy().argmax()) # append the index which has the largest value (labels are one-hot)
testDataLabels[:10] # check what they look like (unshuffled)len(testDataLabels)