LittleVGG

  • Training this on the simpsons character dataset
In [11]:
from __future__ import print_function
import keras
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten, BatchNormalization
from keras.layers import Conv2D, MaxPooling2D
from keras.layers.advanced_activations import ELU
from keras.preprocessing.image import ImageDataGenerator
import os

num_classes = 20
img_rows, img_cols = 32, 32
batch_size = 16

train_data_dir = './simpsons/train'
validation_data_dir = './simpsons/validation'

# Let's use some data augmentaiton 
train_datagen = ImageDataGenerator(
      rescale=1./255,
      rotation_range=30,
      width_shift_range=0.3,
      height_shift_range=0.3,
      horizontal_flip=True,
      fill_mode='nearest')
 
validation_datagen = ImageDataGenerator(rescale=1./255)
 
train_generator = train_datagen.flow_from_directory(
        train_data_dir,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical')
 
validation_generator = validation_datagen.flow_from_directory(
        validation_data_dir,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical')
Found 19548 images belonging to 20 classes.
Found 990 images belonging to 20 classes.

Let's create our LittleVGG Model

In [12]:
model = Sequential()

# First CONV-ReLU Layer
model.add(Conv2D(64, (3, 3), padding = 'same', input_shape = (img_rows, img_cols, 3)))
model.add(Activation('relu'))
model.add(BatchNormalization())

# Second CONV-ReLU Layer
model.add(Conv2D(64, (3, 3), padding = "same", input_shape = (img_rows, img_cols, 3)))
model.add(Activation('relu'))
model.add(BatchNormalization())

# Max Pooling with Dropout 
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))

# 3rd set of CONV-ReLU Layers
model.add(Conv2D(128, (3, 3), padding="same"))
model.add(Activation('relu'))
model.add(BatchNormalization())

# 4th Set of CONV-ReLU Layers
model.add(Conv2D(128, (3, 3), padding="same"))
model.add(Activation('relu'))
model.add(BatchNormalization())

# Max Pooling with Dropout 
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))

# 5th Set of CONV-ReLU Layers
model.add(Conv2D(256, (3, 3), padding="same"))
model.add(Activation('relu'))
model.add(BatchNormalization())

# 6th Set of CONV-ReLU Layers
model.add(Conv2D(256, (3, 3), padding="same"))
model.add(Activation('relu'))
model.add(BatchNormalization())

# Max Pooling with Dropout 
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))

