{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "<img width=\"800px\" src=\"../fidle/img/header.svg\"></img>\n", "\n", "# <!-- TITLE --> [K3MNIST2] - Simple classification with CNN\n", "<!-- DESC --> An example of classification using a convolutional neural network for the famous MNIST dataset\n", "<!-- AUTHOR : Jean-Luc Parouty (CNRS/SIMaP) -->\n", "\n", "## Objectives :\n", " - Recognizing handwritten numbers\n", " - Understanding the principle of a classifier DNN network \n", " - Implementation with Keras \n", "\n", "\n", "The [MNIST dataset](http://yann.lecun.com/exdb/mnist/) (Modified National Institute of Standards and Technology) is a must for Deep Learning. \n", "It consists of 60,000 small images of handwritten numbers for learning and 10,000 for testing.\n", "\n", "\n", "## What we're going to do :\n", "\n", " - Retrieve data\n", " - Preparing the data\n", " - Create a model\n", " - Train the model\n", " - Evaluate the result\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1 - Init python stuff" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "os.environ['KERAS_BACKEND'] = 'torch'\n", "\n", "import keras\n", "\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import sys,os\n", "from importlib import reload\n", "\n", "# Init Fidle environment\n", "import fidle\n", "\n", "run_id, run_dir, datasets_dir = fidle.init('K3MNIST2')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Verbosity during training : 0 = silent, 1 = progress bar, 2 = one line per epoch" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fit_verbosity = 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Override parameters (batch mode) - Just forget this cell" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fidle.override('fit_verbosity')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 2 - Retrieve data\n", "MNIST is one of the most famous historic dataset. \n", "Include in [Keras datasets](https://keras.io/datasets)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()\n", "\n", "x_train = x_train.reshape(-1,28,28,1)\n", "x_test = x_test.reshape(-1,28,28,1)\n", "\n", "print(\"x_train : \",x_train.shape)\n", "print(\"y_train : \",y_train.shape)\n", "print(\"x_test : \",x_test.shape)\n", "print(\"y_test : \",y_test.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 3 - Preparing the data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('Before normalization : Min={}, max={}'.format(x_train.min(),x_train.max()))\n", "\n", "xmax=x_train.max()\n", "x_train = x_train / xmax\n", "x_test = x_test / xmax\n", "\n", "print('After normalization : Min={}, max={}'.format(x_train.min(),x_train.max()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Have a look" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fidle.scrawler.images(x_train, y_train, [27], x_size=5,y_size=5, colorbar=True, save_as='01-one-digit')\n", "fidle.scrawler.images(x_train, y_train, range(5,41), columns=12, save_as='02-many-digits')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 4 - Create model\n", "About informations about : \n", " - [Optimizer](https://keras.io/api/optimizers)\n", " - [Activation](https://keras.io/api/layers/activations)\n", " - [Loss](https://keras.io/api/losses)\n", " - [Metrics](https://keras.io/api/metrics)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model = keras.models.Sequential()\n", "\n", "model.add( keras.layers.Input((28,28,1)) )\n", "\n", "model.add( keras.layers.Conv2D(8, (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.Conv2D(16, (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(100, activation='relu'))\n", "model.add( keras.layers.Dropout(0.5))\n", "\n", "model.add( keras.layers.Dense(10, activation='softmax'))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model.summary()\n", "\n", "model.compile(optimizer='adam',\n", " loss='sparse_categorical_crossentropy',\n", " metrics=['accuracy'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 5 - Train the model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "batch_size = 512\n", "epochs = 16\n", "\n", "history = model.fit( x_train, y_train,\n", " batch_size = batch_size,\n", " epochs = epochs,\n", " verbose = fit_verbosity,\n", " validation_data = (x_test, y_test))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 6 - Evaluate\n", "### 6.1 - Final loss and accuracy\n", "Note : With a DNN, we had a precision of the order of : 97.7%" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "score = model.evaluate(x_test, y_test, verbose=0)\n", "\n", "print(f'Test loss : {score[0]:4.4f}')\n", "print(f'Test accuracy : {score[1]:4.4f}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6.2 - Plot history" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fidle.scrawler.history(history, figsize=(6,4), save_as='03-history')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6.3 - Plot results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#y_pred = model.predict_classes(x_test) Deprecated after 01/01/2021 !!\n", "\n", "y_sigmoid = model.predict(x_test)\n", "y_pred = np.argmax(y_sigmoid, axis=-1)\n", "\n", "fidle.scrawler.images(x_test, y_test, range(0,200), columns=12, x_size=1, y_size=1, y_pred=y_pred, save_as='04-predictions')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6.4 - Plot some errors" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "errors=[ i for i in range(len(x_test)) if y_pred[i]!=y_test[i] ]\n", "errors=errors[:min(24,len(errors))]\n", "fidle.scrawler.images(x_test, y_test, errors[:15], columns=6, x_size=2, y_size=2, y_pred=y_pred, save_as='05-some-errors')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fidle.scrawler.confusion_matrix(y_test,y_pred,range(10),normalize=True, save_as='06-confusion-matrix')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fidle.end()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "<div class=\"todo\">\n", " A few things you can do for fun:\n", " <ul>\n", " <li>Changing the network architecture (layers, number of neurons, etc.)</li>\n", " <li>Display a summary of the network</li>\n", " <li>Retrieve and display the softmax output of the network, to evaluate its \"doubts\".</li>\n", " </ul>\n", "</div>" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "<img width=\"80px\" src=\"../fidle/img/logo-paysage.svg\"></img>" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.9.2 ('fidle-env')", "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.9.2" }, "vscode": { "interpreter": { "hash": "b3929042cc22c1274d74e3e946c52b845b57cb6d84f2d591ffe0519b38e4896d" } } }, "nbformat": 4, "nbformat_minor": 4 }