# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow import keras
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow.keras import layers
import keras
from keras.models import Sequential
from keras.layers.core import Dense,Activation,Dropout
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Flatten,Dropout,Conv2D,MaxPooling2D, BatchNormalization
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import EarlyStopping, Callback
from keras.optimizers import Adam
"""
Importing Dataset
"""
train = pd.read_csv('../input/fashionmnist/fashion-mnist_train.csv')
test = pd.read_csv('../input/fashionmnist/fashion-mnist_test.csv')
"""
Checking the shape of the dataset.It has pixels that range from 0-255
"""
print(train.shape)
print(test.shape)
train.head(5)
test.head(5)
"""
Finding any missing values.
"""
print("train missing values:", train.isnull().any().sum())
print("test missing values:", test.isnull().any().sum())
"""
Getting our y into a different variable and splitting the data
"""
X = train.iloc[:,1:] #taking all but the first row
Y = train.iloc[:,0] #taking only the first row as this is the label

#splitting dataframe using train_test_split
x_train , x_test , y_train , y_test = train_test_split(X, Y , test_size=0.1, random_state=42)
class_names = ['T_shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

plt.figure(figsize=(10, 10))
for i in range(36):
    plt.subplot(6, 6, i + 1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(X.loc[i].values.reshape((28,28))) #calling the .values of each row
    label_index = int(Y[i]) #setting as an int as the number is stored as a string
    plt.title(class_names[label_index])
plt.show()
"""
Normalizing the pixels into the range 0 to 1.
"""
x_train =x_train/255.0
x_test=x_test/255.0
#reshape
x_train =x_train.values.reshape(-1, 28,28,1)
#reshape
x_test = x_test.values.reshape(-1, 28,28,1)
#label encoding
y_train = to_categorical(y_train, num_classes=10)
y_test  = to_categorical(y_test, num_classes=10)
print("X_train shape: ", x_train.shape)
print("X_test shape: ", x_test.shape)
print("y_train shape: ", y_train.shape)
print("y_test shape: ", y_test.shape)
"""
Data Augumnetation.Increase the size of data so that model can get more images to train.
"""
from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
        rotation_range= 10,
        zoom_range = 0.1,
        width_shift_range = 0.1,
        height_shift_range = 0.1
)

datagen.fit(x_train)
train_generator = datagen.flow(x_train, y_train, batch_size = 64)

validation_generator = datagen.flow(x_test, y_test, batch_size = 64)
"""
Creating the Model
"""
model = Sequential() # Initialize the sequential model

# Add CNN convolutions with BatchNormalization and MaxPooling2D
# Avoid overfitting with Dropout
model.add(Conv2D(32, kernel_size = (3,3), input_shape=(28, 28, 1), padding = 'Same', activation='relu'))
model.add(Conv2D(64, kernel_size = (3,3), padding = 'Same', activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2), strides=1, padding='valid'))
model.add(Dropout(0.25))
model.add(Conv2D(64, kernel_size = (3,3), padding = 'Same', activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2), strides=1, padding='valid'))
model.add(Dropout(0.2))

# Convert our matrix to 1-D set of features 
model.add(Flatten())

# Add fully-conected layers
model.add(Dense(512, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.20))
model.add(Dense(128, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.35))
model.add(Dense(10, activation='softmax'))
# Defining the call backs EarlyStopping and myCallback which will stop the training
# if the accuracy reaches 99%

class myCallback(Callback):
    def on_epoch_end(self, epoch, logs={}):
        if(logs.get('accuracy')>0.999):
            print("\nReached 99.9% accuracy so cancelling training!")
            self.model.stop_training = True

# Instantiate callback
mycallback = myCallback()


early_stopping_callback = EarlyStopping(monitor='val_loss', 
                                        patience=3,
                                        verbose = 2,
                                        restore_best_weights=True)


from tensorflow.keras.optimizers import Adam

model.compile(optimizer = Adam(lr = 1e-3),
              loss = 'categorical_crossentropy',
              metrics = ['accuracy'])
model.summary()
!pip install visualkeras
import visualkeras

visualkeras.layered_view(model)
history = model.fit_generator(
    train_generator,
    steps_per_epoch = x_train.shape[0] // 128,
    epochs = 50,
    validation_data = validation_generator,
    validation_steps = x_test.shape[0] // 64,
    callbacks = [mycallback,early_stopping_callback]
)
plt.figure(figsize=(10, 7))
acc = history.history["accuracy"]
loss = history.history["loss"]

epochs = range(len(acc))
val_acc = history.history["val_accuracy"]
val_loss = history.history["val_loss"]


plt.plot(epochs, acc, "g", label="Accuracy")
plt.plot(epochs, loss, "r", label="Loss")

plt.plot(epochs, val_acc, "orange", label="Validation Accuracy")
plt.plot(epochs, val_loss, "brown", label="Vlaidation Loss")
plt.title("Model Accuracy And Loss")
plt.legend()
plt.show()
# Save the model
model.save('model.h5')
predict=model.predict(x_test)
y_pred=[]
for i in range(len(predict)):
    y_pred.append(np.argmax(predict[i]))
for i in range(5):
    print(y_pred[i])
y = np.argmax(y_test, axis=-1)
y
mat=confusion_matrix(y,y_pred)
mat
"""
UPVOTE if you like this Notebook :)
"""