{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "German Traffic Sign Recognition Benchmark (GTSRB)\n",
    "=================================================\n",
    "---\n",
    "Introduction au Deep Learning  (IDLE) - S. Arias, E. Maldonado, JL. Parouty - CNRS/SARI/DEVLOG - 2020  \n",
    "Vesion : 1.2.1\n",
    "\n",
    "## Episode 7 : Full Convolutions\n",
    "\n",
    "Our main steps:\n",
    " - Try n models with n datasets\n",
    "\n",
    "## 1/ Import and init"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "IDLE 2020 - Practical Work Module\n",
      "  Version            : 0.1.4\n",
      "  Run time           : Friday 17 January 2020, 21:38:34\n",
      "  Matplotlib style   : idle/talk.mplstyle\n",
      "  TensorFlow version : 2.0.0\n",
      "  Keras version      : 2.2.4-tf\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras.callbacks import TensorBoard\n",
    "\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import h5py\n",
    "import os,time\n",
    "\n",
    "import pandas as pd\n",
    "import idle.pwk as ooo\n",
    "from importlib import reload\n",
    "from IPython.display import display\n",
    "\n",
    "ooo.init()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2/ Load dataset functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def read_dataset(name):\n",
    "    '''Reads h5 dataset from ./data\n",
    "\n",
    "    Arguments:  dataset name, without .h5\n",
    "    Returns:    x_train,y_train,x_test,y_test data'''\n",
    "    # ---- Read dataset\n",
    "    filename='./data/'+name+'.h5'\n",
    "    with  h5py.File(filename) as f:\n",
    "        x_train = f['x_train'][:]\n",
    "        y_train = f['y_train'][:]\n",
    "        x_test  = f['x_test'][:]\n",
    "        y_test  = f['y_test'][:]\n",
    "\n",
    "    return x_train,y_train,x_test,y_test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3/ Models collection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# A basic model\n",
    "#\n",
    "def get_model_v1(lx,ly,lz):\n",
    "    \n",
    "    model = keras.models.Sequential()\n",
    "    \n",
    "    model.add( keras.layers.Conv2D(96, (3,3), activation='relu', input_shape=(lx,ly,lz)))\n",
    "    model.add( keras.layers.MaxPooling2D((2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.2))\n",
    "\n",
    "    model.add( keras.layers.Conv2D(192, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D((2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.2))\n",
    "\n",
    "    model.add( keras.layers.Flatten()) \n",
    "    model.add( keras.layers.Dense(1500, activation='relu'))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "\n",
    "    model.add( keras.layers.Dense(43, activation='softmax'))\n",
    "    return model\n",
    "    \n",
    "# A more sophisticated model\n",
    "#\n",
    "def get_model_v2(lx,ly,lz):\n",
    "    model = keras.models.Sequential()\n",
    "\n",
    "    model.add( keras.layers.Conv2D(64, (3, 3), padding='same', input_shape=(lx,ly,lz), activation='relu'))\n",
    "    model.add( keras.layers.Conv2D(64, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D(pool_size=(2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.2))\n",
    "\n",
    "    model.add( keras.layers.Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "    model.add( keras.layers.Conv2D(128, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D(pool_size=(2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.2))\n",
    "\n",
    "    model.add( keras.layers.Conv2D(256, (3, 3), padding='same',activation='relu'))\n",
    "    model.add( keras.layers.Conv2D(256, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D(pool_size=(2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.2))\n",
    "\n",
    "    model.add( keras.layers.Flatten())\n",
    "    model.add( keras.layers.Dense(512, activation='relu'))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "    model.add( keras.layers.Dense(43, activation='softmax'))\n",
    "    return model\n",
    "\n",
    "# My sphisticated model, but small and fast\n",
    "#\n",
    "def get_model_v3(lx,ly,lz):\n",
    "    model = keras.models.Sequential()\n",
    "    model.add( keras.layers.Conv2D(32, (3,3),   activation='relu', input_shape=(lx,ly,lz)))\n",
    "    model.add( keras.layers.MaxPooling2D((2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "\n",
    "    model.add( keras.layers.Conv2D(64, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D((2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "\n",
    "    model.add( keras.layers.Conv2D(128, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D((2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "\n",
    "    model.add( keras.layers.Conv2D(256, (3, 3), activation='relu'))\n",
    "    model.add( keras.layers.MaxPooling2D((2, 2)))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "\n",
    "    model.add( keras.layers.Flatten()) \n",
    "    model.add( keras.layers.Dense(1152, activation='relu'))\n",
    "    model.add( keras.layers.Dropout(0.5))\n",
    "\n",
    "    model.add( keras.layers.Dense(43, activation='softmax'))\n",
    "    return model\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4/ Callbacks  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total 0\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "# To clean old logs and saved model, run this cell\n",
    "#\n",
    "/bin/rm -r ./run/logs 2>/dev/null\n",
    "/bin/rm -r ./run/models 2>/dev/null\n",
    "/bin/ls -l ./run  2>/dev/null"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "ooo.mkdir('./run/models')\n",
    "ooo.mkdir('./run/logs')\n",
    "\n",
    "# ---- Callback tensorboard\n",
    "log_dir = \"./run/logs/tb_\" + ooo.tag_now()\n",
    "tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)\n",
    "\n",
    "# ---- Callback ModelCheckpoint - Save best model\n",
    "save_dir = \"./run/models/best-model.h5\"\n",
    "bestmodel_callback = tf.keras.callbacks.ModelCheckpoint(filepath=save_dir, verbose=0, monitor='accuracy', save_best_only=True)\n",
    "\n",
    "# ---- Callback ModelCheckpoint - Save model each epochs\n",
    "save_dir = \"./run/models/model-{epoch:04d}.h5\"\n",
    "savemodel_callback = tf.keras.callbacks.ModelCheckpoint(filepath=save_dir, verbose=0, save_freq=2000*5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6/ Multiple datasets, multiple models ;-)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "def multi_run(datasets, models, batch_size=64, epochs=16):\n",
    "\n",
    "    # ---- Columns of report\n",
    "    #\n",
    "    report={}\n",
    "    report['Dataset']=[]\n",
    "    report['Size']   =[]\n",
    "    for m in models:\n",
    "        report[m+' Accuracy'] = []\n",
    "        report[m+' Duration'] = []\n",
    "\n",
    "    # ---- Let's go\n",
    "    #\n",
    "    for dname in datasets:\n",
    "        print(\"\\nDataset : \",dname)\n",
    "\n",
    "        # ---- Read dataset\n",
    "        x_train,y_train,x_test,y_test = read_dataset(dname)\n",
    "        dsize=os.path.getsize('./data/'+dname+'.h5')/(1024*1024)\n",
    "        report['Dataset'].append(dname)\n",
    "        report['Size'].append(dname)\n",
    "        \n",
    "        # ---- Get the shape\n",
    "        (n,lx,ly,lz) = x_train.shape\n",
    "\n",
    "        # ---- For each model\n",
    "        for kmodel,fmodel in models.items():\n",
    "            print(\"    Run model {}  : \".format(kmodel), end='')\n",
    "            # ---- get model\n",
    "            try:\n",
    "                model=fmodel(lx,ly,lz)\n",
    "                # ---- Compile it\n",
    "                model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])\n",
    "                # ---- Train\n",
    "                start_time = time.time()\n",
    "                history = model.fit(  x_train[:1000], y_train[:1000],\n",
    "                                    batch_size      = batch_size,\n",
    "                                    epochs          = epochs,\n",
    "                                    verbose         = 0,\n",
    "                                    validation_data = (x_test, y_test),\n",
    "                                    callbacks       = [tensorboard_callback, bestmodel_callback, savemodel_callback])\n",
    "                # ---- Result\n",
    "                end_time = time.time()\n",
    "                duration = end_time-start_time\n",
    "                accuracy = max(history.history[\"val_accuracy\"])*100\n",
    "                #\n",
    "                report[kmodel+' Accuracy'].append(accuracy)\n",
    "                report[kmodel+' Duration'].append(duration)\n",
    "                print(\"Accuracy={:.2f} and Duration={:.2f})\".format(accuracy,duration))\n",
    "            except:\n",
    "                report[kmodel+' Accuracy'].append('-')\n",
    "                report[kmodel+' Duration'].append('-')\n",
    "                print('-')\n",
    "    print(\"\\n\")\n",
    "    return report"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Dataset :  set-24x24-L\n",
      "    Run model v1  : Accuracy=9.46 and Duration=7.51)\n",
      "    Run model v3  : -\n",
      "\n",
      "Dataset :  set-24x24-RGB\n",
      "    Run model v1  : Accuracy=15.95 and Duration=7.95)\n",
      "    Run model v3  : -\n",
      "\n",
      "\n",
      "CPU times: user 1min 35s, sys: 3.31 s, total: 1min 38s\n",
      "Wall time: 17 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "# datasets = ['set-24x24-L', 'set-24x24-RGB', 'set-48x48-L', 'set-48x48-RGB', 'set-24x24-L-LHE', 'set-24x24-RGB-HE', 'set-48x48-L-LHE', 'set-48x48-RGB-HE']\n",
    "# models   = {'v1':get_model_v1, 'v2':get_model_v2, 'v3':get_model_v3}\n",
    "\n",
    "datasets = ['set-24x24-L', 'set-24x24-RGB']\n",
    "models   = {'v1':get_model_v1, 'v3':get_model_v3}\n",
    "\n",
    "out    = multi_run(datasets, models, batch_size=64, epochs=2)\n",
    "report = pd.DataFrame (out)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Dataset</th>\n",
       "      <th>Size</th>\n",
       "      <th>v1 Accuracy</th>\n",
       "      <th>v1 Duration</th>\n",
       "      <th>v3 Accuracy</th>\n",
       "      <th>v3 Duration</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>set-24x24-L</td>\n",
       "      <td>set-24x24-L</td>\n",
       "      <td>9.46160</td>\n",
       "      <td>7.514726</td>\n",
       "      <td>-</td>\n",
       "      <td>-</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>set-24x24-RGB</td>\n",
       "      <td>set-24x24-RGB</td>\n",
       "      <td>15.94616</td>\n",
       "      <td>7.946994</td>\n",
       "      <td>-</td>\n",
       "      <td>-</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         Dataset           Size  v1 Accuracy  v1 Duration v3 Accuracy  \\\n",
       "0    set-24x24-L    set-24x24-L      9.46160     7.514726           -   \n",
       "1  set-24x24-RGB  set-24x24-RGB     15.94616     7.946994           -   \n",
       "\n",
       "  v3 Duration  \n",
       "0           -  \n",
       "1           -  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(report)\n",
    "df.to_hdf('foo.h5', 'df')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Dataset</th>\n",
       "      <th>Size</th>\n",
       "      <th>v1 Accuracy</th>\n",
       "      <th>v1 Duration</th>\n",
       "      <th>v3 Accuracy</th>\n",
       "      <th>v3 Duration</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>set-24x24-L</td>\n",
       "      <td>set-24x24-L</td>\n",
       "      <td>11.892320</td>\n",
       "      <td>8.730333</td>\n",
       "      <td>-</td>\n",
       "      <td>-</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>set-24x24-RGB</td>\n",
       "      <td>set-24x24-RGB</td>\n",
       "      <td>12.707838</td>\n",
       "      <td>8.308997</td>\n",
       "      <td>-</td>\n",
       "      <td>-</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         Dataset           Size  v1 Accuracy  v1 Duration v3 Accuracy  \\\n",
       "0    set-24x24-L    set-24x24-L    11.892320     8.730333           -   \n",
       "1  set-24x24-RGB  set-24x24-RGB    12.707838     8.308997           -   \n",
       "\n",
       "  v3 Duration  \n",
       "0           -  \n",
       "1           -  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df=pd.read_hdf('foo.h5', 'df')\n",
    "display(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "\n",
    "### Some results :  \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|         Datasets         |       Size      |     Model : v1     |    Model : v2      |     Model : v3     |\n",
    "|:------------------------:|:---------------:|:------------------:|:------------------:|:------------------:|\n",
    "| set-24x24-L              |          229 Mo |   95.91%  75.04s   |   96.86% 102.28s   |   -      -         |\n",
    "| set-24x24-RGB            |          684 Mo |   96.60%  77.24s   |   97.32% 103.93s   |   -      -         |\n",
    "| set-48x48-L              |          914 Mo | **96.71%** 123.94s |   97.68% 149.57s   |  97.60%  91.53s    |\n",
    "| set-48x48-RGB            |         2736 Mo |   96.36% 117.74s   | **98.20%** 142.63s |  97.28%  91.29s    |\n",
    "| set-24x24-L-LHE          |          229 Mo |   95.95%  66.12s   |   96.75%  89.45s   |   -      -         |\n",
    "| set-24x24-RGB-HE         |          684 Mo |   95.30%  68.89s   |   96.28%  92.15s   |   -      -         |\n",
    "| set-48x48-L-LHE          |          914 Mo |   96.69% 109.28s   |   97.94% 135.17s   | **97.97%** 83.80s  |\n",
    "| set-48x48-RGB-HE         |         2736 Mo |   95.29% 117.70s   | **98.13%** 141.56s |  97.00%  89.38s    |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}