# -*- coding: utf-8 -*-
"""Food / Non-Food code re-construction.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1CmgZEpqOZ0ME3zJToeucD4ZFJatl8tqn
"""

import os
import random
import numpy as np
import tensorflow as tf
from keras import backend as K


def seed_everything():
    '''
    Make sure this function is called before any other part in the script,
    so that the result can be reproducible.
    '''
    # Seed value
    # Apparently you may use different seed values at each stage
    seed_value= 1

    # 1. Set the `PYTHONHASHSEED` environment variable at a fixed value
    os.environ['PYTHONHASHSEED']=str(seed_value)

    # 2. Set the `python` built-in pseudo-random generator at a fixed value
    random.seed(seed_value)

    # 3. Set the `numpy` pseudo-random generator at a fixed value
    np.random.seed(seed_value)

    # 4. Set the `tensorflow` pseudo-random generator at a fixed value
    tf.set_random_seed(seed_value)

    # 5. Configure a new global `tensorflow` session
    session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
    sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
    K.set_session(sess)

seed_everything()
print('Seeds completed.')

from google.colab import drive
drive.mount('/content/drive')

import cv2
import pandas as pd
import matplotlib.pyplot as plt

from keras.layers import Dense,GlobalAveragePooling2D
from keras.applications import ResNet50, InceptionV3, inception_v3, resnet50, mobilenet_v2, MobileNetV2
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Model
from keras.optimizers import Adam, RMSprop
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import ModelCheckpoint
from keras.models import load_model

def get_img_generators(train_path, eval_path, test_path, preprocess_input, batch_sz, target_sz):
    print('target shape:', target_sz)
    print('batch size:', batch_sz)
    height, width = target_sz
    train_gen = ImageDataGenerator(
        preprocessing_function=preprocess_input,
        width_shift_range=.2, 
        height_shift_range=.2,
        fill_mode='nearest',
        rotation_range=40,
        horizontal_flip=True,
        zoom_range=0.2
    )
    test_gen = ImageDataGenerator(preprocessing_function=preprocess_input)
    train_generator = train_gen.flow_from_directory(train_path,
                                                   target_size=(height, width),
                                                   color_mode='rgb',
                                                   class_mode='categorical',
                                                   batch_size=batch_sz,
                                                   shuffle=True)

    eval_generator = test_gen.flow_from_directory(eval_path,
                                                   target_size=(height, width),
                                                   color_mode='rgb',
                                                   class_mode='categorical',
                                                   batch_size=batch_sz,
                                                   shuffle=True)

    test_generator = test_gen.flow_from_directory(test_path,
                                                   target_size=(height, width),
                                                   color_mode='rgb',
                                                   class_mode='categorical',
                                                   batch_size=batch_sz,
                                                   shuffle=True)
    return train_generator, eval_generator, test_generator


def create_model(base, num_of_layers, input_sz=None):
    '''
    num_of_layers: the layers that are trainable
    '''
    # base model
    base_model = base(weights='imagenet', include_top=False, input_shape=input_sz)
    # add custom layers
    x = base_model.output
    x = GlobalAveragePooling2D()(x)
    preds = Dense(2, activation='softmax')(x)
    model = Model(inputs=base_model.input, outputs=preds)
    # freeze layers
    for layer in model.layers[:-num_of_layers]:
        layer.trainable = False
    print('Trainable weights:\n', model.trainable_weights)
    # optimiser
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model


def specify_checkpoint(filepath):
    checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_weights_only=False, save_best_only=True, mode='max')
    callbacks_list = [checkpoint]
    return callbacks_list


def train_model(model, train_generator, eval_generator, callbacks_list, ep=5):
    step_size_train = train_generator.n // train_generator.batch_size
    step_size_eval = eval_generator.n // eval_generator.batch_size
    model.fit_generator(generator=train_generator,
                       steps_per_epoch=step_size_train,
                       validation_data=eval_generator,
                       validation_steps=step_size_eval,
                       use_multiprocessing=True,
                       epochs=ep,
                       callbacks=callbacks_list,
                       verbose=1)
    
    
def test_model(model, test_generator):
    step_size_test = test_generator.n // test_generator.batch_size
    performance = model.evaluate_generator(test_generator, steps=step_size_test, verbose=1)
    return performance

def run_model(params, train_path, eval_path, test_path, train=True, test=False):
    assert params['file_path'] != ''
    print('Building image generators...')
    print(train_path)
    train_generator, eval_generator, test_generator = get_img_generators(train_path, eval_path, test_path,
                                                                         preprocess_input=params['preprocess_func'],
                                                                         batch_sz=params['batch_sz'],
                                                                         target_sz=params['target_sz'])
    if train:
        print('Compiling the model...')
        model = create_model(base=params['base_model'], num_of_layers=params['last_layers'], input_sz=params['input_sz'])
        callbacks_list = specify_checkpoint(params['file_path'])
        print('==== Training ====')
        train_model(model, train_generator, eval_generator, callbacks_list, ep=params['epoch'])
    if test:
        print('==== Testing ====')
        print('Loadint the model with the highest validation accuracy...')
        model = load_model(params['file_path'])
        result = test_model(model, test_generator)
        print(result)

