{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "7d25a4c1-9335-0638-6042-14de1f6c1c4d"
      },
      "source": [
        "#This script is used to execute the following steps\n",
        "\n",
        "- Load images using the [ImageR package](http://dahtah.github.io/imager/), crop them to a standardized dimension and save them as an array \n",
        "- Run a [VGG inspired](http://josephpcohen.com/w/visualizing-cnn-architectures-side-by-side-with-mxnet/) convolutional network using the [MXNET package](https://github.com/dmlc/mxnet/)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "1603b517-e414-96c8-3090-f15f2638cd04"
      },
      "outputs": [],
      "source": [
        "### Load packages\n",
        "library(\"imager\")\n",
        "library(\"data.table\")\n",
        "library(\"dtplyr\")\n",
        "library(\"dplyr\")\n",
        "library(\"readr\")\n",
        "library(\"ggplot2\")\n",
        "library(\"plotly\")\n",
        "library(\"mxnet\")\n",
        "\n",
        "# Input data files are available in the \"../input/\" directory."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "3c444697-778b-bc69-c652-83a6fb812316"
      },
      "source": [
        "###List image files\n",
        "\n",
        "Train and evaluation images are shuffled and test images files are sorted in numerica order as expected by the submission."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "fa5a2374-c986-28b2-5d6d-4039888f7c97"
      },
      "outputs": [],
      "source": [
        "#### Alternate cases of dogs and cats\n",
        "cat_files<- list.files(path = \"../input/train\", pattern = \"cat.+\")\n",
        "dog_files<- list.files(path = \"../input/train\", pattern = \"dog.+\")\n",
        "\n",
        "### randomize image order\n",
        "set.seed(123)\n",
        "cat_files<- sample(cat_files)\n",
        "dog_files<- sample(dog_files)\n",
        "\n",
        "### list test files\n",
        "test_files<- list.files(path = \"../input/test\", pattern = \".+\\\\.jpg\")\n",
        "test_num<- as.numeric(gsub(pattern=\"(\\\\d+)(.+)\", replacement = \"\\\\1\", x = test_files))\n",
        "test_position<- order(test_num)\n",
        "test_files<- test_files[test_position]\n",
        "\n",
        "head(cat_files)\n",
        "head(dog_files)\n",
        "head(test_files)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "56266c94-7db7-1dac-3f46-7b9dc1a54202"
      },
      "source": [
        "###Function to import images as arrays\n",
        "\n",
        "Pixel values are rounded to manage space when run locally from a csv data iterator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "dac235b9-0753-f0c7-c7f0-d73e6561c5eb"
      },
      "outputs": [],
      "source": [
        "### Load images and and convert to array\n",
        "train_prep<- function(start,end, size){\n",
        "\n",
        "  count<- 2*length(start:end)\n",
        "  data_array<- array(NA, dim = c(size, size, 3, count))\n",
        "  \n",
        "  for (i in start:end) {\n",
        "    ### load jpeg into imageR\n",
        "    cat <- load.image(paste(\"../input/train/\", cat_files[i], sep=\"\"))\n",
        "    cat<- resize(cat,size_x = size, size_y = size, size_c = 3, interpolation_type = 2)\n",
        "    cat_array<-t(round(array(cat),4))\n",
        "      \n",
        "    dog <- load.image(paste(\"../input/train/\", dog_files[i], sep=\"\"))\n",
        "    dog<- resize(dog,size_x = size, size_y = size, size_c = 3, interpolation_type = 2)\n",
        "    dog_array<-t(round(array(dog),4))\n",
        "    \n",
        "    data_array[, , , 2*(i-start+1)-1]<- cat_array\n",
        "    data_array[, , , 2*(i-start+1)]<- dog_array\n",
        "  }\n",
        "  return(data_array)\n",
        "}\n",
        "\n",
        "size<- 80\n",
        "train_array<- train_prep(start=1, end=160, size=size)\n",
        "train_labels<- rep(c(0,1), times=160)\n",
        "eval_array<- train_prep(start=10001, end=10032, size=size)\n",
        "eval_labels<- rep(c(0,1), times=32)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "11654b24-fd31-3f97-a8f6-49dc6390de42"
      },
      "source": [
        "###Validate the soundness of the created arrays"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "6695575e-6c41-8298-4c8d-eaa50e6a4e96"
      },
      "outputs": [],
      "source": [
        "img_test<- train_array[, , , 1]\n",
        "dim(img_test)<- c(size, size, 1, 3)\n",
        "img_test<-as.cimg(img_test)\n",
        "plot(img_test)\n",
        "\n",
        "img_test<- train_array[, , , 2]\n",
        "dim(img_test)<- c(size, size, 1, 3)\n",
        "img_test<-as.cimg(img_test)\n",
        "plot(img_test)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "9e523ee1-f9a0-ba74-8136-7d799a5b973e"
      },
      "source": [
        "###Build the symbolic representation of the model"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "da31f4d6-ab96-c275-4fe0-d28c80c2d49b"
      },
      "outputs": [],
      "source": [
        "# input\n",
        "data <- mx.symbol.Variable('data')\n",
        "\n",
        "conv1A <- mx.symbol.Convolution(data=data, kernel=c(3,3), num_filter=32, stride=c(1,1), pad=c(1,1))\n",
        "act1A <- mx.symbol.Activation(data=conv1A, act_type=\"relu\")\n",
        "conv1B <- mx.symbol.Convolution(data=act1A, kernel=c(3,3), num_filter=32, stride=c(1,1), pad=c(1,1))\n",
        "act1B <- mx.symbol.Activation(data=conv1B, act_type=\"relu\")\n",
        "pool1<- mx.symbol.Pooling(data=act1B, pool_type=\"max\", kernel=c(2,2), stride=c(2,2), pad=c(0,0))\n",
        "\n",
        "conv2A <- mx.symbol.Convolution(data=pool1, kernel=c(3,3), num_filter=64, stride=c(1,1), pad=c(1,1))\n",
        "act2A <- mx.symbol.Activation(data=conv2A, act_type=\"relu\")\n",
        "conv2B <- mx.symbol.Convolution(data=act2A, kernel=c(3,3), num_filter=64, stride=c(1,1), pad=c(1,1))\n",
        "act2B <- mx.symbol.Activation(data=conv2B, act_type=\"relu\")\n",
        "pool2<- mx.symbol.Pooling(data=act2B, pool_type=\"max\", kernel=c(2,2), stride=c(2,2), pad=c(0,0))\n",
        "\n",
        "conv3A <- mx.symbol.Convolution(data=pool2, kernel=c(3,3), num_filter=128, stride=c(1,1), pad=c(1,1))\n",
        "act3A <- mx.symbol.Activation(data=conv3A, act_type=\"relu\")\n",
        "conv3B <- mx.symbol.Convolution(data=act3A, kernel=c(3,3), num_filter=128, stride=c(1,1), pad=c(1,1))\n",
        "act3B <- mx.symbol.Activation(data=conv3B, act_type=\"relu\")\n",
        "pool3<- mx.symbol.Pooling(data=act3B, pool_type=\"max\", kernel=c(2,2), stride=c(2,2), pad=c(0,0))\n",
        "\n",
        "conv4A <- mx.symbol.Convolution(data=pool3, kernel=c(3,3), num_filter=128, stride=c(1,1), pad=c(1,1))\n",
        "act4A <- mx.symbol.Activation(data=conv4A, act_type=\"relu\")\n",
        "conv4B <- mx.symbol.Convolution(data=act4A, kernel=c(3,3), num_filter=128, stride=c(1,1), pad=c(1,1))\n",
        "act4B <- mx.symbol.Activation(data=conv4B, act_type=\"relu\")\n",
        "pool4<- mx.symbol.Pooling(data=act4B, pool_type=\"max\", kernel=c(2,2), stride=c(2,2), pad=c(0,0))\n",
        "\n",
        "# flatten\n",
        "flatten <- mx.symbol.Flatten(data=pool4)\n",
        "\n",
        "fc1 <- mx.symbol.FullyConnected(data=flatten, num_hidden=512)\n",
        "act_fc1 <- mx.symbol.Activation(data=fc1, act_type=\"relu\")\n",
        "drop1 <- mx.symbol.Dropout(data=act_fc1, p=0.5)\n",
        "\n",
        "# loss\n",
        "fc_final <- mx.symbol.FullyConnected(data=drop1, num_hidden=2)\n",
        "convnet <- mx.symbol.SoftmaxOutput(data=fc_final)\n",
        "\n",
        "### plot the model structure\n",
        "graph.viz(model=convnet$as.json())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "f1211635-1780-d937-c6ed-dd8deecfaab5"
      },
      "source": [
        "###Train a model on a small sample of data"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "8d99c1d9-4011-f77f-4a4d-583c38d76fd5"
      },
      "outputs": [],
      "source": [
        "devices <- mx.cpu()\n",
        "\n",
        "### combine symbols and create executor for inspection of learned features\n",
        "combined<- mx.symbol.Group(act1A, convnet)\n",
        "executor <- mx.simple.bind(symbol=combined, data=dim(train_array), ctx=devices)\n",
        "\n",
        "mx.set.seed(123)\n",
        "model_mxnet <- mx.model.FeedForward.create(convnet,\n",
        "                                               X=train_array, \n",
        "                                               y=train_labels,, \n",
        "                                               eval.data = list(data=eval_array, label=eval_labels),\n",
        "                                               array.batch.size = 16, \n",
        "                                               ctx=devices, \n",
        "                                               num.round=5,\n",
        "                                               learning.rate=0.05,\n",
        "                                               wd=0.001,\n",
        "                                               momentum=0.9,\n",
        "                                               clip_gradient=1,\n",
        "                                               eval.metric=mx.metric.accuracy, \n",
        "                                               initializer=mx.init.Xavier(rnd_type = \"gaussian\", factor_type = \"avg\", magnitude = 3),\n",
        "                                               epoch.end.callback = mx.callback.log.train.metric(1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "ad0ebb75-a7f0-9854-d14b-08dfe5fbdedf"
      },
      "source": [
        "###Look at the first 16 features learned in first convolution\n",
        "\n",
        "Will make more sense after training on full dataset!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "792584fd-ba93-0218-802a-2cb1f355f5ae"
      },
      "outputs": [],
      "source": [
        "mx.exec.update.arg.arrays(exec = executor, arg.arrays = model_mxnet$arg.params, match.name=TRUE)\n",
        "mx.exec.update.arg.arrays(executor, list(data=mx.nd.array(train_array)), match.name=TRUE)\n",
        "mx.exec.forward(executor, is.train=FALSE)\n",
        "\n",
        "par(mfrow=c(4,4), mar=c(0.1,0.1,0.1,0.1))\n",
        "for (i in 1:16) {\n",
        "  img_array <- as.array(executor$outputs$activation0_output)[,,i,1]\n",
        "  img<-as.cimg(img_array)\n",
        "  plot(img)\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "5cca1eaf-4317-4f85-9ced-d6497b3165e5"
      },
      "source": [
        "###Generate the predictions\n",
        "\n",
        "Training on full data should result in a viable model. Ran locally, logloss is around 0.28 and accuracy 87%."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "ae2e2fa2-ad96-c939-e891-cc97496d9e66"
      },
      "outputs": [],
      "source": [
        "pred_prob<- t(predict(model_mxnet, eval_array))\n",
        "submit <- data.frame(id=1:64, label=pred_prob[, 2])\n",
        "head(submit)"
      ]
    }
  ],
  "metadata": {
    "_change_revision": 0,
    "_is_fork": false,
    "kernelspec": {
      "display_name": "R",
      "language": "R",
      "name": "ir"
    },
    "language_info": {
      "codemirror_mode": "r",
      "file_extension": ".r",
      "mimetype": "text/x-r-source",
      "name": "R",
      "pygments_lexer": "r",
      "version": "3.3.1"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}