{
 "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",
    "    trainer = cntk.Trainer(m, (loss, cntk.classification_error(m, labels)), [learner])\n",
    "    return trainer"
   ]
  },
  {
   "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 833 ms, sys: 553 ms, total: 1.39 s\n",
      "Wall time: 1.38 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 22.6 ms, sys: 28.6 ms, total: 51.2 ms\n",
      "Wall time: 76.1 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 72.1 ms, sys: 224 ms, total: 297 ms\n",
      "Wall time: 303 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "trainer = init_model(sym)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1  |  Accuracy: 0.562500\n",
      "Epoch 2  |  Accuracy: 0.640625\n",
      "Epoch 3  |  Accuracy: 0.625000\n",
      "Epoch 4  |  Accuracy: 0.703125\n",
      "Epoch 5  |  Accuracy: 0.703125\n",
      "Epoch 6  |  Accuracy: 0.765625\n",
      "Epoch 7  |  Accuracy: 0.859375\n",
      "Epoch 8  |  Accuracy: 0.796875\n",
      "Epoch 9  |  Accuracy: 0.781250\n",
      "Epoch 10  |  Accuracy: 0.796875\n",
      "CPU times: user 2min 19s, sys: 21.4 s, total: 2min 40s\n",
      "Wall time: 2min 43s\n"
     ]
    }
   ],
   "source": [
    "%%time \n",
    "# 163s\n",
    "# Train model\n",
    "for j in range(EPOCHS):\n",
    "    for data, label in yield_mb(x_train, y_train, BATCHSIZE, shuffle=True):\n",
    "        trainer.train_minibatch({features: data, labels: label})\n",
    "    # Log (this is just last batch in epoch, not average of batches)\n",
    "    eval_error = trainer.previous_minibatch_evaluation_average\n",
    "    print(\"Epoch %d  |  Accuracy: %.6f\" % (j+1, (1-eval_error)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 850 ms, sys: 337 ms, total: 1.19 s\n",
      "Wall time: 1.4 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.780649038462\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
}