# ---
# 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
# ---

import tensorflow as tf
import random
import pandas as pd
import numpy as np

# Here, **load_files** function from the scikit-learn library is to import dataset.
#
# * **train_files, valid_files, test_files** - numpy arrays containing file paths to images
# * **train_targets, valid_targets, test_targets** - numpy arrays containing onehot-encoded classification labels
# * **dog_names** - list of string-valued dog breed names for translating labels

# + id="92bb87fa" outputId="41eee3cf-3cc3-4050-81ae-633766b1404d"
from sklearn.datasets import load_files       
from keras.utils import np_utils
import numpy as np
from glob import glob

# define function to load train, test, and validation datasets
def load_dataset(path):
    data = load_files(path)
    dog_files = np.array(data['filenames'])
    dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
    return dog_files, dog_targets

# load train, test, and validation datasets
train_files, train_targets = load_dataset(r'../input/dogs-dataset/dogImages/train')
valid_files, valid_targets = load_dataset(r'../input/dogs-dataset/dogImages/valid')
test_files, test_targets = load_dataset(r'../input/dogs-dataset/dogImages/test')

# load list of dog names
dog_names = [item[20:-1] for item in sorted(glob(r'../input/dogs-dataset/dogImages/train/*//'))]

# print statistics about the dataset
print('There are %d total dog categories.' % len(dog_names))
print('There are %s total dog images.\n' % len(np.hstack([train_files, valid_files, test_files])))
print('There are %d training dog images.' % len(train_files))
print('There are %d validation dog images.' % len(valid_files))
print('There are %d test dog images.'% len(test_files))
# -

# Below cell is to record the size of images as they have varying sizes. And some pictures have more than one dog.

# + id="7774f1d0"
## What are the dog image sizes. 
## They are all varying sizes, for some images there are more than one dog 
from matplotlib.pyplot import figure, imshow, axis
from matplotlib.image import imread

def showImages(list_of_files, col=10, wSize=5, hSize=5, mypath='.'):
    fig = figure(figsize=(wSize, hSize))
    number_of_files = len(list_of_files)
    row = 10
    if (number_of_files % col != 0):
        row += 1
    for i in range(row+10):
        a=fig.add_subplot(row, col, i + 1)
        image = imread(list_of_files[i])
        imshow(image)
        axis('off')


# -

# Displaying the images for which we need to test (1st parameter in the function), 2nd and 3rd parameters specify the width and height of the images. 4th parameter is to specify how many images we want to display in a column.

# + id="8d926144"
showImages(test_files, wSize=20, hSize=20, col=4)


# -

# Below function is to plot the breed distribution. 

# + id="698c5916"
## this function plot the breeds distribution 
def plot_breed(df):
    labels = []
    for i in range(df.shape[0]):
        labels.append(dog_names[np.argmax(df[i])])

    df_labels = pd.DataFrame(np.array(labels), columns=["breed"]).reset_index(drop=True)

    fig, ax = plt.subplots(figsize=(10,30))
    df_labels['breed'].value_counts().plot(ax=ax, kind='barh').invert_yaxis()
    ax.set_title('Distribution of Dog breeds')


# + id="0b19100e"
## breed distribution in test data
import matplotlib.pyplot as plt
plot_breed(test_targets)

# + id="2ec18058"
## breed distribution in train data
plot_breed(train_targets)


# + id="609663a8"
def display_img(img_path):
    img = cv2.imread(img_path)
    cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    imgplot = plt.imshow(cv_rgb)
    return imgplot


# + id="d0daaee3"
import cv2
import numpy as np
from matplotlib import pyplot as plt

#### this function returns the shape of image, image itself and the  intensity distribution of an image
def img_hist(df_image, label):
    img = cv2.imread(df_image)
    color = ('b','g','r')
    for i,col in enumerate(color):
        histr = cv2.calcHist([img],[i],None,[256],[0,256])
        plt.plot(histr,color = col)
        plt.xlim([0,256])
        
    print(dog_names[np.argmax(label)])
    print(img.shape)
    plt.show()
    #plt.imshow(img)
    display_img(df_image)


# + id="8a54aa5a"
## here I checked the image sizes, and the  intensity distribution of images from the same breed
## and the result shows images have different resolution, zoom and lightening conditins even for the same breed
## it makes this task even more challenging 
img_hist(train_files[3], train_targets[3])