collection = {
    'mobileNet-v2': {
        'preprocess_func': mobilenet_v2.preprocess_input,
        'base_model': MobileNetV2,
        'last_layers': 1,  # choice: 1, 5
        'batch_sz': 32,
        'target_sz': (256, 256),  # used for loading images
        'input_sz': (256, 256, 3), # used for building a model
        'file_path': 'drive/My Drive/MSc dataset/food5k/Food-5K/models/mobileNet_v2_last_1_256.best.hdf5',
        'epoch': 5
    },
    'inception-v3': {
        'preprocess_func': inception_v3.preprocess_input,
        'base_model': InceptionV3,
        'last_layers': 1,  # choice: 1, 14
        'batch_sz': 32,
        'target_sz': (256, 256),  # used for loading images
        'input_sz': (256, 256, 3), # used for building a model
        'file_path': 'drive/My Drive/MSc dataset/food5k/Food-5K/models/inception_v3_last_1_256_without_aug.best.hdf5',
        'epoch': 5
    },
    'resnet50': {
        'preprocess_func': resnet50.preprocess_input,
        'base_model': ResNet50,
        'last_layers': 6,  # choice: 1, 6
        'batch_sz': 32,
        'target_sz': (256, 256),  # used for loading images
        'input_sz': (256, 256, 3), # used for building a model
        'file_path': 'drive/My Drive/MSc dataset/food5k/Food-5K/models/resnet50_last_6_256.best.hdf5',
        'epoch': 5
    }
}

train_path = "drive/My Drive/MSc dataset/food5k/Food-5K/training"
eval_path = "drive/My Drive/MSc dataset/food5k/Food-5K/validation"
test_path = "drive/My Drive/MSc dataset/food5k/Food-5K/evaluation"

"""**Before each run for a new model, change the file path.**"""

run_model(collection['inception-v3'], train_path, eval_path, test_path, train=False, test=True)
# -*- coding: utf-8 -*-
"""Food / Non-Food code re-construction.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1CmgZEpqOZ0ME3zJToeucD4ZFJatl8tqn
"""

import os
import random
import numpy as np
import tensorflow as tf
from keras import backend as K


def seed_everything():
    '''
    Make sure this function is called before any other part in the script,
    so that the result can be reproducible.
    '''
    # Seed value
    # Apparently you may use different seed values at each stage
    seed_value= 1

    # 1. Set the `PYTHONHASHSEED` environment variable at a fixed value
    os.environ['PYTHONHASHSEED']=str(seed_value)

    # 2. Set the `python` built-in pseudo-random generator at a fixed value
    random.seed(seed_value)

    # 3. Set the `numpy` pseudo-random generator at a fixed value
    np.random.seed(seed_value)

    # 4. Set the `tensorflow` pseudo-random generator at a fixed value
    tf.set_random_seed(seed_value)

    # 5. Configure a new global `tensorflow` session
    session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
    sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
    K.set_session(sess)

seed_everything()
print('Seeds completed.')

from google.colab import drive
drive.mount('/content/drive')

import cv2
import pandas as pd
import matplotlib.pyplot as plt

from keras.layers import Dense,GlobalAveragePooling2D
from keras.applications import ResNet50, InceptionV3, inception_v3, resnet50, mobilenet_v2, MobileNetV2
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Model
from keras.optimizers import Adam, RMSprop
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import ModelCheckpoint
from keras.models import load_model

def get_img_generators(train_path, eval_path, test_path, preprocess_input, batch_sz, target_sz):
    print('target shape:', target_sz)
    print('batch size:', batch_sz)
    height, width = target_sz
    train_gen = ImageDataGenerator(
        preprocessing_function=preprocess_input,
        width_shift_range=.2, 
        height_shift_range=.2,
        fill_mode='nearest',
        rotation_range=40,
        horizontal_flip=True,
        zoom_range=0.2
    )
    test_gen = ImageDataGenerator(preprocessing_function=preprocess_input)
    train_generator = train_gen.flow_from_directory(train_path,
                                                   target_size=(height, width),
                                                   color_mode='rgb',
                                                   class_mode='categorical',
                                                   batch_size=batch_sz,
                                                   shuffle=True)

    eval_generator = test_gen.flow_from_directory(eval_path,
                                                   target_size=(height, width),
                                                   color_mode='rgb',
                                                   class_mode='categorical',
                                                   batch_size=batch_sz,
                                                   shuffle=True)

    test_generator = test_gen.flow_from_directory(test_path,
                                                   target_size=(height, width),
                                                   color_mode='rgb',
                                                   class_mode='categorical',
                                                   batch_size=batch_sz,
                                                   shuffle=True)
    return train_generator, eval_generator, test_generator


