{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "Notebook_2_linear_models.ipynb", "provenance": [], "collapsed_sections": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "e3Y5DhXzUKob", "colab_type": "text" }, "source": [ "## Neural Networks for Data Science Applications\n", "### Lab session 2: Linear models for regression and classification\n", "\n", "**Contents of the lab session**\n", "+ A linear regression example with manual gradient descent.\n", "+ A logistic regression example with objects from tf.keras." ] }, { "cell_type": "code", "metadata": { "id": "-RKZuzNCsoIm", "colab_type": "code", "colab": {} }, "source": [ "# Install TensorFlow 2.0\n", "!pip install -q tensorflow==2.0.0" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "F5StKXvXvrGP", "colab_type": "code", "colab": {} }, "source": [ "import tensorflow as tf" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "yzmwyPsewEvP", "colab_type": "code", "colab": {} }, "source": [ "# This command ensures that the first Matplotlib plot is shown inline\n", "%matplotlib inline" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "kZQugNBkUvEV", "colab_type": "text" }, "source": [ "### 1.1 - Load and preprocess data for linear regression" ] }, { "cell_type": "code", "metadata": { "id": "Ls0qo577wK1z", "colab_type": "code", "colab": {} }, "source": [ "# Import some utility modules from sklearn\n", "from sklearn import datasets, preprocessing, model_selection" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "PCT3-j9xxybR", "colab_type": "code", "colab": {} }, "source": [ "# Load a simple regression dataset\n", "boston = datasets.load_boston()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "tslO1S9mx5uZ", "colab_type": "code", "outputId": "b807430d-7527-498d-d596-fae8c9ce14bc", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "# The dataset is a dictionary\n", "boston.keys()" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "dict_keys(['data', 'target', 'feature_names', 'DESCR', 'filename'])" ] }, "metadata": { "tags": [] }, "execution_count": 7 } ] }, { "cell_type": "code", "metadata": { "id": "izpjIaLxyLBI", "colab_type": "code", "colab": {} }, "source": [ "# Take the input matrix and the output vector\n", "X = boston['data']\n", "y = boston['target']" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "LTXSP97qyV_u", "colab_type": "code", "outputId": "c1f25823-e14a-4a7c-d24f-a23551068d5a", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "# Inspect the shape of the matrix:\n", "# X.shape[0]: number of examples\n", "# X.shape[1]: number of features\n", "X.shape" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "(506, 13)" ] }, "metadata": { "tags": [] }, "execution_count": 9 } ] }, { "cell_type": "code", "metadata": { "id": "lXGSMg1dys_M", "colab_type": "code", "outputId": "e6cded7f-8018-4e6e-c920-d47ccfaf48c3", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "y[0:10]" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "array([24. , 21.6, 34.7, 33.4, 36.2, 28.7, 22.9, 27.1, 16.5, 18.9])" ] }, "metadata": { "tags": [] }, "execution_count": 10 } ] }, { "cell_type": "code", "metadata": { "id": "xHbRK3a5y_4M", "colab_type": "code", "outputId": "51b00e5f-2cdb-471a-ceb1-83f0aee357a4", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "y.shape" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "(506,)" ] }, "metadata": { "tags": [] }, "execution_count": 11 } ] }, { "cell_type": "code", "metadata": { "id": "MuWaR2sizHh7", "colab_type": "code", "colab": {} }, "source": [ "# Useful to do shape computations easily\n", "y = y.reshape(-1, 1)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "JkFNNrxCzN5T", "colab_type": "code", "colab": {} }, "source": [ "# Normalize the data\n", "X = preprocessing.scale(X)\n", "y = preprocessing.scale(y)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "QIfYQGC_z2Se", "colab_type": "code", "colab": {} }, "source": [ "# Split the data into training and test portions\n", "Xtrain, Xtest, ytrain, ytest = \\\n", " model_selection.train_test_split(X, y)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "i4ROx6nhVLgE", "colab_type": "text" }, "source": [ "### 1.2 - Linear regression in TF (low level)" ] }, { "cell_type": "code", "metadata": { "id": "Kjf3cPzj0mhX", "colab_type": "code", "colab": {} }, "source": [ "# Define a utility function to initialize the model variables\n", "# Note: we initialize them as small pseudo-random numbers\n", "def model_init(n_features):\n", " w = tf.Variable(\n", " tf.random.normal(shape=(n_features, 1), stddev=0.01, dtype=tf.float64), \n", " )\n", " b = tf.Variable(0.0, dtype=tf.float64)\n", " return w, b" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "MhKuaZpILNiQ", "colab_type": "code", "colab": {} }, "source": [ "w, b = model_init(13)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "3zFAiYnK2BzW", "colab_type": "code", "colab": {} }, "source": [ "# Compute the linear model outputs\n", "def linear_model(x):\n", " return tf.matmul(x, w) + b" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "cr1w85YZ3NlR", "colab_type": "code", "outputId": "19ccf11c-8c89-4e10-ea0a-b1cae4e7bb26", "colab": { "base_uri": "https://localhost:8080/", "height": 86 } }, "source": [ "linear_model(Xtrain[0:3])" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 17 } ] }, { "cell_type": "code", "metadata": { "id": "47oWKvvY31Vk", "colab_type": "code", "colab": {} }, "source": [ "# A function to compute the squared loss\n", "def squared_loss(ytrue, ypredicted):\n", " return tf.reduce_mean((ytrue - ypredicted)**2.0)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "kO-114gb6VVy", "colab_type": "code", "outputId": "08dc5b32-a061-4e55-eedd-ddcfcc05bffa", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "squared_loss(ytrain, linear_model(Xtrain))" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 71 } ] }, { "cell_type": "code", "metadata": { "id": "G4h6WL3E6bi3", "colab_type": "code", "colab": {} }, "source": [ "# Optimization phase\n", "\n", "for i in range(1000):\n", " \n", " # Step 1: compute the loss\n", " with tf.GradientTape() as tape:\n", " ypredicted = linear_model(Xtrain)\n", " l = squared_loss(ytrain, ypredicted)\n", " \n", " # Step 2: compute the gradients\n", " g = tape.gradient(l, [w, b]) \n", " \n", " # Step 3: do a gradient descent step\n", " w.assign_sub(0.01 * g[0]) # w = w - \\eta * gradient of l w.r.t. w\n", " b.assign_sub(0.01 * g[1]) # b = b - \\eta * gradient of l w.r.t. b" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "GyjS0fo99rBb", "colab_type": "code", "outputId": "0c313b3d-6b09-40db-efa7-da8acffa4f48", "colab": { "base_uri": "https://localhost:8080/", "height": 259 } }, "source": [ "w" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 21 } ] }, { "cell_type": "code", "metadata": { "id": "RNDfHVng-FiJ", "colab_type": "code", "outputId": "6fb06e80-29ab-461f-83ea-c4ecfc19108f", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "# Check the training loss has decreased\n", "squared_loss(ytrain, linear_model(Xtrain))" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 22 } ] }, { "cell_type": "code", "metadata": { "id": "h-G3Mxf8-rHp", "colab_type": "code", "outputId": "d605e9b2-5bc7-4f58-b462-156c63c35d00", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "# Check the test loss (almost no overfitting in this scenario)\n", "squared_loss(ytest, linear_model(Xtest))" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 21 } ] }, { "cell_type": "code", "metadata": { "id": "x9bERv-2ABc3", "colab_type": "code", "colab": {} }, "source": [ "# Check that the solution is optimal by computing the closed form solution to the least-squares problem.\n", "# (X.T * X)^-1 * X.T * y\n", "# Note: in realistic situations there are faster and more numerically stable ways of doing this computation.\n", "XX = tf.matmul(X.transpose(), X)\n", "Xy = tf.matmul(X.transpose(), y)\n", "wopt = tf.matmul(tf.linalg.inv(XX), Xy)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "bWtX3nU-DVMo", "colab_type": "code", "outputId": "31ec5e66-4216-4c8b-ce4c-9c81358c9374", "colab": { "base_uri": "https://localhost:8080/", "height": 286 } }, "source": [ "import matplotlib.pyplot as plt\n", "plt.plot(wopt - w)\n", "plt.plot([0, 12], [0.1, 0.1], '--r')\n", "plt.plot([0, 12], [-0.1, -0.1], '--r')" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[]" ] }, "metadata": { "tags": [] }, "execution_count": 29 }, { "output_type": "display_data", "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] } } ] }, { "cell_type": "markdown", "metadata": { "id": "T1vo2iZxXiht", "colab_type": "text" }, "source": [ "### 1.3 - Load a binary classification dataset" ] }, { "cell_type": "code", "metadata": { "id": "lNO5q23ZEA0D", "colab_type": "code", "colab": {} }, "source": [ "breastcancer = datasets.load_breast_cancer()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "SqH7Az0WJDjK", "colab_type": "code", "colab": {} }, "source": [ "X = breastcancer['data']\n", "y = breastcancer['target']" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "4gGUckplJMVi", "colab_type": "code", "colab": {} }, "source": [ "# We do not need to scale the 0/1 y vector\n", "X = preprocessing.scale(X)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "aBZw0ziYJTTL", "colab_type": "code", "outputId": "e8cde02e-e510-44b5-9dd9-44e6541645ca", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "X.shape" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "(569, 30)" ] }, "metadata": { "tags": [] }, "execution_count": 33 } ] }, { "cell_type": "code", "metadata": { "id": "AZkK3lDuJfS8", "colab_type": "code", "outputId": "e4b8729b-85f8-4663-879a-51688ead18a8", "colab": { "base_uri": "https://localhost:8080/", "height": 69 } }, "source": [ "y[0:50]" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 1, 0, 1, 1])" ] }, "metadata": { "tags": [] }, "execution_count": 35 } ] }, { "cell_type": "code", "metadata": { "id": "FQmn_OZYJgMi", "colab_type": "code", "outputId": "292cb895-4e42-430a-a0e5-7373d1a8ee53", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "y.dtype" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "dtype('int64')" ] }, "metadata": { "tags": [] }, "execution_count": 36 } ] }, { "cell_type": "code", "metadata": { "id": "YgEwvJriJ2uu", "colab_type": "code", "colab": {} }, "source": [ "# For compatibility with some TF functions, we need np.int32 values\n", "import numpy as np\n", "y = y.reshape(-1, 1).astype(np.int32)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "5SNkKYqYKB8X", "colab_type": "code", "colab": {} }, "source": [ "# Split the data, this time with stratification with respect to y, to ensure the same balancing of classes in train and test\n", "Xtrain, Xtest, ytrain, ytest = \\\n", " model_selection.train_test_split(X, y, stratify=y)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Tdg_Gy8ZX3na", "colab_type": "text" }, "source": [ "### 1.4 - Logistic regression with some tf.keras utilities" ] }, { "cell_type": "code", "metadata": { "id": "gl3jBhLQKxl8", "colab_type": "code", "colab": {} }, "source": [ "w, b = model_init(X.shape[1])" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "E2DWdR4bLi5u", "colab_type": "code", "colab": {} }, "source": [ "# Simply wrap the output of a linear model in a sigmoid\n", "logreg_model = lambda x: tf.sigmoid(linear_model(x))" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "nC04O2ZdMJVy", "colab_type": "code", "colab": {} }, "source": [ "# tf.keras has a lot of pre-made objects and functions to simplify code development\n", "from tensorflow.keras import losses, optimizers, metrics" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ZeNJTT78M8gZ", "colab_type": "code", "colab": {} }, "source": [ "# Define a loss\n", "loss = losses.BinaryCrossentropy()\n", "# loss = losses.MeanSquaredError()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "zSPFeiNCNGR_", "colab_type": "code", "colab": {} }, "source": [ "# Define an optimizer\n", "# opt = optimizers.SGD(learning_rate=0.01)\n", "opt = optimizers.RMSprop(learning_rate=0.01)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "TwhT-rqjNmZx", "colab_type": "code", "colab": {} }, "source": [ "ypredicted = logreg_model(Xtrain)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "drFUr6OyNyay", "colab_type": "code", "outputId": "60c96f5b-78a3-487e-c7cd-c2c5835ca8ce", "colab": { "base_uri": "https://localhost:8080/", "height": 210 } }, "source": [ "ypredicted[0:10]" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 78 } ] }, { "cell_type": "code", "metadata": { "id": "4Z2jlKiYN1Y5", "colab_type": "code", "outputId": "2e8cb4f8-48ad-41d0-a772-6e376327a809", "colab": { "base_uri": "https://localhost:8080/", "height": 34 } }, "source": [ "# Metrics accumulate multiple computations before outputting a result\n", "acc = metrics.BinaryAccuracy()\n", "acc.update_state(ytrain, ypredicted)\n", "acc.result()" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 79 } ] }, { "cell_type": "code", "metadata": { "id": "fvehLImeOIXg", "colab_type": "code", "colab": {} }, "source": [ "all_accuracies = []\n", "\n", "for i in range(1000):\n", " \n", " with tf.GradientTape() as tape:\n", " ypredicted = logreg_model(Xtrain)\n", " l = loss(ytrain, ypredicted)\n", " \n", " g = tape.gradient(l, [w, b]) \n", " opt.apply_gradients([(g[0], w), (g[1], b)]) # This time, we let the optimizer do this step\n", " # Note: the input is a list of pairs (gradient, variable)\n", " \n", " # Compute the train accuracy at every iteration\n", " acc.reset_states()\n", " acc.update_state(ytrain, ypredicted)\n", " all_accuracies.append(acc.result())" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "EWrXbIRhRaXi", "colab_type": "code", "outputId": "4830ae18-9d16-4cd5-9351-458cba324023", "colab": { "base_uri": "https://localhost:8080/", "height": 286 } }, "source": [ "import matplotlib.pyplot as plt\n", "plt.plot(all_accuracies)" ], "execution_count": 0, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[]" ] }, "metadata": { "tags": [] }, "execution_count": 81 }, { "output_type": "display_data", "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] } } ] } ] }