{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# A Python Tour of Data Science: Data Exploitation\n", "\n", "[Michaël Defferrard](http://deff.ch), *PhD student*, [EPFL](http://epfl.ch) [LTS2](http://lts2.epfl.ch)\n", "\n", "The data `X.npy` and `y.npy` can be obtained by running the [data acquisition and exploration demo](01_demo_acquisition_exploration.ipynb)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The data is a with 29946 samples of dimensionality 23.\n" ] } ], "source": [ "# Cross-platform (Windows / Mac / Linux) paths.\n", "import os.path\n", "folder = os.path.join('..', 'data', 'credit_card_defaults')\n", "\n", "import numpy as np\n", "X = np.load(os.path.join(folder, 'X.npy'))\n", "y = np.load(os.path.join(folder, 'y.npy'))\n", "n, d = X.shape\n", "print('The data is a {} with {} samples of dimensionality {}.'.format(type(X), n, d))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1 Pre-Processing\n", "\n", "Back to [NumPy](http://www.numpy.org/), the fundamental package for scientific computing with Python. It provides multi-dimensional arrays, data types and linear algebra routines. Note that [scikit-learn](http://scikit-learn.org) provides many helpers for those tasks.\n", "\n", "Pre-processing usually consists of:\n", "1. Data types transformation. The data has not necessarilly the format the chosen learning algorithm expects. This was done in the previous notebook before doing statistics with `statsmodels`.\n", "1. Data normalization. Some algorithms expect data to be centered and scaled. Some will train faster.\n", "1. Data randomization. If the samples are presented in sequence, it'll train faster if they are not correlated.\n", "1. Train / test splitting. You may have to be careful here, e.g. not including future events in the training set." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [], "source": [ "# Center and scale.\n", "# Note: on a serious project, should be done after train / test split.\n", "X = X.astype(np.float)\n", "X -= X.mean(axis=0)\n", "X /= X.std(axis=0)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Split: 10000 testing and 19946 training samples\n" ] } ], "source": [ "# Training and testing sets.\n", "test_size = 10000\n", "print('Split: {} testing and {} training samples'.format(test_size, y.size - test_size))\n", "perm = np.random.permutation(y.size)\n", "X_test = X[perm[:test_size]]\n", "X_train = X[perm[test_size:]]\n", "y_test = y[perm[:test_size]]\n", "y_train = y[perm[test_size:]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2 A first Predictive Model\n", "\n", "The ingredients of a Machine Learning (ML) model are:\n", "1. A predictive function, e.g. the linear transformation $f(x) = x^Tw + b$.\n", "1. An error function, e.g. the least squares $E = \\sum_{i=1}^n \\left( f(x_i) - y_i \\right)^2 = \\| f(X) - y \\|_2^2$.\n", "1. An optional regularization, e.g. the Thikonov regularization $R = \\|w\\|_2^2$.\n", "1. Which makes up the loss / objective function $L = E + \\alpha R$.\n", "\n", "Our model has a sole hyper-parameter, $\\alpha \\geq 0$, which controls the shrinkage.\n", "\n", "A Machine Learning (ML) problem can often be cast as a (convex or smooth) optimization problem which objective is to find the parameters (here $w$ and $b$) who minimize the loss, e.g.\n", "$$\\hat{w}, \\hat{b} = \\operatorname*{arg min}_{w,b} L = \\operatorname*{arg min}_{w,b} \\| Xw + b - y \\|_2^2 + \\alpha \\|w\\|_2^2.$$\n", "\n", "If the problem is convex and smooth, one can compute the gradients\n", "$$\\frac{\\partial L}{\\partial{w}} = 2 X^T (Xw+b-y) + 2\\alpha w,$$\n", "$$\\frac{\\partial L}{\\partial{b}} = 2 \\sum_{i=1}^n (x_i^Tw+b-y_i) = 2 \\sum_{i=1}^n (x_i^Tw-y_i) + 2n \\cdot b,$$\n", "\n", "which can be used in a [gradient descent](https://en.wikipedia.org/wiki/Gradient_descent) scheme or to form closed-form solutions:\n", "$$\\frac{\\partial L}{\\partial{w}} = 0 \\ \\rightarrow \\ 2 X^T X\\hat{w} + 2\\alpha \\hat{w} = 2 X^T y - 2 X^T b \\ \\rightarrow \\ \\hat{w} = (X^T X + \\alpha I)^{-1} X^T (y-b),$$\n", "$$\\frac{\\partial L}{\\partial{b}} = 0 \\ \\rightarrow \\ 2n\\hat{b} = 2\\sum_{i=1}^n (y_i) - \\underbrace{2\\sum_{i=1}^n (x_i^Tw)}_{=0 \\text{ if centered}} \\ \\rightarrow \\ \\hat{b} = \\frac1n I^T y = \\operatorname{mean}(y).$$\n", "\n", "What if the resulting problem is non-smooth ? See the [PyUNLocBoX](http://pyunlocbox.readthedocs.io), a convex optimization toolbox which implements [proximal splitting methods](https://en.wikipedia.org/wiki/Proximal_gradient_method)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 Take a *symbolic* Derivative\n", "\n", "Let's verify our manually derived gradients ! [SymPy](http://www.sympy.org/) is our computer algebra system (CAS) (like [Mathematica](https://www.wolfram.com/mathematica), [Maple](https://www.maplesoft.com/products/Maple)) of choice." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAALcAAAAbBAMAAADMngM5AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIpmJdu8QRM1mu90y\nVKvMIHo8AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACvUlEQVRIDbWVzWsTQRjGn2ST3SSbbfciIqIG\nLRKLYCw9eljaijfbgxQ/Di6SgnhoAuJNyOJBAn5Q/QtiDyIeaooFD4Lkprfm4kUo7aEHvbUoKYIS\n39nZmd2dtDSm9IXMzvub5312NjszCxxSaKNnD8mZbC/iu2o+tuiqaMB8Gat2vFSzh7w4GTj7gFPV\neLFp57fj5ADZuhsvNquJnTg5QLbSU2t1etCAQCv0FJqOio6pYP/8BZO87NWNqSjRwL1bKtwnH6J1\nYpSMliLLFhSADAmfq3CfXPeAL2vfXEV2Hw85sZxghD3d36AvYZDveRkHbne7ynD+2dprjqTPMj2h\neMkSKmU96VwPIZDudrc4Fz6JJqDRzw8Bg3TvS6YajCXOjMyiPIKsZ82GcuGTKgCZzYm2PyKgUJev\nHp10whrq6RMOjriQy/CBndlC6StyG0ZkAwmf3AJQeazzfStgoNZL2RXTi5lr1gymQRPlVL+BZCPR\nLmJ4Aa9CofAxac60j3/6IwEUas3VOpYd1lDvRNLDW8BqcprswHR0XGOHWCEQavX6k+v1+gal6SpQ\nhE7mIRRqHZmGX3H3I4un1HfTDn4B+S2fI91ApQVjG5eBEkesFTNn5o9g8MUooFQPO2EF79Xs/J/Q\nvFLCOpBsYgmJdigVPuxv+Q16PBYCSvVqy+eRpojUDC3eJkeVNt5Ydm4D76FFRMKHvdAdzFFLISFX\nW+40Uq4/IpslWhjhC8042U8aUp5xE5+lJPRhi+oHFvmIMA/UtfY7qMfaKGo0kWSJF+ibl05eAU6P\nz0/y6cV92APemQhGhHmgnp8qn4vWsMr58Qs27YwWd9mjlT60/WVIKMkunSKx47vwCNLp/n5Ej2UJ\nI8J4d8jT2U6kg6uvyIm79KU2F3IOHQJeX2KAPhb/EcbUeVL3PyF1PfRxK/8zR7p/YXyZ18BvtzQA\nAAAASUVORK5CYII=\n", "text/latex": [ "$$a w^{2} + \\left(b + w x - y\\right)^{2}$$" ], "text/plain": [ " 2 2\n", "a⋅w + (b + w⋅x - y) " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMcAAAAUBAMAAADLgTR0AAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIpm7MhCriUTv3c12\nVGZoascqAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC3ElEQVQ4EZWVT2gTQRTGv81mu9sk2y5CQQuV\n0AqCEVst9Gq8eeugECkWjAcvEjQUxEvQXAUhleLBP4d4ErTQ4L+DEboEaikW2goeenL1WEFasLSI\nbX1vNptk021oPsjs7Pt+897MzmYWaEvH26Il/INapX842WKk31aGcPlpCzrI6rCAFPTtIK8a89tR\nGvChBR1kaSXgBvAkwDNtN+i3f1Jwp0p7RPX24EsOoNXfTu4nvBR++wVgbFZhj9g/tikyBsyKlkV8\ntlIEdPpJHbpINM/8jMCRiQWhVWz8wqpoTjEjtMXrD2nRiMWBaKay7CPGT6OrZD7G+Oej87Z0vIYT\nHhPQ4xQwtmA8wix0cxIjKOQlU58n2VewZr+msOoA195rGz4iuwo1bWxr2a7pCO1xgzjhEmgKFIuk\nMeZgAH2hEnahWhKrFyH7Du6LFSZpDd8FthoJZTmBbgdndaFvmu5YaVPDCYcBs0j9fmBQYBqi0w5v\nIEQRvVz++KxcTlNX2gJvZbczDySgUZE6oeE5Ri3ENUSHJHT1Fesd9Skh/gHhdfdBT0H7AxSs2CR0\nSaK2Et4HMEviIm9guC+xRxgbWACyQLfNTKMKVpjScpGvUHq2YFLvFNQ0+lzKS8G2BWZJ/Lh2EXJf\nYo8IFWknFXJGkxJqaBKgWcMoIhxHrGcHammF2EiWp8TyUrBtfYutY46CvPHbvIEsj1DTeEnrN8US\nYkI6tWYEZPLG996duIlPWEtn8QUPhsJ5SHkp2DZ21HXFoTC/jLdwTgK1IrGSMYWLKCyfR/PxOYAC\nDQtlMbu39xep+QuZPEIn5zL33Ay1FGxrmbnKb47TwnGp4nC3vhL051LzDlKL42ccjjcolRu06J+V\nbAj5ut5KfEHQsVJTMFGz3U6CLr1NsfqtRlPYLz4gPQUTnsvXjpLGBzyfFe1IDSx9UIaIo9oAH/Vt\niT5abchYPEF0exPj9M3vzyFK0uf3Pwl3yeiuEMBfAAAAAElFTkSuQmCC\n", "text/latex": [ "$$2 a w + 2 x \\left(b + w x - y\\right)$$" ], "text/plain": [ "2⋅a⋅w + 2⋅x⋅(b + w⋅x - y)" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAAASBAMAAABhvsuwAAAAMFBMVEX///8AAAAAAAAAAAAAAAAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv3aB7AAAAD3RSTlMAIpm7MhCriUTv3c12\nVGZoascqAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB7ElEQVQ4EYWRz2vTYBjHP0mbJU1TjZ5EmJQJ\nCk6kqHgSDP4DRoWBUFguIujQ4GUHQXsVhPayw8RDT4o/cEPQgz2sBlREweJtILi7FwuWDcHGN8mb\n2YSRPZfneb6/yJsHZeaUw+WH5Gr/4dM5JLPGrgSZQ9+CNxkaNI97rRw2uSauGLkKy/A3Zb8nw5RL\npZNiO/TEFRPi4285xigVyYBKl6nfKbZDj10JvuaKAL2bimRAdVQcELtSDytudSEYJKsMEIs50oI+\nP1l3aX448LG/LZfDits8jrIkNmOT+dfaMMH/B7R9vdbhDO2W5u95Ya7mAoTLX8f6JWDT44fLZj7g\nMdPqKmNKtu7qo5qdCzA9ZTBLqSHgGZhFiwLu9HpPe73kpGodt9IvD1HRqEY6tLfPRD13olm4NB6x\ntw9WHV5hyENuPyEQorZtddDFEOmyFbmMIRcc+IZij1HlIdOAWp19HKPkMQ2xTrSJilzihZ+gXMey\nt7iykbBpwHl4J/6g6eNTc79iuRNu6Sp5QsHBxdvXuclJycsA5cnikS6fud8ot2gPznEo409casMY\nw1oY/uFSsJENqIZh2EU9+n7hLsx9aZ5IeSmLXdq1QB5folFLnzABFYziL+TrYh4o2G8w3yqgd6de\ncnZ3UZHiQeDAPz2Ghalvt88tAAAAAElFTkSuQmCC\n", "text/latex": [ "$$2 b + 2 w x - 2 y$$" ], "text/plain": [ "2⋅b + 2⋅w⋅x - 2⋅y" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import sympy as sp\n", "sp.init_printing()\n", "\n", "X, y, w, b, a = sp.symbols('x y w b a')\n", "L = (X*w + b - y)**2 + a*w**2\n", "\n", "dLdw = sp.diff(L, w)\n", "dLdb = sp.diff(L, b)\n", "\n", "from IPython.display import display\n", "display(L)\n", "display(dLdw)\n", "display(dLdb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 Build the Classifier\n", "\n", "Relying on the derived equations, we can implement our model relying only on the [NumPy](http://www.numpy.org/) linear algebra capabilities (really wrappers to [BLAS](http://www.netlib.org/blas) / [LAPACK](http://www.netlib.org/lapack) implementations such as [ATLAS](http://math-atlas.sourceforge.net), [OpenBLAS](http://www.openblas.net) or [MKL](https://software.intel.com/intel-mkl)).\n", "\n", "A ML model is best represented as a class, with hyper-parameters and parameters stored as attributes, and is composed of two essential methods:\n", "1. `y_pred = model.predict(X_test)`: return the predictions $y$ given the features $X$.\n", "1. `model.fit(X_train, y_train)`: learn the model parameters such as to predict $y$ given $X$." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true, "scrolled": false }, "outputs": [], "source": [ "class RidgeRegression(object):\n", " \"\"\"Our ML model.\"\"\"\n", " \n", " def __init__(self, alpha=0):\n", " \"The class' constructor. Initialize the hyper-parameters.\"\n", " self.a = alpha\n", " \n", " def predict(self, X):\n", " \"\"\"Return the predicted class given the features.\"\"\"\n", " return np.sign(X.dot(self.w) + self.b)\n", " \n", " def fit(self, X, y):\n", " \"\"\"Learn the model's parameters given the training data, the closed-form way.\"\"\"\n", " n, d = X.shape\n", " self.b = np.mean(y)\n", " Ainv = np.linalg.inv(X.T.dot(X) + self.a * np.identity(d))\n", " self.w = Ainv.dot(X.T).dot(y - self.b)\n", "\n", " def loss(self, X, y, w=None, b=None):\n", " \"\"\"Return the current loss.\n", " This method is not strictly necessary, but it provides\n", " information on the convergence of the learning process.\"\"\"\n", " w = self.w if w is None else w # The ternary conditional operator\n", " b = self.b if b is None else b # makes those tests concise.\n", " import autograd.numpy as np # See below for autograd.\n", " return np.linalg.norm(np.dot(X, w) + b - y)**2 + self.a * np.linalg.norm(w, 2)**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that our model can learn its parameters and predict targets, it's time to evaluate it. Our metric for binary classification is the accuracy, which gives the percentage of correcly classified test samples. Depending on the application, the time spent for inference or training might also be important." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "accuracy: 80.12%, loss: 6039.06, time: 31.88ms\n" ] } ], "source": [ "def accuracy(y_pred, y_true):\n", " \"\"\"Our evaluation metric, the classification accuracy.\"\"\"\n", " return np.sum(y_pred == y_true) / y_true.size\n", "\n", "def evaluate(model):\n", " \"\"\"Helper function to instantiate, train and evaluate the model.\n", " It returns the classification accuracy, the loss and the execution time.\"\"\"\n", " import time\n", " t = time.process_time()\n", " model.fit(X_train, y_train)\n", " y_pred = model.predict(X_test)\n", " acc = accuracy(y_pred, y_test)\n", " loss = model.loss(X_test, y_test)\n", " t = time.process_time() - t\n", " print('accuracy: {:.2f}%, loss: {:.2f}, time: {:.2f}ms'.format(acc*100, loss, t*1000))\n", " return model\n", "\n", "alpha = 1e-2*n\n", "model = RidgeRegression(alpha)\n", "evaluate(model)\n", "\n", "models = []\n", "models.append(model)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Okay we got around 80% accuracy with such a simple model ! Inference and training time looks good.\n", "\n", "For those of you who don't now about numerical mathematics, solving a linear system of equations by inverting a matrix can be numerically instable. Let's do it the proper way and use a proper solver." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "accuracy: 80.12%, loss: 6039.06, time: 12.73ms\n" ] } ], "source": [ "def fit_lapack(self, X, y):\n", " \"\"\"Better way (numerical stability): solve the linear system with LAPACK.\"\"\"\n", " n, d = X.shape\n", " self.b = np.mean(y)\n", " A = X.T.dot(X) + self.a * np.identity(d)\n", " b = X.T.dot(y - self.b)\n", " self.w = np.linalg.solve(A, b)\n", "\n", "# Let's monkey patch our object (Python is a dynamic language).\n", "RidgeRegression.fit = fit_lapack\n", "\n", "# Yeah just to be sure.\n", "models.append(evaluate(RidgeRegression(alpha)))\n", "assert np.allclose(models[-1].w, models[0].w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.3 Learning as Gradient Descent\n", "\n", "Descending the gradient of our objective will lead us to a local minimum. If the objective is convex, that minimum will be global. Let's implement the gradient computed above and a simple gradient descent algorithm\n", "$$w^{(t+1)} = w^{(t)} - \\gamma \\frac{\\partial L}{\\partial w}$$\n", "where $\\gamma$ is the learning rate, another hyper-parameter." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "loss at iteration 0: 329242.23\n", "loss at iteration 100: 14007.48\n", "loss at iteration 200: 12709.08\n", "loss at iteration 300: 12422.96\n", "loss at iteration 400: 12282.00\n", "loss at iteration 500: 12202.43\n", "loss at iteration 600: 12155.28\n", "loss at iteration 700: 12126.69\n", "loss at iteration 800: 12109.11\n", "loss at iteration 900: 12098.19\n", "accuracy: 80.10%, loss: 6043.24, time: 2005.52ms\n" ] } ], "source": [ "class RidgeRegressionGradient(RidgeRegression):\n", " \"\"\"This model inherits from `ridge_regression`. We overload the constructor, add a gradient\n", " function and replace the learning algorithm, but don't touch the prediction and loss functions.\"\"\"\n", " \n", " def __init__(self, alpha=0, rate=0.1, niter=1000):\n", " \"\"\"Here are new hyper-parameters: the learning rate and the number of iterations.\"\"\"\n", " super().__init__(alpha)\n", " self.rate = rate\n", " self.niter = niter\n", " \n", " def grad(self, X, y, w):\n", " A = X.dot(w) + self.b - y\n", " return 2 * X.T.dot(A) + 2 * self.a * w\n", " \n", " def fit(self, X, y):\n", " n, d = X.shape\n", " self.b = np.mean(y)\n", " \n", " self.w = np.random.normal(size=d)\n", " for i in range(self.niter):\n", " self.w -= self.rate * self.grad(X, y, self.w)\n", " \n", " # Show convergence.\n", " if i % (self.niter//10) == 0:\n", " print('loss at iteration {}: {:.2f}'.format(i, self.loss(X, y)))\n", " \n", "models.append(evaluate(RidgeRegressionGradient(alpha, 1e-6)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tyred of derivating gradients by hand ? Welcome [autograd](https://github.com/HIPS/autograd/), our tool of choice for [automatic differentation](https://en.wikipedia.org/wiki/Automatic_differentiation). Alternatives are [Theano](http://deeplearning.net/software/theano/) and [TensorFlow](https://www.tensorflow.org/)." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "loss at iteration 0: 369004.56\n", "loss at iteration 100: 17165.10\n", "loss at iteration 200: 15181.22\n", "loss at iteration 300: 14218.94\n", "loss at iteration 400: 13598.31\n", "loss at iteration 500: 13169.91\n", "loss at iteration 600: 12867.13\n", "loss at iteration 700: 12650.72\n", "loss at iteration 800: 12495.00\n", "loss at iteration 900: 12382.43\n", "accuracy: 80.25%, loss: 6198.22, time: 2995.56ms\n" ] } ], "source": [ "class RidgeRegressionAutograd(RidgeRegressionGradient):\n", " \"\"\"Here we derive the gradient during construction and update the gradient function.\"\"\"\n", " def __init__(self, *args):\n", " super().__init__(*args)\n", " from autograd import grad\n", " self.grad = grad(self.loss, argnum=2)\n", "\n", "models.append(evaluate(RidgeRegressionAutograd(alpha, 1e-6)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.4 Learning as generic Optimization\n", "\n", "Sometimes we don't want to implement the optimization by hand and would prefer a generic optimization algorithm. Let's make use of [SciPy](https://www.scipy.org/), which provides high-level algorithms for, e.g. [optimization](http://docs.scipy.org/doc/scipy/reference/optimize.html), [statistics](http://docs.scipy.org/doc/scipy/reference/stats.html), [interpolation](http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html), [signal processing](http://docs.scipy.org/doc/scipy/reference/tutorial/signal.html), [sparse matrices](http://docs.scipy.org/doc/scipy/reference/sparse.html), [advanced linear algebra](http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html)." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/usr/lib/python3.5/site-packages/scipy/optimize/_minimize.py:381: RuntimeWarning: Method Nelder-Mead does not use gradient information (jac).\n", " RuntimeWarning)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "accuracy: 73.47%, loss: 13773.20, time: 6211.07ms\n", "accuracy: 80.12%, loss: 6039.06, time: 147.19ms\n" ] } ], "source": [ "class RidgeRegressionOptimize(RidgeRegressionGradient):\n", " \n", " def __init__(self, alpha=0, method=None):\n", " \"\"\"Here's a new hyper-parameter: the optimization algorithm.\"\"\"\n", " super().__init__(alpha)\n", " self.method = method\n", " \n", " def fit(self, X, y):\n", " \"\"\"Fitted with a general purpose optimization algorithm.\"\"\"\n", " n, d = X.shape\n", " self.b = np.mean(y)\n", " \n", " # Objective and gradient w.r.t. the variable to be optimized.\n", " f = lambda w: self.loss(X, y, w)\n", " jac = lambda w: self.grad(X, y, w)\n", " \n", " # Solve the problem !\n", " from scipy.optimize import minimize\n", " w0 = np.random.normal(size=d)\n", " res = minimize(f, w0, method=self.method, jac=jac)\n", " self.w = res.x\n", "\n", "models.append(evaluate(RidgeRegressionOptimize(alpha, method='Nelder-Mead')))\n", "models.append(evaluate(RidgeRegressionOptimize(alpha, method='BFGS')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accuracy may be lower (depending on the random initialization) as the optimization may not have converged to the global minima. Training time is however much longer ! Especially for gradient-less optimizers such as Nelder-Mead." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3 More interactivity\n", "\n", "Interlude: the interactivity of Jupyter notebooks can be pushed forward with [IPython widgets](https://ipywidgets.readthedocs.io). Below, we construct a slider for the model hyper-parameter $\\alpha$, which will train the model and print its performance at each change of the value. Handy when exploring the effects of hyper-parameters ! Although it's less usefull if the required computations are long." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "alpha = 2.99e+05\n", "accuracy: 78.12%, loss: 6819.31, time: 6.25ms\n" ] } ], "source": [ "import ipywidgets\n", "from IPython.display import clear_output\n", "\n", "slider = ipywidgets.widgets.FloatSlider(\n", " value=-2,\n", " min=-4,\n", " max=2,\n", " step=1,\n", " description='log(alpha) / n',\n", ")\n", "\n", "def handle(change):\n", " \"\"\"Handler for value change: fit model and print performance.\"\"\"\n", " value = change['new']\n", " alpha = np.power(10, value) * n\n", " clear_output()\n", " print('alpha = {:.2e}'.format(alpha))\n", " evaluate(RidgeRegression(alpha))\n", "\n", "slider.observe(handle, names='value')\n", "display(slider)\n", "\n", "slider.value = 1 # As if someone moved the slider." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4 Machine Learning made easier\n", "\n", "Tired of writing algorithms ? Try [scikit-learn](http://scikit-learn.org), which provides many ML algorithms and related tools, e.g. metrics, cross-validation, model selection, feature extraction, pre-processing, for [predictive modeling](https://en.wikipedia.org/wiki/Predictive_modelling)." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "accuracy: 80.13%\n" ] } ], "source": [ "from sklearn import linear_model, metrics\n", "\n", "# The previously developed model: Ridge Regression.\n", "model = linear_model.RidgeClassifier(alpha)\n", "model.fit(X_train, y_train)\n", "y_pred = model.predict(X_test)\n", "models.append(model)\n", "\n", "# Evaluate the predictions with a metric: the classification accuracy.\n", "acc = metrics.accuracy_score(y_test, y_pred)\n", "print('accuracy: {:.2f}%'.format(acc*100))\n", "\n", "# It does indeed learn the same parameters.\n", "assert np.allclose(models[-1].coef_, models[0].w, rtol=1e-1)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "accuracy: 81.14%\n" ] } ], "source": [ "# Let's try another model !\n", "models.append(linear_model.LogisticRegression())\n", "models[-1].fit(X_train, y_train)\n", "acc = models[-1].score(X_test, y_test)\n", "print('accuracy: {:.2f}%'.format(acc*100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5 Deep Learning (DL)\n", "\n", "Of course ! We got two low-level Python libraries: (1) [TensorFlow](https://www.tensorflow.org/) and (2) [Theano](http://deeplearning.net/software/theano/). Both of them treat data as tensors and construct a computational graph ([dataflow paradigm](https://en.wikipedia.org/wiki/Dataflow_programming)), composed of any mathematical expressions, that get evaluated on CPUs or GPUs. Theano is the pioneer and features an optimizing compiler which will turn the computational graph into efficient code. TensorFlow has a cleaner API (not need to define expressions as strings) and does not require a compilation step (which is painful when developing models).\n", "\n", "While you'll only use Theano / TensorFlow to develop DL models, these are the higher-level libraries you'll use to define and test DL architectures on your problem:\n", "* [Keras](https://keras.io/): TensorFlow & Theano backends\n", "* [Lasagne](http://lasagne.readthedocs.io): Theano backend\n", "* [nolearn](https://github.com/dnouri/nolearn): sklearn-like abstraction of Lasagne\n", "* [Blocks](http://blocks.readthedocs.io): Theano backend\n", "* [TFLearn](http://tflearn.org): TensorFlow backend" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using Theano backend.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/5\n", "19946/19946 [==============================] - 0s - loss: 0.5380 - acc: 0.7683 \n", "Epoch 2/5\n", "19946/19946 [==============================] - 0s - loss: 0.4853 - acc: 0.8027 \n", "Epoch 3/5\n", "19946/19946 [==============================] - 0s - loss: 0.4706 - acc: 0.8063 \n", "Epoch 4/5\n", "19946/19946 [==============================] - 0s - loss: 0.4623 - acc: 0.8097 \n", "Epoch 5/5\n", "19946/19946 [==============================] - 0s - loss: 0.4577 - acc: 0.8116 \n", " 8064/10000 [=======================>......] - ETA: 0s\n", "\n", "Testing set: [0.45383822469711305, 0.8105]\n" ] } ], "source": [ "import os\n", "os.environ['KERAS_BACKEND'] = 'theano'\n", "import keras\n", "\n", "class NeuralNet(object):\n", " \n", " def __init__(self):\n", " \"\"\"Define Neural Network architecture.\"\"\"\n", " self.model = keras.models.Sequential()\n", " self.model.add(keras.layers.Dense(output_dim=46, input_dim=23, activation='relu'))\n", " self.model.add(keras.layers.Dense(output_dim=1, activation='sigmoid'))\n", " self.model.compile(loss='binary_crossentropy', optimizer='sgd', metrics=['accuracy'])\n", "\n", " def fit(self, X, y):\n", " y = y / 2 + 0.5 # [-1,1] -> [0,1]\n", " self.model.fit(X, y, nb_epoch=5, batch_size=32)\n", "\n", " def predict(self, X):\n", " classes = self.model.predict_classes(X, batch_size=32)\n", " return classes[:,0] * 2 - 1\n", " \n", "models.append(NeuralNet())\n", "models[-1].fit(X_train, y_train)\n", "\n", "loss_acc = models[-1].model.evaluate(X_test, y_test/2+0.5, batch_size=32)\n", "print('\\n\\nTesting set: {}'.format(loss_acc))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6 Evaluation\n", "\n", "Now that we tried several predictive models, it is time to evaluate them with our chosen metrics and choose the one best suited to our particular problem. Let's plot the *classification accuracy* and the *prediction time* for each classifier with [matplotlib](http://matplotlib.org), the goto 2D plotting library for scientific Python. Its API is similar to matlab.\n", "\n", "Result: The NeuralNet gives the best accuracy, by a small margin over the much simple logistic regression, but is the slowest method. Which to choose ? Again, it depends on your priorities." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 8960/10000 [=========================>....] - ETA: 0s" ] }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "plt.style.use('ggplot')\n", "%matplotlib inline\n", "# Or notebook for interaction.\n", "\n", "names, acc, times = [], [], []\n", "for model in models:\n", " import time\n", " t = time.process_time()\n", " y_pred = model.predict(X_test)\n", " times.append((time.process_time()-t) * 1000)\n", " acc.append(accuracy(y_pred, y_test) * 100)\n", " names.append(type(model).__name__)\n", "\n", "plt.figure(figsize=(15,5))\n", "plt.subplot(121)\n", "plt.plot(acc, '.', markersize=20)\n", "plt.title('Accuracy [%]')\n", "plt.xticks(range(len(names)), names, rotation=90)\n", "\n", "plt.subplot(122)\n", "plt.plot(times, '.', markersize=20)\n", "plt.title('Prediction time [ms]')\n", "plt.xticks(range(len(names)), names, rotation=90)\n", "plt.show()" ] } ], "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.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }