{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# High-level CNTK Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import os\n",
    "import sys\n",
    "import cntk\n",
    "from cntk.layers import Convolution2D, MaxPooling, Dense, Dropout\n",
    "from common.params import *\n",
    "from common.utils import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OS:  linux\n",
      "Python:  3.5.2 |Anaconda custom (64-bit)| (default, Jul  2 2016, 17:53:06) \n",
      "[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n",
      "Numpy:  1.13.3\n",
      "CNTK:  2.2\n",
      "GPU:  ['Tesla K80']\n"
     ]
    }
   ],
   "source": [
    "print(\"OS: \", sys.platform)\n",
    "print(\"Python: \", sys.version)\n",
    "print(\"Numpy: \", np.__version__)\n",
    "print(\"CNTK: \", cntk.__version__)\n",
    "print(\"GPU: \", get_gpu_name())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_symbol():\n",
    "    # Weight initialiser from uniform distribution\n",
    "    # Activation (unless states) is None\n",
    "    with cntk.layers.default_options(init = cntk.glorot_uniform(), activation = cntk.relu):\n",
    "        x = Convolution2D(filter_shape=(3, 3), num_filters=50, pad=True)(features)\n",
    "        x = Convolution2D(filter_shape=(3, 3), num_filters=50, pad=True)(x)\n",
    "        x = MaxPooling((2, 2), strides=(2, 2), pad=False)(x)\n",
    "        x = Dropout(0.25)(x)\n",
    "\n",
    "        x = Convolution2D(filter_shape=(3, 3), num_filters=100, pad=True)(x)\n",
    "        x = Convolution2D(filter_shape=(3, 3), num_filters=100, pad=True)(x)\n",
    "        x = MaxPooling((2, 2), strides=(2, 2), pad=False)(x)\n",
    "        x = Dropout(0.25)(x)    \n",
    "        \n",
    "        x = Dense(512)(x)\n",
    "        x = Dropout(0.5)(x)\n",
    "        x = Dense(N_CLASSES, activation=None)(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def init_model(m):\n",
    "    # Loss (dense labels); check if support for sparse labels\n",
    "    loss = cntk.cross_entropy_with_softmax(m, labels)\n",
    "    # Momentum SGD\n",
    "    # https://github.com/Microsoft/CNTK/blob/master/Manual/Manual_How_to_use_learners.ipynb\n",
    "    # unit_gain=False: momentum_direction = momentum*old_momentum_direction + gradient\n",
    "    # if unit_gain=True then ...(1-momentum)*gradient\n",
    "    learner = cntk.momentum_sgd(m.parameters, \n",
    "                                lr=cntk.learning_rate_schedule(LR, cntk.UnitType.minibatch) , \n",
    "                                momentum=cntk.momentum_schedule(MOMENTUM),\n",
    "                                unit_gain=False)\n",
    "    return loss, learner"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Preparing train set...\n",
      "Preparing test set...\n",
      "(50000, 3, 32, 32) (10000, 3, 32, 32) (50000, 10) (10000, 10)\n",
      "float32 float32 float32 float32\n",
      "CPU times: user 846 ms, sys: 543 ms, total: 1.39 s\n",
      "Wall time: 1.39 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Data into format for library\n",
    "x_train, x_test, y_train, y_test = cifar_for_library(channel_first=True, one_hot=True)\n",
    "# CNTK format\n",
    "y_train = y_train.astype(np.float32)\n",
    "y_test = y_test.astype(np.float32)\n",
    "print(x_train.shape, x_test.shape, y_train.shape, y_test.shape)\n",
    "print(x_train.dtype, x_test.dtype, y_train.dtype, y_test.dtype)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 17.1 ms, sys: 32.7 ms, total: 49.8 ms\n",
      "Wall time: 78.5 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Placeholders\n",
    "features = cntk.input_variable((3, 32, 32), np.float32)\n",
    "labels = cntk.input_variable(N_CLASSES, np.float32)\n",
    "# Load symbol\n",
    "sym = create_symbol()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 78.9 ms, sys: 221 ms, total: 300 ms\n",
      "Wall time: 306 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "loss, learner = init_model(sym)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 2min 8s, sys: 24.9 s, total: 2min 32s\n",
      "Wall time: 2min 51s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'epoch_summaries': [{'loss': 1.8166315625, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 1.3537084375, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 1.12093609375, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.97167546875, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.86488921875, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.769997734375, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.707360078125, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.64719390625, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.592496171875, 'metric': 0.0, 'samples': 50000},\n",
       "  {'loss': 0.5582487109375, 'metric': 0.0, 'samples': 50000}],\n",
       " 'updates': [{'loss': 1.8166693029269365, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 1.353612999909971, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 1.120971445237476, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.9715477702864916, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.8647872006542093, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.7700418821522887, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.7073556506832186, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.6472827945567582, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.5925403941761364, 'metric': 0.0, 'samples': 49984},\n",
       "  {'loss': 0.5583446859244958, 'metric': 0.0, 'samples': 49984}]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%time\n",
    "# 171s\n",
    "loss.train((x_train, y_train), \n",
    "           minibatch_size=BATCHSIZE, \n",
    "           max_epochs=EPOCHS,\n",
    "           parameter_learners=[learner])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 964 ms, sys: 220 ms, total: 1.18 s\n",
      "Wall time: 1.43 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "# Predict and then score accuracy\n",
    "# (We don't need softmax -> monotonic function)\n",
    "n_samples = (y_test.shape[0]//BATCHSIZE)*BATCHSIZE\n",
    "y_guess = np.zeros(n_samples, dtype=np.int)\n",
    "y_truth = np.argmax(y_test[:n_samples], axis=-1)\n",
    "c = 0\n",
    "for data, label in yield_mb(x_test, y_test, BATCHSIZE):\n",
    "    predicted_label_probs = sym.eval({features : data})\n",
    "    y_guess[c*BATCHSIZE:(c+1)*BATCHSIZE] = np.argmax(predicted_label_probs, axis=-1)\n",
    "    c += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy:  0.773237179487\n"
     ]
    }
   ],
   "source": [
    "print(\"Accuracy: \", sum(y_guess == y_truth)/len(y_guess))"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [default]",
   "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.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}