Métodos de clasificación

Authors

Óscar Barquero Pérez (oscar.barquero@urjc.es), Carlos Figuera Pozuelo (carlos.figuera@urjc.es) y Rebeca Goya Esteban (rebeca.goyaesteban@urjc.es)

27 de marzo de 2016


Este obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional.

1 Introducción a la práctica

El objetivo de esta lab es que el alumno se familiarice con los conceptos básicos de python, en concreto de la utilización del módulo Sklearn para resolver problemas de clasificación.

Este lab está organizado de la siguiente forma: en la Sección 2, se realiza una introducción más formal al proceso de clasificación utilizando regresión logística. A continuación en la Sección 3, se compararán diferentes clasificadores utilizando sklearn para resolver un problema de clasificación real.

2 Clasificación usando Regresión Logística

2.1 Regresión Logística

El modelo de regresión linear discutido en la práctica anterior se propone bajo la asunción de que la variable respuesta es cuantitativa. En muchas situaciones, la variable respuesta es cualitativa o categóricas, por ejemplo, tipos de manzanas: golden, reineta, Royal Gala. El proceso de asociar cada manzana a un tipo concreto se conoce como clasificación. Por otro lado, habitualmente los métodos utilizados para clasificación estiman, en primer lugar, la probabilidad de pertenencia a cadad una de las posibles categorías, y utilizan estas probabilidades para decidir la clasificación final. Este procedimiento es parecido a la aproximación utilizada por los métodos de regresión lineal.

La regresión logística no trata de predecir el valor numérico de una variable cuantitativa, sino que intenta predecir la probabilidad de que una observación dada pertenezca a una cierta clase (categoría). Asumiendo que estamos, por razones de simplicidad y sin perdida de generalidad, en un caso binario únicamente con dos clases, la regresión logística da como resultado la siguiente probabilidad: $$ P\left[ \ y = 1\ |\ \mathbf{x}\ \right] $$

de tal forma que la probabilidad de pertenencia a la otra clase es: $$ P\left[\ y = 0\ |\ \mathbf{x}\ \right] = 1 - P\left[\ y = 1\ |\ \mathbf{x}\ \right] $$

Por lo que la salida de nuestro modelo de regresión logística debe estar comprendido en el lintervalo $[0,1]$.

La regresión logística asume que podemos utilizar una frontera lineal para separar el espacio de observaciones en dos regiones, una por cada clase. Supongámos que tenemos observaciones en un espacio bi-dimensional, esto es, las observaciones poseen dos características cada una $\mathbf{x} = [x_1 x_2]^T$. En la figura siguiente se pueden apreciar observaciones pertenecientes a dos clases, una representada con círculos negros y otra con círculos blancos.

En la misma figura se presentan tres fronteras lineales diferentes, $H_1, H_2$ y $H_3$. Para el caso de que las observaciones posean más dimensiones, hablamos de hyperplanos frontera, como se puede observar en la siguiente figura

Las fronteras lineales, en dos dimensiones, están definidas por la ecuación de una recta, por lo que puede ser expresada como:

$$h(\mathbf{x}) = w_0 + w_1 x_1 + w_2 x_2$$

que expresado en notación vectorial sería:

$$\mathbf{w}^T \mathbf{x} = 0$$

(Nota: el alumno avisado puede relacionar rápidamente esta ecuación con el modelo de regresión lineal presentado en la práctica previa.)

Suponga a hora el alumno que se tiene una nueva observación $\mathbf{x}^* = [x^*_1,x_2^*]$. Cuando evaluamos nuestra frontera lineal con esta nueva observación se pueden dar tres posibilidades:

  1. $\mathbf{x}^*$ se encuentra en la región de la clase positiva ($y = 1$). De esta forma $h(\mathbf{x}^*) > 0$. Cuanto mayor sea el valor del $|h(\mathbf{x}^*)|$, más lejos se encontrará el punto de la frontera.

  2. $\mathbf{x}^*$ se encuentra en la región de la clase negativa ($y = 0$). De esta forma $h(\mathbf{x}^*) < 0$. Cuanto mayor sea el valor del $|h(\mathbf{x}^*)|$, más lejos se encontrará el punto de la frontera.

  3. $\mathbf{x}^*$ se encuentra en exactamente en la frontera. De esta forma $h(\mathbf{x}^*) = 0$.

Sin embargo, tal y como hemos dicho, necesitamos convertir este valor de $h(\mathbf{x})$ en una probabilidad, es decir en un valor entre $[0,1]$, que además conserve las propiedades de $h(\mathbf{x})$. En concreto, si estamos estimando la probabilidad de pertenecer a la clase positiva ($y = 1$), que el valor sea más cercano a 1 cuanto mayor sea el valor de $h(\mathbf{x})$, que sea $0.5$ cuando $h(\mathbf{x}) = 0$ y que sea cercano a 0 cuanto menor sea $h(\mathbf{x})$. El objetivo actual es, pues, econtrar una transformación $\theta(z)$, tal que:

$$P\left[ \ y = 1\ |\ \mathbf{x}\ \right] = \theta\left(h(\mathbf{x})\right) = \theta\left(\mathbf{w}^T\mathbf{x}\right)$$

y que cumpla con todos los requisitos que hemos impuesto anteriormente. Existen varias funciones $\theta(z)$ que servirían a nuestro propósito. Una de ellas, la empeleada para la regresión logística es la que se conoce como función sigmoide o función logística, cuya expresión es:

$$\theta(z) = \frac{1}{1 + e^{-z}}$$

Ya debería estar un poco más claro por qué se le llama regresión logística a este modelo.

En el siguiente código veremos la representación de la función logística.


In [1]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
def logistic(z):
    return 1.0 / (1.0 + np.exp(-z))

z = np.arange(-7, 7, 0.1)
phi_z = logistic(z)

plt.plot(z, phi_z)
plt.axvline(0.0, color='k')
plt.ylim(-0.1, 1.1)
plt.xlabel('z')
plt.ylabel('$\phi (z)$')

# y axis ticks and gridline
plt.yticks([0.0, 0.5, 1.0])
ax = plt.gca()
ax.yaxis.grid(True)

plt.tight_layout()
plt.show()


Recordando que $z = \mathbf{w}^T\mathbf{x}$, podemos modificar la forma de la regresión logística cambiando los pesos $\mathbf{w}$, que recordamos son los que vamos a estimar minimizando alguna función de coste.

De esta forma ya tenemos un modelo de regresión logística de forma que la probabilidad de que una observación $\mathbf{x}$ pertenezca a la clase 1 viene dada por:

$$P\left[ \ y = 1\ |\ \mathbf{x}\ \right] = \frac{1}{1+e^{-\mathbf{w}^T\mathbf{x}}}$$

y, consecuentemente, diremos que $\mathbf{x} \in C_1$ sii $P\left[ \ y = 1\ |\ \mathbf{x}\ \right] > 0.5$.

Una vez que hemos definido nuesto modelo, falta saber cómo estimamos los coeficientes del modelo $\mathbf{w}$. En regresión lineal, utilizando el modelo de regresión lineal ($\mathbf{w}^T\mathbf{x}$) más un ruido Gaussiano de media cero y varianza $\sigma^2$, llegamos a una expresión cerrada para la estimación de los coeficientes del modelo:

$$\hat{\mathbf{w}} = \left(X^TX\right)^{-1}X^T\mathbf{y}$$

Por contra, en el caso de regresión logística no existe una expresión cerrada, vamos a tener que utilizar otros métodos. En la sección siguiente definimos la función objetivo que optimizaremos, en el caso de regresión logística será el likelihood, y describiremos uno de los métodos más utilizados para su maximización, el gradiente ascent.

2.2 Likelihood de Regresión Logística y Gradient Ascent

El método que utilizaremos para estimar los parámetros del modelo de regresión logística será el de maximizar la función de verosimilitud:

$$\hat{\mathbf{w}} = \underset{\mathbf{w}}{\operatorname{argmax}} L(\mathbf{x},\mathbf{w}) $$

donde $L(\mathbf{x},\mathbf{w})$ es la función de verosimilitud del modelo de regresión logística. Por lo tanto, el trabajo ahora reside en definir la función de verosimilitud. En este punto podemos pensar que nuestro modelo de regresión logística, para una obervación dada $x_n$, definido por: $P\left[ \ y_n = 1\ |\ \mathbf{x_n}\ \right] = \theta\left(h(\mathbf{x_n})\right) = \theta\left(\mathbf{w}^T\mathbf{x_n}\right)$, no es nada más que la probabilidad de que y = 1. Podemos por lo tanto pensar en un experimento en el que lanzamos una modena, y asignamos una probabilidad $p$ a que salga cara ($y = 1$), y por lo tanto, una probabilidad $1-p$ a que salga cruz ($y = 0$). La función de masa de probabilidad que define el lanzamiento de una modena es:

