import tensorflow as tf

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("./data/MNIST_data/", 
                                  one_hot = True)

"""
Explanation: MNIST Data Set - Basic Approach
Get the MNIST Data
End of explanation
"""


type(mnist)

mnist.train.images

mnist.train.num_examples

mnist.test.num_examples

mnist.validation.num_examples

"""
Explanation: Alternative sources of the data just in case: 

http://yann.lecun.com/exdb/mnist/
https://github.com/mrgloom/MNIST-dataset-in-different-formats
End of explanation
"""


import matplotlib.pyplot as plt
%matplotlib inline

# The image is a long array
mnist.train.images[1].shape

# Showing reshaped image
plt.imshow(mnist.train.images[1].reshape(28, 28))

# Showing the image in gist gray scale
plt.imshow(mnist.train.images[1].reshape(28, 28), 
           cmap = 'gist_gray')

mnist.train.images[1].max()

plt.imshow(mnist.train.images[1].reshape(784, 1))

plt.imshow(mnist.train.images[1].reshape(784, 1),
           cmap = 'gist_gray', 
           aspect = 0.02)

"""
Explanation: Visualizing the Data
End of explanation
"""


# Initializing a Placeholder of shape None (number of inputs) by 784
# Tensorflow requires float32
x = tf.placeholder(tf.float32,
                shape = [None, 784])

# Initializing weights between the input layer and the output layer
# It is of shape number_of_features by number_of_neurons_in_the_layer
# Initializing with zeros, which is meh, but we will use it for simplicity

# 10 because 0-9 possible numbers
W = tf.Variable(tf.zeros([784, 10]))

# Initializing biases 
b = tf.Variable(tf.zeros([10]))

# Create the Graph
y = tf.matmul(x, W) + b 

"""
Explanation: Create the Model
End of explanation
"""


# Initializing a Placeholder of shape None (number of inputs) by number_of_classes
y_true = tf.placeholder(tf.float32, 
                        shape = [None, 10])

# Cross Entropy
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels = y_true, 
                                                                          logits = y))

# Gradient Descent
optimizer = tf.train.GradientDescentOptimizer(learning_rate = 0.5)

# Minimizing the loss function 
train = optimizer.minimize(cross_entropy)

"""
Explanation: Loss and Optimizer
End of explanation
"""


# Intializing all variables
init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)
    
    # Train the model for 1000 steps on the training set
    # Using built in batch feeder from mnist for convenience
    for step in range(1000):
        # Training on a batch of 100 examples
        batch_x , batch_y = mnist.train.next_batch(100)
        
        sess.run(train, feed_dict = {x : batch_x,
                                     y_true : batch_y})
        
    # Calculating the number of matches
    matches = tf.equal(tf.argmax(y, 1), 
                       tf.argmax(y_true, 1))
    
    acc = tf.reduce_mean(tf.cast(matches, tf.float32))
    
    # Calculating the accuracy
    print(sess.run(acc, feed_dict = {x : mnist.test.images, 
                                     y_true : mnist.test.labels}))

"""
Explanation: Create Session
End of explanation
"""


