diff --git "a/notebooks/compare_cell_population_models.ipynb" "b/notebooks/compare_cell_population_models.ipynb"
new file mode 100644--- /dev/null
+++ "b/notebooks/compare_cell_population_models.ipynb"
@@ -0,0 +1,839 @@
+{
+ "nbformat": 4,
+ "nbformat_minor": 0,
+ "metadata": {
+ "colab": {
+ "name": "compare_performance_lineage_population_model.ipynb",
+ "provenance": [],
+ "collapsed_sections": [],
+ "mount_file_id": "https://github.com/Mainakdeb/elegant-embryos/blob/main/compare_cell_population_models.ipynb",
+ "authorship_tag": "ABX9TyP0WOHiSMSByxx79fXzHLW5",
+ "include_colab_link": true
+ },
+ "kernelspec": {
+ "name": "python3",
+ "display_name": "Python 3"
+ },
+ "accelerator": "GPU"
+ },
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "view-in-github",
+ "colab_type": "text"
+ },
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pse0jkTnQykk"
+ },
+ "source": [
+ "## Install Latest Version of Scikit-Learn"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "x2BVhAP8MWs6",
+ "outputId": "e0718ee2-62e3-477d-927f-02d41d4aecbd",
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ }
+ },
+ "source": [
+ "!pip install --upgrade scikit-learn"
+ ],
+ "execution_count": 9,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "text": [
+ "Requirement already up-to-date: scikit-learn in /usr/local/lib/python3.7/dist-packages (0.24.1)\n",
+ "Requirement already satisfied, skipping upgrade: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn) (2.1.0)\n",
+ "Requirement already satisfied, skipping upgrade: scipy>=0.19.1 in /usr/local/lib/python3.7/dist-packages (from scikit-learn) (1.4.1)\n",
+ "Requirement already satisfied, skipping upgrade: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn) (1.0.1)\n",
+ "Requirement already satisfied, skipping upgrade: numpy>=1.13.3 in /usr/local/lib/python3.7/dist-packages (from scikit-learn) (1.19.5)\n"
+ ],
+ "name": "stdout"
+ }
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "W9g7UxxIRGFH"
+ },
+ "source": [
+ "## Clone the Devolearn Repository to Access Current Models"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "WsoDRHU-gjA6",
+ "outputId": "79f1b228-68a1-4822-bed6-aff3a5f5aeca",
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ }
+ },
+ "source": [
+ "!bash"
+ ],
+ "execution_count": 14,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "text": [
+ "bash: cannot set terminal process group (59): Inappropriate ioctl for device\n",
+ "bash: no job control in this shell\n",
+ "\u001b[01;34m/content\u001b[00m# !git clone https://github.com/DevoLearn/devolearn.git\n",
+ "bash: !git: event not found\n",
+ "\u001b[01;34m/content\u001b[00m# git clone https://github.com/DevoLearn/devolearn.git\n",
+ "fatal: destination path 'devolearn' already exists and is not an empty directory.\n",
+ "\u001b[01;34m/content\u001b[00m# cd devolearn\n",
+ "\u001b[01;34m/content/devolearn\u001b[00m# git reset --hard 61eb88dbfb1278c6f5fae2843f6e1eb37f1b551a\n",
+ "HEAD is now at 61eb88d readme : add links to datasets (#37)\n",
+ "\u001b[01;34m/content/devolearn\u001b[00m# exit\n",
+ "exit\n"
+ ],
+ "name": "stdout"
+ }
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "6VHs9p0FRTZn"
+ },
+ "source": [
+ "## Copy Datasets from Google Drive"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "AYqrQ3w8Klww"
+ },
+ "source": [
+ "!cp /content/drive/MyDrive/mydata/epic_subsets/epic_10k.csv /content/\r\n",
+ "!cp /content/drive/MyDrive/mydata/epic_subsets/scaled_epic_10k.csv /content/\r\n",
+ "!cp /content/drive/\"My Drive\"/frames_raw.zip /content/\r\n",
+ "!unzip frames_raw.zip"
+ ],
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "dxbht60BRcGt"
+ },
+ "source": [
+ "## Copy Upgraded Model and Scaler from Google Drive"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "2WGu6kTlIW-V",
+ "outputId": "ac4737d9-e79e-4133-a444-81281cbb5d8a",
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ }
+ },
+ "source": [
+ "!cp /content/drive/MyDrive/models/lineage_pop_res_50/10k_res_18_identical_2.pth /content/\r\n",
+ "!cp /content/drive/MyDrive/mydata/scalers/scaler_new_10k.gz /content/"
+ ],
+ "execution_count": 16,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "text": [
+ "cp: cannot stat '/content/drive/MyDrive/models/lineage_pop_res_50/10k_res_18_identical_2.pth': No such file or directory\n"
+ ],
+ "name": "stdout"
+ }
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "FQK-M_rFHqD7"
+ },
+ "source": [
+ "import torch\r\n",
+ "import torchvision.models as models\r\n",
+ "import torch.nn as nn\r\n",
+ "import joblib\r\n",
+ "import pandas as pd\r\n",
+ "import matplotlib.pyplot as plt\r\n",
+ "import cv2\r\n",
+ "import torchvision.transforms as transforms\r\n",
+ "from PIL import Image\r\n",
+ "import numpy as np"
+ ],
+ "execution_count": 17,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Y25dyl2aPrh5"
+ },
+ "source": [
+ "## Set `device` as Cuda (GPU) for Faster Inference."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "9nU-ONOEHjU4",
+ "outputId": "cef2240c-f9f9-4e87-a0ad-dbff2720aa8f"
+ },
+ "source": [
+ "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\r\n",
+ "device"
+ ],
+ "execution_count": 18,
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "device(type='cuda', index=0)"
+ ]
+ },
+ "metadata": {
+ "tags": []
+ },
+ "execution_count": 18
+ }
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "5Zl35YaMQCe8"
+ },
+ "source": [
+ "## Define and Load Model(s)\r\n",
+ "* Architecture: ResNet18 with custom linear layers.\r\n",
+ "\r\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "_vMD6BzfIA9I"
+ },
+ "source": [
+ "model_dummy = models.resnet18(pretrained = True)\r\n",
+ "model_1 = models.resnet18(pretrained = True)\r\n",
+ "model_2 = models.resnet18(pretrained = True)\r\n",
+ "model_1.fc = model_1.fc = nn.Linear(512, 7)\r\n",
+ "model_2.fc = model_2.fc = nn.Linear(512, 7)\r\n",
+ "model_dummy.fc = model_dummy.fc = nn.Linear(512, 7)\r\n",
+ "\r\n",
+ "model_1 = model_1.to(device)\r\n",
+ "model_2 = model_2.to(device)\r\n",
+ "model_dummy=model_dummy.to(device)"
+ ],
+ "execution_count": 19,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3zDqJRmaRRX1"
+ },
+ "source": [
+ "## Loading `model_1` and `scaler_1`\r\n",
+ "* `model_1` uses weights from the cloned devolearn repository, in order to emulate performance of the devolearn library.\r\n",
+ "* To ensure even data scaling, I'm also loading up the scaler from devolearn's repository as `scaler_1`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "xF4P9QOKRN-D",
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "outputId": "61b20e3b-07f3-4e6a-9b30-7038baaef249"
+ },
+ "source": [
+ "model_1.load_state_dict(torch.load(\"/content/devolearn/devolearn/lineage_population_model/estimate_lineage_population.pt\"))\r\n",
+ "scaler_1 = joblib.load(\"/content/devolearn/devolearn/lineage_population_model/scaler/scaler.gz\")"
+ ],
+ "execution_count": 22,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.7/dist-packages/sklearn/base.py:315: UserWarning: Trying to unpickle estimator MinMaxScaler from version 0.23.1 when using version 0.24.1. This might lead to breaking code or invalid results. Use at your own risk.\n",
+ " UserWarning)\n"
+ ],
+ "name": "stderr"
+ }
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "papyC4fHThb2"
+ },
+ "source": [
+ "## Loading `model_1` and `scaler_1`\r\n",
+ "* `model_1` uses weights from the cloned devolearn repository, in order to emulate performance of the devolearn library.\r\n",
+ "* To ensure even data scaling, I'm also loading up the scaler from devolearn's repository as `scaler_1`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "rAVQZqYiIRkT"
+ },
+ "source": [
+ "model_2.load_state_dict(torch.load(\"/content/drive/MyDrive/models/devolearn/estimate_lineage_population.pth\"))\r\n",
+ "scaler_2 = joblib.load(\"/content/scaler_new_10k.gz\")"
+ ],
+ "execution_count": 24,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "OThYP9uAURDh"
+ },
+ "source": [
+ "## Set Models to `eval()` mode\r\n",
+ "model.eval() is a kind of switch for some specific layers/parts of the model that behave differently during training and inference (evaluating) time. For example, Dropouts Layers, BatchNorm Layers etc. You need to turn off them during model evaluation, and .eval() will do it for you. [source](https://stackoverflow.com/questions/60018578/what-does-model-eval-do-in-pytorch#:~:text=answer%20was%20accepted%E2%80%A6-,model.,will%20do%20it%20for%20you.)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "XVObuIsqTlio"
+ },
+ "source": [
+ "model_1.eval()\r\n",
+ "model_2.eval()"
+ ],
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Xz1bTq7WWEJ0"
+ },
+ "source": [
+ "## Plot data distributions:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "hOnivs6f0-kO",
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 446
+ },
+ "outputId": "8296afbe-2ca9-4da4-912d-fd18184ce75d"
+ },
+ "source": [
+ "print(\"Minimun values of each column (devolearn) = \", scaler_1.data_min_)\r\n",
+ "print(\"Maximum values of each column (devolearn) = \", scaler_1.data_max_)\r\n",
+ "print(\"Minimun values of each column (new) = \", scaler_2.data_min_)\r\n",
+ "print(\"Maximum values of each column (new) = \", scaler_2.data_max_)\r\n",
+ "\r\n",
+ "plt.rcParams['figure.figsize'] =10, 6\r\n",
+ "width = 0.40\r\n",
+ "labels=['A','E','M', 'P', 'C', \"D\", \"Z\"]\r\n",
+ "ind = np.arange(7)\r\n",
+ "plt.bar(ind, scaler_1.data_max_, width, label=\"values: old data-set\")\r\n",
+ "plt.bar(ind+width, scaler_2.data_max_, width, label=\"values: new data-set\")\r\n",
+ "plt.xticks(ind, labels)\r\n",
+ "plt.grid()\r\n",
+ "plt.legend(loc='best')\r\n",
+ "plt.show()"
+ ],
+ "execution_count": 26,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "text": [
+ "Minimun values of each column (devolearn) = [0. 0. 0. 0. 0. 0. 0.]\n",
+ "Maximum values of each column (devolearn) = [250. 14. 43. 1. 26. 8. 2.]\n",
+ "Minimun values of each column (new) = [0. 0. 0. 0. 0. 0. 0.]\n",
+ "Maximum values of each column (new) = [250. 16. 78. 1. 31. 16. 2.]\n"
+ ],
+ "name": "stdout"
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "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vny5WrU69D//bdq0qXncsmVLVVVVad++ferQoYOKi4vrbD9kyBA9/PDD2rRpk+68807dfffdys/P1+DBgyP3Y/369ZozZ44++ugjdezYURMmTKjz2g+33Ycffqif//znkqQ777xTl156qQoKCrR48WJNmDBBv/nNb9SxY0ddeOGFeu655yLXlQhOBQIAcJSNHTtW8+fP18KFC2uOApWXl+vUU09V69attWTJEm3YsKHOuO9///tatWqVdu/ere3bt+udd96RJPXo0UNlZWU1waqqqkorV67Uvn379OWXX2rYsGH6/e9/r/LyclVWVh5xve3atVP37t31wgsvSKoOcp9++qkk6eyzz9b777+vFi1aKC0tTTk5OXrkkUc0ZMiQOvsZMmSInn32WUnSihUrak7jffvttzr55JPVvn17lZaW6vXXX68Z07ZtW1VUVBx2u3POOUfFxcUqLi7WpZdeqg0bNigjI0M33nijbrjhBn388cc699xz9de//lXr1q2TJO3YsUOff/55nTkaiyNWAAAcZb1791ZFRYW6detWc8ruqquu0iWXXKLs7GzFYjGdddZZdcZlZmbqiiuuUJ8+fdS9e3f1799fknTCCSdo4cKFmjp1qsrLy7Vnzx5NmzZNZ555pq6++mqVl5fL3TV16lR16NBBhYWFmjt37iFPB9bnmWee0U033aS77rpLVVVVGjdunPr166c2bdooMzNT5557rqTqU4PPPfecsrOz6+zjpptu0nXXXaeePXuqZ8+eGjBggCSpX79+6t+/v8466yxlZmbWnK6Tqq/tGjlyZM11UIfarrb8/Hzdfffdat26tdLT0/X000+rc+fOevLJJzV+/Pia05h33XWXzjzzzDpzNIZFvTCuKcViMW/oPhYhZM1YnNC46dl7dM/yxDJoSdqVCY1rtCR93Dc/P195eXlJmbu5oVfR0Kfo6FU0n3zySU0gweFVVFSobdu2yS4jqVavXq2ePXsesMzMitw9Vt/2nAoEAAAIhGAFAAAQCMEKAAAgEIIVAOC4kwrXFyP1JfI+IVgBAI4re/fu1bZt2whXOCx317Zt25SWlnZE47jdAgDguLJjxw5VVFSorKws2aWkvF27dh1xsDiWpKWl6fTTTz+iMQQrAMBxxd3VvXv3ZJfRLOTn53NriiPEqUAAAIBACFYAAACBEKwAAAACIVgBAAAE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\n",
+ "text/plain": [
+ "