# -*- coding: utf-8 -*-
"""musawenkosi- Tensorflow Classification Practical4.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1E9TWn9biImKHqPIGzecG-RAfEC-ySwHB

## Classification practical

Total marks: 21
"""

from keras.datasets import mnist
import numpy as np
np.random.seed(13378)
import pandas
import tensorflow as tf
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras import metrics
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score
import matplotlib.pyplot as plt
from keras.utils import np_utils

"""## Load the dataset"""

(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

"""## View the shape"""

print('Training data shape : ', X_train.shape, Y_train.shape)
print('Testing data shape : ', X_test.shape, Y_test.shape)

"""## Find the unique numbers from the train labels"""

classes = np.unique(Y_train)
nClasses = len(classes)
print('Total number of outputs : ', nClasses)
print('Output classes : ', classes)

"""## Plot some of the data"""

plt.figure(figsize=[10,5])
 
# Display the first image in training data
plt.subplot(121)
plt.imshow(X_train[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(Y_train[0]))
 
# Display the first image in testing data
plt.subplot(122)
plt.imshow(X_test[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(Y_test[0]))

"""## Flatten the data

In this notebook we won't be making use of the data as "images" but rather as long vectors of length 784

## This is what an example in the dataset looks like
"""

X_train[0].shape

X_train[0]

"""## Task: Convert from image shape to a vector shape

We go from 28x28 pixel sized images to a vector of length 784.

We would like to reshape the training data from shape (60000, 28, 28) to (60000,784). To do this, we can make use of Numpy's *reshape* function. 

Hint: ...reshape(...).astype('float32')
"""

X_train.shape

num_pixels = X_train.shape[1] * X_train.shape[2]

X_train = np.reshape(X_train, (60000,784))
X_test = np.reshape(X_test, (10000,784))

"""## Now the data is a long vector

There are 60,000 examples for which each is a vector of length 784
"""

X_train.shape

"""## View the first example"""

X_train[0].shape

X_train[0]

"""## Task: Normalise

We need to normalise the data since the values range from 0 to 255. Training NNs on data ranging between [0,1] can be easier. To do this, we simply divide by the maximum value, in this case 255.
"""

X_train = (X_train) / (255)# Your code here
X_test = (X_test) / (255)# Your code here

"""## One hot encoding

We're going to want our labels as one-hot vectors, which are vectors that holds mostly 0's and one 1. It's easiest to see this in a example. As a one-hot vector, the number 0 is represented as [1, 0, 0, 0, 0, 0, 0, 0, 0, 0], and 4 is represented as [0, 0, 0, 0, 1, 0, 0, 0, 0, 0].

One-hot encoded vectors allow us to map each category in our set of labels to a vector where only a single value is 1.

0 maps to [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]

1 maps to [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]

2 maps to [0, 0, 2, 0, 0, 0, 0, 0, 0, 0]

Notes on one-hot encoding: https://machinelearningmastery.com/why-one-hot-encode-data-in-machine-learning/

## Before
"""

Y_test[0]

"""## Task: Convert from categorical labels to one-hot encoded vectors

In this case there are 10 classes so we can tell the function to convert into a vector of length 10. You need to convert both the training targets and the testing targets.
"""

Y_train = np_utils.to_categorical(Y_train)
Y_test = np_utils.to_categorical(Y_test)

"""## After"""

Y_test[0]

"""## Task: Create a neural network model"""

def baseline():
  
    model = Sequential()
    model.add(Dense(20, input_dim=784, activation='relu'))
    model.add(Dense(20, activation='relu'))
    model.add(Dense(10, activation='softmax'))
    
    # Compile model
    model.compile(loss='categorical_crossentropy', 
                  optimizer='adam', 
                  metrics=['accuracy'])
    
    return model

"""## Task: Initialise the model"""

model = baseline()# Your code here

"""## Task: Determine the number of trainable parameters

## 13,002
"""

model.summary()# Your code here

"""## Task: Begin training

Fit on the training features and targets. Also make use of the validation data you've set aside above. Set the number of epochs, batch size and also explore various *verbose* values.
"""

X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=0.3)

history = model.fit(X_train, Y_train, validation_data=(X_val, Y_val) ,epochs = 20)

"""## Task: Predict on the test data"""

prediction = model.predict(X_test)
prediction
prediction_classes = np.argmax(prediction, axis=1)
prediction_classes

confusion_matrix(np.argmax(Y_test,1), prediction_classes)

"""## Task: Compute the accuracy"""

accuracy_score(np.argmax(Y_test,1), prediction_classes)

"""###***Extra Work***"""

def plot_hist(h, xsize=6, ysize=10):

    fig_size = plt.rcParams["figure.figsize"]
    plt.rcParams["figure.figsize"] = [xsize, ysize]
    fig, axes = plt.subplots(nrows=1, ncols=1, sharex=True)
    
    # summarize history for Accuracy
    plt.subplot(211)
    plt.plot(h['accuracy'])
    plt.plot(h['val_accuracy'])
    plt.title('Training Performance')
    plt.ylabel('Accuracy')
    plt.xlabel('Epochs')
    plt.legend(['Train', 'Validation'], loc='best')
    
    plt.draw()
    plt.show()

    return

plot_hist(history.history, xsize=8, ysize=12)