├── .gitignore ├── LICENSE ├── README.md └── notebooks ├── 00_Preliminaries.ipynb ├── 01_basics.ipynb ├── 02_data_representation.ipynb ├── 03_basic_principles.ipynb ├── 04_supervised_in_depth.ipynb ├── 05_unsupervised_in_depth.ipynb ├── 06_validation.ipynb ├── fig_code ├── ML_flow_chart.py ├── __init__.py ├── helpers.py ├── linear_regression.py ├── sgd_separator.py └── svm_gui.py ├── images ├── iris_setosa.jpg ├── iris_versicolor.jpg └── iris_virginica.jpg └── solutions ├── 02_faces.py ├── 04_svm_rf.py ├── 05_color_compression.py ├── 06-1_svm_class.py ├── 06-2_unbalanced.py ├── 06-3_5fold_crossval.py ├── 06-4_gridsearch.py ├── 06-5_decisiontree.py └── 06-6_randomforest.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | 37 | 38 | # emacs 39 | *~ 40 | 41 | # ipython 42 | .ipynb_checkpoints -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Jake Vanderplas 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyCon 2014 Scikit-learn Tutorial 2 | 3 | *Instructor: Jake VanderPlas* 4 | 5 | - email: 6 | - twitter: [@jakevdp](https://twitter.com/jakevdp) 7 | - github: [jakevdp](http://github.com/jakevdp) 8 | 9 | This repository will contain files and other info associated with my PyCon 10 | 2014 scikit-learn tutorial. 11 | 12 | ## Installation Notes 13 | This tutorial will require recent installations of *numpy*, *scipy*, 14 | *matplotlib*, *scikit-learn*, and *ipython* with ipython notebook. 15 | The last one is important: you should be able to type 16 | 17 | ipython notebook 18 | 19 | in your terminal window and see the notebook panel load in your web browser. 20 | Because Python 3 compatibility is still being ironed-out for these packages 21 | (we're getting close, I promise!) participants should plan to use Python 2.6 22 | or 2.7 for this tutorial. 23 | 24 | For users who do not yet have these packages installed, a relatively 25 | painless way to install all the requirements is to use a package such as 26 | [Anaconda CE](http://store.continuum.io/ "Anaconda CE"), which can be 27 | downloaded and installed for free. 28 | 29 | ## Downloading the Tutorial Materials 30 | I would highly recommend using git, not only for this tutorial, but for the 31 | general betterment of your life. Once git is installed, you can clone the 32 | material in this tutorial by using the git address shown above: 33 | 34 | git clone git://github.com/jakevdp/sklearn_pycon2014.git 35 | 36 | If you can't or don't want to install git, there is a link above to download 37 | the contents of this repository as a zip file. I may make minor changes to 38 | the repository in the days before the tutorial, however, so cloning the 39 | repository is a much better option. 40 | 41 | 42 | ## Notebook Listing 43 | These notebooks in this repository can be statically viewed using the 44 | excellent [nbviewer](http://nbviewer.ipython.org) site. They will not 45 | be able to be modified within nbviewer. To modify them, first download 46 | the tutorial repository, change to the notebooks directory, and type 47 | ``ipython notebook``. You should see the list in the ipython notebook 48 | launch page in your web browser. 49 | 50 | - [00_Preliminaries.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/00_Preliminaries.ipynb) 51 | - [01_basics.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/01_basics.ipynb) 52 | - [02_data_representation.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/02_data_representation.ipynb) 53 | - [03_basic_principles.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/03_basic_principles.ipynb) 54 | - [04_supervised_in_depth.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/04_supervised_in_depth.ipynb) 55 | - [05_unsupervised_in_depth.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/05_unsupervised_in_depth.ipynb) 56 | - [06_validation.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/jakevdp/sklearn_pycon2014/master/notebooks/06_validation.ipynb) 57 | 58 | Note that some of the code in these notebooks will not work outside the 59 | directory structure of this tutorial, so it is important to clone the full 60 | repository if possible. -------------------------------------------------------------------------------- /notebooks/00_Preliminaries.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:a55628c0abb2e9280b1298cd8a5c88f52701f21aa005e40974533dc38547b65b" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "This notebook was put together by [Jake Vanderplas](http://www.vanderplas.com) for PyCon 2014. Source and license info is on [GitHub](https://github.com/jakevdp/sklearn_pycon2014/)." 16 | ] 17 | }, 18 | { 19 | "cell_type": "heading", 20 | "level": 1, 21 | "metadata": {}, 22 | "source": [ 23 | "An Introduction to scikit-learn: Machine Learning in Python" 24 | ] 25 | }, 26 | { 27 | "cell_type": "heading", 28 | "level": 2, 29 | "metadata": {}, 30 | "source": [ 31 | "http://github.com/jakevdp/sklearn_pycon2014" 32 | ] 33 | }, 34 | { 35 | "cell_type": "heading", 36 | "level": 2, 37 | "metadata": {}, 38 | "source": [ 39 | "Goals of this Tutorial" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "- **Introduce the basics of Machine Learning**, and some skills useful in practice.\n", 47 | "- **Introduce the syntax of scikit-learn**, so that you can make use of the rich toolset available." 48 | ] 49 | }, 50 | { 51 | "cell_type": "heading", 52 | "level": 2, 53 | "metadata": {}, 54 | "source": [ 55 | "Schedule:" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "Outline:\n", 63 | "\n", 64 | "**9:00 - 9:30** Preliminaries: Setup & introduction\n", 65 | "\n", 66 | "- Making sure your computer is set-up\n", 67 | "- What is Machine Learning?\n", 68 | "- Quick review of Numpy and Matplotlib\n", 69 | "\n", 70 | "**9:30 - 10:15** Basic Principles of Machine Learning and the Scikit-learn Interface\n", 71 | "\n", 72 | "- Machine learning data layout\n", 73 | "- Supervised Learning\n", 74 | " - Classification\n", 75 | " - Regression\n", 76 | " - Measuring performance\n", 77 | "- Unsupervised Learning\n", 78 | " - Clustering\n", 79 | " - Dimensionality Reduction\n", 80 | "- Evaluation of models\n", 81 | "- How to choose the right algorithm for your dataset\n", 82 | "\n", 83 | "**10:15 - 11:00** Supervised learning in-depth\n", 84 | "\n", 85 | "- Two important algorithms: Support Vector Machines and Random Forests\n", 86 | "- Application: recognizing handwritten digits\n", 87 | "\n", 88 | "**11:00 - 11:45** Unsupervised learning in-depth\n", 89 | "\n", 90 | "- Two important algorithms: PCA and K Means\n", 91 | "- Application: image color compression\n", 92 | "\n", 93 | "**11:45 - 12:20** Validation and Model Selection\n", 94 | "\n", 95 | "- Overfitting, Underfitting, bias, and variance\n", 96 | "- Improving your fit: validation curves and learning curves\n", 97 | "- Application: facial recognition" 98 | ] 99 | }, 100 | { 101 | "cell_type": "heading", 102 | "level": 2, 103 | "metadata": {}, 104 | "source": [ 105 | "Preliminaries" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "This tutorial requires the following packages:\n", 113 | "\n", 114 | "- Python version 2.6-2.7\n", 115 | "- `numpy` version 1.5 or later: http://www.numpy.org/\n", 116 | "- `scipy` version 0.9 or later: http://www.scipy.org/\n", 117 | "- `matplotlib` version 1.0 or later: http://matplotlib.org/\n", 118 | "- `scikit-learn` version 0.12 or later: http://scikit-learn.org\n", 119 | "- `ipython` version 1.0 or later, with notebook support: http://ipython.org\n", 120 | "\n", 121 | "The easiest way to get these is to use an all-in-one installer such as [Anaconda CE](https://store.continuum.io/) from Continuum. These are available for multiple architectures." 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "### Anaconda Setup\n", 129 | "\n", 130 | "If you're using [Anaconda](https://store.continuum.io/cshop/anaconda/), simpy type\n", 131 | "\n", 132 | "```\n", 133 | "conda install scikit-learn\n", 134 | "```\n", 135 | "\n", 136 | "Otherwise it's best to install from source (requires a C compiler):\n", 137 | "```\n", 138 | "git clone https://github.com/scikit-learn/scikit-learn.git\n", 139 | "cd scikit-learn\n", 140 | "python setup.py install\n", 141 | "```\n", 142 | "\n", 143 | "Scikit-learn requires ``NumPy`` and ``SciPy``, and examples require ``Matplotlib``.\n", 144 | "\n", 145 | "**Note**: some examples used in this tutorial require the scripts in the ``fig_code`` directory, which can be found within the ``notebooks`` subdirectory of the Github repository at [https://github.com/jakevdp/sklearn_pycon2014/](https://github.com/jakevdp/sklearn_pycon2014/)" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "### Alternatives\n", 153 | "\n", 154 | "- **Linux**: If you're on Linux, you can use the linux distribution tools (by typing, for example `apt-get install numpy` or `yum install numpy`.\n", 155 | "\n", 156 | "- **Mac**: If you're on OSX, there are similar tools such as MacPorts or HomeBrew which contain pre-compiled versions of these packages.\n", 157 | "\n", 158 | "- **Windows**: Windows can be challenging: the best bet is probably to use a package installer such as Anaconda, above." 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "### Checking your installation\n", 166 | "\n", 167 | "You can run the following code to check the versions of the packages on your system:\n", 168 | "\n", 169 | "(in IPython notebook, press `shift` and `return` together to execute the contents of a cell)" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "collapsed": false, 175 | "input": [ 176 | "import numpy\n", 177 | "print 'numpy:', numpy.__version__\n", 178 | "\n", 179 | "import scipy\n", 180 | "print 'scipy:', scipy.__version__\n", 181 | "\n", 182 | "import matplotlib\n", 183 | "print 'matplotlib:', matplotlib.__version__\n", 184 | "\n", 185 | "import sklearn\n", 186 | "print 'scikit-learn:', sklearn.__version__" 187 | ], 188 | "language": "python", 189 | "metadata": {}, 190 | "outputs": [ 191 | { 192 | "output_type": "stream", 193 | "stream": "stdout", 194 | "text": [ 195 | "numpy: 1.7.1\n", 196 | "scipy: 0.13.2\n", 197 | "matplotlib: 1.3.1\n", 198 | "scikit-learn: 0.14.1\n" 199 | ] 200 | } 201 | ], 202 | "prompt_number": 1 203 | }, 204 | { 205 | "cell_type": "heading", 206 | "level": 2, 207 | "metadata": {}, 208 | "source": [ 209 | "Useful Resources" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "- **scikit-learn:** http://scikit-learn.org (see especially the narrative documentation)\n", 217 | "- **matplotlib:** http://matplotlib.org (see especially the gallery section)\n", 218 | "- **IPython:** http://ipython.org (also check out http://nbviewer.ipython.org)\n", 219 | "- **astroML:** http://astroML.github.com (shameless plug: this is my project!)" 220 | ] 221 | } 222 | ], 223 | "metadata": {} 224 | } 225 | ] 226 | } -------------------------------------------------------------------------------- /notebooks/03_basic_principles.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:b43dc356ad1ac081584f432443f25e40be6bcc73a8915d9570e7d670650e503b" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "This notebook was put together by [Jake Vanderplas](http://www.vanderplas.com) for PyCon 2014. Source and license info is on [GitHub](https://github.com/jakevdp/sklearn_pycon2014/)." 16 | ] 17 | }, 18 | { 19 | "cell_type": "heading", 20 | "level": 1, 21 | "metadata": {}, 22 | "source": [ 23 | "Basic principles of machine learning" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "Here is where we start diving into the field of machine learning.\n", 31 | "\n", 32 | "By the end of this section you will\n", 33 | "\n", 34 | "- Know the basic categories of supervised learning, including classification and regression problems.\n", 35 | "- Know the basic categories of unsupervised learning, including dimensionality reduction and clustering.\n", 36 | "- Know the basic syntax of the Scikit-learn **estimator** interface.\n", 37 | "- Know why data should be split into a **training set** and **test set**\n", 38 | "\n", 39 | "In addition, we will go over several basic tools within scikit-learn which can be used to accomplish the above tasks." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "collapsed": false, 45 | "input": [ 46 | "%matplotlib inline\n", 47 | "import numpy as np\n", 48 | "import matplotlib.pyplot as plt" 49 | ], 50 | "language": "python", 51 | "metadata": {}, 52 | "outputs": [], 53 | "prompt_number": 1 54 | }, 55 | { 56 | "cell_type": "heading", 57 | "level": 2, 58 | "metadata": {}, 59 | "source": [ 60 | "Problem setting" 61 | ] 62 | }, 63 | { 64 | "cell_type": "heading", 65 | "level": 3, 66 | "metadata": {}, 67 | "source": [ 68 | "A simple definition of machine learning" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "Machine Learning (ML) is about building programs with **tunable parameters** (typically an\n", 76 | "array of floating point values) that are adjusted automatically so as to improve\n", 77 | "their behavior by **adapting to previously seen data.**\n", 78 | "\n", 79 | "In most ML applications, the data is in a 2D array of shape ``[n_samples x n_features]``,\n", 80 | "where the number of features is the same for each object, and each feature column refers\n", 81 | "to a related piece of information about each sample.\n", 82 | "\n", 83 | "Machine learning can be broken into two broad regimes:\n", 84 | "*supervised learning* and *unsupervised learning*.\n", 85 | "We\u2019ll introduce these concepts here, and discuss them in more detail below." 86 | ] 87 | }, 88 | { 89 | "cell_type": "heading", 90 | "level": 3, 91 | "metadata": {}, 92 | "source": [ 93 | "Introducing the scikit-learn estimator object" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "Every algorithm is exposed in scikit-learn via an ''Estimator'' object. For instance a linear regression is:" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "collapsed": false, 106 | "input": [ 107 | "from sklearn.linear_model import LinearRegression" 108 | ], 109 | "language": "python", 110 | "metadata": {}, 111 | "outputs": [], 112 | "prompt_number": 2 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "**Estimator parameters**: All the parameters of an estimator can be set when it is instantiated:" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "collapsed": false, 124 | "input": [ 125 | "model = LinearRegression(normalize=True)\n", 126 | "print model.normalize" 127 | ], 128 | "language": "python", 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "output_type": "stream", 133 | "stream": "stdout", 134 | "text": [ 135 | "True\n" 136 | ] 137 | } 138 | ], 139 | "prompt_number": 3 140 | }, 141 | { 142 | "cell_type": "code", 143 | "collapsed": false, 144 | "input": [ 145 | "print model" 146 | ], 147 | "language": "python", 148 | "metadata": {}, 149 | "outputs": [ 150 | { 151 | "output_type": "stream", 152 | "stream": "stdout", 153 | "text": [ 154 | "LinearRegression(copy_X=True, fit_intercept=True, normalize=True)\n" 155 | ] 156 | } 157 | ], 158 | "prompt_number": 4 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "**Estimated parameters**: When data is fitted with an estimator, parameters are estimated from the data at hand. All the estimated parameters are attributes of the estimator object ending by an underscore:" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "collapsed": false, 170 | "input": [ 171 | "x = np.array([0, 1, 2])\n", 172 | "y = np.array([0, 1, 2])" 173 | ], 174 | "language": "python", 175 | "metadata": {}, 176 | "outputs": [], 177 | "prompt_number": 5 178 | }, 179 | { 180 | "cell_type": "code", 181 | "collapsed": false, 182 | "input": [ 183 | "plt.plot(x, y, marker='o');" 184 | ], 185 | "language": "python", 186 | "metadata": {}, 187 | "outputs": [ 188 | { 189 | "metadata": {}, 190 | "output_type": "display_data", 191 | "png": "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\n/h8qEpStW5V4jAAAAABJRU5ErkJggg==\n", 192 | "text": [ 193 | "" 194 | ] 195 | } 196 | ], 197 | "prompt_number": 6 198 | }, 199 | { 200 | "cell_type": "code", 201 | "collapsed": false, 202 | "input": [ 203 | "X = x[:, np.newaxis] # The input data for sklearn is 2D: (samples == 3 x features == 1)\n", 204 | "X" 205 | ], 206 | "language": "python", 207 | "metadata": {}, 208 | "outputs": [ 209 | { 210 | "metadata": {}, 211 | "output_type": "pyout", 212 | "prompt_number": 7, 213 | "text": [ 214 | "array([[0],\n", 215 | " [1],\n", 216 | " [2]])" 217 | ] 218 | } 219 | ], 220 | "prompt_number": 7 221 | }, 222 | { 223 | "cell_type": "code", 224 | "collapsed": false, 225 | "input": [ 226 | "model.fit(X, y) \n", 227 | "model.coef_" 228 | ], 229 | "language": "python", 230 | "metadata": {}, 231 | "outputs": [ 232 | { 233 | "metadata": {}, 234 | "output_type": "pyout", 235 | "prompt_number": 8, 236 | "text": [ 237 | "array([ 1.])" 238 | ] 239 | } 240 | ], 241 | "prompt_number": 8 242 | }, 243 | { 244 | "cell_type": "heading", 245 | "level": 2, 246 | "metadata": {}, 247 | "source": [ 248 | "Supervised Learning: Classification and regression" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": {}, 254 | "source": [ 255 | "In **Supervised Learning**, we have a dataset consisting of both features and labels.\n", 256 | "The task is to construct an estimator which is able to predict the label of an object\n", 257 | "given the set of features. A relatively simple example is predicting the species of \n", 258 | "iris given a set of measurements of its flower. This is a relatively simple task. \n", 259 | "Some more complicated examples are:\n", 260 | "\n", 261 | "- given a multicolor image of an object through a telescope, determine\n", 262 | " whether that object is a star, a quasar, or a galaxy.\n", 263 | "- given a photograph of a person, identify the person in the photo.\n", 264 | "- given a list of movies a person has watched and their personal rating\n", 265 | " of the movie, recommend a list of movies they would like\n", 266 | " (So-called *recommender systems*: a famous example is the [Netflix Prize](http://en.wikipedia.org/wiki/Netflix_prize)).\n", 267 | "\n", 268 | "What these tasks have in common is that there is one or more unknown\n", 269 | "quantities associated with the object which needs to be determined from other\n", 270 | "observed quantities.\n", 271 | "\n", 272 | "Supervised learning is further broken down into two categories, **classification** and **regression**.\n", 273 | "In classification, the label is discrete, while in regression, the label is continuous. For example,\n", 274 | "in astronomy, the task of determining whether an object is a star, a galaxy, or a quasar is a\n", 275 | "classification problem: the label is from three distinct categories. On the other hand, we might\n", 276 | "wish to estimate the age of an object based on such observations: this would be a regression problem,\n", 277 | "because the label (age) is a continuous quantity." 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "### Classification\n", 285 | "K nearest neighbors (kNN) is one of the simplest learning strategies: given a new, unknown observation, look up in your reference database which ones have the closest features and assign the predominant class.\n", 286 | "\n", 287 | "Let's try it out on our iris classification problem:" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "collapsed": false, 293 | "input": [ 294 | "from sklearn import neighbors, datasets\n", 295 | "iris = datasets.load_iris()\n", 296 | "X, y = iris.data, iris.target\n", 297 | "knn = neighbors.KNeighborsClassifier(n_neighbors=1)\n", 298 | "knn.fit(X, y)\n", 299 | "# What kind of iris has 3cm x 5cm sepal and 4cm x 2cm petal?\n", 300 | "print iris.target_names[knn.predict([[3, 5, 4, 2]])]" 301 | ], 302 | "language": "python", 303 | "metadata": {}, 304 | "outputs": [ 305 | { 306 | "output_type": "stream", 307 | "stream": "stdout", 308 | "text": [ 309 | "['virginica']\n" 310 | ] 311 | } 312 | ], 313 | "prompt_number": 9 314 | }, 315 | { 316 | "cell_type": "code", 317 | "collapsed": false, 318 | "input": [ 319 | "# A plot of the sepal space and the prediction of the KNN\n", 320 | "from fig_code import plot_iris_classification\n", 321 | "plot_iris_classification(neighbors.KNeighborsClassifier, n_neighbors=3)" 322 | ], 323 | "language": "python", 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "metadata": {}, 328 | "output_type": "display_data", 329 | "png": "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\nADAHYIPqAAAAAElFTkSuQmCC\n", 330 | "text": [ 331 | "" 332 | ] 333 | } 334 | ], 335 | "prompt_number": 10 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "#### Quick Exercise\n", 342 | "Now use as an estimator on the same problem: ``sklearn.svm.SVC``.\n", 343 | "\n", 344 | "(Note that you don't have to know what it is to use it. If you finish early, do the same plot as above)." 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "collapsed": false, 350 | "input": [ 351 | "from sklearn.svm import SVC\n", 352 | "unknown_iris = [[3, 5, 4, 2]]\n", 353 | "\n", 354 | "# Use SVC to train and predict the species of the unknown iris...\n", 355 | "\n" 356 | ], 357 | "language": "python", 358 | "metadata": {}, 359 | "outputs": [], 360 | "prompt_number": 11 361 | }, 362 | { 363 | "cell_type": "markdown", 364 | "metadata": {}, 365 | "source": [ 366 | "### Regression\n", 367 | "The simplest possible regression setting is the linear regression one:" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "collapsed": false, 373 | "input": [ 374 | "# Create some simple data\n", 375 | "np.random.seed(0)\n", 376 | "X = np.random.random(size=(20, 1))\n", 377 | "y = 3 * X.squeeze() + 2 + np.random.normal(size=20)" 378 | ], 379 | "language": "python", 380 | "metadata": {}, 381 | "outputs": [], 382 | "prompt_number": 12 383 | }, 384 | { 385 | "cell_type": "code", 386 | "collapsed": false, 387 | "input": [ 388 | "# Fit a linear regression to it\n", 389 | "from sklearn.linear_model import LinearRegression\n", 390 | "model = LinearRegression(fit_intercept=True)\n", 391 | "model.fit(X, y)\n", 392 | "print \"Model coefficient: %.5f, and intercept: %.5f\" % (model.coef_, model.intercept_)" 393 | ], 394 | "language": "python", 395 | "metadata": {}, 396 | "outputs": [ 397 | { 398 | "output_type": "stream", 399 | "stream": "stdout", 400 | "text": [ 401 | "Model coefficient: 3.93491, and intercept: 1.46229\n" 402 | ] 403 | } 404 | ], 405 | "prompt_number": 13 406 | }, 407 | { 408 | "cell_type": "code", 409 | "collapsed": false, 410 | "input": [ 411 | "# Plot the data and the model prediction\n", 412 | "X_test = np.linspace(0, 1, 100)[:, np.newaxis]\n", 413 | "y_test = model.predict(X_test)\n", 414 | "import pylab as pl\n", 415 | "plt.plot(X.squeeze(), y, 'o')\n", 416 | "plt.plot(X_test.squeeze(), y_test);" 417 | ], 418 | "language": "python", 419 | "metadata": {}, 420 | "outputs": [ 421 | { 422 | "metadata": {}, 423 | "output_type": "display_data", 424 | "png": "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\n", 425 | "text": [ 426 | "" 427 | ] 428 | } 429 | ], 430 | "prompt_number": 14 431 | }, 432 | { 433 | "cell_type": "heading", 434 | "level": 2, 435 | "metadata": {}, 436 | "source": [ 437 | "Unsupervised Learning" 438 | ] 439 | }, 440 | { 441 | "cell_type": "markdown", 442 | "metadata": {}, 443 | "source": [ 444 | "**Unsupervised Learning** addresses a different sort of problem. Here the data has no labels,\n", 445 | "and we are interested in finding similarities between the objects in question. In a sense,\n", 446 | "you can think of unsupervised learning as a means of discovering labels from the data itself.\n", 447 | "Unsupervised learning comprises tasks such as *dimensionality reduction*, *clustering*, and\n", 448 | "*density estimation*. For example, in the iris data discussed above, we can used unsupervised\n", 449 | "methods to determine combinations of the measurements which best display the structure of the\n", 450 | "data. As we\u2019ll see below, such a projection of the data can be used to visualize the\n", 451 | "four-dimensional dataset in two dimensions. Some more involved unsupervised learning problems are:\n", 452 | "\n", 453 | "- given detailed observations of distant galaxies, determine which features or combinations of\n", 454 | " features summarize best the information.\n", 455 | "- given a mixture of two sound sources (for example, a person talking over some music),\n", 456 | " separate the two (this is called the [blind source separation](http://en.wikipedia.org/wiki/Blind_signal_separation) problem).\n", 457 | "- given a video, isolate a moving object and categorize in relation to other moving objects which have been seen.\n", 458 | "\n", 459 | "Sometimes the two may even be combined: e.g. Unsupervised learning can be used to find useful\n", 460 | "features in heterogeneous data, and then these features can be used within a supervised\n", 461 | "framework." 462 | ] 463 | }, 464 | { 465 | "cell_type": "markdown", 466 | "metadata": {}, 467 | "source": [ 468 | "### Dimensionality Reduction\n", 469 | "\n", 470 | "Principle Component Analysis (PCA) is a dimension reduction technique that can find the combinations of variables that explain the most variance. Here we'll show an example of using PCA for visualization.\n", 471 | "\n", 472 | "Consider the iris dataset. It cannot be visualized in a single 2D plot, as it has 4 features. We are going to extract 2 combinations of sepal and petal dimensions to visualize it:" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "collapsed": false, 478 | "input": [ 479 | "X, y = iris.data, iris.target\n", 480 | "from sklearn.decomposition import PCA\n", 481 | "pca = PCA(n_components=2)\n", 482 | "pca.fit(X)\n", 483 | "X_reduced = pca.transform(X)\n", 484 | "print \"Reduced dataset shape:\", X_reduced.shape" 485 | ], 486 | "language": "python", 487 | "metadata": {}, 488 | "outputs": [ 489 | { 490 | "output_type": "stream", 491 | "stream": "stdout", 492 | "text": [ 493 | "Reduced dataset shape: (150, 2)\n" 494 | ] 495 | } 496 | ], 497 | "prompt_number": 15 498 | }, 499 | { 500 | "cell_type": "code", 501 | "collapsed": false, 502 | "input": [ 503 | "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y);" 504 | ], 505 | "language": "python", 506 | "metadata": {}, 507 | "outputs": [ 508 | { 509 | "metadata": {}, 510 | "output_type": "display_data", 511 | "png": "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\n", 512 | "text": [ 513 | "" 514 | ] 515 | } 516 | ], 517 | "prompt_number": 16 518 | }, 519 | { 520 | "cell_type": "code", 521 | "collapsed": false, 522 | "input": [ 523 | "print \"Meaning of the 2 components:\"\n", 524 | "for component in pca.components_:\n", 525 | " print \" + \".join(\"%.2f x %s\" % (value, name)\n", 526 | " for value, name in zip(component, iris.feature_names))" 527 | ], 528 | "language": "python", 529 | "metadata": {}, 530 | "outputs": [ 531 | { 532 | "output_type": "stream", 533 | "stream": "stdout", 534 | "text": [ 535 | "Meaning of the 2 components:\n", 536 | "0.36 x sepal length (cm) + -0.08 x sepal width (cm) + 0.86 x petal length (cm) + 0.36 x petal width (cm)\n", 537 | "-0.66 x sepal length (cm) + -0.73 x sepal width (cm) + 0.18 x petal length (cm) + 0.07 x petal width (cm)\n" 538 | ] 539 | } 540 | ], 541 | "prompt_number": 17 542 | }, 543 | { 544 | "cell_type": "markdown", 545 | "metadata": {}, 546 | "source": [ 547 | "### Clustering\n", 548 | "\n", 549 | "Clustering groups together observations that are homogeneous with respect to a given criterion, finding ''clusters'' in the data.\n", 550 | "\n", 551 | "Note that these clusters will uncover relevent hidden structure of the data only if the criterion used highlights it." 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "collapsed": false, 557 | "input": [ 558 | "from sklearn.cluster import KMeans\n", 559 | "k_means = KMeans(n_clusters=3, random_state=0)\n", 560 | "k_means.fit(X_reduced)\n", 561 | "y_pred = k_means.predict(X_reduced)\n", 562 | "\n", 563 | "plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y_pred);" 564 | ], 565 | "language": "python", 566 | "metadata": {}, 567 | "outputs": [ 568 | { 569 | "metadata": {}, 570 | "output_type": "display_data", 571 | "png": "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\n", 572 | "text": [ 573 | "" 574 | ] 575 | } 576 | ], 577 | "prompt_number": 18 578 | }, 579 | { 580 | "cell_type": "heading", 581 | "level": 2, 582 | "metadata": {}, 583 | "source": [ 584 | "A recap on Scikit-learn's estimator interface" 585 | ] 586 | }, 587 | { 588 | "cell_type": "markdown", 589 | "metadata": {}, 590 | "source": [ 591 | "Scikit-learn strives to have a uniform interface across all methods,\n", 592 | "and we\u2019ll see examples of these below. Given a scikit-learn *estimator*\n", 593 | "object named `model`, the following methods are available:\n", 594 | "\n", 595 | "- Available in **all Estimators**\n", 596 | " + `model.fit()` : fit training data. For supervised learning applications,\n", 597 | " this accepts two arguments: the data `X` and the labels `y` (e.g. `model.fit(X, y)`).\n", 598 | " For unsupervised learning applications, this accepts only a single argument,\n", 599 | " the data `X` (e.g. `model.fit(X)`).\n", 600 | "- Available in **supervised estimators**\n", 601 | " + `model.predict()` : given a trained model, predict the label of a new set of data.\n", 602 | " This method accepts one argument, the new data `X_new` (e.g. `model.predict(X_new)`),\n", 603 | " and returns the learned label for each object in the array.\n", 604 | " + `model.predict_proba()` : For classification problems, some estimators also provide\n", 605 | " this method, which returns the probability that a new observation has each categorical label.\n", 606 | " In this case, the label with the highest probability is returned by `model.predict()`.\n", 607 | " + `model.score()` : for classification or regression problems, most (all?) estimators implement\n", 608 | " a score method. Scores are between 0 and 1, with a larger score indicating a better fit.\n", 609 | "- Available in **unsupervised estimators**\n", 610 | " + `model.transform()` : given an unsupervised model, transform new data into the new basis.\n", 611 | " This also accepts one argument `X_new`, and returns the new representation of the data based\n", 612 | " on the unsupervised model.\n", 613 | " + `model.fit_transform()` : some estimators implement this method,\n", 614 | " which more efficiently performs a fit and a transform on the same input data." 615 | ] 616 | }, 617 | { 618 | "cell_type": "heading", 619 | "level": 3, 620 | "metadata": {}, 621 | "source": [ 622 | "Measuring Performance" 623 | ] 624 | }, 625 | { 626 | "cell_type": "markdown", 627 | "metadata": {}, 628 | "source": [ 629 | "An important piece of machine learning is **model validation**: that is, determining how well your model will generalize from the training data to future unlabeled data. Let's look at an example using the *nearest neighbor classifier*. This is a very simple classifier: it simply stores all training data, and for any unknown quantity, simply returns the label of the closest training point.\n", 630 | "\n", 631 | "With the iris data, it very easily returns the correct prediction for each of the input points:" 632 | ] 633 | }, 634 | { 635 | "cell_type": "code", 636 | "collapsed": false, 637 | "input": [ 638 | "from sklearn.neighbors import KNeighborsClassifier\n", 639 | "X, y = iris.data, iris.target\n", 640 | "clf = KNeighborsClassifier(n_neighbors=1)\n", 641 | "clf.fit(X, y)\n", 642 | "y_pred = clf.predict(X)\n", 643 | "print(np.all(y == y_pred))" 644 | ], 645 | "language": "python", 646 | "metadata": {}, 647 | "outputs": [ 648 | { 649 | "output_type": "stream", 650 | "stream": "stdout", 651 | "text": [ 652 | "True\n" 653 | ] 654 | } 655 | ], 656 | "prompt_number": 19 657 | }, 658 | { 659 | "cell_type": "markdown", 660 | "metadata": {}, 661 | "source": [ 662 | "A more useful way to look at the results is to view the **confusion matrix**, or the matrix showing the frequency of inputs and outputs:" 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "collapsed": false, 668 | "input": [ 669 | "from sklearn.metrics import confusion_matrix\n", 670 | "print(confusion_matrix(y, y_pred))" 671 | ], 672 | "language": "python", 673 | "metadata": {}, 674 | "outputs": [ 675 | { 676 | "output_type": "stream", 677 | "stream": "stdout", 678 | "text": [ 679 | "[[50 0 0]\n", 680 | " [ 0 50 0]\n", 681 | " [ 0 0 50]]\n" 682 | ] 683 | } 684 | ], 685 | "prompt_number": 20 686 | }, 687 | { 688 | "cell_type": "markdown", 689 | "metadata": {}, 690 | "source": [ 691 | "For each class, all 50 training samples are correctly identified. But this **does not mean that our model is perfect!** In particular, such a model generalizes extremely poorly to new data. We can simulate this by splitting our data into a *training set* and a *testing set*. Scikit-learn contains some convenient routines to do this:" 692 | ] 693 | }, 694 | { 695 | "cell_type": "code", 696 | "collapsed": false, 697 | "input": [ 698 | "from sklearn.cross_validation import train_test_split\n", 699 | "Xtrain, Xtest, ytrain, ytest = train_test_split(X, y)\n", 700 | "clf.fit(Xtrain, ytrain)\n", 701 | "ypred = clf.predict(Xtest)\n", 702 | "print(confusion_matrix(ytest, ypred))" 703 | ], 704 | "language": "python", 705 | "metadata": {}, 706 | "outputs": [ 707 | { 708 | "output_type": "stream", 709 | "stream": "stdout", 710 | "text": [ 711 | "[[12 0 0]\n", 712 | " [ 0 11 3]\n", 713 | " [ 0 0 12]]\n" 714 | ] 715 | } 716 | ], 717 | "prompt_number": 21 718 | }, 719 | { 720 | "cell_type": "markdown", 721 | "metadata": {}, 722 | "source": [ 723 | "This paints a better picture of the true performance of our classifier: apparently there is some confusion between the second and third species, which we might anticipate given what we've seen of the data above.\n", 724 | "\n", 725 | "This is why it's **extremely important** to use a train/test split when evaluating your models. We'll go into more depth on model evaluation later in this tutorial." 726 | ] 727 | }, 728 | { 729 | "cell_type": "heading", 730 | "level": 2, 731 | "metadata": {}, 732 | "source": [ 733 | "Exercise: Interactive Demo on linearly separable data" 734 | ] 735 | }, 736 | { 737 | "cell_type": "markdown", 738 | "metadata": {}, 739 | "source": [ 740 | "To get a feel for how a classification task works, please run the **svm_gui.py** file available in the repository (use the command below). It will open an interactive window which allows you to click and add points and see how this affects the model.\n", 741 | "\n", 742 | "What is the fewest number of points you can create that are not linearly separable (that is, for which a linear kernel fails to build a correct discriminative model)?" 743 | ] 744 | }, 745 | { 746 | "cell_type": "code", 747 | "collapsed": false, 748 | "input": [ 749 | "# %run fig_code/svm_gui.py\n", 750 | "# or, if this doesn't work, try\n", 751 | "# !python fig_code/svm_gui.py" 752 | ], 753 | "language": "python", 754 | "metadata": {}, 755 | "outputs": [], 756 | "prompt_number": 22 757 | }, 758 | { 759 | "cell_type": "heading", 760 | "level": 2, 761 | "metadata": {}, 762 | "source": [ 763 | "Flow chart: how do I choose what to do with my data set?" 764 | ] 765 | }, 766 | { 767 | "cell_type": "markdown", 768 | "metadata": {}, 769 | "source": [ 770 | "This is a flow chart created by scikit-learn super-contributor [Andreas Mueller](https://github.com/amueller) which gives a nice summary of which algorithms to choose in various situations. Keep it around as a handy reference!" 771 | ] 772 | }, 773 | { 774 | "cell_type": "markdown", 775 | "metadata": {}, 776 | "source": [ 777 | "\n", 778 | "\n", 779 | "Original source on the [scikit-learn website](http://scikit-learn.org/stable/tutorial/machine_learning_map/)" 780 | ] 781 | } 782 | ], 783 | "metadata": {} 784 | } 785 | ] 786 | } -------------------------------------------------------------------------------- /notebooks/fig_code/ML_flow_chart.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tutorial Diagrams 3 | ----------------- 4 | 5 | This script plots the flow-charts used in the scikit-learn tutorials. 6 | """ 7 | 8 | import numpy as np 9 | import pylab as pl 10 | from matplotlib.patches import Circle, Rectangle, Polygon, Arrow, FancyArrow 11 | 12 | def create_base(box_bg = '#CCCCCC', 13 | arrow1 = '#88CCFF', 14 | arrow2 = '#88FF88', 15 | supervised=True): 16 | fig = pl.figure(figsize=(9, 6), facecolor='w') 17 | ax = pl.axes((0, 0, 1, 1), 18 | xticks=[], yticks=[], frameon=False) 19 | ax.set_xlim(0, 9) 20 | ax.set_ylim(0, 6) 21 | 22 | patches = [Rectangle((0.3, 3.6), 1.5, 1.8, zorder=1, fc=box_bg), 23 | Rectangle((0.5, 3.8), 1.5, 1.8, zorder=2, fc=box_bg), 24 | Rectangle((0.7, 4.0), 1.5, 1.8, zorder=3, fc=box_bg), 25 | 26 | Rectangle((2.9, 3.6), 0.2, 1.8, fc=box_bg), 27 | Rectangle((3.1, 3.8), 0.2, 1.8, fc=box_bg), 28 | Rectangle((3.3, 4.0), 0.2, 1.8, fc=box_bg), 29 | 30 | Rectangle((0.3, 0.2), 1.5, 1.8, fc=box_bg), 31 | 32 | Rectangle((2.9, 0.2), 0.2, 1.8, fc=box_bg), 33 | 34 | Circle((5.5, 3.5), 1.0, fc=box_bg), 35 | 36 | Polygon([[5.5, 1.7], 37 | [6.1, 1.1], 38 | [5.5, 0.5], 39 | [4.9, 1.1]], fc=box_bg), 40 | 41 | FancyArrow(2.3, 4.6, 0.35, 0, fc=arrow1, 42 | width=0.25, head_width=0.5, head_length=0.2), 43 | 44 | FancyArrow(3.75, 4.2, 0.5, -0.2, fc=arrow1, 45 | width=0.25, head_width=0.5, head_length=0.2), 46 | 47 | FancyArrow(5.5, 2.4, 0, -0.4, fc=arrow1, 48 | width=0.25, head_width=0.5, head_length=0.2), 49 | 50 | FancyArrow(2.0, 1.1, 0.5, 0, fc=arrow2, 51 | width=0.25, head_width=0.5, head_length=0.2), 52 | 53 | FancyArrow(3.3, 1.1, 1.3, 0, fc=arrow2, 54 | width=0.25, head_width=0.5, head_length=0.2), 55 | 56 | FancyArrow(6.2, 1.1, 0.8, 0, fc=arrow2, 57 | width=0.25, head_width=0.5, head_length=0.2)] 58 | 59 | if supervised: 60 | patches += [Rectangle((0.3, 2.4), 1.5, 0.5, zorder=1, fc=box_bg), 61 | Rectangle((0.5, 2.6), 1.5, 0.5, zorder=2, fc=box_bg), 62 | Rectangle((0.7, 2.8), 1.5, 0.5, zorder=3, fc=box_bg), 63 | FancyArrow(2.3, 2.9, 2.0, 0, fc=arrow1, 64 | width=0.25, head_width=0.5, head_length=0.2), 65 | Rectangle((7.3, 0.85), 1.5, 0.5, fc=box_bg)] 66 | else: 67 | patches += [Rectangle((7.3, 0.2), 1.5, 1.8, fc=box_bg)] 68 | 69 | for p in patches: 70 | ax.add_patch(p) 71 | 72 | pl.text(1.45, 4.9, "Training\nText,\nDocuments,\nImages,\netc.", 73 | ha='center', va='center', fontsize=14) 74 | 75 | pl.text(3.6, 4.9, "Feature\nVectors", 76 | ha='left', va='center', fontsize=14) 77 | 78 | pl.text(5.5, 3.5, "Machine\nLearning\nAlgorithm", 79 | ha='center', va='center', fontsize=14) 80 | 81 | pl.text(1.05, 1.1, "New Text,\nDocument,\nImage,\netc.", 82 | ha='center', va='center', fontsize=14) 83 | 84 | pl.text(3.3, 1.7, "Feature\nVector", 85 | ha='left', va='center', fontsize=14) 86 | 87 | pl.text(5.5, 1.1, "Predictive\nModel", 88 | ha='center', va='center', fontsize=12) 89 | 90 | if supervised: 91 | pl.text(1.45, 3.05, "Labels", 92 | ha='center', va='center', fontsize=14) 93 | 94 | pl.text(8.05, 1.1, "Expected\nLabel", 95 | ha='center', va='center', fontsize=14) 96 | pl.text(8.8, 5.8, "Supervised Learning Model", 97 | ha='right', va='top', fontsize=18) 98 | 99 | else: 100 | pl.text(8.05, 1.1, 101 | "Likelihood\nor Cluster ID\nor Better\nRepresentation", 102 | ha='center', va='center', fontsize=12) 103 | pl.text(8.8, 5.8, "Unsupervised Learning Model", 104 | ha='right', va='top', fontsize=18) 105 | 106 | 107 | 108 | def plot_supervised_chart(annotate=False): 109 | create_base(supervised=True) 110 | if annotate: 111 | fontdict = dict(color='r', weight='bold', size=14) 112 | pl.text(1.9, 4.55, 'X = vec.fit_transform(input)', 113 | fontdict=fontdict, 114 | rotation=20, ha='left', va='bottom') 115 | pl.text(3.7, 3.2, 'clf.fit(X, y)', 116 | fontdict=fontdict, 117 | rotation=20, ha='left', va='bottom') 118 | pl.text(1.7, 1.5, 'X_new = vec.transform(input)', 119 | fontdict=fontdict, 120 | rotation=20, ha='left', va='bottom') 121 | pl.text(6.1, 1.5, 'y_new = clf.predict(X_new)', 122 | fontdict=fontdict, 123 | rotation=20, ha='left', va='bottom') 124 | 125 | def plot_unsupervised_chart(): 126 | create_base(supervised=False) 127 | 128 | 129 | if __name__ == '__main__': 130 | plot_supervised_chart(False) 131 | plot_supervised_chart(True) 132 | plot_unsupervised_chart() 133 | pl.show() 134 | 135 | 136 | -------------------------------------------------------------------------------- /notebooks/fig_code/__init__.py: -------------------------------------------------------------------------------- 1 | from .sgd_separator import plot_sgd_separator 2 | from .linear_regression import plot_linear_regression 3 | from .ML_flow_chart import plot_supervised_chart, plot_unsupervised_chart 4 | from .helpers import plot_iris_classification 5 | -------------------------------------------------------------------------------- /notebooks/fig_code/helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Small helpers for code that is not shown in the notebooks 3 | """ 4 | 5 | from sklearn import neighbors, datasets, linear_model 6 | import pylab as pl 7 | import numpy as np 8 | from matplotlib.colors import ListedColormap 9 | 10 | # Create color maps for 3-class classification problem, as with iris 11 | cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF']) 12 | cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF']) 13 | 14 | def plot_iris_classification(classifier=None, **kwargs): 15 | if classifier is None: 16 | classifier = neighbors.KNeighborsClassifier 17 | 18 | iris = datasets.load_iris() 19 | X = iris.data[:, :2] # we only take the first two features. We could 20 | # avoid this ugly slicing by using a two-dim dataset 21 | y = iris.target 22 | 23 | knn = classifier(**kwargs) 24 | knn.fit(X, y) 25 | 26 | x_min, x_max = X[:, 0].min() - .1, X[:, 0].max() + .1 27 | y_min, y_max = X[:, 1].min() - .1, X[:, 1].max() + .1 28 | xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100), 29 | np.linspace(y_min, y_max, 100)) 30 | Z = knn.predict(np.c_[xx.ravel(), yy.ravel()]) 31 | 32 | # Put the result into a color plot 33 | Z = Z.reshape(xx.shape) 34 | pl.figure() 35 | pl.pcolormesh(xx, yy, Z, cmap=cmap_light) 36 | 37 | # Plot also the training points 38 | pl.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold) 39 | pl.xlabel('sepal length (cm)') 40 | pl.ylabel('sepal width (cm)') 41 | pl.axis('tight') 42 | 43 | 44 | def plot_polynomial_regression(): 45 | rng = np.random.RandomState(0) 46 | x = 2*rng.rand(100) - 1 47 | 48 | f = lambda t: 1.2 * t**2 + .1 * t**3 - .4 * t **5 - .5 * t ** 9 49 | y = f(x) + .4 * rng.normal(size=100) 50 | 51 | x_test = np.linspace(-1, 1, 100) 52 | 53 | pl.figure() 54 | pl.scatter(x, y, s=4) 55 | 56 | X = np.array([x**i for i in range(5)]).T 57 | X_test = np.array([x_test**i for i in range(5)]).T 58 | regr = linear_model.LinearRegression() 59 | regr.fit(X, y) 60 | pl.plot(x_test, regr.predict(X_test), label='4th order') 61 | 62 | X = np.array([x**i for i in range(10)]).T 63 | X_test = np.array([x_test**i for i in range(10)]).T 64 | regr = linear_model.LinearRegression() 65 | regr.fit(X, y) 66 | pl.plot(x_test, regr.predict(X_test), label='9th order') 67 | 68 | pl.legend(loc='best') 69 | pl.axis('tight') 70 | pl.title('Fitting a 4th and a 9th order polynomial') 71 | 72 | pl.figure() 73 | pl.scatter(x, y, s=4) 74 | pl.plot(x_test, f(x_test), label="truth") 75 | pl.axis('tight') 76 | pl.title('Ground truth (9th order polynomial)') 77 | 78 | 79 | -------------------------------------------------------------------------------- /notebooks/fig_code/linear_regression.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from sklearn.linear_model import LinearRegression 4 | 5 | 6 | def plot_linear_regression(): 7 | a = 0.5 8 | b = 1.0 9 | 10 | # x from 0 to 10 11 | x = 30 * np.random.random(20) 12 | 13 | # y = a*x + b with noise 14 | y = a * x + b + np.random.normal(size=x.shape) 15 | 16 | # create a linear regression classifier 17 | clf = LinearRegression() 18 | clf.fit(x[:, None], y) 19 | 20 | # predict y from the data 21 | x_new = np.linspace(0, 30, 100) 22 | y_new = clf.predict(x_new[:, None]) 23 | 24 | # plot the results 25 | ax = plt.axes() 26 | ax.scatter(x, y) 27 | ax.plot(x_new, y_new) 28 | 29 | ax.set_xlabel('x') 30 | ax.set_ylabel('y') 31 | 32 | ax.axis('tight') 33 | 34 | 35 | if __name__ == '__main__': 36 | plot_linear_regression() 37 | plt.show() 38 | -------------------------------------------------------------------------------- /notebooks/fig_code/sgd_separator.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from sklearn.linear_model import SGDClassifier 4 | from sklearn.datasets.samples_generator import make_blobs 5 | 6 | def plot_sgd_separator(): 7 | # we create 50 separable points 8 | X, Y = make_blobs(n_samples=50, centers=2, 9 | random_state=0, cluster_std=0.60) 10 | 11 | # fit the model 12 | clf = SGDClassifier(loss="hinge", alpha=0.01, 13 | n_iter=200, fit_intercept=True) 14 | clf.fit(X, Y) 15 | 16 | # plot the line, the points, and the nearest vectors to the plane 17 | xx = np.linspace(-1, 5, 10) 18 | yy = np.linspace(-1, 5, 10) 19 | 20 | X1, X2 = np.meshgrid(xx, yy) 21 | Z = np.empty(X1.shape) 22 | for (i, j), val in np.ndenumerate(X1): 23 | x1 = val 24 | x2 = X2[i, j] 25 | p = clf.decision_function([x1, x2]) 26 | Z[i, j] = p[0] 27 | levels = [-1.0, 0.0, 1.0] 28 | linestyles = ['dashed', 'solid', 'dashed'] 29 | colors = 'k' 30 | 31 | ax = plt.axes() 32 | ax.contour(X1, X2, Z, levels, colors=colors, linestyles=linestyles) 33 | ax.scatter(X[:, 0], X[:, 1], c=Y, cmap=plt.cm.Paired) 34 | 35 | ax.axis('tight') 36 | 37 | 38 | if __name__ == '__main__': 39 | plot_sgd_separator() 40 | plt.show() 41 | -------------------------------------------------------------------------------- /notebooks/fig_code/svm_gui.py: -------------------------------------------------------------------------------- 1 | """ 2 | ========== 3 | Libsvm GUI 4 | ========== 5 | 6 | A simple graphical frontend for Libsvm mainly intended for didactic 7 | purposes. You can create data points by point and click and visualize 8 | the decision region induced by different kernels and parameter settings. 9 | 10 | To create positive examples click the left mouse button; to create 11 | negative examples click the right button. 12 | 13 | If all examples are from the same class, it uses a one-class SVM. 14 | 15 | """ 16 | from __future__ import division, print_function 17 | 18 | print(__doc__) 19 | 20 | # Author: Peter Prettenhoer 21 | # 22 | # License: BSD 3 clause 23 | 24 | import matplotlib 25 | matplotlib.use('TkAgg') 26 | 27 | from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 28 | from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg 29 | from matplotlib.figure import Figure 30 | from matplotlib.contour import ContourSet 31 | 32 | import Tkinter as Tk 33 | import sys 34 | import numpy as np 35 | 36 | from sklearn import svm 37 | from sklearn.datasets import dump_svmlight_file 38 | from sklearn.externals.six.moves import xrange 39 | 40 | y_min, y_max = -50, 50 41 | x_min, x_max = -50, 50 42 | 43 | 44 | class Model(object): 45 | """The Model which hold the data. It implements the 46 | observable in the observer pattern and notifies the 47 | registered observers on change event. 48 | """ 49 | 50 | def __init__(self): 51 | self.observers = [] 52 | self.surface = None 53 | self.data = [] 54 | self.cls = None 55 | self.surface_type = 0 56 | 57 | def changed(self, event): 58 | """Notify the observers. """ 59 | for observer in self.observers: 60 | observer.update(event, self) 61 | 62 | def add_observer(self, observer): 63 | """Register an observer. """ 64 | self.observers.append(observer) 65 | 66 | def set_surface(self, surface): 67 | self.surface = surface 68 | 69 | def dump_svmlight_file(self, file): 70 | data = np.array(self.data) 71 | X = data[:, 0:2] 72 | y = data[:, 2] 73 | dump_svmlight_file(X, y, file) 74 | 75 | 76 | class Controller(object): 77 | def __init__(self, model): 78 | self.model = model 79 | self.kernel = Tk.IntVar() 80 | self.surface_type = Tk.IntVar() 81 | # Whether or not a model has been fitted 82 | self.fitted = False 83 | 84 | def fit(self): 85 | print("fit the model") 86 | train = np.array(self.model.data) 87 | X = train[:, 0:2] 88 | y = train[:, 2] 89 | 90 | C = float(self.complexity.get()) 91 | gamma = float(self.gamma.get()) 92 | coef0 = float(self.coef0.get()) 93 | degree = int(self.degree.get()) 94 | kernel_map = {0: "linear", 1: "rbf", 2: "poly"} 95 | if len(np.unique(y)) == 1: 96 | clf = svm.OneClassSVM(kernel=kernel_map[self.kernel.get()], 97 | gamma=gamma, coef0=coef0, degree=degree) 98 | clf.fit(X) 99 | else: 100 | clf = svm.SVC(kernel=kernel_map[self.kernel.get()], C=C, 101 | gamma=gamma, coef0=coef0, degree=degree) 102 | clf.fit(X, y) 103 | if hasattr(clf, 'score'): 104 | print("Accuracy:", clf.score(X, y) * 100) 105 | X1, X2, Z = self.decision_surface(clf) 106 | self.model.clf = clf 107 | self.model.set_surface((X1, X2, Z)) 108 | self.model.surface_type = self.surface_type.get() 109 | self.fitted = True 110 | self.model.changed("surface") 111 | 112 | def decision_surface(self, cls): 113 | delta = 1 114 | x = np.arange(x_min, x_max + delta, delta) 115 | y = np.arange(y_min, y_max + delta, delta) 116 | X1, X2 = np.meshgrid(x, y) 117 | Z = cls.decision_function(np.c_[X1.ravel(), X2.ravel()]) 118 | Z = Z.reshape(X1.shape) 119 | return X1, X2, Z 120 | 121 | def clear_data(self): 122 | self.model.data = [] 123 | self.fitted = False 124 | self.model.changed("clear") 125 | 126 | def add_example(self, x, y, label): 127 | self.model.data.append((x, y, label)) 128 | self.model.changed("example_added") 129 | 130 | # update decision surface if already fitted. 131 | self.refit() 132 | 133 | def refit(self): 134 | """Refit the model if already fitted. """ 135 | if self.fitted: 136 | self.fit() 137 | 138 | 139 | class View(object): 140 | """Test docstring. """ 141 | def __init__(self, root, controller): 142 | f = Figure() 143 | ax = f.add_subplot(111) 144 | ax.set_xticks([]) 145 | ax.set_yticks([]) 146 | ax.set_xlim((x_min, x_max)) 147 | ax.set_ylim((y_min, y_max)) 148 | canvas = FigureCanvasTkAgg(f, master=root) 149 | canvas.show() 150 | canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) 151 | canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) 152 | canvas.mpl_connect('key_press_event', self.onkeypress) 153 | canvas.mpl_connect('key_release_event', self.onkeyrelease) 154 | canvas.mpl_connect('button_press_event', self.onclick) 155 | toolbar = NavigationToolbar2TkAgg(canvas, root) 156 | toolbar.update() 157 | self.shift_down = False 158 | self.controllbar = ControllBar(root, controller) 159 | self.f = f 160 | self.ax = ax 161 | self.canvas = canvas 162 | self.controller = controller 163 | self.contours = [] 164 | self.c_labels = None 165 | self.plot_kernels() 166 | 167 | def plot_kernels(self): 168 | self.ax.text(-50, -60, "Linear: $u^T v$") 169 | self.ax.text(-20, -60, "RBF: $\exp (-\gamma \| u-v \|^2)$") 170 | self.ax.text(10, -60, "Poly: $(\gamma \, u^T v + r)^d$") 171 | 172 | def onkeypress(self, event): 173 | if event.key == "shift": 174 | self.shift_down = True 175 | 176 | def onkeyrelease(self, event): 177 | if event.key == "shift": 178 | self.shift_down = False 179 | 180 | def onclick(self, event): 181 | if event.xdata and event.ydata: 182 | if self.shift_down or event.button == 3: 183 | self.controller.add_example(event.xdata, event.ydata, -1) 184 | elif event.button == 1: 185 | self.controller.add_example(event.xdata, event.ydata, 1) 186 | 187 | def update_example(self, model, idx): 188 | x, y, l = model.data[idx] 189 | if l == 1: 190 | color = 'w' 191 | elif l == -1: 192 | color = 'k' 193 | self.ax.plot([x], [y], "%so" % color, scalex=0.0, scaley=0.0) 194 | 195 | def update(self, event, model): 196 | if event == "examples_loaded": 197 | for i in xrange(len(model.data)): 198 | self.update_example(model, i) 199 | 200 | if event == "example_added": 201 | self.update_example(model, -1) 202 | 203 | if event == "clear": 204 | self.ax.clear() 205 | self.ax.set_xticks([]) 206 | self.ax.set_yticks([]) 207 | self.contours = [] 208 | self.c_labels = None 209 | self.plot_kernels() 210 | 211 | if event == "surface": 212 | self.remove_surface() 213 | self.plot_support_vectors(model.clf.support_vectors_) 214 | self.plot_decision_surface(model.surface, model.surface_type) 215 | 216 | self.canvas.draw() 217 | 218 | def remove_surface(self): 219 | """Remove old decision surface.""" 220 | if len(self.contours) > 0: 221 | for contour in self.contours: 222 | if isinstance(contour, ContourSet): 223 | for lineset in contour.collections: 224 | lineset.remove() 225 | else: 226 | contour.remove() 227 | self.contours = [] 228 | 229 | def plot_support_vectors(self, support_vectors): 230 | """Plot the support vectors by placing circles over the 231 | corresponding data points and adds the circle collection 232 | to the contours list.""" 233 | cs = self.ax.scatter(support_vectors[:, 0], support_vectors[:, 1], 234 | s=80, edgecolors="k", facecolors="none") 235 | self.contours.append(cs) 236 | 237 | def plot_decision_surface(self, surface, type): 238 | X1, X2, Z = surface 239 | if type == 0: 240 | levels = [-1.0, 0.0, 1.0] 241 | linestyles = ['dashed', 'solid', 'dashed'] 242 | colors = 'k' 243 | self.contours.append(self.ax.contour(X1, X2, Z, levels, 244 | colors=colors, 245 | linestyles=linestyles)) 246 | elif type == 1: 247 | self.contours.append(self.ax.contourf(X1, X2, Z, 10, 248 | cmap=matplotlib.cm.bone, 249 | origin='lower', alpha=0.85)) 250 | self.contours.append(self.ax.contour(X1, X2, Z, [0.0], colors='k', 251 | linestyles=['solid'])) 252 | else: 253 | raise ValueError("surface type unknown") 254 | 255 | 256 | class ControllBar(object): 257 | def __init__(self, root, controller): 258 | fm = Tk.Frame(root) 259 | kernel_group = Tk.Frame(fm) 260 | Tk.Radiobutton(kernel_group, text="Linear", variable=controller.kernel, 261 | value=0, command=controller.refit).pack(anchor=Tk.W) 262 | Tk.Radiobutton(kernel_group, text="RBF", variable=controller.kernel, 263 | value=1, command=controller.refit).pack(anchor=Tk.W) 264 | Tk.Radiobutton(kernel_group, text="Poly", variable=controller.kernel, 265 | value=2, command=controller.refit).pack(anchor=Tk.W) 266 | kernel_group.pack(side=Tk.LEFT) 267 | 268 | valbox = Tk.Frame(fm) 269 | controller.complexity = Tk.StringVar() 270 | controller.complexity.set("1.0") 271 | c = Tk.Frame(valbox) 272 | Tk.Label(c, text="C:", anchor="e", width=7).pack(side=Tk.LEFT) 273 | Tk.Entry(c, width=6, textvariable=controller.complexity).pack( 274 | side=Tk.LEFT) 275 | c.pack() 276 | 277 | controller.gamma = Tk.StringVar() 278 | controller.gamma.set("0.01") 279 | g = Tk.Frame(valbox) 280 | Tk.Label(g, text="gamma:", anchor="e", width=7).pack(side=Tk.LEFT) 281 | Tk.Entry(g, width=6, textvariable=controller.gamma).pack(side=Tk.LEFT) 282 | g.pack() 283 | 284 | controller.degree = Tk.StringVar() 285 | controller.degree.set("3") 286 | d = Tk.Frame(valbox) 287 | Tk.Label(d, text="degree:", anchor="e", width=7).pack(side=Tk.LEFT) 288 | Tk.Entry(d, width=6, textvariable=controller.degree).pack(side=Tk.LEFT) 289 | d.pack() 290 | 291 | controller.coef0 = Tk.StringVar() 292 | controller.coef0.set("0") 293 | r = Tk.Frame(valbox) 294 | Tk.Label(r, text="coef0:", anchor="e", width=7).pack(side=Tk.LEFT) 295 | Tk.Entry(r, width=6, textvariable=controller.coef0).pack(side=Tk.LEFT) 296 | r.pack() 297 | valbox.pack(side=Tk.LEFT) 298 | 299 | cmap_group = Tk.Frame(fm) 300 | Tk.Radiobutton(cmap_group, text="Hyperplanes", 301 | variable=controller.surface_type, value=0, 302 | command=controller.refit).pack(anchor=Tk.W) 303 | Tk.Radiobutton(cmap_group, text="Surface", 304 | variable=controller.surface_type, value=1, 305 | command=controller.refit).pack(anchor=Tk.W) 306 | 307 | cmap_group.pack(side=Tk.LEFT) 308 | 309 | train_button = Tk.Button(fm, text='Fit', width=5, 310 | command=controller.fit) 311 | train_button.pack() 312 | fm.pack(side=Tk.LEFT) 313 | Tk.Button(fm, text='Clear', width=5, 314 | command=controller.clear_data).pack(side=Tk.LEFT) 315 | 316 | 317 | def get_parser(): 318 | from optparse import OptionParser 319 | op = OptionParser() 320 | op.add_option("--output", 321 | action="store", type="str", dest="output", 322 | help="Path where to dump data.") 323 | return op 324 | 325 | 326 | def main(argv): 327 | op = get_parser() 328 | opts, args = op.parse_args(argv[1:]) 329 | root = Tk.Tk() 330 | model = Model() 331 | controller = Controller(model) 332 | root.wm_title("Scikit-learn Libsvm GUI") 333 | view = View(root, controller) 334 | model.add_observer(view) 335 | Tk.mainloop() 336 | 337 | if opts.output: 338 | model.dump_svmlight_file(opts.output) 339 | 340 | if __name__ == "__main__": 341 | main(sys.argv) 342 | -------------------------------------------------------------------------------- /notebooks/images/iris_setosa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ogrisel/sklearn_pycon2014/5db03a4f8c061ef5e8fb225f5a533d4380ea11e2/notebooks/images/iris_setosa.jpg -------------------------------------------------------------------------------- /notebooks/images/iris_versicolor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ogrisel/sklearn_pycon2014/5db03a4f8c061ef5e8fb225f5a533d4380ea11e2/notebooks/images/iris_versicolor.jpg -------------------------------------------------------------------------------- /notebooks/images/iris_virginica.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ogrisel/sklearn_pycon2014/5db03a4f8c061ef5e8fb225f5a533d4380ea11e2/notebooks/images/iris_virginica.jpg -------------------------------------------------------------------------------- /notebooks/solutions/02_faces.py: -------------------------------------------------------------------------------- 1 | faces = fetch_olivetti_faces() 2 | 3 | # set up the figure 4 | fig = plt.figure(figsize=(6, 6)) # figure size in inches 5 | fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05) 6 | 7 | # plot the faces: 8 | for i in range(64): 9 | ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[]) 10 | ax.imshow(faces.images[i], cmap=plt.cm.bone, interpolation='nearest') 11 | -------------------------------------------------------------------------------- /notebooks/solutions/04_svm_rf.py: -------------------------------------------------------------------------------- 1 | # SVM results 2 | from sklearn.svm import SVC 3 | from sklearn import metrics 4 | 5 | for kernel in ['rbf', 'linear']: 6 | clf = SVC(kernel=kernel).fit(Xtrain, ytrain) 7 | ypred = clf.predict(Xtest) 8 | print("SVC: kernel = {0}".format(kernel)) 9 | print(metrics.f1_score(ytest, ypred)) 10 | plt.figure() 11 | plt.imshow(metrics.confusion_matrix(ypred, ytest), 12 | interpolation='nearest', cmap=plt.cm.binary) 13 | plt.colorbar() 14 | plt.xlabel("true label") 15 | plt.ylabel("predicted label") 16 | plt.title("SVC: kernel = {0}".format(kernel)) 17 | 18 | # random forest results 19 | from sklearn.ensemble import RandomForestClassifier 20 | 21 | for max_depth in [3, 5, 10]: 22 | clf = RandomForestClassifier(max_depth=max_depth).fit(Xtrain, ytrain) 23 | ypred = clf.predict(Xtest) 24 | print("RF: max_depth = {0}".format(max_depth)) 25 | print(metrics.f1_score(ytest, ypred)) 26 | plt.figure() 27 | plt.imshow(metrics.confusion_matrix(ypred, ytest), 28 | interpolation='nearest', cmap=plt.cm.binary) 29 | plt.colorbar() 30 | plt.xlabel("true label") 31 | plt.ylabel("predicted label") 32 | plt.title("RF: max_depth = {0}".format(max_depth)) -------------------------------------------------------------------------------- /notebooks/solutions/05_color_compression.py: -------------------------------------------------------------------------------- 1 | def compress_image(image, n_colors=64): 2 | """Compress an image 3 | 4 | Parameters 5 | ========== 6 | image : numpy array 7 | array of shape (height, width, 3) with integer values between 0 and 255 8 | n_colors : integer 9 | the number of colors in the final compressed image 10 | (i.e. the number of KMeans clusters to fit). 11 | 12 | Returns 13 | ======= 14 | new_image : numpy array 15 | array representing the new image, compressed via KMeans clustering. 16 | It has the same shape and dtype as the input image, but contains 17 | only ``n_colors`` distinct colors. 18 | """ 19 | X = image.reshape(-1, 3) / 255. 20 | model = KMeans(n_colors) 21 | labels = model.fit_predict(X) 22 | colors = model.cluster_centers_ 23 | new_image = colors[labels].reshape(image.shape) 24 | return (255 * new_image).astype(np.uint8) 25 | 26 | 27 | new_image = compress_image(china, 64) 28 | plt.imshow(new_image); 29 | -------------------------------------------------------------------------------- /notebooks/solutions/06-1_svm_class.py: -------------------------------------------------------------------------------- 1 | from sklearn.svm import SVC 2 | # First instantiate the "Support Vector Classifier" (SVC) model 3 | clf = SVC() 4 | 5 | # Next split the data (X and y) into a training and test set 6 | from sklearn.cross_validation import train_test_split 7 | Xtrain, Xtest, ytrain, ytest = train_test_split(X, y) 8 | 9 | # fit the model to the training data 10 | clf.fit(Xtrain, ytrain) 11 | 12 | # compute y_pred, the predicted labels of the test data 13 | ypred = clf.predict(Xtest) 14 | 15 | # Now that this is finished, we'll compute the classification rate 16 | print "accuracy:", np.sum(ytest == ypred) * 1. / len(ytest) -------------------------------------------------------------------------------- /notebooks/solutions/06-2_unbalanced.py: -------------------------------------------------------------------------------- 1 | from sklearn.neighbors import KNeighborsClassifier 2 | from sklearn.naive_bayes import GaussianNB 3 | from sklearn.svm import SVC 4 | 5 | for Class in [KNeighborsClassifier, GaussianNB, SVC]: 6 | cls = Class().fit(X_train, y_train) 7 | y_pred = cls.predict(X_test) 8 | print("-------------------------------------------") 9 | print(Class.__name__) 10 | print(metrics.classification_report(y_pred, y_test)) -------------------------------------------------------------------------------- /notebooks/solutions/06-3_5fold_crossval.py: -------------------------------------------------------------------------------- 1 | C = np.logspace(-2, 2, 10) 2 | scores = [cross_val_score(SVC(C=Ci), X, y, cv=5, scoring='precision').mean() for Ci in C] 3 | plt.semilogx(C, scores); -------------------------------------------------------------------------------- /notebooks/solutions/06-4_gridsearch.py: -------------------------------------------------------------------------------- 1 | from sklearn.neighbors import KNeighborsClassifier 2 | from sklearn.datasets import load_digits 3 | digits = load_digits() 4 | X, y = digits.data, digits.target 5 | 6 | # construct the K Neighbors classifier 7 | knn = KNeighborsClassifier() 8 | 9 | # Use GridSearchCV to find the best accuracy given choice of ``n_neighbors`` 10 | n_neighbors = np.arange(1, 10) 11 | grid = GridSearchCV(knn, param_grid={'n_neighbors': n_neighbors}, 12 | scoring='precision', cv=5) 13 | grid.fit(X, y) 14 | print "best parameter choice:", grid.best_params_ 15 | 16 | # Plot the accuracy as a function of the number of neighbors. 17 | # Does this change significantly if you use more/fewer folds? 18 | scores = [g[1] for g in grid.grid_scores_] 19 | plt.plot(n_neighbors, scores); -------------------------------------------------------------------------------- /notebooks/solutions/06-5_decisiontree.py: -------------------------------------------------------------------------------- 1 | from sklearn.tree import DecisionTreeRegressor 2 | X, y = make_data(500, error=1) 3 | 4 | clf = DecisionTreeRegressor() 5 | max_depth = np.arange(1, 10) 6 | 7 | grid = GridSearchCV(clf, param_grid={'max_depth': max_depth}, 8 | scoring='mean_squared_error', cv=5) 9 | grid.fit(X, y) 10 | 11 | scores = [g[1] for g in grid.grid_scores_] 12 | plt.plot(max_depth, scores); -------------------------------------------------------------------------------- /notebooks/solutions/06-6_randomforest.py: -------------------------------------------------------------------------------- 1 | from sklearn.ensemble import RandomForestRegressor 2 | X, y = make_data(500, error=1) 3 | 4 | clf = RandomForestRegressor(10) 5 | max_depth = np.arange(1, 10) 6 | 7 | grid = GridSearchCV(clf, param_grid={'max_depth': max_depth}, 8 | scoring='mean_squared_error', cv=5) 9 | grid.fit(X, y) 10 | 11 | scores = [g[1] for g in grid.grid_scores_] 12 | plt.plot(max_depth, scores); --------------------------------------------------------------------------------