# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# Reproduce Leaf Counting
# Keras Implementation of InceptionV3
# <NAME>


# +
# ensures back compatibility
from tensorflow.keras import backend as K


# for reading and preprocessing data
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam


# inceptionv3 model from keras with pretrained weights
from tensorflow.python.keras.applications.inception_v3 import InceptionV3, preprocess_input

import tensorflow as tf


# for plots et al.
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import classification_report, confusion_matrix

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sn


# +
# set these parameters

dataset = 'LeafCount'
modelName = 'InceptionV3'

load_weights = True

# +
DATASET_PATH  = '../dataset/'

IMAGE_SIZE    = (256, 256)
NUM_CLASSES   = 9  
BATCH_SIZE    = 8
NUM_EPOCHS    = 20
WEIGHTS_FINAL = ''.join(['model-',modelName,'-',dataset,'.h5'])


# +
# specify data augmentation parameters for training images
train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,
                                   rotation_range=30,
                                   width_shift_range=0.2,
                                   height_shift_range=0.2,
                                   zoom_range=0.4,
                                   horizontal_flip=True,
                                   
                                   fill_mode='nearest',
                                   validation_split=0.1)
train_batches = train_datagen.flow_from_directory(DATASET_PATH + '/train',
                                                  target_size=IMAGE_SIZE,
                                                  interpolation='bicubic',
                                                  class_mode='categorical',
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,
                                                  subset="training",
                                                  classes=['1','2','3','4','5','6','7','8','9+'])

valid_batches = train_datagen.flow_from_directory(DATASET_PATH + '/train',
                                                  target_size=IMAGE_SIZE,
                                                  interpolation='bicubic',
                                                  class_mode='categorical',
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,                                                                                         subset="validation",
                                                  classes=['1','2','3','4','5','6','7','8','9+'])



# +
# show class indices


print('****************')

for cls, idx in train_batches.class_indices.items():
    print('Class #{} = {}'.format(idx, cls))
    
print('****************')

# +
# inceptionv3 - model setup

model = InceptionV3(include_top=False,
                    weights='imagenet',
                    input_tensor=None,
                    input_shape=(IMAGE_SIZE[0], IMAGE_SIZE[1], 3))

# +
# ResNet model from keras with pretrained weights


from tensorflow.python.keras.applications.inception_resnet_v2 import InceptionResNetV2, preprocess_input
# adding final FC layer at the end of model
x = model.output
x = Flatten()(x)
x = Dropout(0.5)(x)
output_layer = Dense( NUM_CLASSES,
                      activation='softmax',                     
                      name='softmax')(x)

model = Model(inputs=model.input,
              outputs=output_layer)



# ensure all layers are trainable
for layer in model.layers:
    layer.trainable = True

# setting up optimizer for model
model.compile(optimizer=Adam(lr=1e-5),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
# -

print(model.summary())

# +
# load saved weights
# if load_weights:
model.load_weights(WEIGHTS_FINAL)

    
    
# -

# train the model
hist = model.fit_generator(train_batches,
                    steps_per_epoch = train_batches.samples // BATCH_SIZE,
                    validation_data = valid_batches,
                    validation_steps = valid_batches.samples // BATCH_SIZE,
                    epochs = NUM_EPOCHS)

# save trained weights
model.save(WEIGHTS_FINAL)

# +
# Plot Results

N=NUM_EPOCHS

plt.style.use("ggplot")
plt.figure()
plt.plot(np.arange(0, N), hist.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), hist.history["val_loss"], label="val_loss")

plt.title("Training/Validation Loss on Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Loss")
plt.legend(loc="lower left")

plt.savefig("plot_loss.png")

plt.figure()
plt.plot(np.arange(0, N), hist.history["accuracy"], label="train_acc")
plt.plot(np.arange(0, N), hist.history["val_accuracy"], label="val_acc")

plt.title("Training/Validation Accuracy on Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Accuracy")
plt.legend(loc="lower left")
plt.savefig("plot.png")


# +
# test model

test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)
test_batches = test_datagen.flow_from_directory(DATASET_PATH + '/test',
                                                  target_size=IMAGE_SIZE,
                                                  interpolation='bicubic',
                                                
                                                  class_mode='categorical',
                                                  shuffle=False,
                                                  batch_size=1,
                                                  classes=['1','2','3','4','5','6','7','8','9+'])






# out = model.evaluate_generator(test_batches, use_multiprocessing=True)
# print(list(zip(model.metrics_names,out)))
# -

test_batches.reset()


Y_pred = model.predict_generator(test_batches, use_multiprocessing=True)
# print(len(Y_pred))

# +
y_pred = np.argmax(Y_pred, axis=1)

print('\n\n Classification Report\n')
target_names = list(test_batches.class_indices.keys())
print(target_names)
print(classification_report(list(test_batches.classes), y_pred, target_names=target_names))



print('\n\nConfusion Matrix\n')

cm = confusion_matrix(test_batches.classes, y_pred)

row_sums = cm.sum(axis=1)
cm = cm / row_sums

# df_cm = pd.DataFrame(cm, index = ['cocklebur','foxtail','pigweed','ragweed'], columns = ['cocklebur','foxtail','pigweed','ragweed'])

df_cm = pd.DataFrame(cm)
# print(df_cm)


# flights = df_cm.pivot("month", "year", "passengers")


plt.figure(figsize = (5,5))

plt.title(modelName)
sn.heatmap(df_cm, annot=True, fmt='0.0%', cbar=False, 
           xticklabels=['1','2','3','4','5','6','7','8','9+'],
           yticklabels=['1','2','3','4','5','6','7','8','9+'])


saveName = ''.join([modelName,'-',dataset,'.png'])
plt.savefig(saveName)

# -



StarcoderdataJupyterScriptsDedupFiltered