def create_model(base, num_of_layers, input_sz=None):
    '''
    num_of_layers: the layers that are trainable
    '''
    # base model
    base_model = base(weights='imagenet', include_top=False, input_shape=input_sz)
    # add custom layers
    x = base_model.output
    x = GlobalAveragePooling2D()(x)
    preds = Dense(2, activation='softmax')(x)
    model = Model(inputs=base_model.input, outputs=preds)
    # freeze layers
    for layer in model.layers[:-num_of_layers]:
        layer.trainable = False
    print('Trainable weights:\n', model.trainable_weights)
    # optimiser
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model


def specify_checkpoint(filepath):
    checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_weights_only=False, save_best_only=True, mode='max')
    callbacks_list = [checkpoint]
    return callbacks_list


def train_model(model, train_generator, eval_generator, callbacks_list, ep=5):
    step_size_train = train_generator.n // train_generator.batch_size
    step_size_eval = eval_generator.n // eval_generator.batch_size
    model.fit_generator(generator=train_generator,
                       steps_per_epoch=step_size_train,
                       validation_data=eval_generator,
                       validation_steps=step_size_eval,
                       use_multiprocessing=True,
                       epochs=ep,
                       callbacks=callbacks_list,
                       verbose=1)
    
    
def test_model(model, test_generator):
    step_size_test = test_generator.n // test_generator.batch_size
    performance = model.evaluate_generator(test_generator, steps=step_size_test, verbose=1)
    return performance

def run_model(params, train_path, eval_path, test_path, train=True, test=False):
    assert params['file_path'] != ''
    print('Building image generators...')
    print(train_path)
    train_generator, eval_generator, test_generator = get_img_generators(train_path, eval_path, test_path,
                                                                         preprocess_input=params['preprocess_func'],
                                                                         batch_sz=params['batch_sz'],
                                                                         target_sz=params['target_sz'])
    if train:
        print('Compiling the model...')
        model = create_model(base=params['base_model'], num_of_layers=params['last_layers'], input_sz=params['input_sz'])
        callbacks_list = specify_checkpoint(params['file_path'])
        print('==== Training ====')
        train_model(model, train_generator, eval_generator, callbacks_list, ep=params['epoch'])
    if test:
        print('==== Testing ====')
        print('Loadint the model with the highest validation accuracy...')
        model = load_model(params['file_path'])
        result = test_model(model, test_generator)
        print(result)

collection = {
    'mobileNet-v2': {
        'preprocess_func': mobilenet_v2.preprocess_input,
        'base_model': MobileNetV2,
        'last_layers': 1,  # choice: 1, 5
        'batch_sz': 32,
        'target_sz': (256, 256),  # used for loading images
        'input_sz': (256, 256, 3), # used for building a model
        'file_path': 'drive/My Drive/MSc dataset/food5k/Food-5K/models/mobileNet_v2_last_1_256.best.hdf5',
        'epoch': 5
    },
    'inception-v3': {
        'preprocess_func': inception_v3.preprocess_input,
        'base_model': InceptionV3,
        'last_layers': 1,  # choice: 1, 14
        'batch_sz': 32,
        'target_sz': (256, 256),  # used for loading images
        'input_sz': (256, 256, 3), # used for building a model
        'file_path': 'drive/My Drive/MSc dataset/food5k/Food-5K/models/inception_v3_last_1_256_without_aug.best.hdf5',
        'epoch': 5
    },
    'resnet50': {
        'preprocess_func': resnet50.preprocess_input,
        'base_model': ResNet50,
        'last_layers': 6,  # choice: 1, 6
        'batch_sz': 32,
        'target_sz': (256, 256),  # used for loading images
        'input_sz': (256, 256, 3), # used for building a model
        'file_path': 'drive/My Drive/MSc dataset/food5k/Food-5K/models/resnet50_last_6_256.best.hdf5',
        'epoch': 5
    }
}

train_path = "drive/My Drive/MSc dataset/food5k/Food-5K/training"
eval_path = "drive/My Drive/MSc dataset/food5k/Food-5K/validation"
test_path = "drive/My Drive/MSc dataset/food5k/Food-5K/evaluation"

"""**Before each run for a new model, change the file path.**"""

run_model(collection['inception-v3'], train_path, eval_path, test_path, train=False, test=True)
# -*- coding: utf-8 -*- Food / Non-Food code re-construction.ipynb  Automatically generated by Colaboratory.  Original file is located at     https://colab.research.google.com/drive/1CmgZEpqOZ0ME3zJToeucD4ZFJatl8tqn Make sure this function is called before any other part in the script,     so that the result can be reproducible. # Seed value # Apparently you may use different seed values at each stage # 1. Set the `PYTHONHASHSEED` environment variable at a fixed value # 2. Set the `python` built-in pseudo-random generator at a fixed value # 3. Set the `numpy` pseudo-random generator at a fixed value # 4. Set the `tensorflow` pseudo-random generator at a fixed value # 5. Configure a new global `tensorflow` session num_of_layers: the layers that are trainable # base model # add custom layers # freeze layers # optimiser # choice: 1, 5 # used for loading images # used for building a model # choice: 1, 14 # used for loading images # used for building a model # choice: 1, 6 # used for loading images # used for building a model **Before each run for a new model, change the file path.**