$$P(y|x) = p^y\left(1-p\right)^{(1-y)}$$

Este modelo probabilístico es completamente análogo al nuestro donde $p = P(y_n = 1|\mathbf{w},\mathbf{x_n})$ y $1-p = P(y_n = 0|\mathbf{w},\mathbf{x_n})$, de esta forma, la función de verosimilitud para una observación dada $(\mathbf{x}_n,\mathbf{y}_n)$ sera:

$$L(\mathbf{x_n},\mathbf{w})= P(y_n|\mathbf{w},\mathbf{x_n}) = P(y_n = 1|\mathbf{w},\mathbf{x_n})^{y_n}P(y_n = 0|\mathbf{w},\mathbf{x_n})^{(1-y_n)}$$

Donde:

$$P(y_n = 0|\mathbf{w},\mathbf{x_n}) = 1 - P(y_n = 1|\mathbf{w},\mathbf{x_n}) = 1 - \frac{1}{1+e^{-\mathbf{w}^T\mathbf{x}_n}} = \frac{e^{-\mathbf{w}^T\mathbf{x}_n}}{1+e^{-\mathbf{w}^T\mathbf{x}_n}}$$

Asumiendo que las observaciones se toman de forma independiente, podemos escribir la función de verosimilitud conjunta, para todas las observaciones como:

$$L\left(\mathbf{X},\mathbf{w}\right) = p(\mathbf{y}|\mathbf{w},\mathbf{X}) = \prod_{n = 1}^{N} P(y_n = 1|\mathbf{w},\mathbf{x_n})^{y_n}P(y_n = 0|\mathbf{w},\mathbf{x_n})^{(1-y_n)}$$

Podemo escribir de forma un poco más compacta esta expresión utilizando la siguiente nomenclatura $P(y_n = 1|\mathbf{w},\mathbf{x_n}) = \rho_n$, donde $\rho_n$ es la probabilidad de que la observación $n-ésima$ tenga como valor de la variables respuesta $y_n = 1$.

$$L\left(\mathbf{X},\mathbf{w}\right) = \prod_{n = 1}^{N} \rho_n^{y_n}(1-\rho_n)^{(1-y_n)}$$

Durante el curso hemos visto que en estos casos parece razonable utilizar la función logaritmo de $L$ ($\log L$), lo cual simplifica los calculos posteriores; de esta forma:

$$\log\left(L\left(\mathbf{X},\mathbf{w}\right)\right) = \sum_{n = 1}^{N} y_n\log(\rho_n) + (1-y_n)\log(1-\rho_n)$$

Nota: cabe destacar que la función objetivo $J(\mathbf{w}) = - \log\left(L\left(\mathbf{X},\mathbf{w}\right)\right)$ se conoce como Cross-Entropy Error, y que maximizar $\log\left(L\left(\mathbf{X},\mathbf{w}\right)\right)$ equivale a minimizar $J(\mathbf{w})$.

So far, so good. Ahora estamos en disposición de una función de verosimilitud (en realidad del logaritmo), que bien utilizada nos permitirá estimar los parámetros de mi modelo, esto es: vamos a utilizar como estimadores $\hat{\mathbf{w}}$, aquellos que maximizan la función $\log(L)$. Sin embargo hemos comenzado este lab indicando que no vamos a poder encontrar una expresión cerrada para $\hat{\mathbf{w}}$ como resultado de derivar la función $\log(L)$. Cómo podemos resolver este problema? Gradient Descent (Ascent) al rescate.

El descenso por gradiente es un método de optimización, que nos permite minimizar (o maximizar) funciones objetivo de forma iterativa; como puede ser $\log(L)$ o cross-entropy error. La premisa fundamental es que dado un punto, en este caso $\mathbf{w}$, del espacio de busqueda, es decir del dominio de la función optimizar, vamos actualizar (update) ese punto, en el siguiente paso, en la dirección opuesta al gradiente de la función a optimizar. Esto sería para minimizar, si queremos maximizar nos moveremos en la dirección del gradiente. Vamos a generar intuición analizando un caso muy sencillo, en el que queremos minimizar una función objetivo $J(w)$, donde el parámetro $w$ es un escalar. Si somos afortunados, la función $J(w)$ será convexa.

Vamos a ilustrar el método del gradient descent en esta función.

  1. El primer paso es seleccionar un punto, $w^{(0)}$, en el espacio de búsqueda (dominio de la función $J(w)$).

  2. A continuación, calculamos el gradiente de la función objetivo para minimizar en el punto $w^{(0)}$: $\nabla J\left(w^{(0)}\right)$. El grandiente tiene la virtud de indicar la dirección de mayor crecimiento de la función $J(w)$, de forma que si el objetivo es "viajar" en dirección al mínimo deberemos dirigirnos en la dirección "menos el gradiente".

  3. Actualizamos el valor del punto $w^{(0)}$, moviéndonos en la dirección de máximo descenso: $$w^{(1)} = w^{(0)} - \eta \nabla J\left(w^{(0)}\right) $$

De forma genérica, si estamos en el paso $\tau$, los pesos en el paso $\tau + 1$ se actualizan de acuerdo con la regla: $$w^{(\tau + 1)} = w^{(\tau)} - \eta \nabla J\left(w^{(\tau)}\right) $$ $\eta$ nos indica el cuanto variamos $w$ en cada iteración. Si $\eta$ es muy pequeño, en cada iteración la variación de $w$ será muy pequeña, por lo que el algoritmo puede tardar mucho en converger, y en ocasiones puede no llegar nunca al mínimo. Mientras que si $\eta$ es muy grande podemos tener problemas, de forma que en vez de ir hacia el mínimo nos movamos en dirección contraria.

  1. Iteramos el proceso anterior hasta alcanzar el mínimo de la función.

En la siguiente figura vemos un ejemplo en el que el espacio de búsqueda es bidimensional, $\mathbf{w} = [w_0,w_1]$

Bien munidos, como estamos, con una función de verosimilitud que maximizar y un procedimiento para maximizar funciones, sólo puedrá atribuirse a nuestra perece si no somos capaces de obtener un estimador para los modelos de nuestro parámetro. Para completar todos nuestros preparativos necesitamos calcular el gradiente de nuestra función objetivo. El alumno puede demostrar que:

$$\frac{\partial L(\mathbf{w})}{\partial w_i} = \sum_{n = 1}^{N} (y_n - \rho_n)x_{n_i} $$

Si escribimos esta expresión en forma matricial:

$$\nabla L(\mathbf{w}) = X^T(Y - T)$$

NOTA esta ecuación no incluye el término de bias, para este término, la expresión de la parcial es:

$$\frac{\nabla L(\mathbf{w})}{\partial w_0} = \sum_{n = 1}^{N}(y_n-\rho_n)x_{n_{0}} = \sum_{n = 1}^{N}(y_n-\rho_n)$$

3. Comparación de métodos de clasificación con sklearn

3.1 Base de datos de clasificación

En esta práctica vamos a estudiar el problema de intentar predecir el llamado Apgar index. Este índice se utiliza en la práctica clínica para evaluar el estado general del recién nacido después del parto. En este índice es habitual hacer la siguiente división, si Apgar > 6 se considera una situación normal, si Apgar $\le$ 6 situación anormal o sospechosa. El problema que se nos plantea es el siguiente: somos capaces de predecir el índice Apgar utilizando diferentes parámetros de la señal de fetal heart rate (FHR). El FHR es una señal que se adquiere de forma rutinaria durante el trabajo de parto, por lo que sería muy útil si fuesemos capaces de, simplemente analizando esa señal, predecir cuál va a ser el índice Apgar, esto permitiría tomar medidas rápidas, o incluso adelantar el parto.

Nosotros vamos a abordar este problema desde el punto de vista de clasificación, de forma que los recien nacidos pueden pertenecer a una de las dos siguientes clases: Normal, $y = 1$, si Apgar > 7; y anormal $y = 0$, si Apgar $\le$ 7.

Las variables explicativas, características o features serán los parámetros estimados a partir del FHR, en concreto serán:

  • Duration: Duration in minutes of the FHR tracing
  • Baseline: Basal value of the FHR in beat/min
  • Acelnum: Number of FHR accelerations
  • Acelrate: Number of FHR accelerations per minute
  • ASTV: Percentage of the total duration with abnormal short term variability
  • MSTV: Average duration of the time intervals with abnormal short term variability
  • ALTV: Percentage of the total duration with abnormal long term variability
  • MLTV: Average duration of the time intervals with abnormal long term variability

La fuente de esta base de datos es Dr. Diogo Ayres-de-Campos, Fac. Med. Univ. Porto