# + id="b28f665c"
img_hist(train_files[4],train_targets[4])

# + id="385b78fb"
img_hist(train_files[57], train_targets[57])
# -

# Below function is to find the list of labels and save them as a pandas data 

# + id="19a0dc97"
labels_train = []
labels_test = []

for i in range(train_files.shape[0]):
    labels_train.append(dog_names[np.argmax(train_targets[i])])
    
for i in range(test_files.shape[0]):
    labels_test.append(dog_names[np.argmax(test_targets[i])])
# -

# This function plots the breeds distribution in the train dataset

# + id="47058c1d"
from sklearn.preprocessing import LabelEncoder

def dist_breed(labels):
    encoder = LabelEncoder()
    breeds_encoded = encoder.fit_transform(labels)
    n_classes = len(encoder.classes_)
    
    breeds = pd.DataFrame(np.array(breeds_encoded), columns=["breed"]).reset_index(drop=True)
    breeds['freq'] = breeds.groupby('breed')['breed'].transform('count')
    avg = breeds.freq.mean()
    
    title = 'Distribution of Dog Breeds in training Dataset\n (%3.0f samples per class on average)' % avg
    f, ax = plt.subplots(1, 1, figsize=(10, 6))
    ax.set_xticks([])
    
    ax.hlines(avg, 0, n_classes - 1, color='white')
    ax.set_title(title, fontsize=18)
    _ = ax.hist(breeds_encoded, bins=n_classes)
    
    return(breeds["freq"].describe())


# + id="639eb127"
dist_breed(labels_train)
# -

# We use a pre-trained ResNet-50 model to detect dogs in images. The first line of code downloads the ResNet-50 model, along with weights that have been trained on ImageNet, a very large, very popular dataset used for image classification and other vision tasks. ImageNet contains over 10 million URLs, each linking to an image containing an object from one of 1000 categories. Given an image, this pre-trained ResNet-50 model returns a prediction (derived from the available categories in ImageNet) for the object that is contained in the image.

# + id="83af70f2"
from tensorflow.python.keras.applications.resnet import ResNet50

# define ResNet50 model
ResNet50_model = ResNet50(weights='imagenet')

# + [markdown] id="f9147305"
# ### **Pre-process the Data**
# When using TensorFlow as backend, Keras CNNs require a 4D array (4D tensor) as input, with shape (nb_samples, rows, columns, channels), where nb_samples corresponds to the total number of images (or samples), and rows, columns, and channels correspond to the number of rows, columns, and channels for each image, respectively.
#
# The path_to_tensor() function takes a string-valued file path to a color image as input and returns a 4D tensor suitable for supplying to a Keras CNN. The function first loads the image and resizes it to a square image that is 224 x 224 pixels. Next, the image is converted to an array, which is then resized to a 4D tensor. In this case, since we are working with color images, each image has three channels. Likewise, since we are processing a single image (or sample), the returned tensor will always have shape (1,224,224,3)
#
# The paths_to_tensor function takes a numpy array of string-valued image paths as input and returns a 4D tensor with shape (nb_samples, 224, 224, 3)
#
# Here, nb_samples is the number of samples, or number of images, in the supplied array of image paths. We take nb_samples as the number of 3D tensors (where each 3D tensor corresponds to a different image) in this dataset.

# + id="456f081a"
from keras.preprocessing import image                  
from tqdm import tqdm

def path_to_tensor(img_path):
    # loads RGB image as PIL.Image.Image type
    img = image.load_img(img_path, target_size=(224, 224))
    # convert PIL.Image.Image type to 3D tensor with shape (224, 224, 3)
    x = image.img_to_array(img)
    # convert 3D tensor to 4D tensor with shape (1, 224, 224, 3) and return 4D tensor
    return np.expand_dims(x, axis=0)

def paths_to_tensor(img_paths):
    list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
    return np.vstack(list_of_tensors)


# + [markdown] id="_fGd7JZ4g8GR"
# ### **Making Predictions with ResNet-50**
# Getting the 4D tensor ready for ResNet-50, and for any other pre-trained model in Keras, requires some additional processing. First, the RGB image is converted to BGR by reordering the channels. All pre-trained models have the additional normalization step that the mean pixel (expressed in RGB as [103.939, 116.779, 123.68] and calculated from all pixels in all images in ImageNet) must be subtracted from every pixel in each image (implemented in the imported function preprocess_input)
#
# Using the model to extract the predictions: Predict method, returns an array whose -th entry is the model's predicted probability that the image belongs to the -th ImageNet category (implemented in the ResNet50_predict_labels function)
#
# By taking the argmax of the predicted probability vector, we obtain an integer corresponding to the model's predicted object class, which we can identify with an object category through the use of this [dictionary](https://gist.github.com/yrevar/942d3a0ac09ec9e5eb3a).

# + id="ea14db27"
from tensorflow.python.keras.applications.imagenet_utils import preprocess_input, decode_predictions

def ResNet50_predict_labels(img_path):
    # returns prediction vector for image located at img_path
    img = preprocess_input(path_to_tensor(img_path))
    return np.argmax(ResNet50_model.predict(img))


# + [markdown] id="uh28hyT9zylA"
# ### **Write a Dog Detector**
# In the dictionary, the categories corresponding to dogs appear in an uninterrupted sequence and correspond to dictionary keys 151-268, inclusive, to include all categories from 'Chihuahua' to 'Mexican hairless'. Thus, in order to check to see if an image is predicted to contain a dog by the pre-trained ResNet-50 model, we need only check if the ResNet50_predict_labels function above returns a value between 151 and 268 (inclusive). 
#
# Implemented using dog_detector() function, which returns True if a dog is detected in an image (and False if not)

# + id="ssbmkbctzuBS"
### returns "True" if a dog is detected in the image stored at img_path
def dog_detector(img_path):
    prediction = ResNet50_predict_labels(img_path)
    return ((prediction <= 268) & (prediction >= 151))


# + [markdown] id="1qsI28fb329m"
# ### **Create a CNN to Classify Dog Breeds**
# -

# ### Pre-process the Data
# We rescale the images by dividing every pixel in every image by 255.

# + id="JKGYVTt337Ww"
from PIL import ImageFile                            
ImageFile.LOAD_TRUNCATED_IMAGES = True                 

# pre-process the data for Keras
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255

# + [markdown] id="zp9dmodi848S"
# Created a 4-layer CNN in Keras that classifies dog breeds with Relu activation function. The model starts with an input image of (224,224,3) color channels. The first layer produces an output with 16 feature channels that is used as an input for the next layer. The second, third, and last layer have 32, 64, 128 filters, respectively, with max-pooling of size 2. It would be ideal for input and output features to have the same size. So, I decided to use same padding, to go off the edge of images and pad with zeros, for all the layers in my network with the stride of 1. The number of nodes in the last fully connected layer is 133, the same size as dog categories, with softmax function to get the probabilities.

# + id="zu1w36GS87Zi"
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.models import Sequential

model = Sequential()

# architucture

# layer 1
model.add(Conv2D(filters=16, kernel_size=2, padding='same', activation='relu', input_shape=(224,224,3)))
model.add(MaxPooling2D(pool_size=2))

# layer 2
model.add(Conv2D(filters=32, kernel_size=2 , padding='same' , activation='relu'))
model.add(MaxPooling2D(pool_size=2))

# layer 3
model.add(Conv2D(filters=64 , kernel_size=2 , padding='same' , activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.4))

# layer 4
model.add(Conv2D(filters=128 , kernel_size=2 , padding='same' , activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.4))

# 2 fully connected layers
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(133,activation='softmax'))

model.summary()

# + id="_asGMIRi9Ug5"
# compile the model
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
# -

# Using model checkpointing to save the model that attains the best validation loss.

# + id="hJD9c-4y9Uah"
from keras.callbacks import ModelCheckpoint  

epochs = 25

checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5', 
                               verbose=1, save_best_only=True)

history = model.fit(train_tensors, train_targets, 
          validation_data=(valid_tensors, valid_targets),
          epochs=epochs, batch_size=20, callbacks=[checkpointer], verbose=1)

# + id="rFDa3Fjy-XgR"
# Load the Model with the Best Validation Loss
model.load_weights('saved_models/weights.best.from_scratch.hdf5')

# + [markdown] id="FAuEHzrv_M2Q"
# ### **Testing the model**
# -

# Trying out model on the test dataset of dog images. Aim to get test accuracy is greater than 1%

