In [ ]:
# https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html
In [ ]:
import os
import math
import matplotlib.pyplot as plt
import numpy as np
from keras.engine import  Model
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.optimizers import Adam, SGD
from keras.callbacks import ModelCheckpoint, Callback
from keras.preprocessing.image import load_img, img_to_array, array_to_img, ImageDataGenerator
from keras_vggface.vggface import VGGFace
In [ ]:
DATA_DIR = '../data/preprocessed/'
BATCH_SIZE = 32
GRAYSCALE = False
INPUT_DIM = (128, 128, 1 if GRAYSCALE else 3)
AUGMENTATION_FACTOR = 3
EPOCHS = 100
RANDOM_SEED = 123
LOAD_WEIGHTS = True
In [ ]:
train_datagen = ImageDataGenerator(
        rotation_range=10,
        rescale=1./255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)

datagen = ImageDataGenerator(rescale=1./255)

generator_base_params = {
    'target_size': INPUT_DIM[:2],
    'class_mode': 'categorical',
    'color_mode': 'grayscale' if GRAYSCALE else 'rgb',
    'batch_size': BATCH_SIZE,
    'seed': RANDOM_SEED
}

train_generator = train_datagen.flow_from_directory(DATA_DIR + 'train', shuffle=True, **generator_base_params) 
validation_generator = datagen.flow_from_directory(DATA_DIR + 'validation', shuffle=True, **generator_base_params)
test_generator = datagen.flow_from_directory(DATA_DIR + 'test', shuffle=True, **generator_base_params)

n_train = train_generator.n
n_validation = validation_generator.n
n_test = test_generator.n
In [ ]:
def get_model():
    vgg_model = VGGFace(include_top=False, input_shape=INPUT_DIM, pooling='max')

    top_model = Sequential(name='top')
    top_model.add(Dense(128, activation='relu', input_shape=vgg_model.output_shape[1:]))
    top_model.add(Dropout(0.5))
    top_model.add(Dense(4, activation='softmax'))
    
    if LOAD_WEIGHTS:
        top_model.load_weights('top_model_weights.hdf5') # TODO retrain with two dense layers (256, 128) instead of one (64)
    
    for layer in vgg_model.layers[:-3]:
        layer.trainable = False
    
    model = Sequential()
    model.add(vgg_model)
    model.add(top_model)
    
    opt = SGD(lr=1e-4, momentum=0.9)
    model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['acc'])
    
    return model
In [ ]:
model = get_model()
callbacks = [
    ModelCheckpoint('final-{epoch:02d}.hdf5', monitor='val_acc', verbose=1, save_best_only=False, mode='max')
]
In [ ]:
history = model.fit_generator(
        train_generator,
        steps_per_epoch=n_train // BATCH_SIZE,
        epochs=EPOCHS,
        validation_data=validation_generator,
        validation_steps=n_validation // BATCH_SIZE,
        callbacks=callbacks)
In [ ]:
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
In [ ]:
modelfiles = [f for f in os.listdir('.') if f.endswith('.hdf5') and f.startswith('final')]
for f in modelfiles:
    model.load_weights(f)
    result = model.evaluate_generator(
        test_generator,
        steps=n_test // BATCH_SIZE
    )
    print(f'{f}: {result[1]}')
In [ ]:
model.load_weights('final-22-0.546.hdf5') # 0.546