{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "c581bca7-1af0-baaa-6dea-36e907de10be"
      },
      "outputs": [],
      "source": [
        "# This Python 3 environment comes with many helpful analytics libraries installed\n",
        "# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n",
        "# For example, here's several helpful packages to load in \n",
        "\n",
        "import numpy as np # linear algebra\n",
        "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n",
        "\n",
        "# Input data files are available in the \"../input/\" directory.\n",
        "# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n",
        "\n",
        "from subprocess import check_output\n",
        "print(check_output([\"ls\", \"../input\"]).decode(\"utf8\"))\n",
        "\n",
        "# Any results you write to the current directory are saved as output."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "18099a07-a1a2-b566-053e-1a4351beb920"
      },
      "outputs": [],
      "source": [
        "%matplotlib inline\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib.cm as cm\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "# settings\n",
        "LEARNING_RATE = 1e-4\n",
        "# set to 20000 on local environment to get 0.99 accuracy\n",
        "TRAINING_ITERATIONS = 2500        \n",
        "    \n",
        "DROPOUT = 0.5\n",
        "BATCH_SIZE = 50\n",
        "\n",
        "# set to 0 to train on all available data\n",
        "VALIDATION_SIZE = 2000\n",
        "\n",
        "# image number to output\n",
        "IMAGE_TO_DISPLAY = 10"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "b0eb7ccd-40e7-94f6-1d5a-b3d651ff31b6"
      },
      "outputs": [],
      "source": [
        "# read training data from CSV file \n",
        "data = pd.read_csv('../input/train.csv')\n",
        "\n",
        "print('data({0[0]},{0[1]})'.format(data.shape))\n",
        "print (data.tail())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "8f959fbb-ccc8-bcd9-d0d3-4ff13af8cde6"
      },
      "outputs": [],
      "source": [
        "images = data.iloc[:,1:].values\n",
        "print (len(images[0]))\n",
        "images = images.astype(np.float)\n",
        "\n",
        "# convert from [0:255] => [0.0:1.0]\n",
        "images = np.multiply(images, 1.0 / 255.0)\n",
        "\n",
        "print('images({0[0]},{0[1]})'.format(images.shape))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "4467dcc5-dee3-6328-eb19-03443ced0f36"
      },
      "outputs": [],
      "source": [
        "image_size = images.shape[1]\n",
        "print ('image_size => {0}'.format(image_size))\n",
        "\n",
        "# in this case all images are square\n",
        "image_width = image_height = np.ceil(np.sqrt(image_size)).astype(np.uint8)\n",
        "\n",
        "print ('image_width => {0}\\nimage_height => {1}'.format(image_width,image_height))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "999797bc-d83d-fd02-b971-fda82e975533"
      },
      "outputs": [],
      "source": [
        "# display image\n",
        "def display(img):\n",
        "    \n",
        "    # (784) => (28,28)\n",
        "    one_image = img.reshape(image_width,image_height)\n",
        "    \n",
        "    plt.axis('off')\n",
        "    plt.imshow(one_image, cmap=cm.binary)\n",
        "\n",
        "# output image     \n",
        "display(images[IMAGE_TO_DISPLAY])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "6b9d8fed-846b-8a14-881c-4d97963e02a3"
      },
      "outputs": [],
      "source": [
        "labels_flat = data[[0]].values.ravel()\n",
        "\n",
        "print('labels_flat({0})'.format(len(labels_flat)))\n",
        "print ('labels_flat[{0}] => {1}'.format(IMAGE_TO_DISPLAY,labels_flat[IMAGE_TO_DISPLAY]))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "4a34b682-166f-4265-6690-dd21872b50e8"
      },
      "outputs": [],
      "source": [
        "labels_count = np.unique(labels_flat).shape[0]\n",
        "\n",
        "print('labels_count => {0}'.format(labels_count))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "e7af0dce-f8b9-5d13-3309-b60a0b1dc1e1"
      },
      "outputs": [],
      "source": [
        "# convert class labels from scalars to one-hot vectors\n",
        "# 0 => [1 0 0 0 0 0 0 0 0 0]\n",
        "# 1 => [0 1 0 0 0 0 0 0 0 0]\n",
        "# ...\n",
        "# 9 => [0 0 0 0 0 0 0 0 0 1]\n",
        "def dense_to_one_hot(labels_dense, num_classes):\n",
        "    num_labels = labels_dense.shape[0]\n",
        "    index_offset = np.arange(num_labels) * num_classes\n",
        "    labels_one_hot = np.zeros((num_labels, num_classes))\n",
        "    labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1\n",
        "    return labels_one_hot\n",
        "\n",
        "labels = dense_to_one_hot(labels_flat, labels_count)\n",
        "labels = labels.astype(np.uint8)\n",
        "print('labels({0[0]},{0[1]})'.format(labels.shape))\n",
        "print ('labels[{0}] => {1}'.format(IMAGE_TO_DISPLAY,labels[IMAGE_TO_DISPLAY]))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "bf2bc784-2d78-3df7-5c8c-1074d0f9d7d7"
      },
      "outputs": [],
      "source": [
        "# split data into training & validation\n",
        "validation_images = images[:VALIDATION_SIZE]\n",
        "validation_labels = labels[:VALIDATION_SIZE]\n",
        "\n",
        "train_images = images[VALIDATION_SIZE:]\n",
        "train_labels = labels[VALIDATION_SIZE:]\n",
        "\n",
        "\n",
        "print('train_images({0[0]},{0[1]})'.format(train_images.shape))\n",
        "print('validation_images({0[0]},{0[1]})'.format(validation_images.shape))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "2013f718-0f67-7e7e-b570-4752eef05d6b"
      },
      "outputs": [],
      "source": [
        "# weight initialization\n",
        "def weight_variable(shape):\n",
        "    initial = tf.truncated_normal(shape, stddev=0.1)\n",
        "    return tf.Variable(initial)\n",
        "\n",
        "def bias_variable(shape):\n",
        "    initial = tf.constant(0.1, shape=shape)\n",
        "    return tf.Variable(initial)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "7a76fa37-43bd-2d04-bc5d-0eb9a9056ffe"
      },
      "outputs": [],
      "source": [
        "# convolution\n",
        "def conv2d(x, W):\n",
        "    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "4d41c05d-7df2-fb3f-4a5e-eb2e9eb94a7b"
      },
      "outputs": [],
      "source": [
        "# pooling\n",
        "# [[0,3],\n",
        "#  [4,2]] => 4\n",
        "\n",
        "# [[0,1],\n",
        "#  [1,1]] => 1\n",
        "\n",
        "def max_pool_2x2(x):\n",
        "    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "d8bab950-748b-4ecc-bb66-dce324032ed6"
      },
      "outputs": [],
      "source": [
        "# input & output of NN\n",
        "\n",
        "# images\n",
        "x = tf.placeholder('float', shape=[None, image_size])\n",
        "# labels\n",
        "y_ = tf.placeholder('float', shape=[None, labels_count])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "92b75607-b73f-a23a-ea3f-d0a536d0f9f9"
      },
      "outputs": [],
      "source": [
        "# first convolutional layer\n",
        "W_conv1 = weight_variable([5, 5, 1, 32])\n",
        "b_conv1 = bias_variable([32])\n",
        "\n",
        "# (40000,784) => (40000,28,28,1)\n",
        "image = tf.reshape(x, [-1,image_width , image_height,1])\n",
        "#print (image.get_shape()) # =>(40000,28,28,1)\n",
        "\n",
        "\n",
        "h_conv1 = tf.nn.relu(conv2d(image, W_conv1) + b_conv1)\n",
        "#print (h_conv1.get_shape()) # => (40000, 28, 28, 32)\n",
        "h_pool1 = max_pool_2x2(h_conv1)\n",
        "#print (h_pool1.get_shape()) # => (40000, 14, 14, 32)\n",
        "\n",
        "\n",
        "# Prepare for visualization\n",
        "# display 32 fetures in 4 by 8 grid\n",
        "layer1 = tf.reshape(h_conv1, (-1, image_height, image_width, 4 ,8))  \n",
        "\n",
        "# reorder so the channels are in the first dimension, x and y follow.\n",
        "layer1 = tf.transpose(layer1, (0, 3, 1, 4,2))\n",
        "\n",
        "layer1 = tf.reshape(layer1, (-1, image_height*4, image_width*8)) "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "9e1c0f5e-8477-584d-2f5a-437f72464315"
      },
      "outputs": [],
      "source": [
        "# second convolutional layer\n",
        "W_conv2 = weight_variable([5, 5, 32, 64])\n",
        "b_conv2 = bias_variable([64])\n",
        "\n",
        "h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)\n",
        "#print (h_conv2.get_shape()) # => (40000, 14,14, 64)\n",
        "h_pool2 = max_pool_2x2(h_conv2)\n",
        "#print (h_pool2.get_shape()) # => (40000, 7, 7, 64)\n",
        "\n",
        "# Prepare for visualization\n",
        "# display 64 fetures in 4 by 16 grid\n",
        "layer2 = tf.reshape(h_conv2, (-1, 14, 14, 4 ,16))  \n",
        "\n",
        "# reorder so the channels are in the first dimension, x and y follow.\n",
        "layer2 = tf.transpose(layer2, (0, 3, 1, 4,2))\n",
        "\n",
        "layer2 = tf.reshape(layer2, (-1, 14*4, 14*16)) "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "554c23f9-8f69-d267-bbf3-54c099eb646e"
      },
      "outputs": [],
      "source": [
        "# densely connected layer\n",
        "W_fc1 = weight_variable([7 * 7 * 64, 1024])\n",
        "b_fc1 = bias_variable([1024])\n",
        "\n",
        "# (40000, 7, 7, 64) => (40000, 3136)\n",
        "h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])\n",
        "\n",
        "h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)\n",
        "#print (h_fc1.get_shape()) # => (40000, 1024)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "a1c37a94-79d8-7ec0-b32a-fedf405458f9"
      },
      "outputs": [],
      "source": [
        "# dropout\n",
        "keep_prob = tf.placeholder('float')\n",
        "h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "f81eeb24-5444-f319-7a16-ac8cba78a589"
      },
      "outputs": [],
      "source": [
        "# readout layer for deep net\n",
        "W_fc2 = weight_variable([1024, labels_count])\n",
        "b_fc2 = bias_variable([labels_count])\n",
        "\n",
        "y = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)\n",
        "\n",
        "#print (y.get_shape()) # => (40000, 10)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "5ff31d67-c24a-a8c0-87cd-90c14e5a68b9"
      },
      "outputs": [],
      "source": [
        "# cost function\n",
        "cross_entropy = -tf.reduce_sum(y_*tf.log(y))\n",
        "\n",
        "\n",
        "# optimisation function\n",
        "train_step = tf.train.AdamOptimizer(LEARNING_RATE).minimize(cross_entropy)\n",
        "\n",
        "# evaluation\n",
        "correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))\n",
        "\n",
        "accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "2701537e-41fa-0966-a6f2-1daa8237b2bc"
      },
      "outputs": [],
      "source": [
        "# prediction function\n",
        "#[0.1, 0.9, 0.2, 0.1, 0.1 0.3, 0.5, 0.1, 0.2, 0.3] => 1\n",
        "predict = tf.argmax(y,1)# prediction function\n",
        "#[0.1, 0.9, 0.2, 0.1, 0.1 0.3, 0.5, 0.1, 0.2, 0.3] => 1\n",
        "predict = tf.argmax(y,1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "5f937960-028c-4ba0-b2e1-59be4ab449a6"
      },
      "outputs": [],
      "source": [
        "epochs_completed = 0\n",
        "index_in_epoch = 0\n",
        "num_examples = train_images.shape[0]\n",
        "\n",
        "# serve data by batches\n",
        "def next_batch(batch_size):\n",
        "    \n",
        "    global train_images\n",
        "    global train_labels\n",
        "    global index_in_epoch\n",
        "    global epochs_completed\n",
        "    \n",
        "    start = index_in_epoch\n",
        "    index_in_epoch += batch_size\n",
        "    \n",
        "    # when all trainig data have been already used, it is reorder randomly    \n",
        "    if index_in_epoch > num_examples:\n",
        "        # finished epoch\n",
        "        epochs_completed += 1\n",
        "        # shuffle the data\n",
        "        perm = np.arange(num_examples)\n",
        "        np.random.shuffle(perm)\n",
        "        train_images = train_images[perm]\n",
        "        train_labels = train_labels[perm]\n",
        "        # start next epoch\n",
        "        start = 0\n",
        "        index_in_epoch = batch_size\n",
        "        assert batch_size <= num_examples\n",
        "    end = index_in_epoch\n",
        "    return train_images[start:end], train_labels[start:end]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "41dca9c6-e469-c80c-9e3b-8314546680ac"
      },
      "outputs": [],
      "source": [
        "# start TensorFlow session\n",
        "init = tf.initialize_all_variables()\n",
        "sess = tf.InteractiveSession()\n",
        "\n",
        "sess.run(init)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "8826efbf-653e-9d6e-e5c6-80c64af6f0ae"
      },
      "outputs": [],
      "source": [
        "# visualisation variables\n",
        "train_accuracies = []\n",
        "validation_accuracies = []\n",
        "x_range = []\n",
        "\n",
        "display_step=1\n",
        "\n",
        "for i in range(TRAINING_ITERATIONS):\n",
        "\n",
        "    #get new batch\n",
        "    batch_xs, batch_ys = next_batch(BATCH_SIZE)        \n",
        "\n",
        "    # check progress on every 1st,2nd,...,10th,20th,...,100th... step\n",
        "    if i%display_step == 0 or (i+1) == TRAINING_ITERATIONS:\n",
        "        \n",
        "        train_accuracy = accuracy.eval(feed_dict={x:batch_xs, \n",
        "                                                  y_: batch_ys, \n",
        "                                                  keep_prob: 1.0})       \n",
        "        if(VALIDATION_SIZE):\n",
        "            validation_accuracy = accuracy.eval(feed_dict={ x: validation_images[0:BATCH_SIZE], \n",
        "                                                            y_: validation_labels[0:BATCH_SIZE], \n",
        "                                                            keep_prob: 1.0})                                  \n",
        "            print('training_accuracy / validation_accuracy => %.2f / %.2f for step %d'%(train_accuracy, validation_accuracy, i))\n",
        "            \n",
        "            validation_accuracies.append(validation_accuracy)\n",
        "            \n",
        "        else:\n",
        "             print('training_accuracy => %.4f for step %d'%(train_accuracy, i))\n",
        "        train_accuracies.append(train_accuracy)\n",
        "        x_range.append(i)\n",
        "        \n",
        "        # increase display_step\n",
        "        if i%(display_step*10) == 0 and i:\n",
        "            display_step *= 10\n",
        "    # train on batch\n",
        "    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys, keep_prob: DROPOUT})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "22affefc-6b61-89ee-b5d8-289f28620f7a"
      },
      "outputs": [],
      "source": [
        "# check final accuracy on validation set  \n",
        "if(VALIDATION_SIZE):\n",
        "    validation_accuracy = accuracy.eval(feed_dict={x: validation_images, \n",
        "                                                   y_: validation_labels, \n",
        "                                                   keep_prob: 1.0})\n",
        "    print('validation_accuracy => %.4f'%validation_accuracy)\n",
        "    plt.plot(x_range, train_accuracies,'-b', label='Training')\n",
        "    plt.plot(x_range, validation_accuracies,'-g', label='Validation')\n",
        "    plt.legend(loc='lower right', frameon=False)\n",
        "    plt.ylim(ymax = 1.1, ymin = 0.7)\n",
        "    plt.ylabel('accuracy')\n",
        "    plt.xlabel('step')\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "06539181-0511-7ce7-1039-f0b15a0b4237"
      },
      "outputs": [],
      "source": [
        "# read test data from CSV file \n",
        "test_images = pd.read_csv('../input/test.csv').values\n",
        "test_images = test_images.astype(np.float)\n",
        "\n",
        "# convert from [0:255] => [0.0:1.0]\n",
        "test_images = np.multiply(test_images, 1.0 / 255.0)\n",
        "\n",
        "print('test_images({0[0]},{0[1]})'.format(test_images.shape))\n",
        "\n",
        "\n",
        "# predict test set\n",
        "#predicted_lables = predict.eval(feed_dict={x: test_images, keep_prob: 1.0})\n",
        "\n",
        "# using batches is more resource efficient\n",
        "predicted_lables = np.zeros(test_images.shape[0])\n",
        "for i in range(0,test_images.shape[0]//BATCH_SIZE):\n",
        "    predicted_lables[i*BATCH_SIZE : (i+1)*BATCH_SIZE] = predict.eval(feed_dict={x: test_images[i*BATCH_SIZE : (i+1)*BATCH_SIZE], \n",
        "                                                                                keep_prob: 1.0})\n",
        "\n",
        "\n",
        "print('predicted_lables({0})'.format(len(predicted_lables)))\n",
        "\n",
        "# output test image and prediction\n",
        "display(test_images[IMAGE_TO_DISPLAY])\n",
        "print ('predicted_lables[{0}] => {1}'.format(IMAGE_TO_DISPLAY,predicted_lables[IMAGE_TO_DISPLAY]))\n",
        "\n",
        "# save results\n",
        "np.savetxt('submission_softmax.csv', \n",
        "           np.c_[range(1,len(test_images)+1),predicted_lables], \n",
        "           delimiter=',', \n",
        "           header = 'ImageId,Label', \n",
        "           comments = '', \n",
        "           fmt='%d')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "4f26176f-c988-9e1a-e154-48c4804e0116"
      },
      "outputs": [],
      "source": [
        "layer1_grid = layer1.eval(feed_dict={x: test_images[IMAGE_TO_DISPLAY:IMAGE_TO_DISPLAY+1], keep_prob: 1.0})\n",
        "plt.axis('off')\n",
        "plt.imshow(layer1_grid[0], cmap=cm.seismic )\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "7acf9fd9-bd22-af8c-e2cf-dd2e16c4ec7a"
      },
      "outputs": [],
      "source": [
        "sess.close()"
      ]
    }
  ],
  "metadata": {
    "_change_revision": 0,
    "_is_fork": false,
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}