# + id="iLYv-rhQ-ctz"
# get index of predicted dog breed for each image in test set
dog_breed_predictions = [np.argmax(model.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]

# report test accuracy
test_accuracy = 100*np.sum(np.array(dog_breed_predictions)==np.argmax(test_targets, axis=1))/len(dog_breed_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)
# -

# In order to make the most of our few training examples, we will "augment" them via a number of random transformations, so that our model would never see twice the exact same picture. This helps prevent overfitting and helps the model generalize better.

# + id="jMA7rW0m_09g"
## data augmentation
from keras.preprocessing.image import ImageDataGenerator

## create a generator that rotate, zoom and flip the images
traingen = ImageDataGenerator(rotation_range=40,
        width_shift_range=0.1,
        height_shift_range=0.1,
        rescale=1/255,
        shear_range=0.04,
        zoom_range=0.2,
        horizontal_flip=True,
        vertical_flip= False,
        fill_mode='nearest')
validgen = ImageDataGenerator(rescale=1/255)

## apply the generator on test and valid sets
traingen.fit(train_tensors)
validgen.fit(valid_tensors)

df_training = traingen.flow(train_tensors , train_targets , batch_size = 20)
df_validation = validgen.flow(valid_tensors , valid_targets, batch_size = 20)

# + id="t6Rd7IFmACpD"
from tensorflow.keras.optimizers import Adam
model.compile(optimizer= Adam(), loss='categorical_crossentropy', metrics=['accuracy'])

# + id="ae9ncQK9Aehf"
from keras.callbacks import ModelCheckpoint

checkpointer = ModelCheckpoint(filepath='saved_models/weights.initial_scratch_model_aug.hdf5', verbose = 0, save_best_only=True)
model.fit_generator(df_training, epochs = 25 , steps_per_epoch = train_tensors.shape[0]//32 , 
                   callbacks=[checkpointer] , verbose=1 , 
                   validation_data= df_validation , validation_steps = valid_tensors.shape[0]//32)

# + id="ISNxHGrqAgtn"
## Here, the data augmenation accuracy becomes lower than original data. It usually causes overfitting, but in this case it is underfitting.
## need to work more on the generator 
dog_breed_predictions_aug = [np.argmax(model.predict(np.expand_dims(tensor, axis = 0))) for tensor in test_tensors]

test_accuracy_aug = 100*np.sum(np.array(dog_breed_predictions_aug)==np.argmax(test_targets, axis=1))/len(dog_breed_predictions_aug)
print('Test accuracy with Data Augmentation: %.f%%' % test_accuracy_aug)


# +
def extract_VGG16(tensor): 
	from keras.applications.vgg16 import VGG16, preprocess_input
	return VGG16(weights='imagenet', include_top=False).predict(preprocess_input(tensor))

def extract_VGG19(tensor):
	from keras.applications.vgg19 import VGG19, preprocess_input
	return VGG19(weights='imagenet', include_top=False).predict(preprocess_input(tensor))

def extract_Resnet50(tensor):
	from keras.applications.resnet50 import ResNet50, preprocess_input
	return ResNet50(weights='imagenet', include_top=False).predict(preprocess_input(tensor))

def extract_Xception(tensor):
	from keras.applications.xception import Xception, preprocess_input
	return Xception(weights='imagenet', include_top=False).predict(preprocess_input(tensor))

def extract_InceptionV3(tensor):
	from keras.applications.inception_v3 import InceptionV3, preprocess_input
	return InceptionV3(weights='imagenet', include_top=False).predict(preprocess_input(tensor))
# -

# ## **Use a CNN to Classify Dog Breeds**
# To reduce training time without sacrificing accuracy, train a CNN using transfer learning. 

# + id="N0gUR8IKB8In"
# Obtain Bottleneck Features
import numpy as np
bottleneck_features = np.load(r'../input/vgg16-data/DogVGG16Data.npz')
train_VGG16 = bottleneck_features['train']
valid_VGG16 = bottleneck_features['valid']
test_VGG16 = bottleneck_features['test']

train_VGG16.shape[1:]

# + [markdown] id="PSx-Vmv6CCpO"
# ### **Model Architecture**
# The model uses the the pre-trained VGG-16 model as a fixed feature extractor, where the last convolutional output of VGG-16 is fed as input to our model. We only add a global average pooling layer and a fully connected layer, where the latter contains one node for each dog category and is equipped with a softmax.

# + id="P8p2EunSCCCd"
VGG16_model = Sequential()
VGG16_model.add(GlobalAveragePooling2D(input_shape=train_VGG16.shape[1:]))
VGG16_model.add(Dense(133, activation='softmax'))

VGG16_model.summary()

# + [markdown] id="mRSjvE4GCTQN"
# ### **Compile the Model**

# + id="yuN43deVCOit"
VGG16_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

# + [markdown] id="jeO5dzdlCXnO"
# ### **Train the Model**

# + id="AHD6mi8FCr_F"
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.VGG16.hdf5', 
                               verbose=1, save_best_only=True)

VGG16_model.fit(train_VGG16, train_targets, 
          validation_data=(valid_VGG16, valid_targets),
          epochs=20, batch_size=20, callbacks=[checkpointer], verbose=1)

# + id="RfTdyVYxCzrN"
# Load the Model with the Best Validation Loss
VGG16_model.load_weights('saved_models/weights.best.VGG16.hdf5')

# + id="GszWUUYSC4JX"
# Test the Model

# get index of predicted dog breed for each image in test set
VGG16_predictions = [np.argmax(VGG16_model.predict(np.expand_dims(feature, axis=0))) for feature in test_VGG16]

# report test accuracy
test_accuracy = 100*np.sum(np.array(VGG16_predictions)==np.argmax(test_targets, axis=1))/len(VGG16_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)


# + [markdown] id="wBgbnUQdDGb7"
# ### **Predict Dog Breed with the Model**

# + id="V_ltcr5pDDnk"
#from extract_bottleneck_features import extract_VGG16

def VGG16_predict_breed(img_path):
    # extract bottleneck features
    bottleneck_feature = extract_VGG16(path_to_tensor(img_path))
    # obtain predicted vector
    predicted_vector = VGG16_model.predict(bottleneck_feature)
    # return dog breed that is predicted by the model
    return dog_names[np.argmax(predicted_vector)]


# + id="HFR3oPi0DLSD"
def display_img(img_path):
    img = cv2.imread(img_path)
    cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    imgplot = plt.imshow(cv_rgb)
    return imgplot


# + [markdown] id="4mxQpGQqDW4j"
# ### **Creating a CNN to Classify Dog Breeds (using Transfer Learning)**

# + id="wyicqcA8DWc2"
def other_bottleneck_features(path):
    bottleneck_features = np.load(path)
    train = bottleneck_features['train'] 
    valid = bottleneck_features['valid']
    test = bottleneck_features['test']
    return train,valid,test


# + id="ooph6KB7F_4s"
### Obtain bottleneck features from another pre-trained CNN.
train_Xception , valid_Xception, test_Xception = other_bottleneck_features('../input/xception/DogXceptionData.npz')
train_Resnet50 , valid_Resnet50, test_Resnet50 = other_bottleneck_features('../input/resnet/DogResnet50Data.npz')
train_VGG19 , valid_VGG19, test_VGG19 = other_bottleneck_features('../input/vgg16-data/DogVGG16Data.npz')
train_Inception , valid_Inception, test_Inception = other_bottleneck_features('../input/inception/DogInceptionV3Data.npz')

# + id="XdimmgdBKUpT"
Xception_model = Sequential()
Xception_model.add(GlobalAveragePooling2D(input_shape=(train_Xception.shape[1:])))
Xception_model.add(Dense(133, activation='softmax'))

Xception_model.summary()

# + id="6WVp-LPsK1nq"
Resnet50_model = Sequential()
Resnet50_model.add(GlobalAveragePooling2D(input_shape=(train_Resnet50.shape[1:])))
Resnet50_model.add(Dense(133, activation='softmax'))

Resnet50_model.summary()

# + id="XvpQrbJrK2rh"
VGG19_model = Sequential()
VGG19_model.add(GlobalAveragePooling2D(input_shape=(train_VGG19.shape[1:])))
VGG19_model.add(Dense(133, activation='softmax'))

VGG19_model.summary()

# + id="hn5CPkW3LABg"
Inception_model = Sequential()
Inception_model.add(GlobalAveragePooling2D(input_shape=(train_Inception.shape[1:])))
Inception_model.add(Dense(133, activation='softmax'))

Inception_model.summary()

# + id="uAXce5uZLKRb"
### Compile the model
Xception_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
Resnet50_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
VGG19_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
Inception_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

# + id="Zx4uxC9JLOxQ"
### Train the model.
checkpointer_Xception = ModelCheckpoint(filepath='saved_models/weights.best.Xception.hdf5', verbose=1 , save_best_only =True)

Xception_history = Xception_model.fit(train_Xception, train_targets,
                  validation_data = (valid_Xception , valid_targets),
                  epochs=25, batch_size=20, callbacks=[checkpointer_Xception], verbose=1)

# + id="LQDewPdXLUUw"
### Train the model.
checkpointer_Resnet50 = ModelCheckpoint(filepath='saved_models/weights.best.Resnet50.hdf5', verbose=1 , save_best_only =True)

Resnet50_model.fit(train_Resnet50, train_targets,
                  validation_data = (valid_Resnet50 , valid_targets),
                  epochs=25, batch_size=20, callbacks=[checkpointer_Resnet50], verbose=1)

# + id="SgksSweLLYuK"
### Train the model.
checkpointer_VGG19 = ModelCheckpoint(filepath='saved_models/weights.best.VGG19.hdf5', verbose=1 , save_best_only =True)

VGG19_model.fit(train_VGG19, train_targets,
                  validation_data = (valid_VGG19 , valid_targets),
                  epochs=25, batch_size=20, callbacks=[checkpointer_VGG19], verbose=1)

# + id="XXhiaAMlLd9w"
### Train the model.
checkpointer_Inception = ModelCheckpoint(filepath='saved_models/weights.best.Inception.hdf5', verbose=1 , save_best_only =True)

Inception_model.fit(train_Inception, train_targets,
                  validation_data = (valid_Inception , valid_targets),
                  epochs=25, batch_size=20, callbacks=[checkpointer_Inception], verbose=1)

# + id="OpwnhEz0Lh93"
### Load the model weights with the best validation loss.
Xception_model.load_weights('saved_models/weights.best.Xception.hdf5')
Resnet50_model.load_weights('saved_models/weights.best.Resnet50.hdf5')
VGG19_model.load_weights('saved_models/weights.best.VGG19.hdf5')
Inception_model.load_weights('saved_models/weights.best.Inception.hdf5')

# + id="JeseeoSgLlub"
Xception_model.load_weights('saved_models/weights.best.Xception.hdf5')


# + id="YPzhuf_7Lni3"
# a function that returns the prediction accuracy on test data
def evaluate_model (model, model_name,tensors,targets):
    predicted = [np.argmax(model.predict(np.expand_dims(feature, axis=0))) for feature in tensors]
    test_accuracy = 100*np.sum(np.array(predicted)==np.argmax(targets, axis=1))/len(predicted)
    
    print (f'{model_name} accuracy on test data is {test_accuracy}%') 


# + id="dDgL7H84LrLo"
###  Calculate classification accuracy on the test dataset.
evaluate_model(Xception_model, "Xception" , test_Xception, test_targets)
evaluate_model(Resnet50_model,"Resnet50", test_Resnet50, test_targets)
evaluate_model(VGG19_model,"VGG19", test_VGG19, test_targets)
evaluate_model(Inception_model,"InceptionV3", test_Inception, test_targets)

# + id="e6LZLlEMLwnC"
## plot the history of loss and accuracy for train and valid data for the best model, Xception
loss = Xception_history.history['loss']
val_loss = Xception_history.history['val_loss']

plt.figure(figsize=(10,8))
plt.plot(loss,"--", linewidth=3 , label="train")
plt.plot(val_loss, linewidth=3 , label="valid")

plt.legend(['train','test'], loc='upper left')
plt.grid()
plt.ylabel('loss')
plt.xlabel('Epoch')
plt.title('Xception Model Loss')
plt.legend(['train','test'], loc='upper left')
plt.show()

# + id="BRmAzjxnLyCL"
## Xception model with augmentation and fine tuning
from keras.optimizers import SGD
from keras.preprocessing.image import load_img, img_to_array, ImageDataGenerator
from keras.layers.normalization import BatchNormalization

Xception_model_aug = Sequential()
Xception_model_aug.add(GlobalAveragePooling2D(input_shape=(train_Xception.shape[1:])))
Xception_model_aug.add(BatchNormalization())
Xception_model_aug.add(Dense(133, activation='softmax'))

traingen = ImageDataGenerator(rotation_range=40,
        width_shift_range=0.2,
        height_shift_range=0.2,
        rescale=1/255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True,
        fill_mode='nearest')
validgen = ImageDataGenerator(rescale=1/255)

traingen.fit(train_Xception)
validgen.fit(valid_Xception)

df_training = traingen.flow(train_Xception , train_targets , batch_size = 20)
df_validation = validgen.flow(valid_Xception , valid_targets, batch_size = 20)

checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.Xception.hdf5', verbose = 0, save_best_only=True)

sgd = SGD(lr= 1e-3 , decay=1e-6, momentum=0.9 , nesterov = True)

# compile 
Xception_model_aug.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])


