{ "cells": [ { "cell_type": "markdown", "source": [ "Estimación de la performance en linea para un modelo de aprendizaje\n", "===================================================================" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Motivación\n", "\n", "Antes de intentar estimar la performance de un modelo de aprendizaje automático en en linea, es útil introducir algunos conceptos que servirán de fundamentos para las técnicas que veremos en esta sección.\n", "\n", "### Validez y confiabilidad\n", "\n", "Validez hace referencia al grado en que una medición realmente mide lo que pretende medir, es decir, que tan exacta es esta medición. Confiabilidad hace referencia a que tan probable es que obtengamos el mismo valor si repitieramos la medición multiples veces bajo las mismas condiciones, es decir, que tan precisa es esta medición.\n", "\n", "Para que los resultados de una evaluación o un experiento sean útiles, las mediciones que realizamos tienen que ser tanto válidas como confiables. Esto es sumamente importante sobre todo cuando comunicamos los resultados al negocio ya que los mismo suelenasumir que los modelos de aprendizaje funcionan correctamente en general. Es decir, estarán interesados en saber que tan propenso es nuestro modelo a cometer errores.\n", "\n", "### Intervalo de confianza\n", "\n", "Métodos cuantitativos para la comparación de la performance de un modelo de aprendizaje automático tipicamente se realizan utilizando una prueba de hipótesis basados en la significancia estadística de la diferencia de los valores observados o utilizando intervalos de confianza. Un **intervalo de confianza** representa los límites en la estimación de una variable. Es herramienta que se utiliza para cuantificar la incertidumbre en una estimación.\n", "\n", "Un intervalo de confianza proporciona un límite superior e inferior y una probabilidad. Tomado solo como una medida de rango, el intervalo de confianza a menudo se denomina margen de error y puede usarse para representar gráficamente la incertidumbre de una estimación en gráficos mediante el uso de barras de error.\n", "\n", "En general, cuanto mayor sea la muestra desde la cual estámos estimando los parámetros, más precisa será la estimación y menor (mejor) el intervalo de confianza.\n", " - **Intervalo de confianza más pequeño:** una estimación más precisa.\n", " - **Intervalo de confianza más grande:** una estimación menos precisa.\n", "\n" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## Ejemplo\n", "\n", "Para visualizar el concepto de intervalo de confianza, usaremos los datos de IRIS dataset. El conjunto de datos de IRIS es parte de la biblioteca `sklearn` que constan de 3 tipos diferentes de longitud de pétalo y sépalo (Setosa, Versicolour y Virginica), descriptos por la longitud del sépalo, el ancho del sépalo, la longitud del pétalo y el ancho del pétalo:" ], "metadata": {} }, { "cell_type": "code", "execution_count": 2, "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from sklearn import datasets\n", "\n", "iris = datasets.load_iris()\n", "X = iris.data[:,:2]\n", "y = iris.target" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "Entrenaremos un modelo para resolver el problema. Primero, dividiremos los datos en conjuntos de entrenamiento reservando una porciónde los mismos para nuestro proceso de estimación de performance. La idea de reservar esta porción es simular los datos que estarían disponibles cuando el modelo finalmente acanza producción." ], "metadata": {} }, { "cell_type": "code", "execution_count": 3, "source": [ "from sklearn import svm\n", "from sklearn.model_selection import train_test_split\n", "\n", "X_train, X_eval, y_train, y_eval = train_test_split(X, y, test_size=0.33, random_state=42, stratify=y)\n", "\n", "model = svm.SVC(C=1.0, kernel='linear', gamma=0.5, probability=True)\n", "model = model.fit(X_train, y_train)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Métricas con distribuciones conocidas\n", "\n", "Dado que estamos trabajando con un problema de clasificación, utilizaremos métricas de proporciones para estimar el intervalo de confianza en donde se encuentra la métrica `accuracy` de nuestro modelo." ], "metadata": {} }, { "cell_type": "code", "execution_count": 4, "source": [ "from sklearn.metrics import accuracy_score\n", "\n", "y_pred = model.predict(X_eval)\n", "accuracy = accuracy_score(y_eval, y_pred)\n", "\n", "print(\"Accuracy:\", accuracy)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Accuracy: 0.76\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "#### statsmodels\n", "\n", "`statsmodels` es un librería de Python que proporciona clases y funciones para la estimación de diferentes modelos estadísticos, así como para realizar pruebas estadísticas y exploración de datos estadísticos. Esta libería se publica bajo la licencia BSD de código abierto. Puede instalar esta liberia desde `conda` o `pip`:\n", "\n", "```\n", " !pip install statsmodels\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Calculamos el intervalo de confianza de nuestra métrica utilizando la libreria `statsmodels` con un nivel de confidencia de 95%." ], "metadata": {} }, { "cell_type": "code", "execution_count": 26, "source": [ "from statsmodels.stats import proportion\n", "\n", "alpha = 0.95\n", "sample_size = len(y_pred)\n", "\n", "proportion.proportion_confint(accuracy*sample_size, sample_size, alpha)" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "(0.7562125936684756, 0.7637874063315244)" ] }, "metadata": {}, "execution_count": 26 } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Con esta información podemos concluir que:\n", "\n", " - La exactitud de nuetro modelo probablemente se encuentra dentro del rango 75,6% y 76,4%\n", " - Nuestro modelo probablemente comete un error que se encuentra dentro del 23,6% y el 24,4% " ], "metadata": {} }, { "cell_type": "markdown", "source": [ ".. note:: Una utilidad interesante disponible en la librería `statsmodels` es la posibilidad de calcular cual es el tamaño de muestra que necesitariamos si quisieramos obtener un determinado intervalo de confianza. Esto lo podemos lograr utilizando el método `proportion.samplesize_confint_proportion`" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Metricas con distribuciones no conocidas\n", "\n", "A menudo no conocemos la distribución de la métrica de performance que estamos utilizando y, por lo tanto, no identifiquemos correctamente la forma de calcular su intervalo de confianza. En estos casos, podemos utilizar un método de remuestreo basado en `bootstrap` para calcular los intervalos de confianza. Estos intervalos de confianza se los suele nombrar `intervalos de confianza de bootstrap` o `bootstrap confidence intervals`.\n", "\n", "**Bootstrap** es una estrategia de remuestreo con reemplazo que no requiere suposiciones sobre la distribución de datos. Es una herramienta poderosa que nos permite hacer inferencias sobre las estadísticas de la población (por ejemplo, media, varianza) cuando tenemos un número finito de muestras." ], "metadata": {} }, { "cell_type": "markdown", "source": [ "#### bootstrapped\n", "\n", "`bootstrapped` es una biblioteca de Python distribuida por `facebook` que permite construir intervalos de confianza a partir de datos. En particular, provee diversos métodos muy útiles en una variedad de contextos incluyendo análisis de pruebas A/B. Para mayor información sobre esta librería puede revisar su [repositorio en GitHub](https://github.com/facebookarchive/bootstrapped).\n", "\n", "```\n", " !pip install bootstrapped\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Para demostrar la metodología, estimaremos la métrica `F1` del mismo problema de clasificación mediante el método de bootstrapping. Para lograrlo, primero construiremos un conjunto de datos que contenga la clase verdadera de cada una de las muestras y las predicciones de nuestro modelo." ], "metadata": {} }, { "cell_type": "code", "execution_count": 5, "source": [ "samples = pd.DataFrame({ 'class': y_eval, 'predicted': y_pred })" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "Luego, construiremos una función, `calculate_f1`, que será la responsable de calcular la métrica en cuestión. Esta función será invocada 2 veces por la librería `bootstrapped`:\n", "\n", " - La primera vez, será llamada para calcular el valor de la métrica en cuestión.\n", " - La segunda vez, será llamada con un conjunto de datos representando los remuestreos (bootstrapping) que se solicitaron. Es decir, que el arreglo que recibimos en el parametro `values` tendrá una dimensión extra correspondiente al numero de remuestreos generados." ], "metadata": {} }, { "cell_type": "code", "execution_count": 26, "source": [ "from sklearn.metrics import f1_score\n", "\n", "def calculate_f1(values):\n", " if values.ndim == 2:\n", " return [f1_score(values[:,0], values[:,1], average='weighted')]\n", " else:\n", " return np.array([f1_score(v[:,0], v[:,1], average='weighted') for v in values])" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "Finalmente, generamos la estimación de la métrica invocando al método `bs.bootstrap` donde los parámetros son:\n", "\n", " - **X:** muestra de datos con dimensiones (numero_de_muestras, numero_de_features).\n", " - **stat_func:** función estadística que deseamos calcular.\n", " - **num_iterations:** cantidad de remuestreos que se realizarán sobre el conjunto de datos original.\n", " - **alpha:** nivel de significancia que estamos búscando" ], "metadata": {} }, { "cell_type": "code", "execution_count": 29, "source": [ "import bootstrapped.bootstrap as bs\n", "\n", "bs.bootstrap(samples.values, stat_func=calculate_f1, num_iterations=100, alpha=0.05)" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "0.7566315789473684 (0.6536431578947369, 0.8824414958966043)" ] }, "metadata": {}, "execution_count": 29 } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "`bootstrapped` proporciona intervalos de confianza basados en pivotes (también conocidos como empíricos) basados en un remuestreo de bootstrap. Aquí podemos ver que el valor verdadero de `F1` se encuentra probablemente en el rango `(0.61, 0.87)`, con un 95% de confidencia." ], "metadata": {} }, { "cell_type": "markdown", "source": [], "metadata": {} } ], "metadata": { "orig_nbformat": 4, "language_info": { "name": "python", "version": "3.8.5", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "kernelspec": { "name": "python3", "display_name": "Python 3.8.5 64-bit ('base': conda)" }, "interpreter": { "hash": "bea38c2984299ac640e8421861d34b2e05ee614f6236d2975c05eeb77366835f" } }, "nbformat": 4, "nbformat_minor": 2 }