{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CS 20 : TensorFlow for Deep Learning Research\n",
    "## Lecture 07 : ConvNet in TensorFlow\n",
    "### ConvNet mnist by low-level\n",
    "- Creating the **data pipeline** with `tf.data`\n",
    "- Creating the model as **Class** with `tf.nn`\n",
    "- Training the model with **Drop out** technique by `tf.nn.dropout`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.12.0\n"
     ]
    }
   ],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "import os, sys\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import tensorflow as tf\n",
    "%matplotlib inline\n",
    "\n",
    "print(tf.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load and Pre-process data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "(x_train, y_train), (x_tst, y_tst) = tf.keras.datasets.mnist.load_data()\n",
    "x_train = x_train  / 255\n",
    "x_train = x_train.reshape(-1, 28, 28, 1).astype(np.float32)\n",
    "x_tst = x_tst / 255\n",
    "x_tst = x_tst.reshape(-1, 28, 28, 1).astype(np.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(55000, 28, 28, 1) (55000,)\n",
      "(5000, 28, 28, 1) (5000,)\n"
     ]
    }
   ],
   "source": [
    "tr_indices = np.random.choice(range(x_train.shape[0]), size = 55000, replace = False)\n",
    "\n",
    "x_tr = x_train[tr_indices]\n",
    "y_tr = y_train[tr_indices].astype(np.int32)\n",
    "\n",
    "x_val = np.delete(arr = x_train, obj = tr_indices, axis = 0)\n",
    "y_val = np.delete(arr = y_train, obj = tr_indices, axis = 0).astype(np.int32)\n",
    "\n",
    "print(x_tr.shape, y_tr.shape)\n",
    "print(x_val.shape, y_val.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define SimpleCNN class by low-level api"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleCNN:\n",
    "    def __init__(self, X, y, n_of_classes):\n",
    "        \n",
    "        self._X = X\n",
    "        self._y = y\n",
    "        self._keep_prob = tf.placeholder(dtype = tf.float32)\n",
    "        \n",
    "        conv1 = self._conv_relu(inputs = self._X, filters = 32, k_size = 5, stride = 1, padding = 'SAME',\n",
    "                                scope_name = 'conv1')\n",
    "        pool1 = self._max_pool(inputs = conv1, k_size = 2, stride = 2, padding = 'SAME',\n",
    "                               scope_name = 'pool1')\n",
    "        conv2 = self._conv_relu(inputs = pool1, filters = 64, k_size = 5, stride = 1, padding = 'SAME',\n",
    "                                scope_name = 'conv2')\n",
    "        pool2 = self._max_pool(inputs = conv2, k_size = 2, stride = 2, padding = 'SAME',\n",
    "                               scope_name = 'pool2')\n",
    "        flat_dim = pool2.get_shape().as_list()[1:]\n",
    "        flattened = tf.reshape(tensor = pool2, shape = [-1,flat_dim[0] * flat_dim[1] * flat_dim[2]])\n",
    "\n",
    "        pre_fc = self._fully_connected(inputs = flattened, out_dim = 1024, scope_name = 'fc1')\n",
    "        fc = tf.nn.relu(pre_fc)\n",
    "        dropped = tf.nn.dropout(x = fc, keep_prob = self._keep_prob)\n",
    "        \n",
    "        self._score = self._fully_connected(inputs = dropped, out_dim = n_of_classes, scope_name = 'score')\n",
    "        self.ce_loss = self._loss(labels = self._y, logits = self._score, scope_name = 'ce_loss')\n",
    "        \n",
    "        with tf.variable_scope('prediction'):\n",
    "            self._prediction = tf.argmax(input = self._score, axis = -1)\n",
    "        \n",
    "    def _conv_relu(self, inputs, filters, k_size, stride = 1, padding = 'SAME', scope_name = 'conv'):\n",
    "        with tf.variable_scope(scope_name, reuse = tf.AUTO_REUSE):\n",
    "            in_channels = inputs.get_shape().as_list()[-1]\n",
    "            filter_weights = tf.get_variable(name = 'weights', shape = [k_size, k_size, in_channels, filters],\n",
    "                                             dtype = tf.float32, initializer = tf.truncated_normal_initializer())\n",
    "            biases = tf.get_variable(name = 'biases', shape = [filters], dtype = tf.float32,\n",
    "                                            initializer = tf.truncated_normal_initializer())\n",
    "            conv = tf.nn.conv2d(input = inputs, filter = filter_weights,\n",
    "                                strides = [1, stride, stride, 1], padding = padding)\n",
    "            pre_activation = conv + biases\n",
    "            activation = tf.nn.relu(pre_activation)\n",
    "            return activation\n",
    "        \n",
    "    def _max_pool(self, inputs, k_size = 2, stride = 2, padding = 'SAME', scope_name = 'max_pool'):\n",
    "        with tf.variable_scope(scope_name, reuse = tf.AUTO_REUSE):\n",
    "            pool = tf.nn.max_pool(value = inputs, ksize = [1, k_size, k_size, 1],\n",
    "                                  strides = [1, stride, stride, 1], padding = padding)\n",
    "            return pool\n",
    "        \n",
    "    def _fully_connected(self, inputs, out_dim, scope_name = 'fc'):\n",
    "        with tf.variable_scope(scope_name) :\n",
    "            in_dim = inputs.get_shape().as_list()[-1]\n",
    "            weights = tf.get_variable(name = 'weights', shape = [in_dim, out_dim],\n",
    "                                      dtype = tf.float32, initializer = tf.truncated_normal_initializer())\n",
    "            biases = tf.get_variable(name = 'biases', shape = [out_dim], dtype = tf.float32,\n",
    "                                     initializer = tf.truncated_normal_initializer())\n",
    "            pre_activation = tf.matmul(inputs, weights) + biases\n",
    "            return pre_activation\n",
    "        \n",
    "    def _loss(self, labels, logits, scope_name):\n",
    "        with tf.variable_scope(scope_name):\n",
    "            ce_loss = tf.losses.sparse_softmax_cross_entropy(labels = labels, logits = logits)\n",
    "            return ce_loss\n",
    "        \n",
    "    def predict(self, sess, x_data, keep_prob = 1.):\n",
    "        feed_prediction = {self._X : x_data, self._keep_prob : keep_prob}\n",
    "        return sess.run(self._prediction, feed_dict = feed_prediction)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a model of SimpleCNN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "550\n"
     ]
    }
   ],
   "source": [
    "# hyper-parameter\n",
    "lr = .01\n",
    "epochs = 30\n",
    "batch_size = 100\n",
    "total_step = int(x_tr.shape[0] / batch_size)\n",
    "print(total_step)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<BatchDataset shapes: ((?, 28, 28, 1), (?,)), types: (tf.float32, tf.int32)>\n",
      "<BatchDataset shapes: ((?, 28, 28, 1), (?,)), types: (tf.float32, tf.int32)>\n"
     ]
    }
   ],
   "source": [
    "## create input pipeline with tf.data\n",
    "# for train\n",
    "tr_dataset = tf.data.Dataset.from_tensor_slices((x_tr, y_tr))\n",
    "tr_dataset = tr_dataset.shuffle(buffer_size = 10000)\n",
    "tr_dataset = tr_dataset.batch(batch_size = batch_size)\n",
    "tr_iterator = tr_dataset.make_initializable_iterator()\n",
    "print(tr_dataset)\n",
    "\n",
    "# for validation\n",
    "val_dataset = tf.data.Dataset.from_tensor_slices((x_val,y_val))\n",
    "val_dataset = val_dataset.batch(batch_size = batch_size)\n",
    "val_iterator = val_dataset.make_initializable_iterator()\n",
    "print(val_dataset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "## define Iterator\n",
    "# tf.data.Iterator.from_string_handle의 output_shapes는 default = None이지만 꼭 값을 넣는 게 좋음\n",
    "handle = tf.placeholder(dtype = tf.string)\n",
    "iterator = tf.data.Iterator.from_string_handle(string_handle = handle,\n",
    "                                               output_types = tr_iterator.output_types,\n",
    "                                               output_shapes = tr_iterator.output_shapes)\n",
    "\n",
    "x_data, y_data = iterator.get_next()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "## connecting data pipeline with model\n",
    "cnn = SimpleCNN(X = x_data, y = y_data, n_of_classes = 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create training op and train model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "## create training op\n",
    "opt = tf.train.AdamOptimizer(learning_rate = lr)\n",
    "\n",
    "# equal to 'var_list = None'\n",
    "training_op = opt.minimize(loss = cnn.ce_loss)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch :   5, tr_loss : 7.832, val_loss : 6.412\n",
      "epoch :  10, tr_loss : 3.983, val_loss : 4.290\n",
      "epoch :  15, tr_loss : 1.084, val_loss : 1.116\n",
      "epoch :  20, tr_loss : 0.568, val_loss : 0.408\n",
      "epoch :  25, tr_loss : 0.586, val_loss : 0.364\n",
      "epoch :  30, tr_loss : 0.549, val_loss : 0.408\n"
     ]
    }
   ],
   "source": [
    "sess_config = tf.ConfigProto(gpu_options=tf.GPUOptions(allow_growth=True))\n",
    "sess = tf.Session(config = sess_config)\n",
    "sess.run(tf.global_variables_initializer())\n",
    "tr_handle, val_handle = sess.run(fetches = [tr_iterator.string_handle(), val_iterator.string_handle()])\n",
    "\n",
    "tr_loss_hist = []\n",
    "val_loss_hist = []\n",
    "\n",
    "for epoch in range(epochs):\n",
    "\n",
    "    avg_tr_loss = 0\n",
    "    avg_val_loss = 0\n",
    "    tr_step = 0\n",
    "    val_step = 0\n",
    "\n",
    "    # for mini-batch training\n",
    "    sess.run(tr_iterator.initializer)    \n",
    "    try:\n",
    "        while True:\n",
    "            _, tr_loss = sess.run(fetches = [training_op, cnn.ce_loss],\n",
    "                                               feed_dict = {handle : tr_handle, cnn._keep_prob : .5})\n",
    "            avg_tr_loss += tr_loss\n",
    "            tr_step += 1\n",
    "            \n",
    "    except tf.errors.OutOfRangeError:\n",
    "        pass\n",
    "\n",
    "    # for validation\n",
    "    sess.run(val_iterator.initializer)\n",
    "    try:\n",
    "        while True:\n",
    "            val_loss = sess.run(fetches = cnn.ce_loss, feed_dict = {handle : val_handle, cnn._keep_prob : 1.})\n",
    "            avg_val_loss += val_loss\n",
    "            val_step += 1\n",
    "    \n",
    "    except tf.errors.OutOfRangeError:\n",
    "        pass\n",
    "\n",
    "    avg_tr_loss /= tr_step\n",
    "    avg_val_loss /= val_step\n",
    "    tr_loss_hist.append(avg_tr_loss)\n",
    "    val_loss_hist.append(avg_val_loss)\n",
    "    \n",
    "    if (epoch + 1) % 5 == 0:\n",
    "        print('epoch : {:3}, tr_loss : {:.3f}, val_loss : {:.3f}'.format(epoch + 1, avg_tr_loss, avg_val_loss))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7fd94c4950f0>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(tr_loss_hist, label = 'train')\n",
    "plt.plot(val_loss_hist, label = 'validation')\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test acc: 93.45%\n"
     ]
    }
   ],
   "source": [
    "yhat = cnn.predict(sess = sess, x_data = x_tst)\n",
    "print('test acc: {:.2%}'.format(np.mean(yhat == y_tst)))"
   ]
  }
 ],
 "metadata": {
  "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.8"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}