{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Boosting en Machine Learning con Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Esta notebook fue creada originalmente como un blog post por [Raúl E. López Briega](http://relopezbriega.com.ar/) en [Matemáticas, Analisis de datos y Python](http://relopezbriega.github.io). El contenido esta bajo la licencia BSD.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\"Machine" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> \"La opinión de toda una multitud es siempre más creíble que la de una minoría.\"\n", "\n", "**[Miguel de Unamuno](https://es.wikipedia.org/wiki/Miguel_de_Unamuno)**\n", "\n", "## Introducción\n", "\n", "La meta de construir sistemas que puedan adaptarse a sus entornos y aprender de su experiencia ha atraído a investigadores de muchos campos, como la [Informática](https://es.wikipedia.org/wiki/Inform%C3%A1tica),\n", "[Matemáticas](https://es.wikipedia.org/wiki/Matem%C3%A1ticas), [Física](https://es.wikipedia.org/wiki/F%C3%ADsica), [Neurociencia](https://es.wikipedia.org/wiki/Neurociencia) y la [Ciencia cognitiva](https://es.wikipedia.org/wiki/Ciencia_cognitiva). Intuitivamente, para que un *algoritmo de aprendizaje* sea efectivo y preciso en sus predicciones, debería reunir tres condiciones básicas: \n", "\n", "1. Debería ser entrenado con *suficientes* datos de entrenamiento.\n", "2. Sus resultados deberían *ajustarse* bastante bien a los ejemplos de entrenamiento (lo que significaría tener una tasa de error baja).\n", "3. Debería ser lo suficientemente \"simple\". Esta última condición, que las reglas más simples suelen ser las mejores, se conoce a menudo con el nombre de \"[La navaja de Occam](https://es.wikipedia.org/wiki/Navaja_de_Ockham)\".\n", "\n", "Muchos algoritmos se han creado y existen aún muchos por descubrir; pero unos de los que ha ganado mucha atracción en los últimos años por su simpleza y su gran éxito en competencias como [kraggle](https://www.kaggle.com/), son los algoritmos de [Boosting](https://es.wikipedia.org/wiki/Boosting). \n", "\n", "## ¿Qué es Boosting?\n", "\n", "[Boosting](https://es.wikipedia.org/wiki/Boosting) es un **enfoque de [Machine Learning](http://relopezbriega.github.io/category/machine-learning.html) basado en la idea de crear una regla de predicción altamente precisa combinando muchas reglas relativamente débiles e imprecisas**. Una teoría notablemente rica ha evolucionado en torno al [Boosting](https://es.wikipedia.org/wiki/Boosting), con conexiones a una amplia gama de ramas de la ciencia, incluyendo [estadísticas](http://relopezbriega.github.io/category/pobabilidad-y-estadistica.html), [teoría de juegos](https://es.wikipedia.org/wiki/Teor%C3%ADa_de_juegos), [optimización convexa](http://relopezbriega.github.io/blog/2017/01/18/problemas-de-optimizacion-con-python/) y [geometría de la información](https://es.wikipedia.org/wiki/Geometr%C3%ADa_de_la_informaci%C3%B3n). Los algoritmos de [Boosting](https://es.wikipedia.org/wiki/Boosting) han tenido éxito práctico con aplicaciones, por ejemplo, en [biología](https://es.wikipedia.org/wiki/Biolog%C3%ADa), [visión](https://es.wikipedia.org/wiki/Visi%C3%B3n_artificial) y [procesamiento del lenguaje natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). En varios momentos de su historia, el [Boosting](https://es.wikipedia.org/wiki/Boosting) ha sido objeto de controversia por el misterio y la paradoja que parece presentar. El [Boosting](https://es.wikipedia.org/wiki/Boosting) asume la disponibilidad de un *algoritmo de aprendizaje base o débil* que, dado ejemplos de entrenamiento etiquetados, produce un *clasificador base o débil*. El objetivo de [Boosting](https://es.wikipedia.org/wiki/Boosting) es el de mejorar el rendimiento del *algoritmo de aprendizaje* al tratarlo como una \"caja negra\" que se puede llamar repetidamente, como una subrutina. Si bien el *algoritmo de aprendizaje base* puede ser rudimentario y moderadamente inexacto, no es del todo trivial ni poco informativo y debe obtener resultados mejores a los que se podrían obtener en forma aleatoria. La idea fundamental detrás de [Boosting](https://es.wikipedia.org/wiki/Boosting) es elegir *conjuntos de entrenamiento* para el *algoritmo de aprendizaje base* de tal manera que lo obligue a *inferir* algo nuevo sobre los datos cada vez que se lo llame. Uno de los primeros algoritmos de [Boosting](https://es.wikipedia.org/wiki/Boosting) en tener éxito en problemas de clasificación binaria fue [AdaBoost](https://en.wikipedia.org/wiki/AdaBoost).\n", "\n", "## AdaBoost\n", "\n", "[AdaBoost](https://en.wikipedia.org/wiki/AdaBoost) es la abreviatura de *adaptive boosting*, es un algoritmo que puede ser utilizado junto con otros *algoritmos de aprendizaje* para mejorar su rendimiento. \n", "[AdaBoost](https://en.wikipedia.org/wiki/AdaBoost) funciona eligiendo un *algoritmo base* (por ejemplo [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n)) y mejorándolo iterativamente al tomar en cuenta los casos incorrectamente clasificados en el conjunto de entrenamiento. \n", "\n", "En [AdaBoost](https://en.wikipedia.org/wiki/AdaBoost) asignamos *pesos* iguales a todos los ejemplos de entrenamiento y elegimos un *algoritmo base*. En cada paso de iteración, aplicamos el *algoritmo base* al conjunto de entrenamiento y aumentamos los pesos de los ejemplos incorrectamente clasificados. Iteramos n veces, cada vez aplicando el *algoritmo base* en el conjunto de entrenamiento con *pesos* actualizados. El modelo final es la suma ponderada de los resultados de los n *algoritmos base*.\n", "[AdaBoost](https://en.wikipedia.org/wiki/AdaBoost) en conjunto con [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) se ha mostrado sumamente efectivo en varios problemas de [Machine Learning](http://relopezbriega.github.io/category/machine-learning.html). Veamos un ejemplo en [Python](https://www.python.org/)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# \n", "# Importando las librerías que vamos a utilizar\n", "import pandas as pd\n", "import numpy as np \n", "import matplotlib.pyplot as plt \n", "import seaborn as sns \n", "from sklearn.model_selection import train_test_split\n", "from sklearn.ensemble import GradientBoostingClassifier\n", "from sklearn.ensemble import AdaBoostClassifier\n", "from sklearn.metrics import accuracy_score\n", "from sklearn.tree import DecisionTreeClassifier\n", "from sklearn.tree import export_graphviz\n", "from sklearn.datasets import load_breast_cancer\n", "from sklearn.preprocessing import LabelEncoder\n", "from sklearn.preprocessing import OneHotEncoder\n", "import graphviz\n", "import xgboost as xgb\n", "\n", "# graficos incrustados\n", "%matplotlib inline\n", "\n", "# parametros esteticos de seaborn\n", "sns.set_palette(\"deep\", desat=.6)\n", "sns.set_context(rc={\"figure.figsize\": (8, 4)})" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
mean radiusmean texturemean perimetermean areamean smoothnessmean compactnessmean concavitymean concave pointsmean symmetrymean fractal dimension...worst radiusworst textureworst perimeterworst areaworst smoothnessworst compactnessworst concavityworst concave pointsworst symmetryworst fractal dimension
017.9910.38122.801001.00.118400.277600.30010.147100.24190.07871...25.3817.33184.602019.00.16220.66560.71190.26540.46010.11890
120.5717.77132.901326.00.084740.078640.08690.070170.18120.05667...24.9923.41158.801956.00.12380.18660.24160.18600.27500.08902
219.6921.25130.001203.00.109600.159900.19740.127900.20690.05999...23.5725.53152.501709.00.14440.42450.45040.24300.36130.08758
311.4220.3877.58386.10.142500.283900.24140.105200.25970.09744...14.9126.5098.87567.70.20980.86630.68690.25750.66380.17300
420.2914.34135.101297.00.100300.132800.19800.104300.18090.05883...22.5416.67152.201575.00.13740.20500.40000.16250.23640.07678
\n", "

5 rows × 30 columns

\n", "
" ], "text/plain": [ " mean radius mean texture mean perimeter mean area mean smoothness \\\n", "0 17.99 10.38 122.80 1001.0 0.11840 \n", "1 20.57 17.77 132.90 1326.0 0.08474 \n", "2 19.69 21.25 130.00 1203.0 0.10960 \n", "3 11.42 20.38 77.58 386.1 0.14250 \n", "4 20.29 14.34 135.10 1297.0 0.10030 \n", "\n", " mean compactness mean concavity mean concave points mean symmetry \\\n", "0 0.27760 0.3001 0.14710 0.2419 \n", "1 0.07864 0.0869 0.07017 0.1812 \n", "2 0.15990 0.1974 0.12790 0.2069 \n", "3 0.28390 0.2414 0.10520 0.2597 \n", "4 0.13280 0.1980 0.10430 0.1809 \n", "\n", " mean fractal dimension ... worst radius \\\n", "0 0.07871 ... 25.38 \n", "1 0.05667 ... 24.99 \n", "2 0.05999 ... 23.57 \n", "3 0.09744 ... 14.91 \n", "4 0.05883 ... 22.54 \n", "\n", " worst texture worst perimeter worst area worst smoothness \\\n", "0 17.33 184.60 2019.0 0.1622 \n", "1 23.41 158.80 1956.0 0.1238 \n", "2 25.53 152.50 1709.0 0.1444 \n", "3 26.50 98.87 567.7 0.2098 \n", "4 16.67 152.20 1575.0 0.1374 \n", "\n", " worst compactness worst concavity worst concave points worst symmetry \\\n", "0 0.6656 0.7119 0.2654 0.4601 \n", "1 0.1866 0.2416 0.1860 0.2750 \n", "2 0.4245 0.4504 0.2430 0.3613 \n", "3 0.8663 0.6869 0.2575 0.6638 \n", "4 0.2050 0.4000 0.1625 0.2364 \n", "\n", " worst fractal dimension \n", "0 0.11890 \n", "1 0.08902 \n", "2 0.08758 \n", "3 0.17300 \n", "4 0.07678 \n", "\n", "[5 rows x 30 columns]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Utilzando el dataset breast cancer\n", "cancer = load_breast_cancer()\n", "\n", "# dataset en formato tabular\n", "pd.DataFrame(data=cancer.data,\n", " columns=cancer.feature_names).head()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Precisión modelo inicial train/test 0.962/0.888\n" ] } ], "source": [ "# Separando los datos en sets de entrenamiento y evaluación\n", "X_train, X_test, y_train, y_test = train_test_split(cancer.data, \n", " cancer.target, random_state=1)\n", "\n", "# Armando un simple arbol de decisión\n", "tree = DecisionTreeClassifier(max_depth=2, random_state=0)\n", "tree.fit(X_train, y_train)\n", "print('Precisión modelo inicial train/test {0:.3f}/{1:.3f}'\n", " .format(tree.score(X_train, y_train), tree.score(X_test, y_test)))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "Tree\n", "\n", "\n", "0\n", "\n", "worst perimeter <= 106.05\n", "samples = 426\n", "value = [157, 269]\n", "class = benign\n", "\n", "\n", "1\n", "\n", "worst concave points <= 0.1589\n", "samples = 259\n", "value = [9, 250]\n", "class = benign\n", "\n", "\n", "0->1\n", "\n", "\n", "True\n", "\n", "\n", "4\n", "\n", "worst texture <= 20.645\n", "samples = 167\n", "value = [148, 19]\n", "class = malignant\n", "\n", "\n", "0->4\n", "\n", "\n", "False\n", "\n", "\n", "2\n", "\n", "samples = 253\n", "value = [4, 249]\n", "class = benign\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "3\n", "\n", "samples = 6\n", "value = [5, 1]\n", "class = malignant\n", "\n", "\n", "1->3\n", "\n", "\n", "\n", "\n", "5\n", "\n", "samples = 16\n", "value = [4, 12]\n", "class = benign\n", "\n", "\n", "4->5\n", "\n", "\n", "\n", "\n", "6\n", "\n", "samples = 151\n", "value = [144, 7]\n", "class = malignant\n", "\n", "\n", "4->6\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Dibujando el modelo\n", "export_graphviz(tree, out_file=\"tree.dot\", class_names=[\"malignant\", \"benign\"],\n", "feature_names=cancer.feature_names, impurity=False, filled=True)\n", "\n", "with open(\"tree.dot\") as f:\n", " dot_graph = f.read()\n", "graphviz.Source(dot_graph)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Utilizando AdaBoost para aumentar la precisión\n", "ada = AdaBoostClassifier(base_estimator=tree, n_estimators=500, \n", " learning_rate=1.5, random_state=1)\n", "# Ajustando los datos\n", "ada = ada.fit(X_train, y_train)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Precisión modelo con AdaBoost train/test 1.000/0.965\n" ] } ], "source": [ "# Imprimir la precisión.\n", "y_train_pred = ada.predict(X_train)\n", "y_test_pred = ada.predict(X_test)\n", "ada_train = accuracy_score(y_train, y_train_pred)\n", "ada_test = accuracy_score(y_test, y_test_pred)\n", "print('Precisión modelo con AdaBoost train/test {0:.3f}/{1:.3f}'\n", " .format(ada_train, ada_test))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para este ejemplo utilizamos el [conjunto de datos](https://es.wikipedia.org/wiki/Conjunto_de_datos) [breast cancer](https://www.kaggle.com/uciml/breast-cancer-wisconsin-data) que ya viene en cargado en [scikit-learn](http://scikit-learn.org/); la idea es clasificar casos de cáncer de pecho según varios atributos de los tumores. \n", "\n", "En primer lugar, creamos un clasificador simple, un [árbol de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) de hasta dos niveles de profundidad. Este clasificador tuvo un rendimiento bastante bueno, logrando una precisión del 96% con los datos de entrenamiento y del 89% con los datos de evaluación.\n", "\n", "Luego aplicamos [AdaBoost](https://en.wikipedia.org/wiki/AdaBoost) sobre el mismo modelo para mejorar la precisión. Vemos que el modelo con [AdaBoost](https://en.wikipedia.org/wiki/AdaBoost) logra una precisión del 100% en los datos de entrenamiento y del 96% en los datos de evaluación. Debemos tener en cuenta que una precisión del 100% sobre los datos de entrenamiento, puede ser un indicio de que el modelo tal vez este [sobreajustado](http://relopezbriega.github.io/blog/2016/05/29/machine-learning-con-python-sobreajuste/). El [sobreajuste](http://relopezbriega.github.io/blog/2016/05/29/machine-learning-con-python-sobreajuste/) es uno de los riesgo que suele traer aparejado la utilización de las técnicas de [Boosting](https://es.wikipedia.org/wiki/Boosting).\n", "\n", "A partir del éxito inicial de [AdaBoost](https://en.wikipedia.org/wiki/AdaBoost), las técnicas de [Boosting](https://es.wikipedia.org/wiki/Boosting) fueron evolucionando hacia un modelo estadístico más generalizado, tratando el problema como un caso de [optimización](http://relopezbriega.github.io/blog/2017/01/18/problemas-de-optimizacion-con-python/) numérica en dónde el objetivo es minimizar la [función de perdida](https://en.wikipedia.org/wiki/Loss_functions_for_classification) del modelo mediante la adición de los *algoritmos de aprendizaje débiles* utilizando un procedimiento de optimización del tipo de [gradiente descendiente](https://en.wikipedia.org/wiki/Gradient_descent). Esta generalización permitió utilizar funciones arbitrarias de pérdida diferenciables, ampliando la técnica más allá de los problemas de clasificación binaria hacia problemas de regresión y de clasificación multi-variable. Esta nueva familia de algoritmos de [Boosting](https://es.wikipedia.org/wiki/Boosting) se conocen bajo el nombre de [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting).\n", "\n", "## Gradient Boosting\n", "\n", "El [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) implica tres elementos:\n", "\n", "* Una [función de perdida](https://en.wikipedia.org/wiki/Loss_functions_for_classification) a optimizar .\n", "* Un *algoritmo de aprendizaje débil* para hacer las predicciones.\n", "* Un modelo aditivo para añadir los *algoritmos de aprendizaje débiles* que minimizan la [función de perdida](https://en.wikipedia.org/wiki/Loss_functions_for_classification).\n", "\n", "\n", "### Función de pérdida\n", "La [función de perdida](https://en.wikipedia.org/wiki/Loss_functions_for_classification) utilizada va a depender del tipo de problema al que nos enfrentamos. La principal característica que debe poseer, es que sea [diferenciable](https://es.wikipedia.org/wiki/C%C3%A1lculo_diferencial). Existen varias funciones de *pérdida* estándar. Por ejemplo, para problemas de [regresión](https://es.wikipedia.org/wiki/An%C3%A1lisis_de_la_regresi%C3%B3n) podemos utilizar un [error cuadrático](https://es.wikipedia.org/wiki/Error_cuadr%C3%A1tico_medio) y para problemas de clasificación podemos utilizar una pérdida logarítmica o una [entropía cruzada](https://es.wikipedia.org/wiki/Entrop%C3%ADa_cruzada).\n", "\n", "### Algoritmo de aprendizaje débil\n", "El algoritmo de aprendizaje débil que se utiliza en el [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) es el de [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n). Específicamente se usan árboles de regresión que producen valores reales para las divisiones y cuya salida se puede sumar, permitiendo que los resultados de los modelos subsiguientes sean agregados y corrijan los errores promediando las predicciones. \n", "Es común restringir a los [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) de manera específica para asegurarnos que el algoritmo permanezca *débil*. Se suelen restringir el número máximo de capas, nodos, divisiones u hojas. \n", "\n", "### Modelo aditivo\n", "Los [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) son agregados de a uno a la vez, y los árboles existentes en el modelo no cambian. Para determinar los *parámetros* que tendrán cada uno de los [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) que son agregados al modelo se utiliza un procedimiento de [gradiente descendiente](https://en.wikipedia.org/wiki/Gradient_descent) que minimizará la [función de perdida](https://en.wikipedia.org/wiki/Loss_functions_for_classification). De esta forma se van agregando árboles con distintos parámetros de forma tal que la combinación de ellos minimiza la *pérdida* del modelo y mejora la predicción.\n", "\n", "\n", "[Árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) con [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) es uno de los modelos más poderosos y más utilizados para problemas de aprendizaje supervisado. Su principal inconveniente es que requieren un ajuste cuidadoso de los parámetros y puede requerir mucho tiempo de entrenamiento. Al igual que otros modelos basados en árboles, el algoritmo funciona y escala bien con una mezcla de características binarias y continuas. Asimismo, también arrastra el problema de los [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) en los casos en que los datos están dispersos y tienen una alta dimensionalidad. Veamos un ejemplo con [scikit-learn](http://scikit-learn.org/) utilizando los mismos datos de [breast cancer](https://www.kaggle.com/uciml/breast-cancer-wisconsin-data) del ejemplo anterior." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Precisión Gradient Boosting train/test 0.991/0.937\n" ] } ], "source": [ "# Armando el modelo con parametro max_depth\n", "gbrt = GradientBoostingClassifier(random_state=0, n_estimators=500,\n", " max_depth=1, learning_rate=0.01)\n", "# Ajustando el modelo\n", "gbrt.fit(X_train, y_train)\n", "print('Precisión Gradient Boosting train/test {0:.3f}/{1:.3f}'\n", " .format(gbrt.score(X_train, y_train), gbrt.score(X_test, y_test)))" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/png": "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\nQmCC\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Graficando la importancia de cada atributo\n", "n_atributos = cancer.data.shape[1]\n", "plt.barh(range(n_atributos), gbrt.feature_importances_, align='center')\n", "plt.yticks(np.arange(n_atributos), cancer.feature_names)\n", "plt.xlabel(\"Importancia de atributo\")\n", "plt.ylabel(\"Atributo\")\n", "plt.show();" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Los principales parámetros de los modelos de [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) con [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) son el número de árboles, `n_estimators`, y la tasa de aprendizaje, `learning_rate`, que controla el grado en que a cada árbol se le permite corregir los errores de los árboles anteriores. Estos dos parámetros están altamente interconectados en el sentido de que si bajamos el valor en la tasa de aprendizaje vamos a necesitar un número mayor de árboles para construir un modelo de complejidad similar. Como podemos ver en el ejemplo, aplicando [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) con árboles de tan solo un nivel de profundidad, logramos una precisión del 99 % sobre los datos de entrenamiento y del 93 % sobre los datos de evaluación.\n", "\n", "Si deseamos aplicar el algoritmo de [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) a un problema de gran escala, entonces la librería que sobresale por su facilidad de utilización y rendimiento es [XGBoost](http://xgboost.readthedocs.io/en/latest/).\n", "\n", "## XGBoost\n", "\n", "[XGBoost](http://xgboost.readthedocs.io/en/latest/) significa eXtreme Gradient Boosting. Es el algoritmo que ha estado dominando recientemente los problemas [Machine learning](http://relopezbriega.github.io/blog/2015/10/10/machine-learning-con-python/) y las competiciones de [Kaggle](https://www.kaggle.com/) con datos estructurados o tabulares. [XGBoost](http://xgboost.readthedocs.io/en/latest/) es una implementación de [árboles de decisión](https://es.wikipedia.org/wiki/%C3%81rbol_de_decisi%C3%B3n) con [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) diseñada para minimizar la velocidad de ejecución y maximizar el rendimiento. Posee una interface para varios lenguajes de programación, entre los que se incluyen [Python](https://www.python.org/), [R](https://www.r-project.org/), [Julia](https://julialang.org/) y [Scala](https://www.scala-lang.org/).\n", "\n", "Internamente, [XGBoost](http://xgboost.readthedocs.io/en/latest/) representa todos los problemas como un caso de modelado predictivo de regresión que sólo toma valores numéricos como entrada. Si nuestros datos están en un formato diferente, primero vamos a tener que transformarlos para poder hacer uso de todo el poder de esta librería. El hecho de trabajar sólo con datos numéricos es lo que hace que esta librería sea tan eficiente. \n", "\n", "Veamos como la podemos utilizar en [Python](https://www.python.org/) con un ejemplo. Para este caso vamos a trabajar con el [dataset](https://es.wikipedia.org/wiki/Conjunto_de_datos) [UCI breast-cancer](http://archive.ics.uci.edu/ml/datasets/Breast+Cancer), el cual contiene todos [datos categóricos](http://relopezbriega.github.io/blog/2016/02/29/analisis-de-datos-categoricos-con-python/) que vamos a tener que transformar. Este conjunto de datos describe los detalles técnicos de las biopsias de cáncer de mama y la tarea de predicción es predecir si el paciente tiene o no una recurrencia del cáncer, o no." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Unnamed: 0agemenopausetumor-sizeinv-nodesnode-capsdeg-maligbreastbreast-quadirradiatClass
00'40-49''premeno''15-19''0-2''yes''3''right''left_up''no''recurrence-events'
11'50-59''ge40''15-19''0-2''no''1''right''central''no''no-recurrence-events'
22'50-59''ge40''35-39''0-2''no''2''left''left_low''no''recurrence-events'
33'40-49''premeno''35-39''0-2''yes''3''right''left_low''yes''no-recurrence-events'
44'40-49''premeno''30-34''3-5''yes''2''left''right_up''no''recurrence-events'
\n", "
" ], "text/plain": [ " Unnamed: 0 age menopause tumor-size inv-nodes node-caps deg-malig \\\n", "0 0 '40-49' 'premeno' '15-19' '0-2' 'yes' '3' \n", "1 1 '50-59' 'ge40' '15-19' '0-2' 'no' '1' \n", "2 2 '50-59' 'ge40' '35-39' '0-2' 'no' '2' \n", "3 3 '40-49' 'premeno' '35-39' '0-2' 'yes' '3' \n", "4 4 '40-49' 'premeno' '30-34' '3-5' 'yes' '2' \n", "\n", " breast breast-quad irradiat Class \n", "0 'right' 'left_up' 'no' 'recurrence-events' \n", "1 'right' 'central' 'no' 'no-recurrence-events' \n", "2 'left' 'left_low' 'no' 'recurrence-events' \n", "3 'right' 'left_low' 'yes' 'no-recurrence-events' \n", "4 'left' 'right_up' 'no' 'recurrence-events' " ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Ejemplo de XGBoost\n", "\n", "# cargando los datos\n", "cancer2 = pd.read_csv('https://relopezbriega.github.io/downloads/datasets-uci-breast-cancer.csv')\n", "cancer2.head()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(286, 9)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Divido los datos en data y target.\n", "cancer_data = cancer2.values\n", "cancer2.data = cancer_data[:,0:9]\n", "cancer2.data = cancer2.data.astype(str)\n", "cancer2.target = cancer_data[:,9]\n", "cancer2.data.shape" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# Aplico el enconding para transformar los datos de entrada a valores\n", "# numericos utilizando OneHotEncoder\n", "encoded_data = None\n", "for i in range(0, cancer2.data.shape[1]):\n", " label_encoder = LabelEncoder()\n", " feature = label_encoder.fit_transform(cancer2.data[:,i])\n", " feature = feature.reshape(cancer2.data.shape[0], 1)\n", " onehot_encoder = OneHotEncoder(sparse=False)\n", " feature = onehot_encoder.fit_transform(feature)\n", " if encoded_data is None:\n", " encoded_data = feature\n", " else:\n", " encoded_data = np.concatenate((encoded_data, feature), axis=1)\n", " \n", "# Aplico LaberEncoder a los valores de la variable target.\n", "label_encoder = LabelEncoder()\n", "label_encoder = label_encoder.fit(cancer2.target)\n", "encoded_y = label_encoder.transform(cancer2.target)\n", "\n", "# Separando los datos en sets de entrenamiento y evaluación\n", "X_train, X_test, y_train, y_test = train_test_split(encoded_data, \n", " encoded_y, random_state=1)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,\n", " gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,\n", " min_child_weight=1, missing=None, n_estimators=100, nthread=-1,\n", " objective='binary:logistic', reg_alpha=0, reg_lambda=1,\n", " scale_pos_weight=1, seed=0, silent=True, subsample=1)\n", "Precisión xgboost train/test 0.879/0.694\n" ] } ], "source": [ "# Construyo el modelo y ajusto los datos.\n", "modelo = xgb.XGBClassifier()\n", "modelo.fit(X_train, y_train)\n", "\n", "# Realizo las predicciones\n", "y_pred = modelo.predict(X_train)\n", "predicciones = [round(value) for value in y_pred]\n", "# Evalúo las predicciones\n", "precision_train = accuracy_score(y_train, predicciones)\n", "\n", "# Repito el proceso con datos de evaluacion\n", "y_pred = modelo.predict(X_test)\n", "predicciones = [round(value) for value in y_pred]\n", "\n", "# Evalúo las predicciones\n", "precision_test = accuracy_score(y_test, predicciones)\n", "print(modelo)\n", "print('Precisión xgboost train/test {0:.3f}/{1:.3f}'\n", " .format(precision_train, precision_test))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En este ejemplo logramos una precisión de 88 % con los datos de entrenamiento y del 69 % con los datos de evaluación. \n", "\n", "Con esto termina este artículo. Espero les haya sido de utilidad y puedan explorar todo el poder predictivo de [XGBoost](http://xgboost.readthedocs.io/en/latest/). Asimismo, otra implementación de [Gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) que deberíamos tener en cuenta ya que también ha obtenido muy buenos resultados en términos de precisión y rendimiento es [LightGBM](https://github.com/Microsoft/LightGBM), que forma parte del [Distributed Machine Learning Toolkit](http://www.dmtk.io/) de Microsoft. \n", "\n", "Saludos!\n", "\n", "*Este post fue escrito por [Raúl e. López Briega](http://relopezbriega.github.io/) utilizando [Jupyter notebook](http://jupyter.org/). Pueden descargar este [notebook](https://github.com/relopezbriega/relopezbriega.github.io/blob/master/downloads/boosting.ipynb) o ver su version estática en [nbviewer](http://nbviewer.ipython.org/github/relopezbriega/relopezbriega.github.io/blob/master/downloads/boosting.ipynb).*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 2 }