{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", " \n", "## [mlcourse.ai](https://mlcourse.ai) – Open Machine Learning Course \n", "\n", "Author: [Yury Kashnitsky](https://yorko.github.io). Translated and edited by [Christina Butsko](https://www.linkedin.com/in/christinabutsko/), [Nerses Bagiyan](https://www.linkedin.com/in/nersesbagiyan/), [Yulia Klimushina](https://www.linkedin.com/in/yuliya-klimushina-7168a9139), and [Yuanyuan Pao](https://www.linkedin.com/in/yuanyuanpao/). This material is subject to the terms and conditions of the [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license. Free use is permitted for any non-commercial purpose." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#
Topic 4. Linear Classification and Regression\n", "##
Part 5. Validation and Learning Curves" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "from matplotlib import pyplot as plt\n", "from sklearn.linear_model import (LogisticRegression, LogisticRegressionCV,\n", " SGDClassifier)\n", "from sklearn.model_selection import learning_curve, validation_curve\n", "from sklearn.pipeline import Pipeline\n", "from sklearn.preprocessing import PolynomialFeatures, StandardScaler" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have an idea of model validation, cross-validation, and regularization. Let's consider the bigger question:\n", "\n", "**What to do if the quality of the model is dissatisfying?**\n", "\n", "- Should we make the model more complicated or more simple?\n", "- Should we add more features?\n", "- Do we simply need more data for training?\n", "\n", "The answers to these questions are not obvious. In particular, sometimes a more complex model can lead to a deterioration in performance. Other times, adding new observations will not bring noticeable changes. In fact, the ability to make the right decision and choose the right method to improve the model distinguishes a good professional from a bad one." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will work our data on customer churn of telecom operator." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = pd.read_csv(\"../../data/telecom_churn.csv\").drop(\"State\", axis=1)\n", "data[\"International plan\"] = data[\"International plan\"].map({\"Yes\": 1, \"No\": 0})\n", "data[\"Voice mail plan\"] = data[\"Voice mail plan\"].map({\"Yes\": 1, \"No\": 0})\n", "\n", "y = data[\"Churn\"].astype(\"int\").values\n", "X = data.drop(\"Churn\", axis=1).values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**We will train logistic regression with stochastic gradient descent. Later in the course, we will have a separate article on this topic.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "alphas = np.logspace(-2, 0, 20)\n", "sgd_logit = SGDClassifier(loss=\"log\", n_jobs=-1, random_state=17, max_iter=5)\n", "logit_pipe = Pipeline(\n", " [\n", " (\"scaler\", StandardScaler()),\n", " (\"poly\", PolynomialFeatures(degree=2)),\n", " (\"sgd_logit\", sgd_logit),\n", " ]\n", ")\n", "val_train, val_test = validation_curve(\n", " logit_pipe, X, y, \"sgd_logit__alpha\", alphas, cv=5, scoring=\"roc_auc\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**As a first step, we will construct validation curves showing how the quality (ROC-AUC) on training and test sets varies with the regularization parameter.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_with_err(x, data, **kwargs):\n", " mu, std = data.mean(1), data.std(1)\n", " lines = plt.plot(x, mu, \"-\", **kwargs)\n", " plt.fill_between(\n", " x,\n", " mu - std,\n", " mu + std,\n", " edgecolor=\"none\",\n", " facecolor=lines[0].get_color(),\n", " alpha=0.2,\n", " )\n", "\n", "\n", "plot_with_err(alphas, val_train, label=\"training scores\")\n", "plot_with_err(alphas, val_test, label=\"validation scores\")\n", "plt.xlabel(r\"$\\alpha$\")\n", "plt.ylabel(\"ROC AUC\")\n", "plt.legend()\n", "plt.grid(True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The trend is quite visible and is very common.\n", "\n", "- For simple models, training and validation errors are close and large. This suggests that the model **underfitted**, meaning it does not have a sufficient number of parameters.\n", "\n", "- For highly sophisticated models, training and validation errors differ significantly. This can be explained by **overfitting**. When there are too many parameters or regularization is not strict enough, the algorithm can be \"distracted\" by the noise in the data and lose track of the overall trend.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How much data is needed?\n", "\n", "The more data the model uses, the better. But how do we understand whether new data will helpful in any given situation? For example, is it rational to spend $N$ for assessors to double the dataset?\n", "\n", "Since the new data can be unavailable, it is reasonable to vary the size of the training set and see how the quality of the solution depends on the amount of training data. This is how we get **learning curves**.\n", "\n", "The idea is simple: we display the error as a function of the number of examples used in training. The parameters of the model are fixed in advance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_learning_curve(degree=2, alpha=0.01):\n", " train_sizes = np.linspace(0.05, 1, 20)\n", " logit_pipe = Pipeline(\n", " [\n", " (\"scaler\", StandardScaler()),\n", " (\"poly\", PolynomialFeatures(degree=degree)),\n", " (\n", " \"sgd_logit\",\n", " SGDClassifier(n_jobs=-1, random_state=17, alpha=alpha, max_iter=5),\n", " ),\n", " ]\n", " )\n", " N_train, val_train, val_test = learning_curve(\n", " logit_pipe, X, y, train_sizes=train_sizes, cv=5, scoring=\"roc_auc\"\n", " )\n", " plot_with_err(N_train, val_train, label=\"training scores\")\n", " plot_with_err(N_train, val_test, label=\"validation scores\")\n", " plt.xlabel(\"Training Set Size\")\n", " plt.ylabel(\"AUC\")\n", " plt.legend()\n", " plt.grid(True);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see what we get for the linear model. We will set the regularization coefficient to be quite large." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_learning_curve(degree=2, alpha=10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A typical situation: for a small amounts of data, errors between training and cross-validation sets are quite different, indicating overfitting. For that same model but with a large amount of data, errors \"converge\", indicating underfitting.\n", " \n", "If we add more data, error on the training set will not grow. On the other hand, the error on the test data will not be reduced.\n", " \n", "So, we see that the errors \"converged\", and the addition of new data will not help. Actually this case is the most interesting for business. It is possible that we increase the size of the dataset by 10x, but, without changing the complexity of the model, this additional data may not help. Therefore the strategy of \"set once, then use 10 times\" might not work.\n", " \n", "What happens if we reduce the regularization coefficient to 0.05?\n", " \n", "We see a good trend - the curves gradually converge, and if we move farther to the right i.e. add more data to the model, we can improve the quality on the validation set even more. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_learning_curve(degree=2, alpha=0.05)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, what if we make the model even more complex by setting alpha = 10-4?\n", "\n", "Overfitting is seen - AUC decreases on both the training and the validation sets." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_learning_curve(degree=2, alpha=1e-4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Constructing these curves can help understand which way to go and how to properly adjust the complexity of the model for new data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Conclusions on the learning and validation curves:**\n", "\n", "\n", "- Error on the training set says nothing about the quality of the model by itself\n", "- Cross-validation error shows how well the model fits the data (the existing trend in the data) while retaining the ability to generalize to new data\n", "- **Validation curve** is a graph showing the results on training and validation sets depending on the **complexity of the model**:\n", " + if the two curves are close to each other and both errors are large, it is a sign of *underfitting*\n", " + if the two curves are far from each other, it is a sign of *overfitting*\n", "- **Learning Curve** is a graph showing the results on training and validation sets depending on the number of observations:\n", " + if the curves converge, adding new data won't help, and it is necessary to change the complexity of the model \n", " + if the curves have not converged, adding new data can improve the result\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Useful resources\n", "- Main course [site](https://mlcourse.ai), [course repo](https://github.com/Yorko/mlcourse.ai), and YouTube [channel](https://www.youtube.com/watch?v=QKTuw4PNOsU&list=PLVlY_7IJCMJeRfZ68eVfEcu-UcN9BbwiX)\n", "- Medium [\"story\"](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-4-linear-classification-and-regression-44a41b9b5220) based on this notebook\n", "- Course materials as a [Kaggle Dataset](https://www.kaggle.com/kashnitsky/mlcourse)\n", "- If you read Russian: an [article](https://habrahabr.ru/company/ods/blog/323890/) on Habrahabr with ~ the same material. And a [lecture](https://youtu.be/oTXGQ-_oqvI) on YouTube\n", "- A nice and concise overview of linear models is given in the book [“Deep Learning”](http://www.deeplearningbook.org) (I. Goodfellow, Y. Bengio, and A. Courville).\n", "- Linear models are covered practically in every ML book. We recommend “Pattern Recognition and Machine Learning” (C. Bishop) and “Machine Learning: A Probabilistic Perspective” (K. Murphy).\n", "- If you prefer a thorough overview of linear model from a statistician’s viewpoint, then look at “The elements of statistical learning” (T. Hastie, R. Tibshirani, and J. Friedman).\n", "- The book “Machine Learning in Action” (P. Harrington) will walk you through implementations of classic ML algorithms in pure Python.\n", "- [Scikit-learn](http://scikit-learn.org/stable/documentation.html) library. These guys work hard on writing really clear documentation.\n", "- Scipy 2017 [scikit-learn tutorial](https://github.com/amueller/scipy-2017-sklearn) by Alex Gramfort and Andreas Mueller.\n", "- One more [ML course](https://github.com/diefimov/MTH594_MachineLearning) with very good materials.\n", "- [Implementations](https://github.com/rushter/MLAlgorithms) of many ML algorithms. Search for linear regression and logistic regression." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "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.7.3" }, "name": "lesson7_part5_overfitting_validation.ipynb" }, "nbformat": 4, "nbformat_minor": 1 }