La primera parte de esta práctica consistirá en leer los datos utilizando Pandas y generar nuestra variable respuesta binaria, $y$.

Ejercicio 1. Lectura de la base de datos y generación de la variable clasificación

En este ejercicio se pide que el alumno lea la base de datos utilizando pandas y genera una variable, $y$, que se utilizará como etiqueta, o pertenencia a una clase.


In [40]:
#read data using pandas
import pandas as pd
import numpy as np

fhr = pd.read_csv("FHR-Apgar.csv", delimiter=';')
fhr


Out[40]:
HOSPITAL NAME Apgar 1 Apgar 5 Duration Baseline Acelnum Acelrate ASTV MSTV ALTV MLTV
0 HUC PMGVG 9 10 44 127 3 0.07 72 0.4 16 9.6
1 HUC MCSR 8 10 55 126 23 0.42 59 1.1 0 10.8
2 HUC SMCM 9 10 46 135 9 0.20 67 0.7 1 10.6
3 HUC MFMBN 9 10 54 131 25 0.46 66 0.9 0 8.7
4 HUC EMSO 9 10 47 142 12 0.26 61 0.8 10 7.6
5 HUC CMMRJ 9 10 47 130 11 0.23 68 0.8 16 8.6
6 HUC CMRAM 9 10 39 131 6 0.15 61 0.9 0 12.6
7 HUC PCJSD 9 10 50 128 10 0.20 59 3.1 3 7.1
8 HUC LCHS 9 10 42 130 18 0.43 57 0.9 0 7.9
9 HUC CISS 9 10 51 117 26 0.51 54 1.4 0 13.7
10 HUC SMMC 8 10 51 134 20 0.39 65 1.2 5 8.6
11 HUC TMJSA 9 10 41 141 8 0.20 63 0.8 1 8.9
12 HUC MLRDF 9 10 60 124 47 0.78 57 1.9 0 1.9
13 HUC CMCFR 6 8 47 140 0 0.00 70 0.9 25 7.1
14 HUC MLCBC 8 10 60 131 22 0.37 60 2.4 0 6.8
15 HUC SFCR 9 10 38 130 13 0.34 57 1.0 5 7.4
16 HUC MFMM 8 10 47 124 5 0.11 67 1.5 1 9.0
17 HUC ETCF 9 10 42 142 2 0.05 67 1.0 0 10.7
18 HUC MGLR 9 10 60 115 19 0.32 59 0.9 2 10.1
19 HUC MGCRL 9 10 56 145 6 0.11 67 0.8 4 10.9
20 HUC MDAMS 9 10 60 128 21 0.35 59 1.3 3 7.7
21 HUC CISRV 5 9 42 142 12 0.29 65 1.4 0 9.6
22 HUC MAFBMC 10 10 53 132 23 0.43 57 1.0 0 7.8
23 HUC GJAC 9 10 50 138 6 0.12 73 0.7 5 8.6
24 HUC IMFBP 9 10 40 150 1 0.03 71 0.6 11 8.4
25 HUC PGF 9 10 53 128 22 0.42 59 2.2 0 7.8
26 HGSA RMSNP 10 10 60 136 8 0.13 47 0.7 7 11.4
27 HGSA IRF 10 10 40 131 7 0.18 33 1.1 6 12.4
28 HGSA MAFFC 10 10 41 125 23 0.56 38 1.3 2 5.7
29 HGSA GARA 6 8 40 124 9 0.23 40 1.0 1 12.3
... ... ... ... ... ... ... ... ... ... ... ... ...
210 HSJ CSA 7 9 54 126 17 0.31 61 1.5 0 8.7
211 HSJ MCT 9 10 43 122 26 0.60 54 2.1 0 11.3
212 HSJ MMV 7 8 54 124 17 0.31 65 0.9 0 9.3
213 HSJ MLCR 9 10 60 133 7 0.12 68 0.6 8 8.1
214 HSJ CMT 9 10 41 129 11 0.27 63 1.1 2 7.6
215 HSJ MCSN 6 8 51 138 4 0.08 66 11.9 7 20.5
216 HSJ FGO 6 9 60 156 0 0.00 75 0.7 62 5.5
217 HSJ MHM 9 10 40 130 1 0.03 72 2.2 6 4.2
218 HSJ MCAB 4 6 41 149 0 0.00 67 0.8 31 6.9
219 HSJ MCO 4 8 39 115 0 0.00 72 0.5 95 2.7
220 HSJ PCC 3 6 43 171 0 0.00 77 0.5 54 5.1
221 HSJ SCB 5 9 40 141 1 0.03 78 0.6 6 11.5
222 HSJ MFC 6 9 50 131 6 0.12 61 2.1 2 20.5
223 HSJ MMB 2 7 43 135 0 0.00 85 0.6 75 5.0
224 HSJ AACST 4 7 52 125 0 0.00 86 0.4 38 8.0
225 HSJ JMP 1 5 40 123 0 0.00 88 0.3 71 5.7
226 HSJ AMRC 5 8 60 141 0 0.00 75 1.1 12 14.3
227 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
228 NaN NaN 118 NaN NaN NaN NaN NaN NaN NaN NaN NaN
229 NaN NaN 109 NaN NaN NaN NaN NaN NaN NaN NaN NaN
230 NaN 1 1 0 NaN 1 0 NaN NaN NaN NaN NaN
231 NaN 2 2 0 NaN 3 0 NaN NaN NaN NaN NaN
232 NaN 3 1 0 NaN 4 0 NaN NaN NaN NaN NaN
233 NaN 4 4 1 NaN 8 1 NaN NaN NaN NaN NaN
234 NaN 5 7 1 NaN 15 2 NaN NaN NaN NaN NaN
235 NaN 6 16 3 NaN 31 5 NaN NaN NaN NaN NaN
236 NaN 7 30 6 NaN 61 11 NaN NaN NaN NaN NaN
237 NaN 8 45 16 NaN 106 27 NaN NaN NaN NaN NaN
238 NaN 9 102 35 NaN 208 62 NaN NaN NaN NaN NaN
239 NaN 10 16 162 NaN 224 224 NaN NaN NaN NaN NaN

