{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "2bf6e055-8947-53dd-023d-c2388b034968"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "4d1ef4e0-6ffc-1d30-18e8-67eb7a03af13"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from tqdm import tqdm\n",
        "from IPython.display import display, Image, HTML\n",
        "import os\n",
        "import cv2\n",
        "\n",
        "TRAIN_DIR = '../input/train'\n",
        "TEST_DIR = '../input/test'\n",
        "IMG_SIZE = 64"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "09304d83-35a4-ae72-c0a9-e086e62f3431"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "90dd442c-918c-75c1-51fe-c47efd127943"
      },
      "outputs": [],
      "source": [
        "def create_input_data(im):\n",
        "    img = cv2.imread(im, cv2.IMREAD_COLOR)\n",
        "    if (img.shape[0] >= img.shape[1]): # height is greater than width\n",
        "       resizeto = (IMG_SIZE, int (round (IMG_SIZE * (float (img.shape[1])  / img.shape[0]))));\n",
        "    else:\n",
        "       resizeto = (int (round (IMG_SIZE * (float (img.shape[0])  / img.shape[1]))), IMG_SIZE);\n",
        "    \n",
        "    img2 = cv2.resize(img, (resizeto[1], resizeto[0]), interpolation=cv2.INTER_CUBIC)\n",
        "    img3 = cv2.copyMakeBorder(img2, 0, IMG_SIZE - img2.shape[0], 0, IMG_SIZE - img2.shape[1], cv2.BORDER_CONSTANT, 0)\n",
        "        \n",
        "    return img3[:,:,::-1]\n",
        "#     img = cv2.imread(im, cv2.IMREAD_COLOR)\n",
        "#     img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))\n",
        "#     return np.array(img / 255)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "7dbc9749-607a-19a2-b55d-831912c97259"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "ab674bb9-bb6c-6ef4-08c1-c09e7c4f0573"
      },
      "outputs": [],
      "source": [
        "def one_hot_encode(img):        \n",
        "    if 'cat' in img:\n",
        "        return np.array([0, 1])\n",
        "    else:\n",
        "        return np.array([1, 0])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "73d4e49c-12d2-28be-2f92-89c9af19165d"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "735cf56f-826a-5bf1-45d1-294cecc26a3e"
      },
      "outputs": [],
      "source": [
        "training_img = []\n",
        "training_label = []\n",
        "\n",
        "testing_img = []\n",
        "testing_label = []\n",
        "\n",
        "for img in tqdm(os.listdir(TRAIN_DIR)):\n",
        "    training_path = os.path.join(TRAIN_DIR, img)\n",
        "    \n",
        "    train_img = create_input_data(training_path)\n",
        "    training_img.append(np.array(train_img))\n",
        "    \n",
        "    train_label = one_hot_encode(img)\n",
        "    training_label.append(np.array(train_label))\n",
        "    \n",
        "for img in tqdm(os.listdir(TEST_DIR)):\n",
        "    testing_path = os.path.join(TEST_DIR, img)\n",
        "    \n",
        "    test_img = create_input_data(testing_path)\n",
        "    testing_img.append(np.array(test_img))\n",
        "    \n",
        "    test_label = one_hot_encode(img)\n",
        "    testing_label.append(np.array(test_label))\n",
        "\n",
        "training_img = np.array(training_img, dtype=np.float32)\n",
        "training_label = np.array(training_label, dtype=np.float32)\n",
        "\n",
        "testing_img = np.array(testing_img, dtype=np.float32)\n",
        "testing_label = np.array(testing_label, dtype=np.float32)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "b4df2a05-8f50-b6a0-a414-055388caaa94"
      },
      "outputs": [],
      "source": [
        "index = 15000\n",
        "plt.imshow(training_img[index])\n",
        "plt.show()\n",
        "print(training_label[index])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "25b634c6-434e-25c8-fb98-243b0028fe75"
      },
      "source": [
        "### Input\n",
        "The neural network needs to read the image data, one-hot encoded labels, and dropout keep probability. Implement the following functions\n",
        "* Implement neural_net_image_input\n",
        "    * Return a TF Placeholder    \n",
        "    * Set the shape using image_shape with batch size set to None.\n",
        "    * Name the TensorFlow placeholder \"x\" using the TensorFlow name parameter in the TF Placeholder.\n",
        "* Implement neural_net_label_input\n",
        "    * Return a TF Placeholder\n",
        "    * Set the shape using n_classes with batch size set to None.\n",
        "    * Name the TensorFlow placeholder \"y\" using the TensorFlow name parameter in the TF Placeholder.\n",
        "* Implement neural_net_keep_prob_input\n",
        "    * Return a TF Placeholder for dropout keep probability.\n",
        "    * Name the TensorFlow placeholder \"keep_prob\" using the TensorFlow name parameter in the TF Placeholder.\n",
        "    * These names will be used at the end of the project to load your saved model.\n",
        "\n",
        "Note: _None_ for shapes in TensorFlow allow for a dynamic size."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "bc429bf1-dc6a-6ba0-46d2-948e34013fd0"
      },
      "outputs": [],
      "source": [
        "def neural_net_image_input(image_size):\n",
        "    return tf.placeholder(tf.float32, [None] + list(image_size), 'x')\n",
        "\n",
        "def neural_net_label_input():\n",
        "    return tf.placeholder(tf.float32, [None, 2], 'y')\n",
        "\n",
        "def neural_net_keep_prob():\n",
        "    return tf.placeholder(tf.float32, None, 'keep_prob')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "94a5c19b-4beb-097f-a02a-96ee2aa8c088"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "01cf2987-73a5-adc6-242d-48fa9c47cfd4"
      },
      "outputs": [],
      "source": [
        "def conv2d_maxpool(x_tensor, conv_num_outputs, conv_ksize=(2,2), conv_strides=[1,1,1,1], pool_ksize=[1,2,2,1], pool_strides=[1,2,2,1]):\n",
        "    dimension = x_tensor.get_shape().as_list()\n",
        "    shape = list(conv_ksize + (dimension[-1],) + (conv_num_outputs,))\n",
        "    weight = tf.Variable(tf.truncated_normal(shape, 0, 0.1))\n",
        "    bias = tf.Variable(tf.zeros(conv_num_outputs))\n",
        "    \n",
        "    conv_layer = tf.nn.conv2d(x_tensor, weight, strides=conv_strides, padding='SAME')\n",
        "    conv_layer = tf.nn.bias_add(conv_layer, bias)\n",
        "    conv_layer = tf.nn.relu(conv_layer)\n",
        "    \n",
        "    conv_layer = tf.nn.max_pool(conv_layer, ksize=pool_ksize, strides=pool_strides, padding='SAME')\n",
        "    \n",
        "    return conv_layer"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "339f750c-1f1e-3476-0d02-a7c548be0ef9"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "93a04505-c9a3-4e6c-a69d-b26684ab2188"
      },
      "outputs": [],
      "source": [
        "def flatten(x_tensor):\n",
        "    dimension = x_tensor.get_shape().as_list()\n",
        "    return tf.reshape(x_tensor, [-1, np.prod(dimension[1:])])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "afbd2dfd-3bba-d3f0-0c34-1605616bd72d",
        "collapsed": true
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "9e5e5835-d795-bcc0-f55b-718018e6e92a"
      },
      "outputs": [],
      "source": [
        "def fully_conn(x_tensor, num_outputs):\n",
        "    dimension = x_tensor.get_shape().as_list()\n",
        "    shape = list((dimension[-1],) + (num_outputs,))\n",
        "    weights = tf.Variable(tf.truncated_normal(shape, 0, 0.1))\n",
        "    bias = tf.Variable(tf.zeros(num_outputs))\n",
        "    \n",
        "    fully_conn = tf.nn.relu(tf.add(tf.matmul(x_tensor, weights), bias))\n",
        "    \n",
        "    return fully_conn"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "c88633fb-1834-8712-4904-3b3c4d8d3e6b"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "3c9ea56a-5bde-06b3-889d-51975bb66a1b"
      },
      "outputs": [],
      "source": [
        "def output(x_tensor, num_outputs):\n",
        "    dimension = x_tensor.get_shape().as_list()\n",
        "    shape = list((dimension[-1],) + (num_outputs,))\n",
        "    weights = tf.Variable(tf.truncated_normal(shape, 0, 0.01))\n",
        "    bias = tf.Variable(tf.zeros(num_outputs))\n",
        "    \n",
        "    output = tf.add(tf.matmul(x_tensor, weights), bias)\n",
        "    \n",
        "    return output"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "d432bf0f-6928-b568-e736-fabb549d25d9"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "b67b5ac7-9730-c1c1-d1a8-687d2e724eb0"
      },
      "outputs": [],
      "source": [
        "def conv_net(x, keep_prob):\n",
        "        \n",
        "    model = conv2d_maxpool(x, conv_num_outputs=32)    \n",
        "    model = tf.nn.dropout(model, keep_prob)\n",
        "    \n",
        "    model = conv2d_maxpool(x, conv_num_outputs=64)    \n",
        "    model = tf.nn.dropout(model, keep_prob)\n",
        "    \n",
        "    model = flatten(model)\n",
        "    model = tf.nn.dropout(model, keep_prob)\n",
        "    \n",
        "    model = fully_conn(model, 128)\n",
        "    \n",
        "    model = output(model, 2)\n",
        "    \n",
        "    return model\n",
        "\n",
        "##############################\n",
        "## Build the Neural Network ##\n",
        "##############################\n",
        "\n",
        "# Remove previous weights, bias, inputs, etc..\n",
        "tf.reset_default_graph()\n",
        "\n",
        "# Inputs\n",
        "x = neural_net_image_input((IMG_SIZE, IMG_SIZE, 3))\n",
        "y = neural_net_label_input()\n",
        "keep_prob = neural_net_keep_prob()\n",
        "\n",
        "# Model\n",
        "logits = conv_net(x, keep_prob)\n",
        "\n",
        "# Name logits Tensor, so that is can be loaded from disk after training\n",
        "logits = tf.identity(logits, name='logits')\n",
        "\n",
        "# Loss and Optimizer\n",
        "cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))\n",
        "optimizer = tf.train.AdamOptimizer(1e-3).minimize(cost)\n",
        "\n",
        "# Accuracy\n",
        "correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(y, 1))\n",
        "accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "91da20e0-ebc1-a6e1-c3fc-f48bc5f4f635"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "b7681600-bf2f-2c61-43db-420472996694"
      },
      "outputs": [],
      "source": [
        "def train_neural_network(session, optimizer, keep_probability, feature_batch, label_batch):\n",
        "    session.run(optimizer, feed_dict={x:feature_batch, y:label_batch, keep_prob:keep_probability})"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "44a0a8d2-c6b6-9882-85e1-43db8702ae32"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "ca0a4c4e-be32-c55a-2ff8-85135b8dfef8"
      },
      "outputs": [],
      "source": [
        "def print_stats(session, feature_batch, label_batch, cost, accuracy):\n",
        "    loss = session.run(cost, feed_dict={x:feature_batch, y:label_batch, keep_prob:0.7})\n",
        "    valid_acc = sess.run(accuracy, feed_dict={\n",
        "                x: training_img[:batch_size],\n",
        "                y: training_label[:batch_size],\n",
        "                keep_prob: 0.7})\n",
        "    print('Loss: {:>10.4f} Validation Accuracy: {:.6f}'.format(loss, valid_acc))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "3e02e280-00cd-f25f-e9c9-d0bbb405edb8"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "5f929689-943b-b6c7-1974-0f90687a1283"
      },
      "outputs": [],
      "source": [
        "# TODO: Tune Parameters\n",
        "epochs = 10\n",
        "batch_size = 64\n",
        "keep_probability = 0.7"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "a88209f9-7aab-b720-8eb6-ed57c97093b5"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "f3e4b115-d179-d1d8-5c9e-56895b1cf987"
      },
      "outputs": [],
      "source": ""
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "66e5851b-beb1-ff11-cf7a-cf8e4ea9690f"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "03834202-47ac-eb5f-2c5d-1f44a9d299f8"
      },
      "outputs": [],
      "source": [
        "save_model_path = './image_classification'\n",
        "\n",
        "print('Training...')\n",
        "with tf.Session() as sess:\n",
        "    # Initializing the variables\n",
        "    sess.run(tf.global_variables_initializer())\n",
        "    \n",
        "    # Training cycle\n",
        "    for epoch in range(epochs):\n",
        "        # Loop over all batches\n",
        "        n_batches = 5\n",
        "        for batch_i in range(1, n_batches + 1):\n",
        "            batch_features = training_img[batch_i:batch_size]\n",
        "            batch_labels = training_label[batch_i:batch_size]\n",
        "            \n",
        "            train_neural_network(sess, optimizer, keep_probability, batch_features, batch_labels)\n",
        "            \n",
        "            print('Epoch {:>2}, Batch {}:  '.format(epoch + 1, batch_i), end='')\n",
        "            print_stats(sess, batch_features, batch_labels, cost, accuracy)\n",
        "\n",
        "    # Save Model\n",
        "    saver = tf.train.Saver()\n",
        "    save_path = saver.save(sess, save_model_path)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "1d5a081d-f2f3-3a1b-2cc4-547297b29e91"
      },
      "source": ""
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "b01a6962-87d2-d64f-a215-bcd5d59d55ee"
      },
      "outputs": [],
      "source": [
        "# Set batch size if not already set\n",
        "try:\n",
        "    if batch_size:\n",
        "        pass\n",
        "except NameError:\n",
        "    batch_size = 64\n",
        "\n",
        "save_model_path = './image_classification'\n",
        "n_samples = 4\n",
        "top_n_predictions = 3\n",
        "\n",
        "def test_model():\n",
        "    \"\"\"\n",
        "    Test the saved model against the test dataset\n",
        "    \"\"\"\n",
        "\n",
        "#     test_features, test_labels = pickle.load(open('preprocess_training.p', mode='rb'))\n",
        "    loaded_graph = tf.Graph()\n",
        "\n",
        "    with tf.Session(graph=loaded_graph) as sess:\n",
        "        # Load model\n",
        "        loader = tf.train.import_meta_graph(save_model_path + '.meta')\n",
        "        loader.restore(sess, save_model_path)\n",
        "\n",
        "        # Get Tensors from loaded model\n",
        "        loaded_x = loaded_graph.get_tensor_by_name('x:0')\n",
        "        loaded_y = loaded_graph.get_tensor_by_name('y:0')\n",
        "        loaded_keep_prob = loaded_graph.get_tensor_by_name('keep_prob:0')\n",
        "        loaded_logits = loaded_graph.get_tensor_by_name('logits:0')\n",
        "        loaded_acc = loaded_graph.get_tensor_by_name('accuracy:0')\n",
        "        \n",
        "        test_epoch = 25\n",
        "        # Get accuracy in batches for memory limitations\n",
        "        test_batch_acc_total = 0\n",
        "        test_batch_count = 0\n",
        "        \n",
        "        n_batches = 500\n",
        "        b_size = 0\n",
        "        for i in range(25):\n",
        "            test_batch_acc_total += sess.run(\n",
        "                loaded_acc,\n",
        "                feed_dict={loaded_x: testing_img[b_size:n_batches], loaded_y: testing_label[b_size:n_batches], loaded_keep_prob: 1.0})\n",
        "            test_batch_count += 1        \n",
        "\n",
        "            print('Batch {:>2}:  Testing Accuracy: {}\\n'.format(i + 1, test_batch_acc_total/test_batch_count), end='')\n",
        "            \n",
        "            b_size = n_batches + 1\n",
        "            n_batches += 500\n",
        "\n",
        "\n",
        "test_model()"
      ]
    }
  ],
  "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
}