{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\npynet: multi modal slice orientation prediction\n===============================================\n\nCredit: A Grigis\n\nThis practice focuses on a meaningless toy example inspired from the MNIST\nmanuscript numbers classification challenge that is considered as the\n'hello world' example for neural networks.\n\nHere, we have to recognize wether a brain slice image is axial, sagittal or\ncoronal and comes from MRI T1w, MRI T2w or CT modality. So, there are 9\nclasses.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Common imports\nimport pynet\nimport numpy as np\nimport os\nimport glob"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "And of course we import PyTorch\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import torch\ntorch.__version__"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Read the data\n-------------\n\nWe define training + valid sets vs test set using k-fold cross-validations.\n\nA validation step is a useful way to avoid overfitting. At each epoch, the\nneural network is evaluated on the validation set, but not trained on it.\nIf the validation loss starts to grow, it means that the network is\noverfitting the training set, and that it is time to stop the training.\n\nThe following cell create stratified test, train, and validation loaders.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pynet.datasets import fetch_orientation\nfrom pynet.datasets import DataManager\n\ndata = fetch_orientation(\n    datasetdir=\"/tmp/orientation\",\n    flatten=True)\nmanager = DataManager(\n    input_path=data.input_path,\n    labels=[\"label\"],\n    metadata_path=data.metadata_path,\n    number_of_folds=10,\n    batch_size=1000,\n    stratify_label=\"label\",\n    test_size=0.1,\n    sample_size=(1 if \"CI_MODE\" not in os.environ else 0.1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Displaying some images of the test dataset.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pynet.plotting import plot_data\n\ndataset = manager[\"test\"]\nsample = dataset.inputs.reshape(-1, data.height, data.width)\nsample = np.expand_dims(sample, axis=1)\nplot_data(sample, nb_samples=5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Simple neural network\n---------------------\n\nThe simplest way to create, train and test a network is to use Sequential\ncontainer.\nWith a sequential container, you can quickly design a linear stack of layers\nand so, many kinds of models (LSTM, CNN, ...).\nHere we create a simple Multilayer Perceptron (MLP) for multi-class softmax\nclassification.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import collections\nimport torch\nimport torch.nn as nn\nimage_size = data.height * data.width\nnb_neurons = 16\n\n\nclass OneLayerMLP(nn.Module):\n    \"\"\"  Simple one hidden layer percetron.\n    \"\"\"\n    def __init__(self, image_size, nb_neurons, nb_classes):\n        \"\"\" Initialize the instance.\n\n        Parameters\n        ----------\n        image_size: int\n            the number of elemnts in the image.\n        nb_neurons: int\n            the number of neurons of the hidden layer.\n        nb_classes: int\n            the number of classes.\n        \"\"\"\n        super(OneLayerMLP, self).__init__()\n        self.layers = nn.Sequential(collections.OrderedDict([\n            (\"linear1\", nn.Linear(image_size, nb_neurons)),\n            (\"relu1\", nn.ReLU()),\n            (\"linear2\", nn.Linear(nb_neurons, nb_classes)),\n            (\"softmax\", nn.LogSoftmax(dim=1))\n        ]))\n\n    def forward(self, x):\n        x = self.layers(x)\n        return x\n\n\nmodel = OneLayerMLP(image_size, nb_neurons, 9)\nprint(model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Then we configure the parameters of the training step and train the model.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pynet.interfaces import DeepLearningInterface\n\ncl = DeepLearningInterface(\n    optimizer_name=\"Adam\",\n    learning_rate=1e-4,\n    loss_name=\"NLLLoss\",\n    metrics=[\"accuracy\"],\n    model=model)\ntest_history, train_history = cl.training(\n    manager=manager,\n    nb_epochs=10,\n    checkpointdir=\"/tmp/orientation\",\n    fold_index=0,\n    with_validation=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We focus now on test predictions.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\nfrom pprint import pprint\nfrom sklearn.metrics import classification_report\nfrom pynet.plotting import plot_data\n\ny_pred, X, y_true, loss, values = cl.testing(\n    manager=manager,\n    with_logit=True,\n    predict=True)\npprint(data.labels)\nX = X.reshape(-1, data.height, data.width)\nX = np.expand_dims(X, axis=1)\nprint(classification_report(y_true, y_pred, target_names=data.labels.values()))\ntitles = [\"{0}-{1}\".format(data.labels[it1], data.labels[it2])\n          for it1, it2 in zip(y_pred, y_true)]\nplot_data(X, labels=titles, nb_samples=5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Watch learning curves.\nDuring the training, we saved the loss and the accuracy at each iteration in\nthe lists losses and accuracies.\nThe following lines display the corresponding curves.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pynet.plotting import plot_history\n\nprint(train_history)\nplot_history(train_history)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Convolutional neural network\n----------------------------\n\nNow we will create a neural network using convolutional layers.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "class My_Net(torch.nn.Module):\n    def __init__(self):\n        super(My_Net, self).__init__()\n        self.conv1 = torch.nn.Conv2d(1, 8, kernel_size=3, stride=1, padding=1)\n        self.conv2 = torch.nn.Conv2d(8, 16, kernel_size=3, stride=2, padding=1)\n        self.maxpool = torch.nn.MaxPool2d(kernel_size=2)\n        self.linear = torch.nn.Linear(16 * 16 * 16, 9)\n\n    def forward(self, X):\n        X = X.view(-1, 1, 64, 64)\n        X = torch.nn.functional.relu(self.conv1(X))\n        X = torch.nn.functional.relu(self.conv2(X))\n        X = self.maxpool(X)\n        X = self.linear(X.view(-1, 16 * 16 * 16))\n        return torch.nn.functional.log_softmax(X, dim=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Here, we check how the input size changes through each layer.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "model = My_Net()\n\nX_train = sample\nX = torch.from_numpy(X_train[:10, :]).float()\nprint(X.shape)\nX = torch.nn.functional.relu(model.conv1(X))\nprint(X.shape)\nX = torch.nn.functional.relu(model.conv2(X))\nprint(X.shape)\nX = model.maxpool(X)\nprint(X.shape)\nX = model.linear(X.view(-1, 16 * 16 * 16))\nprint(X.shape)\nX = torch.nn.functional.log_softmax(X, dim=1)\nprint(X.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Then we configure the parameters of the training step and train the model.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "cl = DeepLearningInterface(\n    optimizer_name=\"Adam\",\n    learning_rate=1e-5,\n    loss_name=\"NLLLoss\",\n    metrics=[\"accuracy\"],\n    model=model)\ntest_history, train_history = cl.training(\n    manager=manager,\n    nb_epochs=10,\n    checkpointdir=\"/tmp/orientation\",\n    fold_index=0,\n    with_validation=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We focus now on test predictions.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\nfrom pprint import pprint\nfrom sklearn.metrics import classification_report\nfrom pynet.plotting import plot_data\n\ny_pred, X, y_true, loss, values = cl.testing(\n    manager=manager,\n    with_logit=True,\n    predict=True)\npprint(data.labels)\nX = X.reshape(-1, data.height, data.width)\nX = np.expand_dims(X, axis=1)\nprint(classification_report(y_true, y_pred, target_names=data.labels.values()))\ntitles = [\"{0}-{1}\".format(data.labels[it1], data.labels[it2])\n          for it1, it2 in zip(y_pred, y_true)]\nplot_data(X, labels=titles, nb_samples=5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Watch learning curves.\nDuring the training, we saved the loss and the accuracy at each iteration in\nthe lists losses and accuracies.\nThe following lines display the corresponding curves.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pynet.plotting import plot_history\n\nprint(train_history)\nplot_history(train_history)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Compare the fully-connected network with the CNN\n------------------------------------------------\n\nBelow is a comparison in terms of trainable parameters for both models.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "cnn_model = My_Net()\nprint(\"Number of parameters in the CNN: \",\n      sum(p.numel() for p in cnn_model.parameters()))\n\nimage_size = data.height * data.width\nnb_neurons = 16\nmodel = OneLayerMLP(image_size, nb_neurons, 9)\nprint(\"Number of parameters in the fully connected: \",\n      sum(p.numel() for p in model.parameters()))\n\nimport os\nif \"CI_MODE\" not in os.environ:\n    import matplotlib.pyplot as plt\n    plt.show()"
      ]
    }
  ],
  "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.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}