240 rows × 12 columns

Ejercicio 2. Eliminiación de casos NaN

En esta base de datos aparecen una serie de datos etiquetados como NaN, estos son datos no válidos, por lo que es necesario tratar estos datos. Una forma de hacelro, si disponemos de suficientes datos es eliminar los casos con NaN.


In [56]:
# Hay que valorar si merece la pena eliminar toda una fila que contenga algún NaN o directamente todas las
# que contengan todos los NaN, aunque puede ser que nos interesen algunos valores de las columnas
fhr = fhr.dropna()
#Esta instruccion genera un array donde cualquier valor de Apgar 1 > 7, lo ponemos a 1, en otro caso a 0
fhr['Y'] = np.where(fhr['Apgar 1'] > 7, 1, 0)
fhr


Out[56]:
HOSPITAL NAME Apgar 1 Apgar 5 Duration Baseline Acelnum Acelrate ASTV MSTV ALTV MLTV Y
0 HUC PMGVG 9 10 44 127 3 0.07 72 0.4 16 9.6 1
1 HUC MCSR 8 10 55 126 23 0.42 59 1.1 0 10.8 1
2 HUC SMCM 9 10 46 135 9 0.20 67 0.7 1 10.6 1
3 HUC MFMBN 9 10 54 131 25 0.46 66 0.9 0 8.7 1
4 HUC EMSO 9 10 47 142 12 0.26 61 0.8 10 7.6 1
5 HUC CMMRJ 9 10 47 130 11 0.23 68 0.8 16 8.6 1
6 HUC CMRAM 9 10 39 131 6 0.15 61 0.9 0 12.6 1
7 HUC PCJSD 9 10 50 128 10 0.20 59 3.1 3 7.1 1
8 HUC LCHS 9 10 42 130 18 0.43 57 0.9 0 7.9 1
9 HUC CISS 9 10 51 117 26 0.51 54 1.4 0 13.7 1
10 HUC SMMC 8 10 51 134 20 0.39 65 1.2 5 8.6 1
11 HUC TMJSA 9 10 41 141 8 0.20 63 0.8 1 8.9 1
12 HUC MLRDF 9 10 60 124 47 0.78 57 1.9 0 1.9 1
13 HUC CMCFR 6 8 47 140 0 0.00 70 0.9 25 7.1 0
14 HUC MLCBC 8 10 60 131 22 0.37 60 2.4 0 6.8 1
15 HUC SFCR 9 10 38 130 13 0.34 57 1.0 5 7.4 1
16 HUC MFMM 8 10 47 124 5 0.11 67 1.5 1 9.0 1
17 HUC ETCF 9 10 42 142 2 0.05 67 1.0 0 10.7 1
18 HUC MGLR 9 10 60 115 19 0.32 59 0.9 2 10.1 1
19 HUC MGCRL 9 10 56 145 6 0.11 67 0.8 4 10.9 1
20 HUC MDAMS 9 10 60 128 21 0.35 59 1.3 3 7.7 1
21 HUC CISRV 5 9 42 142 12 0.29 65 1.4 0 9.6 0
22 HUC MAFBMC 10 10 53 132 23 0.43 57 1.0 0 7.8 1
23 HUC GJAC 9 10 50 138 6 0.12 73 0.7 5 8.6 1
24 HUC IMFBP 9 10 40 150 1 0.03 71 0.6 11 8.4 1
25 HUC PGF 9 10 53 128 22 0.42 59 2.2 0 7.8 1
26 HGSA RMSNP 10 10 60 136 8 0.13 47 0.7 7 11.4 1
27 HGSA IRF 10 10 40 131 7 0.18 33 1.1 6 12.4 1
28 HGSA MAFFC 10 10 41 125 23 0.56 38 1.3 2 5.7 1
29 HGSA GARA 6 8 40 124 9 0.23 40 1.0 1 12.3 0
... ... ... ... ... ... ... ... ... ... ... ... ... ...
196 HSJ SAS 6 9 40 134 8 0.20 63 0.7 18 6.8 0
197 HSJ MEG 9 10 58 130 29 0.50 52 3.4 0 2.3 1
199 HSJ PMM 8 10 37 110 14 0.38 48 1.8 0 8.5 1
200 HSJ MHD 9 10 32 134 22 0.69 59 0.7 0 5.1 1
201 HSJ LFT 8 9 57 131 10 0.18 64 1.5 0 10.6 1
202 HSJ MBC 9 10 37 133 18 0.49 58 6.7 4 19.3 1
203 HSJ MTA 9 10 41 128 5 0.12 70 0.5 4 9.4 1
204 HSJ MFG 9 10 49 136 19 0.39 60 2.4 0 5.0 1
205 HSJ LMS 9 10 30 121 9 0.30 61 1.6 0 7.0 1
206 HSJ MHG 5 8 45 151 0 0.00 78 0.6 46 7.4 0
207 HSJ ECG 4 6 43 131 0 0.00 76 0.4 33 7.7 0
208 HSJ MAR 9 10 42 140 6 0.14 70 0.6 25 9.6 1
209 HSJ FMM 9 10 50 125 22 0.44 62 1.4 0 9.2 1
210 HSJ CSA 7 9 54 126 17 0.31 61 1.5 0 8.7 0
211 HSJ MCT 9 10 43 122 26 0.60 54 2.1 0 11.3 1
212 HSJ MMV 7 8 54 124 17 0.31 65 0.9 0 9.3 0
213 HSJ MLCR 9 10 60 133 7 0.12 68 0.6 8 8.1 1
214 HSJ CMT 9 10 41 129 11 0.27 63 1.1 2 7.6 1
215 HSJ MCSN 6 8 51 138 4 0.08 66 11.9 7 20.5 0
216 HSJ FGO 6 9 60 156 0 0.00 75 0.7 62 5.5 0
217 HSJ MHM 9 10 40 130 1 0.03 72 2.2 6 4.2 1
218 HSJ MCAB 4 6 41 149 0 0.00 67 0.8 31 6.9 0
219 HSJ MCO 4 8 39 115 0 0.00 72 0.5 95 2.7 0
220 HSJ PCC 3 6 43 171 0 0.00 77 0.5 54 5.1 0
221 HSJ SCB 5 9 40 141 1 0.03 78 0.6 6 11.5 0
222 HSJ MFC 6 9 50 131 6 0.12 61 2.1 2 20.5 0
223 HSJ MMB 2 7 43 135 0 0.00 85 0.6 75 5.0 0
224 HSJ AACST 4 7 52 125 0 0.00 86 0.4 38 8.0 0
225 HSJ JMP 1 5 40 123 0 0.00 88 0.3 71 5.7 0
226 HSJ AMRC 5 8 60 141 0 0.00 75 1.1 12 14.3 0

223 rows × 13 columns

 Ejercicio 3

El siguiente ejercicio consistirá en obtener la matrix de datos $X$ y el vector de etiquetas $y$. Cabe recordar en este punto que sería conveniente realizar un análisis exploratorio de los datos, tal y como se describión en el Lab 1.


In [57]:
%matplotlib inline


x_var_name = list(fhr)[4:-1] #Matriz X de caracteristicas
y_var_name = list(fhr)[-1] #Vector Y de respuesta
# X --> |    |
#       | ML |
# Y --> |    |

X = fhr[x_var_name]
Y = fhr[y_var_name]

Ejercicio 4

Una vez tenemos claros nuestros datos el siguiente paso, siempre, siempre, debe ser separar nuestros datos en dos conjunto: train y test.

Eventualmente, el conjunto train, resultante después del split anterior, puede ser subdividido para la selección de hyperparameters, pero la división incial nos va a servir para evaluar las prestaciones de nuestro modelo final (una vez seleccionados los hyperparameters). Test set

Este tipo de particiones se pueden realizar sencillamente con sklearn Cross-validation: evaluation estimator performance


In [93]:
from sklearn.model_selection import train_test_split

#test_size es el porcentaje de datos que utilizamos para test y otros para datos
#random_state son numeros pseudo aleatorios para que se haga el split en distintos datos
#Esto devuelve 2 matrices X y 2 vectores Y
#Si modificamos el test_size demasiado (muy bajo), estamos siendo muy optimistas para nuestro modelo
#El objetivo es ajustar (20-30% de test) lo mejor posible nuestro modelo
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=0)

print X_train.shape
print X_test.shape

#(156, 8)
#(67, 8)


(156, 8)
(67, 8)

Ejercicio 5

Pareciera que estamos listos para aplicar nuestros algoritmos de clasificación .... Uff, todavía falta un pequeño detalle, que en ocasiones marca la diferencia. Tenemos la siguiente pregunta:

  • **¿existe alguna diferencia, de al menos un orden de magnitud, entre el valor de las diferentes variables?

Si la respuesta a la anterior pregunta es , entonces se recomienda normalizar (estandarizar) nuestros datos Normalize). Se puede probar la diferencia utilizando normalización y sin utilizarla.

Esta fase se puede realizar de forma sencilla con sklearn: Preprocessing data


In [92]:
from sklearn.linear_model import LogisticRegression

#cremos un modelo vacio
log_reg = LogisticRegression() #Habra que modificar sus parametros

#Ajustamos el modelo
log_reg.fit(X_train, y_train)
print log_reg


#Hacemos la prediccion
y_pred = log_reg.predict(X_test)

#Vemos la accuracy
from sklearn.metrics import accuracy_score
acc = accuracy_score(y_test, y_pred)
print "Soy el puto amo de ML, con un % de: ", acc, '%'


LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False)
Soy el puto amo de ML, con un % de:  0.746268656716 %

In [ ]:

Ejercicio 6

Por fin llegó. Ahora estamos en condiciones de entrenar nuestro primer algoritmo de clasificación. Comenzaremos por uno de los más sencillos: Regresión Logística. Este modelo nos servirá para aprender la API que utiliza sklearn para todos los algoritmos de aprendizaje máquina Logistic Regression. La diferencia entre ellos estriba en el hecho de los parámetros que tiene cada algoritmo. Algunos dirán que en la selección de esos hyperparameters radica una de las partes artísticas del machine learning. A mi me gusta más hablar de la parte artesana, creo que somos más artesanos que artistas.

En lo que sigue: aprenderemos a ajustar un modelo de clasificación, que será el de regresión logística y a evaluar las prestaciones. A continuación compararemos con diferentes modelos, en los que tendremos que seleccionar diferentes hyperparemeters.

Manos a la obra


In [ ]:

Ejercicio 7

Vamos a probar con otro tipo de clasificadores. Por ejemplo: naïve bayes, [Support Vector Machines] (http://scikit-learn.org/stable/modules/svm.html#svm-classification), Random Forest


In [97]:
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV

#grid search, todas las posibles combinaciones y hacemos cross-validation, rejilla logaritmica desde 10⁻⁴ hasta 10¹.
# con 25 valores para cada uno
#Es otro tipo de modelado
tuned_parameters = {'gamma' : np.logspace(-4, 1, 25), 'C' : np.logspace(1, 4, 25)}

svm = SVC(kernel = 'rbf')
grid = GridSearchCV(svm, param_grid = tuned_parameters, cv = 10, n_jobs = -1)

grid.fit(X_train, y_train)

y_pred_svm = grid.predict(X_test)

acc2 = accuracy_score(y_test, y_pred_svm)

print "SVM algorythm: ", acc2, '%'


SVM algorythm:  0.776119402985 %