{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# AA-UTE 2024" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Práctica 1 - Introducción a Scikit-learn\n", "\n", "### Objetivos:\n", "En esta práctica se introduce el módulo `scikit-learn` o `\"sklearn\"` que facilita el acceso a diversas herramientas estándar del área de aprendizaje automático, como pueden ser: \n", "\n", "- Entrenamiento de clasificadores \n", "- Separación de datos\n", "- Cálculo de métricas de desempeño de los modelos \n", "- Pre-procesamientos y transformaciones\n", "- Pipelines \n", "\n", "En este notebook se visitarán las primeras tres y se espera que logren identificar partes escenciales en modelos de clasificación que brinda esta herramienta.\n", "\n", "Por información adicional puede consultar la [página oficial](https://scikit-learn.org/stable/getting_started.html) y [otros tutoriales](https://scikit-learn.org/stable/tutorial/index.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Importar bibliotecas clásicas\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parte 1 - Cargar Dataset Iris" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1.1 Levantar el conjunto usando la [librería sklearn](https://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html).\n", "\n", "_Nota_: Será de utilidad pasarle el argumento `as_frame` con valor en **True**." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.datasets import load_iris\n", "iris = # COMPLETAR: cargar dataset iris mediante sklearn" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1.2 Una vez cargada la base (en formato de `diccionario python`), imprimir sus _metadatos/atributos_ usando el método `.keys()`. ¿Qué es cada elemento?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# COMPLETAR: imprimir keys" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# COMPLETAR: imprimir la descripción" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1.3 Extraer el dataframe de los datos levantados" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df_iris = iris.frame" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df_iris.info()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df_iris.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1.4 Visualizar el espacio de características de **_dos en dos_**. Se recomienda usar la función [`pairplot`](https://python-charts.com/correlation/pairs-plot-seaborn/) de la librería `seaborn`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import seaborn as sns\n", "\n", "df_features = df_iris.drop('target',axis=1) # quedarse con las características \n", "\n", "df_targets = df_iris['target'] # quedarse con los targets\n", "\n", "##############################\n", "### COMPLETAR con pairplot ###\n", "##############################" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Analizar las gráficas y elegir dos de las características de los datos para el entrenamiento de las siguientes partes.\n", "\n", "¿Por qué seleccionó esas dos?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Características a elegir\n", "chosen_features = # COMPLETAR\n", "\n", "# Dejar datos en arrays numpy\n", "X = df_features[chosen_features].to_numpy()\n", "y = df_targets.to_numpy()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1.5 Dividir los datos en conjunto de entrenamiento (**_train_**) y de prueba (**_test_**) usando `train_test_split` de sklearn. Usar un 80% para train y un 20% para test." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import train_test_split\n", "\n", "# Separar en train y test \n", "X_train, X_test, y_train, y_test = # COMPLETAR" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Verificar porcentajes de conjuntos entrenamiento/prueba\n", "\n", "train_percent = 100 * len(X_train) / len(iris.data)\n", "print('Porcentaje de datos en conjunto de entrenamiento: ', f'{train_percent:.1f} %')\n", "\n", "test_percent = 100 * len(X_test) / len(iris.data)\n", "print('Porcentaje de datos en conjunto de prueba: ', f'{test_percent:.1f} %')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parte 2 - Algoritmo de los K vecinos más cercanos (kNN)\n", "[Ver documentación de sklearn](https://scikit-learn.org/1.4/modules/generated/sklearn.neighbors.KNeighborsClassifier.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 Inicializar clasificador y ajuste a datos (método _fit_)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.neighbors import KNeighborsClassifier\n", "from sklearn.tree import DecisionTreeClassifier # (para más adelante)\n", "\n", "# Inicializar clasificador kNN \n", "clf = # COMPLETAR\n", "\n", "# Entrenar el clasificador con los datos train usando el método \"fit\"\n", "clf. # COMPLETAR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 Métodos de predicción (_predict_) y de puntaje (_score_)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Elegir dato y predecir. Variar el dato a predecir cambiando \n", "# el valor de la variable \"indice\" (por ejemplo: 0, 5, 7)\n", "indice = 7\n", "dato = X_test[ indice ]\n", "etiqueta = y_test[ indice ]\n", "\n", "# Predecir con método predict del clasificador\n", "dato_pred = clf. # COMPLETAR: predecir la clase de la variable \"dato\"\n", "\n", "print(f'Predicción dato: {iris.target_names[dato_pred]}')\n", "print(f'Ground-truth dato: {iris.target_names[etiqueta]}')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Predecir en todo el conjunto de test\n", "y_pred = clf. # COMPLETAR\n", "\n", "# Calcular la tasa de aciertos (accuracy):\n", "# 1 - A mano usando las predicciones y np.count_nonzero()\n", "# 2 - Usando el método \"score\" del clasificador\n", "\n", "manual_acc = # COMPLETAR: calcular tasa de aciertos con numpy\n", "\n", "clf_acc = clf. # COMPLETAR: usar método score del clasificador\n", "\n", "# Verificar que den lo mismo\n", "\n", "print('Cuanta manual:',manual_acc)\n", "print('Score del clasificador:',clf_acc)\n", "print('-----------------------------------')\n", "print('Dan lo mismo?', manual_acc == clf_acc)\n", "print('-----------------------------------')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**EXPERIMENTAR**\n", "\n", "Investigar qué método del clasificador devuelve las probabilidades de pertenencia de cada clase. ¿Coinciden con las predicciones?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y_test_proba = clf. # COMPLETAR\n", "\n", "y_test_max_proba = np.argmax(y_test_proba,axis=1) # Quedarse con la predicción más probable\n", "\n", "# Comparar con la salida del .predict()\n", "print('¿Son iguales las predicciones?',np.all(y_test_max_proba == y_pred))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parte 3 - Arbol de decisión\n", "\n", "[Ver documentación de sklearn](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Repetir el procedimiento anterior pero usando el clasificador de Arbol de Decisión.\n", "\n", "Para ello sustituir `clf = KNeighborsClassifier()` por `clf = DecisionTreeClassifier()` de la parte 2.1, o volver a crear las celdas anteriores con dicho cambio." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#####################################\n", "### COMPLETAR: mismo que parte 2) ###\n", "### pero con árbol de decisión ###\n", "#####################################" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parte 4 - Matriz de confusión" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4.1 Volver a entrenar cada clasificador por separado. Obtener sus predicciones y score en test" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "clf_knn = # COMPLETAR: inicializar kNN\n", "# COMPLETAR training kNN\n", "y_pred_knn = # COMPLETAR prediccion\n", "clf_knn_test_score = # COMPLETAR score\n", "\n", "clf_tree = # COMPLETAR: inicializar árbol\n", "# COMPLETAR training árbol de decisión\n", "y_pred_tree = # COMPLETAR prediccion\n", "clf_tree_test_score = # COMPLETAR score" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4.2 Para cada modelo entrenado calcular su [**matriz de confusión**](https://es.wikipedia.org/wiki/Matriz_de_confusi%C3%B3n) en los datos de test. Qué significan las filas y columnas de la matriz? Mostrar los valores de la matriz en absoluto y normalizadas por cada fila.\n", "\n", "Calcularla con [`confusion_matrix`](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html) de sklearn." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.metrics import confusion_matrix\n", "\n", "normalizar_matriz = None # None o \"true\"\n", "\n", "mat_conf_knn = # COMPLETAR\n", "mat_conf_tree = # COMPLETAR\n", "\n", "print('-----------------------')\n", "print('CM de kNN:')\n", "print( mat_conf_knn )\n", "print('-----------------------')\n", "print('CM de árbol de decisión:')\n", "print( mat_conf_tree )\n", "print('-----------------------')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Mejor visualización usando los clasificadores, datos y etiquetas\n", "\n", "from sklearn.metrics import ConfusionMatrixDisplay\n", "\n", "normalizar_matriz = 'true' # None o \"true\"\n", "\n", "# Función para desplegar \n", "def display_conf_mat_classifiers(classifiers, classifiers_name, \n", " X, y, ax, classes_labels, \n", " title=None):\n", " #\n", " for i, (clf_name, classifier) in enumerate(zip(classifiers_name,classifiers)):\n", " disp = ConfusionMatrixDisplay.from_estimator(\n", " classifier,\n", " X,\n", " y,\n", " display_labels=classes_labels,\n", " cmap=plt.cm.Blues,\n", " normalize=normalizar_matriz,\n", " ax=ax[i]\n", " )\n", " disp.ax_.set_title(clf_name)\n", " plt.tight_layout()\n", "\n", " plt.show()\n", "\n", "fig, ax = plt.subplots(1,2, figsize=(8,3))\n", "\n", "display_conf_mat_classifiers([clf_knn, clf_tree],[f'kNN (acc={clf_knn_test_score:.2f})',f'DecisionTree (acc={clf_tree_test_score:.2f})'], \n", " X_test, y_test, ax, classes_labels=iris.target_names, \n", " title=None)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**RESPONDER**\n", "¿Cómo se puede obtener el accuracy a partir de la matriz de confusión? Pensar para la matriz sin normalizar.\n", "\n", "_Respuesta_:\n", "\n", "Puede verificarlo con el código de abajo para las clasificaciones de kNN." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "acc_confmat_knn = # COMPLETAR\n", "\n", "print('¿Coincide con accuracy?', acc_confmat_knn==clf_knn_test_score)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**EXPERIMENTAR**\n", "Ver el score/accuracy de cada modelo, para los conjuntos de train y test, esta vez variando los hiperparámetros:\n", "- _KNeighborsClassifier_ : `n_neighbors` $=\\{1,4,10\\}$\n", "- _DecisionTreeClassifier_ : `max_depth` $=\\{1,10,200\\}$\n", "\n", "Para ello:\n", "1. Inicializar el clasificador con el hiperparámetro a probar\n", "1. Entrenar con los datos de train\n", "1. Obtener el accuracy en train\n", "1. Obtener el accuracy en test\n", "\n", "¿Para cuáles de estos valores hay un mejor desempeño en el conjunto de train que el de test?\n", "\n", "¿Y viceversa?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Variar hiperparámetro \"n_neighbors\" del KNN\n", "n_neighbors = 4\n", "clf_knn = # COMPLERTAR inicializar knn con n_neighbors\n", "# COMPLERTAR training\n", "y_score_train_knn = # COMPLERTAR score en train\n", "y_score_test_knn = # COMPLERTAR score en test\n", "\n", "\n", "# Variar hiperparámetro \"max_depth\" del arbol de decision\n", "max_depth = 1\n", "clf_tree = # COMPLERTAR inicializar arbol con max_depth\n", "# COMPLERTAR training \n", "y_score_train_tree = # COMPLERTAR score en train \n", "y_score_test_tree = # COMPLERTAR score en test\n", "\n", "\n", "print('--------------- kNN ----------------')\n", "print(f'Train: {y_score_train_knn:.3f}\\t||\\tTest: {y_score_test_knn:.3f}')\n", "\n", "print('\\n-------- Arbol de decisión ---------')\n", "print(f'Train: {y_score_train_tree:.3f}\\t||\\tTest: {y_score_test_tree:.3f}')\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.13" } }, "nbformat": 4, "nbformat_minor": 2 }