{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Python for Signal Processing\n", "Danilo Greco, PhD - danilo.greco@uniparthenope.it - University of Naples Parthenope" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "9WjsBPzEFKMW" }, "source": [ "# Deep Learning Example\n", "\n", "This example is inspired by [MIT Deep Learning](https://deeplearning.mit.edu).\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "0SyO72xOFKMX" }, "source": [ "## Pre-requisites:\n", "\n", "It is possible to run this notebook locally on a jupyter installation or we recommend that you run this this notebook in the cloud on Google Colab because it is possible to test the execution both on the standard CPU kernel and on a GPU powered one.\n", "\n", "You can switch between them by selecting the following menuitems: Edit $\\rightarrow$ Notebook settings $\\rightarrow$ Hardware accelerator\n", "\n", "Local execution requires to [install TensorFlow](https://www.tensorflow.org/install/), which is not always an easy task, and [tf.keras](https://www.tensorflow.org/guide/keras) which\n", "is TensorFlow's high-level API for building and training deep learning models. \n", "\n", "You may find detailed documentation at [Keras Guide](https://www.tensorflow.org/guide/keras).\n", "\n", "## Required imports:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "executionInfo": { "elapsed": 9048, "status": "ok", "timestamp": 1588525634389, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "gHoRYxKGFKMe", "outputId": "da845f03-5c00-4788-9936-c213e58a21d5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.5.0\n" ] } ], "source": [ "# TensorFlow and tf.keras\n", "import tensorflow as tf\n", "from tensorflow import keras\n", "from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense\n", "\n", "# Commonly used modules\n", "import numpy as np\n", "import os\n", "import sys\n", "\n", "# Images, plots, display, and visualization\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", "print(tf.__version__)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "YRGrsd9uFKOb" }, "source": [ "## Example: classification of MNIST dataset with Convolutional Neural Networks\n", "\n", "We are now going to build a convolutional neural network (CNN) classifier to classify images of handwritten digits in the MNIST dataset.\n", "\n", "The MNIST dataset containss 70,000 grayscale images of handwritten digits at a resolution of 28 by 28 pixels. The task is to take one of these images as input and predict the most likely digit contained in the image (along with a relative confidence in this prediction):\n", "\n", "\n", "\n", "The images are 28x28 NumPy arrays, with pixel values ranging between 0 and 255. \n", "\n", "The *labels* are an array of integers, ranging from 0 to 9.\n", "\n", "The dataset can be downloaded from this repository:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 51 }, "colab_type": "code", "executionInfo": { "elapsed": 1588, "status": "ok", "timestamp": 1588525640927, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "34UQqKpyFKOk", "outputId": "67910638-9af9-4c86-a654-791b627dbddc" }, "outputs": [], "source": [ "(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()\n", "\n", "# reshape images to specify that it's a single channel\n", "train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)\n", "test_images = test_images.reshape(test_images.shape[0], 28, 28, 1)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "CHsgfXYLFKOs" }, "source": [ "Let's convert the image pixels into a range of 0 to 1 before feeding to the neural network model by dividing the values of both the *training set* and the *testing set* by 255." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "colab": {}, "colab_type": "code", "id": "NLeTnDZWFKOv" }, "outputs": [], "source": [ "def preprocess_images(imgs): # should work for both a single image and multiple images\n", " sample_img = imgs if len(imgs.shape) == 2 else imgs[0]\n", " assert sample_img.shape in [(28, 28, 1), (28, 28)], sample_img.shape # make sure images are 28x28 and single-channel (grayscale)\n", " return imgs / 255.0\n", "\n", "train_images = preprocess_images(train_images)\n", "test_images = preprocess_images(test_images)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "ftNdQX_ZFKPD" }, "source": [ "Display the first *count* images from the *training set* and display the class name below each image. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 95 }, "colab_type": "code", "executionInfo": { "elapsed": 2599, "status": "ok", "timestamp": 1588525661367, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "WvQDj48KFKPH", "outputId": "a8e27fed-bf30-496b-8e9d-ebd2d34f9ba6" }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "count = 15\n", "plt.figure(figsize=(16,1))\n", "for i in range(count):\n", " plt.subplot(1,count,i+1)\n", " plt.xticks([])\n", " plt.yticks([])\n", " plt.grid(False)\n", " plt.imshow(train_images[i].reshape(28, 28), cmap=plt.cm.binary)\n", " plt.xlabel(train_labels[i])" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "EqYEqUJxFKPS" }, "source": [ "### Build the model\n", "\n", "Building the neural network requires configuring the layers of the model, then compiling the model. Here follows one possible configuration that produces good results on the MNIST dataset:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 442 }, "colab_type": "code", "executionInfo": { "elapsed": 1066, "status": "ok", "timestamp": 1588526094624, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "oWOa5LOVFKPT", "outputId": "f810f4ed-ffce-4d53-b6d5-6015747ae0e4" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"sequential\"\n", "_________________________________________________________________\n", "Layer (type) Output Shape Param # \n", "=================================================================\n", "conv2d (Conv2D) (None, 26, 26, 32) 320 \n", "_________________________________________________________________\n", "conv2d_1 (Conv2D) (None, 24, 24, 64) 18496 \n", "_________________________________________________________________\n", "max_pooling2d (MaxPooling2D) (None, 12, 12, 64) 0 \n", "_________________________________________________________________\n", "dropout (Dropout) (None, 12, 12, 64) 0 \n", "_________________________________________________________________\n", "flatten (Flatten) (None, 9216) 0 \n", "_________________________________________________________________\n", "dense (Dense) (None, 128) 1179776 \n", "_________________________________________________________________\n", "dropout_1 (Dropout) (None, 128) 0 \n", "_________________________________________________________________\n", "dense_1 (Dense) (None, 10) 1290 \n", "=================================================================\n", "Total params: 1,199,882\n", "Trainable params: 1,199,882\n", "Non-trainable params: 0\n", "_________________________________________________________________\n", "None\n" ] } ], "source": [ "model = keras.Sequential()\n", "# 32 convolution filters used each of size 3x3\n", "model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))\n", "# 64 convolution filters used each of size 3x3\n", "model.add(Conv2D(64, (3, 3), activation='relu'))\n", "# choose the best features via pooling\n", "model.add(MaxPooling2D(pool_size=(2, 2)))\n", "# randomly turn neurons on and off to improve convergence\n", "model.add(Dropout(0.25))\n", "# flatten since too many dimensions, we only want a classification output\n", "model.add(Flatten())\n", "# fully connected to get all relevant data\n", "model.add(Dense(128, activation='relu'))\n", "# one more dropout\n", "model.add(Dropout(0.5))\n", "# output a softmax to squash the matrix into output probabilities\n", "model.add(Dense(10, activation='softmax'))\n", "print(model.summary())" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "fUHo8mPbFKPb" }, "source": [ "Before the model is ready for training, it needs a few more settings. These are added during the model's *compile* step:\n", "\n", "* *Loss function* - measures how accurate the model is during training, we want to minimize this with the optimizer.\n", "* *Optimizer* - how the model is updated based on the data it sees and its loss function.\n", "* *Metrics* - used to monitor the training and testing steps. \"accuracy\" is the fraction of images that are correctly classified." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": {}, "colab_type": "code", "id": "gPDJn26KFKPe" }, "outputs": [], "source": [ "model.compile(optimizer=tf.optimizers.Adam(), \n", " loss='sparse_categorical_crossentropy',\n", " metrics=['accuracy'])" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "CzydQVk1FKPm" }, "source": [ "### Train the model\n", "\n", "Being in a context of *supervised learning*, training the neural network model requires to call the `model.fit` method on the training data `train_images` and the relevant ground truth `train_labels`.\n", "\n", "To make predictions we use a test set `test_images` and check the predictions against the `test_labels` array. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 204 }, "colab_type": "code", "executionInfo": { "elapsed": 26577, "status": "ok", "timestamp": 1588526136887, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "UuerNpGCFKPn", "outputId": "02b987bf-c11b-4b23-a167-df97dc79b190" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(60000, 28, 28, 1)\n", "Epoch 1/5\n", "1730/1875 [==========================>...] - ETA: 19s - loss: 0.2041 - accuracy: 0.9383" ] } ], "source": [ "print(train_images.shape)\n", "history = model.fit(train_images, train_labels, epochs=5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "executionInfo": { "elapsed": 4267, "status": "ok", "timestamp": 1588526145251, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "QQWml8C1Hh2j", "outputId": "4777244a-6854-4538-9e00-fde981cb64a0" }, "outputs": [], "source": [ "acc = np.mean(history.history['accuracy'])\n", "'Current accuracy on the training data is {:.2f} %'.format(acc*100)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "lMJhdLVsFKP4" }, "source": [ "### Evaluate generalization accuracy\n", "\n", "Model performance is assessed on the test dataset:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 68 }, "colab_type": "code", "executionInfo": { "elapsed": 1589, "status": "ok", "timestamp": 1588526157021, "user": { "displayName": "alberto cabri", "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GiK1udzlVWHuhhAmhv--XV0kKVt6Vo722O-iPJD=s64", "userId": "13479437405885655509" }, "user_tz": -120 }, "id": "AaC6oI7iFKP5", "outputId": "4cbd4542-10a9-4b02-9a65-e18d69489ca3" }, "outputs": [], "source": [ "print(test_images.shape)\n", "test_loss, test_acc = model.evaluate(test_images, test_labels)\n", "\n", "'Test accuracy: {:.2f} %'.format(test_acc*100)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "VnEM-SL5FKQA" }, "source": [ "Most often, the accuracy on the test dataset is a little less than the accuracy on the training dataset. This gap between training accuracy and test accuracy is an example of *overfitting*. In our case, the accuracy is better, due to successful regularization accomplished with the Dropout layers." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "accelerator": "GPU", "colab": { "collapsed_sections": [], "name": "HW_Deep_Learning.ipynb", "provenance": [ { "file_id": "https://github.com/lexfridman/mit-deep-learning/blob/master/tutorial_deep_learning_basics/deep_learning_basics.ipynb", "timestamp": 1587905678271 } ], "toc_visible": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.6" } }, "nbformat": 4, "nbformat_minor": 1 }