# First set of FC or Dense Layers
model.add(Flatten())
model.add(Dense(256))
model.add(Activation('relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))

# Second set of FC or Dense Layers
model.add(Dense(256))
model.add(Activation('relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))

# Final Dense Layer
model.add(Dense(num_classes))
model.add(Activation("softmax"))

print(model.summary())
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_13 (Conv2D)           (None, 32, 32, 64)        1792      
_________________________________________________________________
activation_19 (Activation)   (None, 32, 32, 64)        0         
_________________________________________________________________
batch_normalization_17 (Batc (None, 32, 32, 64)        256       
_________________________________________________________________
conv2d_14 (Conv2D)           (None, 32, 32, 64)        36928     
_________________________________________________________________
activation_20 (Activation)   (None, 32, 32, 64)        0         
_________________________________________________________________
batch_normalization_18 (Batc (None, 32, 32, 64)        256       
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 16, 16, 64)        0         
_________________________________________________________________
dropout_11 (Dropout)         (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_15 (Conv2D)           (None, 16, 16, 128)       73856     
_________________________________________________________________
activation_21 (Activation)   (None, 16, 16, 128)       0         
_________________________________________________________________
batch_normalization_19 (Batc (None, 16, 16, 128)       512       
_________________________________________________________________
conv2d_16 (Conv2D)           (None, 16, 16, 128)       147584    
_________________________________________________________________
activation_22 (Activation)   (None, 16, 16, 128)       0         
_________________________________________________________________
batch_normalization_20 (Batc (None, 16, 16, 128)       512       
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 8, 8, 128)         0         
_________________________________________________________________
dropout_12 (Dropout)         (None, 8, 8, 128)         0         
_________________________________________________________________
conv2d_17 (Conv2D)           (None, 8, 8, 256)         295168    
_________________________________________________________________
activation_23 (Activation)   (None, 8, 8, 256)         0         
_________________________________________________________________
batch_normalization_21 (Batc (None, 8, 8, 256)         1024      
_________________________________________________________________
conv2d_18 (Conv2D)           (None, 8, 8, 256)         590080    
_________________________________________________________________
activation_24 (Activation)   (None, 8, 8, 256)         0         
_________________________________________________________________
batch_normalization_22 (Batc (None, 8, 8, 256)         1024      
_________________________________________________________________
max_pooling2d_9 (MaxPooling2 (None, 4, 4, 256)         0         
_________________________________________________________________
dropout_13 (Dropout)         (None, 4, 4, 256)         0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 4096)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 256)               1048832   
_________________________________________________________________
activation_25 (Activation)   (None, 256)               0         
_________________________________________________________________
batch_normalization_23 (Batc (None, 256)               1024      
_________________________________________________________________
dropout_14 (Dropout)         (None, 256)               0         
_________________________________________________________________
dense_8 (Dense)              (None, 256)               65792     
_________________________________________________________________
activation_26 (Activation)   (None, 256)               0         
_________________________________________________________________
batch_normalization_24 (Batc (None, 256)               1024      
_________________________________________________________________
dropout_15 (Dropout)         (None, 256)               0         
_________________________________________________________________
dense_9 (Dense)              (None, 20)                5140      
_________________________________________________________________
activation_27 (Activation)   (None, 20)                0         
=================================================================
Total params: 2,270,804
Trainable params: 2,267,988
Non-trainable params: 2,816
_________________________________________________________________
None

Let's take a look at our model

In [6]:
%matplotlib inline
import keras
from keras.models import Sequential
from keras.utils.vis_utils import plot_model
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

plot_model(model, to_file='LittleVGG.png', show_shapes=True, show_layer_names=True)
img = mpimg.imread('LittleVGG.png')
plt.figure(figsize=(100,70))
imgplot = plt.imshow(img) 

Training our LittleVGG Model!

In [4]:
from keras.optimizers import RMSprop, SGD, Adam
from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau

                     
checkpoint = ModelCheckpoint("/home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5",
                             monitor="val_loss",
                             mode="min",
                             save_best_only = True,
                             verbose=1)

earlystop = EarlyStopping(monitor = 'val_loss', 
                          min_delta = 0, 
                          patience = 3,
                          verbose = 1,
                          restore_best_weights = True)

reduce_lr = ReduceLROnPlateau(monitor = 'val_loss',
                              factor = 0.2,
                              patience = 3,
                              verbose = 1,
                              min_delta = 0.00001)

# we put our call backs into a callback list
callbacks = [earlystop, checkpoint, reduce_lr]

# We use a very small learning rate 
model.compile(loss = 'categorical_crossentropy',
              optimizer = Adam(lr=0.01),
              metrics = ['accuracy'])

nb_train_samples = 19548
nb_validation_samples = 990
epochs = 10

history = model.fit_generator(
    train_generator,
    steps_per_epoch = nb_train_samples // batch_size,
    epochs = epochs,
    callbacks = callbacks,
    validation_data = validation_generator,
    validation_steps = nb_validation_samples // batch_size)
Epoch 1/10
1221/1221 [==============================] - 550s 451ms/step - loss: 2.9079 - acc: 0.1458 - val_loss: 2.7280 - val_acc: 0.1527

Epoch 00001: val_loss improved from inf to 2.72799, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 2/10
1221/1221 [==============================] - 574s 470ms/step - loss: 2.4356 - acc: 0.2501 - val_loss: 2.6773 - val_acc: 0.2341

Epoch 00002: val_loss improved from 2.72799 to 2.67725, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 3/10
1221/1221 [==============================] - 568s 465ms/step - loss: 2.1633 - acc: 0.3489 - val_loss: 2.2249 - val_acc: 0.3480

Epoch 00003: val_loss improved from 2.67725 to 2.22494, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 4/10
1221/1221 [==============================] - 540s 442ms/step - loss: 1.9157 - acc: 0.4300 - val_loss: 1.7352 - val_acc: 0.4949

Epoch 00004: val_loss improved from 2.22494 to 1.73521, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 5/10
1221/1221 [==============================] - 549s 450ms/step - loss: 1.6782 - acc: 0.5079 - val_loss: 1.4488 - val_acc: 0.5554

Epoch 00005: val_loss improved from 1.73521 to 1.44881, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 6/10
1221/1221 [==============================] - 546s 447ms/step - loss: 1.5045 - acc: 0.5662 - val_loss: 1.3074 - val_acc: 0.6191

Epoch 00006: val_loss improved from 1.44881 to 1.30739, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 7/10
1221/1221 [==============================] - 590s 483ms/step - loss: 1.3883 - acc: 0.6037 - val_loss: 1.1316 - val_acc: 0.6807

Epoch 00007: val_loss improved from 1.30739 to 1.13157, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 8/10
1221/1221 [==============================] - 534s 437ms/step - loss: 1.3167 - acc: 0.6263 - val_loss: 1.3950 - val_acc: 0.6088

Epoch 00008: val_loss did not improve from 1.13157
Epoch 9/10
1221/1221 [==============================] - 626s 513ms/step - loss: 1.2239 - acc: 0.6549 - val_loss: 0.9800 - val_acc: 0.7351

Epoch 00009: val_loss improved from 1.13157 to 0.97996, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5
Epoch 10/10
1221/1221 [==============================] - 675s 553ms/step - loss: 1.1530 - acc: 0.6790 - val_loss: 0.8667 - val_acc: 0.7700

Epoch 00010: val_loss improved from 0.97996 to 0.86668, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5

Performance Analysis

In [10]:
import matplotlib.pyplot as plt
import sklearn
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np

# We need to recreate our validation generator with shuffle = false
validation_generator = validation_datagen.flow_from_directory(
        validation_data_dir,
        target_size=(img_rows, img_cols),
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

class_labels = validation_generator.class_indices
class_labels = {v: k for k, v in class_labels.items()}
classes = list(class_labels.values())

nb_train_samples = 19548
nb_validation_samples = 990

#Confution Matrix and Classification Report
Y_pred = model.predict_generator(validation_generator, nb_validation_samples // batch_size+1)
y_pred = np.argmax(Y_pred, axis=1)

print('Confusion Matrix')
print(confusion_matrix(validation_generator.classes, y_pred))
print('Classification Report')
target_names = list(class_labels.values())
print(classification_report(validation_generator.classes, y_pred, target_names=target_names))

plt.figure(figsize=(8,8))
cnf_matrix = confusion_matrix(validation_generator.classes, y_pred)

plt.imshow(cnf_matrix, interpolation='nearest')
plt.colorbar()
tick_marks = np.arange(len(classes))
_ = plt.xticks(tick_marks, classes, rotation=90)
_ = plt.yticks(tick_marks, classes)
Found 990 images belonging to 20 classes.
Confusion Matrix
[[32  0  0  1  0  0  4  9  0  0  1  0  0  0  0  0  1  0  0  0]
 [ 0 45  0  2  0  0  0  0  0  1  0  0  0  0  0  0  2  0  0  0]
 [ 0  0 39  1  2  0  1  1  0  2  0  1  1  0  0  0  0  2  0  0]
 [ 0  0  0 35  6  1  0  2  0  1  0  0  0  0  0  2  0  1  0  0]
 [ 0  0  0  0 48  0  0  0  0  0  0  0  0  0  1  1  0  0  0  0]
 [ 0  0  0  0 10 34  2  1  0  0  0  0  0  0  0  1  1  0  0  0]
 [ 0  0  0  0  1  0 38  0  0  1  0  0  0  0  2  0  3  2  1  2]
 [ 0  0  2  0  0  0  0 43  0  1  2  0  1  0  0  0  0  1  0  0]
 [ 0  0  0  0  3  0  0  0 42  1  0  1  0  0  1  1  0  0  1  0]
 [ 0  0  0  0  1  0  0  0  0 49  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  1  5  0  0  2  0  0 32  0  0  0  0  0  5  4  0  1]
 [ 0  0 11  3  2  0  0  0  0  0  0 31  0  0  0  0  1  1  1  0]
 [ 0  0  0  0 10  0  0  0  0  1  0  0 37  0  2  0  0  0  0  0]
 [ 0  0  0  1  1  2  0  0  0  0  1  0  0  3  0 36  0  5  1  0]
 [ 0  0  0  0  1  0  0  0  0  0  0  0  1  0 46  0  1  0  0  0]
 [ 0  0  0  0  4  1  0  0  0  0  0  0  0  0  1 44  0  0  0  0]
 [ 0  0  0  0  2  0  0  0  0  0  0  0  0  0  0  0 47  0  0  0]
 [ 0  0  0  0  4  3  2  0  0  0  0  0  0  0  1  1  2 37  0  0]
 [ 0  0  0  0  4  1  1  0  0  0  0  0  0  0  0  1  0  0 43  0]
 [ 0  0  0  0  3  1  2  1  0  1  1  0  0  0  0  0  0  0  0 38]]
Classification Report
                          precision    recall  f1-score   support

  abraham_grampa_simpson       1.00      0.67      0.80        48
  apu_nahasapeemapetilon       1.00      0.90      0.95        50
            bart_simpson       0.75      0.78      0.76        50
charles_montgomery_burns       0.80      0.73      0.76        48
            chief_wiggum       0.45      0.96      0.61        50
          comic_book_guy       0.79      0.69      0.74        49
          edna_krabappel       0.76      0.76      0.76        50
           homer_simpson       0.73      0.86      0.79        50
           kent_brockman       1.00      0.84      0.91        50
        krusty_the_clown       0.84      0.98      0.91        50
           lenny_leonard       0.86      0.64      0.74        50
            lisa_simpson       0.94      0.62      0.75        50
           marge_simpson       0.93      0.74      0.82        50
            mayor_quimby       1.00      0.06      0.11        50
     milhouse_van_houten       0.85      0.94      0.89        49
             moe_szyslak       0.51      0.88      0.64        50
            ned_flanders       0.75      0.96      0.84        49
            nelson_muntz       0.70      0.74      0.72        50
       principal_skinner       0.91      0.86      0.89        50
            sideshow_bob       0.93      0.81      0.86        47

               micro avg       0.77      0.77      0.77       990
               macro avg       0.82      0.77      0.76       990
            weighted avg       0.82      0.77      0.76       990

Let's reload our saved classifier

If we just trained our classifer, we an use model instead.

In [13]:
from keras.models import load_model

# 77% Accuracy after just 10 Epochs
classifier = load_model('/home/deeplearningcv/DeepLearningCV/Trained Models/simpsons_little_vgg.h5')

Some quick fire testing code

In [15]:
from keras.models import load_model
from keras.preprocessing import image
import numpy as np
import os
import cv2
import numpy as np
from os import listdir
from os.path import isfile, join
import re

def draw_test(name, pred, im, true_label):
    BLACK = [0,0,0]
    expanded_image = cv2.copyMakeBorder(im, 160, 0, 0, 300 ,cv2.BORDER_CONSTANT,value=BLACK)
    cv2.putText(expanded_image, "predited - "+ pred, (20, 60) , cv2.FONT_HERSHEY_SIMPLEX,1, (0,0,255), 2)
    cv2.putText(expanded_image, "true - "+ true_label, (20, 120) , cv2.FONT_HERSHEY_SIMPLEX,1, (0,255,0), 2)
    cv2.imshow(name, expanded_image)
    

def getRandomImage(path, img_width, img_height):
    """function loads a random images from a random folder in our test path """
    folders = list(filter(lambda x: os.path.isdir(os.path.join(path, x)), os.listdir(path)))
    random_directory = np.random.randint(0,len(folders))
    path_class = folders[random_directory]
    file_path = path + path_class
    file_names = [f for f in listdir(file_path) if isfile(join(file_path, f))]
    random_file_index = np.random.randint(0,len(file_names))
    image_name = file_names[random_file_index]
    final_path = file_path + "/" + image_name
    return image.load_img(final_path, target_size = (img_width, img_height)), final_path, path_class

# dimensions of our images
img_width, img_height = 32, 32

files = []
predictions = []
true_labels = []

# predicting images
for i in range(0, 10):
    path = './simpsons/validation/' 
    img, final_path, true_label = getRandomImage(path, img_width, img_height)
    files.append(final_path)
    true_labels.append(true_label)
    x = image.img_to_array(img)
    x = x * 1./255
    x = np.expand_dims(x, axis=0)
    images = np.vstack([x])
    classes = classifier.predict_classes(images, batch_size = 10)
    predictions.append(classes)
    
for i in range(0, len(files)):
    image = cv2.imread((files[i]))
    image = cv2.resize(image, None, fx=5, fy=5, interpolation = cv2.INTER_CUBIC)
    draw_test("Prediction", class_labels[predictions[i][0]], image, true_labels[i])
    cv2.waitKey(0)

cv2.destroyAllWindows()
In [ ]: