{ "cells": [ { "cell_type": "markdown", "id": "53fc7a33", "metadata": { "id": "53fc7a33" }, "source": [ "# Ejemplo: Comparando modelos utilizando 5x2 cross-validation" ] }, { "cell_type": "markdown", "source": [ "En este ejemplo, veremos como utilizar la técnica 5x2 cross-validation para la comparación de performance de dos modelos de aprendizaje automático. Para mas detalle de la técnica puede referirse aÑ\n", "- [1] Dietterich TG (1998) Approximate Statistical Tests for Comparing Supervised Classification Learning Algorithms. Neural Comput 10:1895–1923." ], "metadata": { "id": "DDk1yLpFbEVE" }, "id": "DDk1yLpFbEVE" }, { "cell_type": "markdown", "id": "88e56467", "metadata": { "id": "88e56467" }, "source": [ "## Introducción" ] }, { "cell_type": "markdown", "id": "7c43de90", "metadata": { "id": "7c43de90" }, "source": [ "Instalamos la librerias necesarias" ] }, { "cell_type": "code", "execution_count": null, "id": "66d3d6e5", "metadata": { "id": "66d3d6e5" }, "outputs": [], "source": [ "!wget https://raw.githubusercontent.com/santiagxf/E72102/master/docs/develop/modeling/selection/code/5x2.txt \\\n", " --quiet --no-clobber\n", "!pip install -r 5x2.txt --quiet" ] }, { "cell_type": "markdown", "id": "8018f6fe", "metadata": { "id": "8018f6fe" }, "source": [ "### Sobre el conjunto de datos del censo UCI\n", "\n", "El conjunto de datos del censo de la UCI es un conjunto de datos en el que cada registro representa a una persona. Cada registro contiene 14 columnas que describen a una una sola persona, de la base de datos del censo de Estados Unidos de 1994. Esto incluye información como la edad, el estado civil y el nivel educativo. La tarea es determinar si una persona tiene un ingreso alto (definido como ganar más de $50 mil al año). Esta tarea, dado el tipo de datos que utiliza, se usa a menudo en el estudio de equidad, en parte debido a los atributos comprensibles del conjunto de datos, incluidos algunos que contienen tipos sensibles como la edad y el género, y en parte también porque comprende una tarea claramente del mundo real." ] }, { "cell_type": "markdown", "metadata": { "id": "Yug-f_kFSEya" }, "source": [ "Descargamos los datos" ], "id": "Yug-f_kFSEya" }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "5DnZh49DSEya" }, "outputs": [], "source": [ "!wget https://santiagxf.blob.core.windows.net/public/datasets/uci_census.zip \\\n", " --quiet --no-clobber\n", "!mkdir -p datasets/uci_census\n", "!unzip -qq uci_census.zip -d datasets/uci_census" ], "id": "5DnZh49DSEya" }, { "cell_type": "markdown", "id": "d34c4d95", "metadata": { "id": "d34c4d95" }, "source": [ "Preparando nuestros conjuntos de datos" ] }, { "cell_type": "code", "execution_count": 6, "id": "fca1c236", "metadata": { "id": "fca1c236" }, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "\n", "df = pd.read_csv('datasets/uci_census/data/adult-train.csv')\n", "\n", "train = df.drop(['income'], axis=1)\n", "target = df['income'].to_numpy()" ] }, { "cell_type": "markdown", "id": "00f404e3", "metadata": { "id": "00f404e3" }, "source": [ "## Preparación de los datos para el ejemplo" ] }, { "cell_type": "markdown", "id": "9a21dd73", "metadata": { "id": "9a21dd73" }, "source": [ "Realizaremos un pequeño preprocesamiento antes de entrenar el modelo:\n", "\n", "- Imputaremos los valores faltantes de las caracteristicas numéricas con la media\n", "- Imputaremos los valores faltantes de las caracteristicas categóricas con el valor `?`\n", "- Escalaremos los valores numericos utilizando un `StandardScaler`\n", "- Codificaremos las variables categóricas utilizando `OneHotEncoder`" ] }, { "cell_type": "code", "execution_count": 7, "id": "52c4bb1f", "metadata": { "id": "52c4bb1f" }, "outputs": [], "source": [ "from typing import Tuple, List\n", "\n", "import sklearn\n", "from sklearn.pipeline import Pipeline\n", "from sklearn.impute import SimpleImputer\n", "from sklearn.preprocessing import StandardScaler, OneHotEncoder\n", "from sklearn.compose import ColumnTransformer\n", "\n", "\n", "def prepare(X: pd.DataFrame) -> Tuple[np.ndarray, sklearn.compose.ColumnTransformer]:\n", " pipe_cfg = {\n", " 'num_cols': X.dtypes[X.dtypes == 'int64'].index.values.tolist(),\n", " 'cat_cols': X.dtypes[X.dtypes == 'object'].index.values.tolist(),\n", " }\n", " \n", " num_pipe = Pipeline([\n", " ('num_imputer', SimpleImputer(strategy='median')),\n", " ('num_scaler', StandardScaler())\n", " ])\n", " \n", " cat_pipe = Pipeline([\n", " ('cat_imputer', SimpleImputer(strategy='constant', fill_value='?')),\n", " ('cat_encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))\n", " ])\n", " \n", " transformations = ColumnTransformer([\n", " ('num_pipe', num_pipe, pipe_cfg['num_cols']),\n", " ('cat_pipe', cat_pipe, pipe_cfg['cat_cols'])\n", " ])\n", " X = transformations.fit_transform(X)\n", " \n", " return X, transformations\n", "\n", "\n", "train, transformations = prepare(train)" ] }, { "cell_type": "markdown", "id": "7f3f26a9", "metadata": { "id": "7f3f26a9" }, "source": [ "## Definiendo nuestros modelos a comparar\n", "\n", "Para demostrar la técnica, utilizaremos dos clasificadores basados en `LightGBM`" ] }, { "cell_type": "code", "execution_count": 57, "id": "ddc722a2", "metadata": { "id": "ddc722a2" }, "outputs": [], "source": [ "from lightgbm import LGBMClassifier\n", "\n", "clf1 = LGBMClassifier(n_estimators=100, n_jobs=2)\n", "clf2 = LGBMClassifier(n_estimators=100, reg_alpha=1, reg_lambda=1, min_split_gain=2, n_jobs=2)" ] }, { "cell_type": "markdown", "id": "53fcb15c", "metadata": { "id": "53fcb15c" }, "source": [ "## Procedimiento de 5x2" ] }, { "cell_type": "markdown", "source": [ "Utilizaremos la libreria `mlxtend` que dispone de una implementation de este procedimiento. La utilización de la misma es bastante sencilla:" ], "metadata": { "id": "wPqvwZuFbsAU" }, "id": "wPqvwZuFbsAU" }, { "cell_type": "code", "source": [ "from mlxtend.evaluate import paired_ttest_5x2cv" ], "metadata": { "id": "WMwFW7yvbrEY" }, "id": "WMwFW7yvbrEY", "execution_count": 65, "outputs": [] }, { "cell_type": "markdown", "source": [ "Iniciamos el test:" ], "metadata": { "id": "9-ZceCl-b9Pr" }, "id": "9-ZceCl-b9Pr" }, { "cell_type": "code", "source": [ "statistic, pvalue = paired_ttest_5x2cv(clf1, clf2,\n", " X=train, y=target, scoring='accuracy')" ], "metadata": { "id": "2OiDTschb4RJ" }, "id": "2OiDTschb4RJ", "execution_count": 69, "outputs": [] }, { "cell_type": "markdown", "source": [ "Notemos que aqui las hipótesis nula y alternativa son como sigue:\n", "\n", "- **H0:** La diferencia en performance de los dos modelos es zero (los modelos son iguales).\n", "- **HA:** La diferencia en performance de los modelos es distinta de zero (los modelos son distintos).\n" ], "metadata": { "id": "DrQ0cjfkVeqp" }, "id": "DrQ0cjfkVeqp" }, { "cell_type": "markdown", "source": [ "Tomamos una decisión:" ], "metadata": { "id": "CQTjDynHcMdv" }, "id": "CQTjDynHcMdv" }, { "cell_type": "code", "source": [ "if pvalue > 0.05:\n", " print(\"No podemos tomar ninguna conclusión. No se puede rechazar la idea de que ambos modelos son equivalente\")\n", "else:\n", " print(\"Existe suficiente evidencia para rechazar la idea de que los modelos son equivalentes en favor de \\\n", " una alternativa de que los modelos son distintos.\")\n", " \n", "print(\"\\nValor estadístico:\", statistic)\n", "print(\"p-value:\", pvalue)" ], "metadata": { "id": "Z4125De7Xzlk", "outputId": "474c6af4-7dc7-4fc0-c469-358b897f7ad5", "colab": { "base_uri": "https://localhost:8080/" } }, "id": "Z4125De7Xzlk", "execution_count": 70, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "No podemos tomar ninguna conclusión. No se puede rechazar la idea de que ambos modelos son equivalente\n", "\n", "Valor estadístico: 1.281174535572354\n", "p-value: 0.25633323457069157\n" ] } ] } ], "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.8.11" }, "colab": { "name": "5x2.ipynb", "provenance": [], "toc_visible": true } }, "nbformat": 4, "nbformat_minor": 5 }