Xception_model_aug.fit_generator(df_training, epochs = 25 , steps_per_epoch = train_Xception.shape[0]//20 , 
                   callbacks=[checkpointer] , verbose=1 , 
                   validation_data= df_validation , validation_steps = valid_Xception.shape[0]//20)

# + id="S_7TUQmjL8qf"
## here, again, with data augentation the accuracy decreased
evaluate_model(Xception_model_aug, "fine_tuned Xception" , test_Xception, test_targets)

# + id="C_lEJkI3MAwG"
## finetuned the Exception model by minimizing the cross-entropy loss function using stochastic gradient descent 
## and learning rate of 0.001

Xception_model_aug = Sequential()
Xception_model_aug.add(GlobalAveragePooling2D(input_shape=(train_Xception.shape[1:])))
# Xception_model_aug.add(BatchNormalization())
Xception_model_aug.add(Dense(133, activation='softmax'))


checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.Xception.hdf5', verbose = 0, save_best_only=True)

sgd = SGD(lr= 1e-3 , decay=1e-6, momentum=0.9 , nesterov = True)

# compile 
Xception_model_aug.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])


Xception_model_aug.fit(train_Xception , train_targets, 
               validation_data = (valid_Xception, valid_targets),
               shuffle = True,
               batch_size = 20,
               epochs = 25,
               verbose = 1)

# + id="Y6YTFcoQMHGE"
## with fine tuning the accuracy increased by almost 1.5% 
evaluate_model(Xception_model_aug, "fine_tuned Xception" , test_Xception, test_targets)

# + id="h1r-dQF5MHwX"
## I also wanted to fine tune the Resnet50 model to see how the accuracy will change, since I was sure it woul perform well 
## on this kind of animal images. 

Resnet50_model_fine = Sequential()
Resnet50_model_fine.add(GlobalAveragePooling2D(input_shape=(train_Xception.shape[1:])))
# Xception_model_aug.add(BatchNormalization())
Resnet50_model_fine.add(Dense(133, activation='softmax'))


checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.Resnet50.hdf5', verbose = 0, save_best_only=True)

sgd = SGD(lr= 1e-3 , decay=1e-6, momentum=0.9 , nesterov = True)

# compile 
Resnet50_model_fine.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])


Resnet50_model_fine.fit(train_Xception , train_targets, 
               validation_data = (valid_Xception, valid_targets),
               shuffle = True,
               batch_size = 20,
               epochs = 25,
               verbose = 1)

# + id="n5zwB1pBMMFA"
## the same accuracy as Xception 
evaluate_model(Resnet50_model_fine, "fine_tuned Resnet50" , test_Xception, test_targets)


# + id="qv9DRx7BMO2P"
### a function that takes a path to an image as input
### and returns the dog breed that is predicted by the model.
def Xception_predict_breed (img_path):
    # extract the bottle neck features
    bottleneck_feature = extract_Xception(path_to_tensor(img_path)) 
    ## get a vector of predicted values
    predicted_vector = Xception_model.predict(bottleneck_feature) 
    
    ## return the breed
    return dog_names[np.argmax(predicted_vector)]


# + id="_qoQT-G4MWt3"
import cv2
from matplotlib import pyplot as plt
def display_img(img_path):
    img = cv2.imread(img_path)
    cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    imgplot = plt.imshow(cv_rgb)
    return imgplot

def breed_identifier(img_path):
    display_img(img_path)
    prediction = Xception_predict_breed(img_path)
    if dog_detector(img_path) == True:
        print('picture is a dog')
        return print (f"This dog is a {prediction}\n")
    else:
        return print('Hm we couldn''t identify, try a differnt photo')


# + id="TJN8lA6CMgH-"
breed_identifier(r'../input/testing/dogtest.jfif')
# -

breed_identifier(r'../input/testing/dog2test.jfif')

StarcoderdataJupyterScriptsDedupFiltered