In this notebook, I am going to apply end-to-end transfer learning on EuroSat data with MobileNetV2 architecture.
Eurosat is a dataset and deep learning benchmark for land use and land cover classification. The dataset is based on Sentinel-2 satellite images covering 13 spectral bands and consisting out of 10 classes with in total 27,000 labeled and geo-referenced images. You can find out more details about dataset here
import matplotlib.pyplot as plt
import numpy as np
import itertools
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.preprocessing import image_dataset_from_directory
from tensorflow.keras.models import Model
from tensorflow.keras.layers.experimental.preprocessing import RandomFlip, RandomRotation
When training and evaluating deep learning models in Keras, generating a dataset from image files stored on disk is simple and fast. Call image_data_set_from_directory()
to read from the directory and create both training and validation datasets.
You'll also set your seeds to match each other, so your training and validation sets don't overlap.
directory = '2750'
batch_size = 32
image_size = (64,64)
train_ds, val_ds = image_dataset_from_directory(directory,
image_size=image_size,
batch_size=batch_size,
seed=101,
shuffle =True,
validation_split=0.2,
subset="both")
Found 27000 files belonging to 10 classes. Using 21600 files for training. Using 5400 files for validation.
class_names = train_ds.class_names
plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
Using prefetch()
prevents a memory bottleneck that can occur when reading from disk. It sets aside some data and keeps it ready for when it's needed, by creating a source dataset from your input data, applying a transformation to preprocess it, then iterating over the dataset one element at a time. Because the iteration is streaming, the data doesn't need to fit into memory.
train_ds = train_ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
val_ds = val_ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
To increase diversity in the training set and help your model learn the data better, it's standard practice to augment the images by transforming them, i.e., randomly flipping and rotating them. Keras' Sequential API offers a straightforward method for these kinds of data augmentations, with built-in, customizable preprocessing layers. These layers are saved with the rest of your model and can be re-used later.
def data_augmenter():
data_augmentation = tf.keras.Sequential()
data_augmentation.add(RandomFlip('horizontal'))
data_augmentation.add(RandomRotation(0.2))
return data_augmentation
MobileNetV2 was trained on ImageNet and is optimized to run on mobile and other low-power applications. It's 155 layers deep and very efficient for object detection and image segmentation tasks, as well as classification tasks like this one. The architecture has three defining characteristics:
Key things to remember while using transfer learning:
Delete the top layer (the classification layer)
include_top
in base_model
as False Add a new classifier layer
Freeze the base model and train the newly-created classifier layer
base model.trainable=False
to avoid changing the weights and train only the new layerbase_model
to False to avoid keeping track of statistics in the batch norm layerdef Euro_model(image_shape = image_size, classes = None, data_augmentation=data_augmenter()):
input_shape = image_shape + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=input_shape,
include_top=False, # <== Important!!!!
weights="imagenet")
base_model.trainable = False
X_inputs = tf.keras.Input(shape=input_shape)
X = tf.keras.layers.Rescaling(1.0/255)(X_inputs)
X = data_augmentation(X)
X= base_model(X, training = False)
X = tf.keras.layers.AveragePooling2D((2, 2))(X)
X = tf.keras.layers.Dropout(0.4)(X)
X = tf.keras.layers.Flatten()(X)
X = tf.keras.layers.Dense(classes, activation='softmax')(X)
model = Model(inputs = X_inputs, outputs = X)
return model
model = Euro_model(image_size, 10)
WARNING:tensorflow:`input_shape` is undefined or non-square, or `rows` is not in [96, 128, 160, 192, 224]. Weights for input shape (224, 224) will be loaded as the default.
model.summary()
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_2 (InputLayer) [(None, 64, 64, 3)] 0 rescaling (Rescaling) (None, 64, 64, 3) 0 sequential (Sequential) (None, 64, 64, 3) 0 mobilenetv2_1.00_224 (Funct (None, 2, 2, 1280) 2257984 ional) average_pooling2d (AverageP (None, 1, 1, 1280) 0 ooling2D) dropout (Dropout) (None, 1, 1, 1280) 0 flatten (Flatten) (None, 1280) 0 dense (Dense) (None, 10) 12810 ================================================================= Total params: 2,270,794 Trainable params: 12,810 Non-trainable params: 2,257,984 _________________________________________________________________
model.compile(optimizer=keras.optimizers.Adam(1e-3),loss="sparse_categorical_crossentropy",
metrics=["sparse_categorical_accuracy"])
epochs = 20
history = model.fit(train_ds,epochs=epochs,validation_data=val_ds)
Epoch 1/20 WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op. 675/675 [==============================] - 131s 178ms/step - loss: 1.1192 - sparse_categorical_accuracy: 0.6386 - val_loss: 0.6521 - val_sparse_categorical_accuracy: 0.7820 Epoch 2/20 675/675 [==============================] - 117s 173ms/step - loss: 0.7806 - sparse_categorical_accuracy: 0.7396 - val_loss: 0.5880 - val_sparse_categorical_accuracy: 0.8037 Epoch 3/20 675/675 [==============================] - 117s 173ms/step - loss: 0.7372 - sparse_categorical_accuracy: 0.7510 - val_loss: 0.5947 - val_sparse_categorical_accuracy: 0.7989 Epoch 4/20 675/675 [==============================] - 116s 172ms/step - loss: 0.7257 - sparse_categorical_accuracy: 0.7580 - val_loss: 0.5938 - val_sparse_categorical_accuracy: 0.7976 Epoch 5/20 675/675 [==============================] - 116s 172ms/step - loss: 0.7234 - sparse_categorical_accuracy: 0.7546 - val_loss: 0.5621 - val_sparse_categorical_accuracy: 0.8157 Epoch 6/20 675/675 [==============================] - 116s 172ms/step - loss: 0.7230 - sparse_categorical_accuracy: 0.7567 - val_loss: 0.5667 - val_sparse_categorical_accuracy: 0.8135 Epoch 7/20 675/675 [==============================] - 117s 173ms/step - loss: 0.7244 - sparse_categorical_accuracy: 0.7573 - val_loss: 0.5601 - val_sparse_categorical_accuracy: 0.8146 Epoch 8/20 675/675 [==============================] - 117s 173ms/step - loss: 0.7168 - sparse_categorical_accuracy: 0.7593 - val_loss: 0.5805 - val_sparse_categorical_accuracy: 0.8106 Epoch 9/20 675/675 [==============================] - 111s 165ms/step - loss: 0.7230 - sparse_categorical_accuracy: 0.7609 - val_loss: 0.5579 - val_sparse_categorical_accuracy: 0.8148 Epoch 10/20 675/675 [==============================] - 111s 164ms/step - loss: 0.7263 - sparse_categorical_accuracy: 0.7575 - val_loss: 0.5685 - val_sparse_categorical_accuracy: 0.8167 Epoch 11/20 675/675 [==============================] - 111s 164ms/step - loss: 0.7126 - sparse_categorical_accuracy: 0.7603 - val_loss: 0.5759 - val_sparse_categorical_accuracy: 0.8156 Epoch 12/20 675/675 [==============================] - 112s 165ms/step - loss: 0.7229 - sparse_categorical_accuracy: 0.7620 - val_loss: 0.5565 - val_sparse_categorical_accuracy: 0.8204 Epoch 13/20 675/675 [==============================] - 111s 165ms/step - loss: 0.7201 - sparse_categorical_accuracy: 0.7590 - val_loss: 0.5541 - val_sparse_categorical_accuracy: 0.8209 Epoch 14/20 675/675 [==============================] - 111s 165ms/step - loss: 0.7076 - sparse_categorical_accuracy: 0.7640 - val_loss: 0.5709 - val_sparse_categorical_accuracy: 0.8135 Epoch 15/20 675/675 [==============================] - 112s 165ms/step - loss: 0.7121 - sparse_categorical_accuracy: 0.7618 - val_loss: 0.5557 - val_sparse_categorical_accuracy: 0.8241 Epoch 16/20 675/675 [==============================] - 111s 164ms/step - loss: 0.7125 - sparse_categorical_accuracy: 0.7612 - val_loss: 0.5720 - val_sparse_categorical_accuracy: 0.8161 Epoch 17/20 675/675 [==============================] - 114s 168ms/step - loss: 0.7049 - sparse_categorical_accuracy: 0.7662 - val_loss: 0.5575 - val_sparse_categorical_accuracy: 0.8217 Epoch 18/20 675/675 [==============================] - 120s 178ms/step - loss: 0.7108 - sparse_categorical_accuracy: 0.7629 - val_loss: 0.5450 - val_sparse_categorical_accuracy: 0.8257 Epoch 19/20 675/675 [==============================] - 111s 165ms/step - loss: 0.7129 - sparse_categorical_accuracy: 0.7639 - val_loss: 0.5640 - val_sparse_categorical_accuracy: 0.8154 Epoch 20/20 675/675 [==============================] - 112s 166ms/step - loss: 0.7198 - sparse_categorical_accuracy: 0.7654 - val_loss: 0.5706 - val_sparse_categorical_accuracy: 0.8146
acc = [0.] + history.history['sparse_categorical_accuracy']
val_acc = [0.] + history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
plt.figure(figsize=(8, 8), dpi=200)
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.ylim([min(plt.ylim()),1])
plt.title('Training and Validation Accuracy')
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
With initial 20 epochs, the training accuracy and validation accuracy is 76.07% and 81.46% respectively, without any overfitting, but there is large avoidable bais ( as the original authors of paper has achieved 98% accuracy) which can be overcome by training the model for longer time and fine tuning the later layers.
To achieve this, just unfreeze the final layers and re-run the optimizer with a smaller learning rate, while keeping all the other layers frozen. The important takeaway is that the later layers are the part of your network that contain the fine details that are more specific to problem.
First, unfreeze the base model by setting base_model.trainable=True
, set a layer to fine-tune from, then re-freeze all the layers before it. Run it again for another few epochs, and see if accuracy improved!
base_model = model.layers[3]
base_model.trainable = True
print("Number of layers in the base model: ", len(base_model.layers))
Number of layers in the base model: 154
fine_tune_at = 110
for layer in base_model.layers[:fine_tune_at]:
layer.trainable = None
model.summary()
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_2 (InputLayer) [(None, 64, 64, 3)] 0 rescaling (Rescaling) (None, 64, 64, 3) 0 sequential (Sequential) (None, 64, 64, 3) 0 mobilenetv2_1.00_224 (Funct (None, 2, 2, 1280) 2257984 ional) average_pooling2d (AverageP (None, 1, 1, 1280) 0 ooling2D) dropout (Dropout) (None, 1, 1, 1280) 0 flatten (Flatten) (None, 1280) 0 dense (Dense) (None, 10) 12810 ================================================================= Total params: 2,270,794 Trainable params: 1,755,978 Non-trainable params: 514,816 _________________________________________________________________
model.compile(optimizer=keras.optimizers.Adam(1e-4),loss="sparse_categorical_crossentropy",
metrics=["sparse_categorical_accuracy"])
fine_tune_epochs = 10
total_epochs = epochs + fine_tune_epochs
history_fine = model.fit(train_ds,
epochs=total_epochs,
initial_epoch=history.epoch[-1],
validation_data=val_ds)
Epoch 20/30 WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op. 675/675 [==============================] - 173s 241ms/step - loss: 0.7676 - sparse_categorical_accuracy: 0.7433 - val_loss: 0.3835 - val_sparse_categorical_accuracy: 0.8759 Epoch 21/30 675/675 [==============================] - 163s 241ms/step - loss: 0.4832 - sparse_categorical_accuracy: 0.8369 - val_loss: 0.3834 - val_sparse_categorical_accuracy: 0.8730 Epoch 22/30 675/675 [==============================] - 164s 242ms/step - loss: 0.4330 - sparse_categorical_accuracy: 0.8531 - val_loss: 0.3421 - val_sparse_categorical_accuracy: 0.8870 Epoch 23/30 675/675 [==============================] - 170s 251ms/step - loss: 0.3915 - sparse_categorical_accuracy: 0.8657 - val_loss: 0.3500 - val_sparse_categorical_accuracy: 0.8796 Epoch 24/30 675/675 [==============================] - 170s 251ms/step - loss: 0.3693 - sparse_categorical_accuracy: 0.8767 - val_loss: 0.3089 - val_sparse_categorical_accuracy: 0.8969 Epoch 25/30 675/675 [==============================] - 170s 251ms/step - loss: 0.3333 - sparse_categorical_accuracy: 0.8872 - val_loss: 0.2706 - val_sparse_categorical_accuracy: 0.9072 Epoch 26/30 675/675 [==============================] - 171s 253ms/step - loss: 0.3346 - sparse_categorical_accuracy: 0.8882 - val_loss: 0.3221 - val_sparse_categorical_accuracy: 0.8898 Epoch 27/30 675/675 [==============================] - 169s 250ms/step - loss: 0.3217 - sparse_categorical_accuracy: 0.8908 - val_loss: 0.2376 - val_sparse_categorical_accuracy: 0.9204 Epoch 28/30 675/675 [==============================] - 166s 246ms/step - loss: 0.3102 - sparse_categorical_accuracy: 0.8965 - val_loss: 0.3338 - val_sparse_categorical_accuracy: 0.8902 Epoch 29/30 675/675 [==============================] - 163s 242ms/step - loss: 0.3239 - sparse_categorical_accuracy: 0.8921 - val_loss: 0.2549 - val_sparse_categorical_accuracy: 0.9144 Epoch 30/30 675/675 [==============================] - 164s 243ms/step - loss: 0.2991 - sparse_categorical_accuracy: 0.9028 - val_loss: 0.3406 - val_sparse_categorical_accuracy: 0.8894
acc += history_fine.history['sparse_categorical_accuracy']
val_acc += history_fine.history['val_sparse_categorical_accuracy']
loss += history_fine.history['loss']
val_loss += history_fine.history['val_loss']
plt.figure(figsize=(8, 8), dpi = 200)
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0, 1])
plt.plot([epochs-1,epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([epochs-1,epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
At 30 epochs after fine tuning and decreasing the learning, the training accuracy and validation accuracy is 90.28% and 88.94% respectively, with about 1.5% overfitting, but there is still avoidable bais which I will try to decrease by training model for more 10 epochs at same learing rate.
fine_tune_epochs_2 = 10
total_epochs = epochs + fine_tune_epochs + fine_tune_epochs_2
history_fine_2 = model.fit(train_ds,
epochs=total_epochs,
initial_epoch=history_fine.epoch[-1],
validation_data=val_ds)
Epoch 30/40 675/675 [==============================] - 163s 241ms/step - loss: 0.2866 - sparse_categorical_accuracy: 0.9066 - val_loss: 0.2723 - val_sparse_categorical_accuracy: 0.9104 Epoch 31/40 675/675 [==============================] - 164s 243ms/step - loss: 0.2817 - sparse_categorical_accuracy: 0.9039 - val_loss: 0.3205 - val_sparse_categorical_accuracy: 0.8959 Epoch 32/40 675/675 [==============================] - 163s 241ms/step - loss: 0.2883 - sparse_categorical_accuracy: 0.9048 - val_loss: 0.2730 - val_sparse_categorical_accuracy: 0.9081 Epoch 33/40 675/675 [==============================] - 162s 240ms/step - loss: 0.2656 - sparse_categorical_accuracy: 0.9093 - val_loss: 0.2669 - val_sparse_categorical_accuracy: 0.9154 Epoch 34/40 675/675 [==============================] - 163s 242ms/step - loss: 0.2642 - sparse_categorical_accuracy: 0.9109 - val_loss: 0.2726 - val_sparse_categorical_accuracy: 0.9093 Epoch 35/40 675/675 [==============================] - 163s 241ms/step - loss: 0.2649 - sparse_categorical_accuracy: 0.9101 - val_loss: 0.2431 - val_sparse_categorical_accuracy: 0.9196 Epoch 36/40 675/675 [==============================] - 163s 242ms/step - loss: 0.2753 - sparse_categorical_accuracy: 0.9087 - val_loss: 0.2486 - val_sparse_categorical_accuracy: 0.9183 Epoch 37/40 675/675 [==============================] - 163s 241ms/step - loss: 0.2565 - sparse_categorical_accuracy: 0.9146 - val_loss: 0.2958 - val_sparse_categorical_accuracy: 0.9050 Epoch 38/40 675/675 [==============================] - 170s 252ms/step - loss: 0.2540 - sparse_categorical_accuracy: 0.9158 - val_loss: 0.2453 - val_sparse_categorical_accuracy: 0.9219 Epoch 39/40 675/675 [==============================] - 169s 250ms/step - loss: 0.2498 - sparse_categorical_accuracy: 0.9200 - val_loss: 0.2635 - val_sparse_categorical_accuracy: 0.9133 Epoch 40/40 675/675 [==============================] - 163s 242ms/step - loss: 0.2479 - sparse_categorical_accuracy: 0.9162 - val_loss: 0.2222 - val_sparse_categorical_accuracy: 0.9263
acc += history_fine_2.history['sparse_categorical_accuracy']
val_acc += history_fine_2.history['val_sparse_categorical_accuracy']
loss += history_fine_2.history['loss']
val_loss += history_fine_2.history['val_loss']
plt.figure(figsize=(8, 8), dpi =200)
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0, 1])
plt.plot([epochs-1,epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.plot([epochs+fine_tune_epochs-1,epochs+fine_tune_epochs-1],
plt.ylim(), label='Start Fine Tuning 2')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([epochs-1,epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.plot([epochs+fine_tune_epochs-1,epochs+fine_tune_epochs-1],
plt.ylim(), label='Start Fine Tuning 2')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
The training accuracy and validation accuracy is 91.62% and 92.63% respectively after 40 epochs. Now the model is performing quide good with some avoidable bias and no overfitting on training data.
model.compile(optimizer=keras.optimizers.Adam(1e-5),loss="sparse_categorical_crossentropy",
metrics=["sparse_categorical_accuracy"])
fine_tune_epochs_3 = 20
total_epochs = epochs + fine_tune_epochs + fine_tune_epochs_2 +fine_tune_epochs_3
history_fine_3 = model.fit(train_ds,
epochs=total_epochs,
initial_epoch=history_fine_2.epoch[-1],
validation_data=val_ds)
Epoch 40/60 WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op. WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op. 675/675 [==============================] - 175s 243ms/step - loss: 0.1791 - sparse_categorical_accuracy: 0.9399 - val_loss: 0.2167 - val_sparse_categorical_accuracy: 0.9313 Epoch 41/60 675/675 [==============================] - 162s 240ms/step - loss: 0.1676 - sparse_categorical_accuracy: 0.9426 - val_loss: 0.2112 - val_sparse_categorical_accuracy: 0.9309 Epoch 42/60 675/675 [==============================] - 162s 240ms/step - loss: 0.1629 - sparse_categorical_accuracy: 0.9455 - val_loss: 0.2129 - val_sparse_categorical_accuracy: 0.9302 Epoch 43/60 675/675 [==============================] - 162s 239ms/step - loss: 0.1612 - sparse_categorical_accuracy: 0.9454 - val_loss: 0.2122 - val_sparse_categorical_accuracy: 0.9307 Epoch 44/60 675/675 [==============================] - 162s 240ms/step - loss: 0.1616 - sparse_categorical_accuracy: 0.9437 - val_loss: 0.2108 - val_sparse_categorical_accuracy: 0.9346 Epoch 45/60 675/675 [==============================] - 163s 241ms/step - loss: 0.1539 - sparse_categorical_accuracy: 0.9449 - val_loss: 0.2027 - val_sparse_categorical_accuracy: 0.9359 Epoch 46/60 675/675 [==============================] - 163s 241ms/step - loss: 0.1508 - sparse_categorical_accuracy: 0.9467 - val_loss: 0.2110 - val_sparse_categorical_accuracy: 0.9326 Epoch 47/60 675/675 [==============================] - 162s 240ms/step - loss: 0.1474 - sparse_categorical_accuracy: 0.9485 - val_loss: 0.2004 - val_sparse_categorical_accuracy: 0.9348 Epoch 48/60 675/675 [==============================] - 163s 241ms/step - loss: 0.1448 - sparse_categorical_accuracy: 0.9496 - val_loss: 0.2127 - val_sparse_categorical_accuracy: 0.9341 Epoch 49/60 675/675 [==============================] - 162s 241ms/step - loss: 0.1476 - sparse_categorical_accuracy: 0.9489 - val_loss: 0.2130 - val_sparse_categorical_accuracy: 0.9326 Epoch 50/60 675/675 [==============================] - 162s 240ms/step - loss: 0.1480 - sparse_categorical_accuracy: 0.9473 - val_loss: 0.2090 - val_sparse_categorical_accuracy: 0.9337 Epoch 51/60 675/675 [==============================] - 162s 240ms/step - loss: 0.1427 - sparse_categorical_accuracy: 0.9510 - val_loss: 0.2103 - val_sparse_categorical_accuracy: 0.9352 Epoch 52/60 675/675 [==============================] - 167s 248ms/step - loss: 0.1482 - sparse_categorical_accuracy: 0.9482 - val_loss: 0.2046 - val_sparse_categorical_accuracy: 0.9328 Epoch 53/60 675/675 [==============================] - 172s 255ms/step - loss: 0.1391 - sparse_categorical_accuracy: 0.9517 - val_loss: 0.1999 - val_sparse_categorical_accuracy: 0.9389 Epoch 54/60 675/675 [==============================] - 170s 251ms/step - loss: 0.1418 - sparse_categorical_accuracy: 0.9498 - val_loss: 0.2045 - val_sparse_categorical_accuracy: 0.9348 Epoch 55/60 675/675 [==============================] - 169s 251ms/step - loss: 0.1375 - sparse_categorical_accuracy: 0.9530 - val_loss: 0.2019 - val_sparse_categorical_accuracy: 0.9367 Epoch 56/60 675/675 [==============================] - 170s 251ms/step - loss: 0.1330 - sparse_categorical_accuracy: 0.9528 - val_loss: 0.2102 - val_sparse_categorical_accuracy: 0.9357 Epoch 57/60 675/675 [==============================] - 171s 253ms/step - loss: 0.1370 - sparse_categorical_accuracy: 0.9518 - val_loss: 0.1953 - val_sparse_categorical_accuracy: 0.9393 Epoch 58/60 675/675 [==============================] - 170s 251ms/step - loss: 0.1333 - sparse_categorical_accuracy: 0.9539 - val_loss: 0.2029 - val_sparse_categorical_accuracy: 0.9361 Epoch 59/60 675/675 [==============================] - 174s 258ms/step - loss: 0.1331 - sparse_categorical_accuracy: 0.9533 - val_loss: 0.1988 - val_sparse_categorical_accuracy: 0.9372 Epoch 60/60 675/675 [==============================] - 164s 242ms/step - loss: 0.1325 - sparse_categorical_accuracy: 0.9532 - val_loss: 0.2051 - val_sparse_categorical_accuracy: 0.9367
acc += history_fine_3.history['sparse_categorical_accuracy']
val_acc += history_fine_3.history['val_sparse_categorical_accuracy']
loss += history_fine_3.history['loss']
val_loss += history_fine_3.history['val_loss']
plt.figure(figsize=(8, 8), dpi = 200)
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0, 1])
plt.plot([epochs-1,epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.plot([epochs+fine_tune_epochs-1,epochs+fine_tune_epochs-1],
plt.ylim(), label='Start Fine Tuning 2')
plt.plot([total_epochs-fine_tune_epochs_3-1,total_epochs-fine_tune_epochs_3-1],
plt.ylim(), label='Start Fine Tuning 3')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([epochs-1,epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.plot([epochs+fine_tune_epochs-1,epochs+fine_tune_epochs-1],
plt.ylim(), label='Start Fine Tuning 2')
plt.plot([total_epochs-fine_tune_epochs_3-1,total_epochs-fine_tune_epochs_3-1],
plt.ylim(), label='Start Fine Tuning 3')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
The accuracy of training and validation sets after 60 epochs is 95.32% and 94.67% with about 1% overfiiting on training data. There is still avoidable bias in the model which can be decreased by either unfreezing few more layers at the end or by adding few more fully connected layers at the end after flattening of encodings.
model.save("Eurosat_MN_e50_d003_A.h5")