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
In [31]:
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)Out [31]:
In [30]:
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
tf.config.get_visible_devices()Out [30]:
In [32]:
import gc
gc.collect()Out [32]:
Helpers
Get some helper functions
In [33]:
# !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:
In [3]:
# Download data from Google Storage (already preformatted)
# !wget https://storage.googleapis.com/ztm_tf_course/food_vision/101_food_classes_10_percent.zip In [4]:
# unzip_data("101_food_classes_10_percent.zip")In [5]:
# to get a brief "inspection" of the data
# walk_through_dir("101_food_classes_10_percent")Build Train & Test Vars
In [34]:
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
In [27]:
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
In [28]:
# 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
In [29]:
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
In [10]:
base_model = tf.keras.applications.efficientnet.EfficientNetB0(include_top=False)
base_model.trainable = FalseIn [11]:
howManyClasses = len(testingData10p.class_names)
howManyClassesOut [11]:
Build
In [12]:
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)In [13]:
m0.summary()Compile & Fit
In [14]:
m0.compile(loss="categorical_crossentropy",
optimizer=tf.keras.optimizers.Adam(), # use Adam with default settings
metrics=["accuracy"])In [15]:
howManyValidationSteps = int(0.15 * len(testingData10p))
howManyValidationStepsOut [15]:
In [16]:
m0History = 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
In [17]:
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
In [32]:
base_model.trainable = TrueIn [33]:
# RE-Freeze all-but-5 layers
for layer in base_model.layers[:-5]:
layer.trainable = FalseRe-Compile
In [34]:
m0.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(1e-4), # 10x lower learning rate than default
metrics=['accuracy'])In [35]:
for idx, layer in enumerate(m0.layers):
if(layer.trainable == True):
print(f'layer idx {idx}: {layer.name} is trainable')In [ ]:
for idx, layer in enumerate(base_model.layers):
if(layer.trainable == True):
print(f'layer idx {idx}: {layer.name} is trainable')Fine-Tune & Fit
In [40]:
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
In [41]:
compare_historys(original_history=m0History,
new_history=m0FineTunedHistory,
initial_epochs=5)In [46]:
m0Loss, m0Accuracy = m0.evaluate(testingData10p)In [47]:
m0Loss, m0AccuracyOut [47]:
Saving The Model
In [42]:
m0.save('food101-10p-transfer-EfficientNetB0')Load The Model
In [44]:
loadedModel = tf.keras.models.load_model('food101-10p-transfer-EfficientNetB0')In [45]:
# Check to see if loaded model is a trained model
loaded_loss, loaded_accuracy = loadedModel.evaluate(testingData10p)
loaded_loss, loaded_accuracyOut [45]:
Make Predictions
Here, pass the testing dataset to the model to make predictions on each testing image
In [48]:
m0PredictionProbabilities = m0.predict(testingData10p, verbose=1)In [49]:
len(m0PredictionProbabilities)Out [49]:
In [51]:
# should be (numberOfImages, numberOfClasses)
m0PredictionProbabilities.shapeOut [51]:
In [52]:
# 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()}")In [54]:
# 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]Out [54]:
Get Labels From test data
In [55]:
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)Out [55]:
In [56]:
len(testDataLabels)Out [56]: