# dogscats/
#     train/
#         dogs/
#         cats/
#     valid/
#         dogs/
#         cats/
#     test/
#         dogs/
#         cats/
#     sample/
#         <same as for dogscats/>

# Making sure python knows where to look for my utilities folder (it's in this notebook's parent folder).
# I know the naming can be confusing, but it's to not get in the way of the imports below
from os import path as ospath, getcwd; from sys import path as syspath
current_dir = getcwd()
syspath.insert(1, ospath.join(current_dir, '../utils'))
syspath.insert(1, ospath.join(current_dir, '..'))

"""
Explanation: FAI1 - Practical Deep Learning I
Homework Jupyter Notebook for Lesson 1: Image Classification
See the full course at: http://course.fast.ai/
Setup & Imports
Keras requires a directory structure based on the classes you are looking at. Our directory structure will be as follows:
End of explanation
"""


# this ensures matplotlib plots within this window
%matplotlib inline
# path to my sample data directory. Contains 180 imgs in ../train/ and 20 
# imgs in ../valid/ The full dataset contains 25,000 imgs.
# path = 'data/dogscats/sample/'
path = 'data/dogscats/'
# The libraries to be imported (taken from https://github.com/fastai/courses/blob/master/deeplearning1/nbs/lesson1.ipynb)
from __future__ import division, print_function
import os, json
import numpy as np
np.set_printoptions(precision=4, linewidth=100)
from matplotlib import pyplot as plt
import utils; reload(utils)
from utils import plots
import vgg16; reload(vgg16)
from vgg16 import Vgg16

"""
Explanation: Setup matplotlib to plot inside the notebook, import NumPy, SciPy, VGG, and the appropriate Keras libraries.
End of explanation
"""


vgg = Vgg16()

"""
Explanation: Vgg16 Image Recognition Example
We'll do a small test-run of Vgg16 to show what the data & labels look like. Afterwards we'll finetune & fit the model to our dataset (so that Vgg16 is looking for cats vs dogs instead of its standard 1,000 categories), run it on our full test set, and create a submissions csv file to upload to Kaggle and get scored.
First create a Vgg16 object:
End of explanation
"""


batches = vgg.get_batches(path+'train',batch_size=4)

imgs, labels = next(batches)
plots(imgs, titles=labels)

"""
Explanation: Get some batches from the sample training folder. vgg.get_batches(..) is an iterator that returns a batch of images and their labels, indefinitely.
End of explanation
"""


vgg.classes[:5]

"""
Explanation: To get an idea of the categories Vgg16 usually works with, here're the first 5:
End of explanation
"""


batch_size = 32
batches = vgg.get_batches(path+'train', batch_size=batch_size)
val_batches = vgg.get_batches(path+'valid', batch_size=batch_size)

"""
Explanation: Vgg16 finetuned for Dogs vs Cats:
What we do now is finetune and fit our model to our specific dataset. Finetuning further trains a pre-trained Machine-Learning model (Vgg16). A training set is trained on, and a validation set is tested against. In finetuning, we're only changing a small subset of our model's parameters since our pre-trained model is already very close to our final desired model. This is done by calling fit() after calling finetune()
End of explanation
"""


vgg.finetune(batches)
vgg.fit(batches, val_batches, nb_epoch=1)

"""
Explanation: Calling fineunte() modifies the model so that it'll be trained based on the data in the batches provided: dog vs cat. The model's parameters are then fitted using the training data, and accuracy is reported using teh validation set. An epoch is 1 full pass through the training data.
End of explanation
"""


# Save the weights from the first epoch
vgg.model.save_weights(path + 'ft1.hd5')

nb_epoch = 3
vgg.model.optimizer.lr = 0.01
for epoch in xrange(nb_epoch):
    print "Starting Epoch %d" % epoch
    vgg.fit(batches, val_batches, nb_epoch=nb_epoch)
print "Completed %d fit operations" % nb_epoch

"""
Explanation: More training
To try and get into the top 50% of the Cats v Dogs Redux competition, I'm going to train the model for a few more epochs and see how that works out. I'm also going to tune the learning rate to η = 0.01 and see how that works. Since the first epoch took just under 2 hours, I'm going to save the weights from it, just in case.
End of explanation
"""


path = 'data/dogscats/'

"""
Explanation: The model is ready to classify dogs vs cats. In order to complete this assignment, we'll have to create a csv (comma-separated-values) file of IDs and labels of the full 25,000 set of images and upload it to Kaggle for scoring.
First modify the path to the full dataset:
End of explanation
"""


batch_size = 32
batches, predictions = vgg.test(path+'test',batch_size=batch_size)
filenames = batches.filenames

"""
Explanation: vgg.test(..) will return the entire set of batches & predictions (labels). We'll extract the ID-number from batches.filenames, and the label from the second column of predictions.
End of explanation
"""


vgg.model.load_weights(path+'ft1.hd5')

# ids = np.array([int(f[f.find('.')+1:-4]) for f in filenames])
ids = np.array([int(f[8:f.find('.')]) for f in filenames])
dog_predictions = predictions[:,1]

# NOTE: this is important due to logloss error: clip the probabilities
dog_predictions = dog_predictions.clip(min=0.05, max=0.95)
# dog_predictions = np.clip(dog_predictions, 0.05, 0.95) # in case .clip doesn't work
# you know what's funny, as I'm writing this I have no clue where the clip function is coming from..
# Python? NumPy? Utils?

submission = np.stack([ids, dog_predictions], axis=1)
submission_file_name = 'submission_HW1_.csv'
np.savetxt(submission_file_name, submission, fmt='%d,%.5f',header='id,label',comments='')

%pwd

"""
Explanation: The syntax for getting the IDs below is: make an array of the index of the period plus one, to fourth-from-end character, of each item f in filenames. The file extensions are all ".jpg" which is 4 characters long, and the ID number of each file comes after a period as such: "dog.0010.jpg" or "cat.1001.jpg" & etc.
End of explanation
"""


