Aplicación de un algoritmo de Aprendizaje de Máquina¶

Cuando llevamos a la práctica el aprendizaje de máquina, generalmente seguimos una metodología bien establecidad que nos dice qué pasos seguir para llevar a cabo la aplicación de un algoritmo de aprendizaje a un problema específico.

Si bien cada algorimto y cada problema presenta particularidades que deben ser exploradas, existe una metodología general que buscaremos abordar aquí. Esta metodología consiste, básicamente, en los siguientes pasos:

  • Preparación de los datos: obtener representaciones vectoriales de nuestro problema. Saber si contamos o no con supervisación, pues esto determinará la familia de algoritmos que podremos aplicar.
  • Obtención de datasets: Se separarán los datos en dos subconjuntos:
    • Conjunto de entrenamiento: Sirve para estimar el modelo de aprendizaje. Generalmente es el 70\% de los datos totales.
    • Conjunto de evaluación: Nos servirá para evaluar el desempeño final del modelo de aprendizaje. Generalmente es el 30 \% de los datos totales.
    • Muchas veces, se tomará también un conjunto de validación. Generalmente se toma el 10 \% de los datos totales (en este caso, la evaluación será el 20\%).
  • Selección de un algoritmo de aprendizaje: Esto dependerá de varios factores, como el problema o tarea que nos enfrentamos (si es clasificacion, aprendizaje reforzado), el tipo de datos (supervisados, no supervisados), la cantidad de datos, etc.
  • Entrenamiento: En el entrenamiento, la máquina "aprenderá" un modelo de aprendizaje. En este paso, será importante ajustar hiperparámetros del modelo (generalmente con un conjunto de validación).
  • Evaluación: Finalmente, se determinará que tan bien generaliza lo aprendido la máquina de aprendizaje. Para esto, se tomará una métrica de evaluación que cuantitativamente muestre la capacidad de nuestro modelo de aprendizaje.

Preparación de los datos¶

Los algoritmos de aprendizaje de máquina trabajan con datos representados por vectores en $\mathbb{R}^d$. Estos datos se obtienen a partir de un generador que determina la distribución original de éstos.

Aquí tomaremos un ejemplo muy sencillo de clasificación: buscaremos una máquina de aprendizaje que pueda decidir si un objeto (representado por un vector de rasgos) es o no un gato.

Utilizaremos las siguientes paqueterías:

In [1]:
import pandas as pd
import numpy as np

Cargaremos un archivo de datos. Este archivo es originalmente un csv en que cada columna representa un rasgo de nuestros datos.

In [2]:
data = pd.read_csv('cat_data.csv')
data
Out[2]:
¿es animal? ¿es mamífero? ¿es felino? ¿es doméstico? ¿tiene dos orejas? ¿es negro? ¿tiene cuatro patas? ¿es gato?
0 1 1 1 1 1 1 1 1
1 0 0 0 1 0 1 0 0
2 1 0 1 1 0 1 1 0
3 1 1 0 1 1 0 1 0
4 1 1 1 0 1 0 1 0
5 1 1 1 1 0 0 0 1
6 1 0 0 1 1 1 0 0
7 1 1 1 1 0 0 1 1
8 1 0 0 1 0 0 0 0
9 0 0 0 0 0 0 0 0
10 1 1 1 0 1 1 1 0
11 1 1 1 0 1 0 1 0
12 1 1 1 1 1 0 1 1
13 1 1 1 1 1 0 0 1

Contamos con 14 datos (renglones). Cada uno de estos datos cuenta con 8 rasgos que lo describen (columnas). Sin embargo, el último rasgo (última columna) es la clasificación a la que pertenece cada objetos; es decir, la variable "¿es gato?" es precisamente el problema que queremos resolver.

Denotaremos cada ejemplo como un vector:

$$x = (X_1,...,X_n)$$

Donde $X_i$, $i=1,...,n$ es una variable (columna) que representa un rasgo de ese dato especifico.

Obtención de la supervisación¶

Como hemos observados, la última variable responde a la clase del objeto (si es gato o no lo es), por lo que tomaremos esta columna como el objetivo de la clasificación. Para esto, separaremos esta matriz en los vectores que representan los objetos ($X$) y sus clases ($Y$).

In [3]:
#Convertir los datos a numpy
npData = data.to_numpy()
#Ejemplos
X = npData[:,:-1]
#Clases de los ejemplos
Y = npData[:,-1]

#Tamaño de los datos
#Unidades de entrada
m,n = X.shape

Los datos $X$ es una matriz del número de datos por la dimensión de estos. Cada renglón representa un dato de entrada, estos son vectores $x= (X_1,...,X_7) \in \mathbb{R}^7$.

La supervisación $Y$ es un vector que asigna a cada dato una clase: 1 si es gato, 0 si no es gato.

Finalmente, podemos decir que tenemos un conjunto supervisado dado por:

$$\mathcal{S} = \{(x,y) : x \in \mathbb{R}^7, y \in \{0,1\}\}$$

Cada par $(x,y)$ representa un vector/dato de entrada y su clase $y$ (si es o no gato). Por lo que se puede ver que el problema es una clasificación binaria.

In [4]:
print(X)
print(Y)
[[1 1 1 1 1 1 1]
 [0 0 0 1 0 1 0]
 [1 0 1 1 0 1 1]
 [1 1 0 1 1 0 1]
 [1 1 1 0 1 0 1]
 [1 1 1 1 0 0 0]
 [1 0 0 1 1 1 0]
 [1 1 1 1 0 0 1]
 [1 0 0 1 0 0 0]
 [0 0 0 0 0 0 0]
 [1 1 1 0 1 1 1]
 [1 1 1 0 1 0 1]
 [1 1 1 1 1 0 1]
 [1 1 1 1 1 0 0]]
[1 0 0 0 0 1 0 1 0 0 0 0 1 1]

Finalmente, podemos identificar aquí los elementos de un problema de aprendizaje para este caso particular:

  • Tarea: Clasificar un objeto en la clase "gato" (1) o "no gato" (0).
  • Experiencia: Un conjunto de datos (vectores) con variables que describen los objetos, así como la clase asociada a cada dato.
  • Medida de desempeño: Determinaremos el desempeño según la máquina sea capaz de clasificar adecuadamente un objeto en las clases gato o no gato.

Obtención de datasets¶

Ahora que tenemos los datos (o experiencia) necesitamos separar estos datos en entrenamiento y evaluación (no usaremos validación aquí).

Para hacer esto tomaremos un 70\% para entrenamiento y un 30\% de la evaluación: la selección de ambos conjuntos se hará aleatoriamente. Asimismo, nos deberemos asegurar que ningún dato de entrenamiento se encuentre en la evaluación, es decir, que no se repitan datos entre ambos conjuntos.

Para hacer esta selección utilizaremos la paquetería SKLearn que nos permite exportar la función train_test_split que separa los datos aleatoria y disjuntamente asignándole un tamaño a la evaluación.

In [5]:
#Importamos la paquetería necesaria
from sklearn.model_selection import train_test_split

#Separamos los datos con la función train_test_split
#El conjutno de evaluación será del 30% (0.3)
X_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size=0.3)
In [6]:
print('Elementos en el entrenamiento: {}\nElementos en la evaluación: {}'.format(len(X_train), len(X_test)))
Elementos en el entrenamiento: 9
Elementos en la evaluación: 5

Ya que contamos sólo con 14 datos, 9 de estos (70\%) forman parte del entrenamiento y los 5 (30\%) restantes de la evaluación.

Selección de algoritmo y entrenamiento¶

Una vez que hemos preparado los datos y los hemos separado en entrenamiento y evaluación, necesitamos elegir un algoritmo.

Dado que tenemos un problema supervisado, debemos seleccionar un algoritmo de aprendizaje supervisado. Seleccionaremos el método de Bayes ingenuo que es un algoritmo basado en inferencia bayesiana. No profundizaremos en este método; usaremos, de nuevo, la paquetería de SKLearn.

In [7]:
from sklearn.naive_bayes import CategoricalNB as Bayes

El algoritmo de Bayes ingenuo tiene pocos hiperparámetros, en este caso se cuenta con una hiperparámetro (alpha) que se encarga de ajustar las probabilidades (smoothing), pero en el que no porfundizaremos.

In [8]:
LM = Bayes(alpha=1)

Una vez que hemos determinado nuestra máquina de aprendizaje, procederemos a entrenarlo. El entrenamiento consiste en aprender los parámetros que se ajusten mejor a los datos de entrenamiento. Cada método de aprendizaje tiene parámetros específicos que debe aprender. No profindizaremos en esto.

Es importante señalar que el entrenamiento se hace a partir de los datos de entrenamiento ($X$ y $Y$) y únicamente de estos. En este paso, la máquina de aprendizaje no debe tener ningún contacto con el conjunto de evaluación.

In [9]:
LM.fit(X_train, Y_train)
Out[9]:
CategoricalNB(alpha=1)

El resultado del entrenamiento es un modelo de aprendizaje: este modelo consiste en la máquina de aprendizaje con los parámetros que mejor se ajustan a los datos. Es decir, bajo estos parámetros deseamos que la máquina de aprendizaje sea capaz de generalizar el conocimiento que adquirió de los datos de entrenamiento.

Evaluación¶

Para saber qué tan bien la máquina de aprendizaje "aprendio", evaluaremos su desempeño: es decir, la capacidad de generalizar que tiene la máquina.

Para esto, aplicaremos el modelo de aprendizaje a datos que no hayan sido vistos antes. Estos datos son precisamente los datos de evaluación. Por esta razón es importante que durante el entrenamiento la máquina de aprendizaje no haya tenido contacto con ellos.

Podemos entonces aplicar el modelo de aprendizaje a estos datos.

In [10]:
#Aplicamos el modelo a los datos de evaluación
Y_LM = LM.predict(X_test)

print(Y_LM)
[1 1 1 1 0]

En este caso, la máquina propone una clasificación posible que, espereamos, sea lo más parecido a la clasificación lineal. Para evaluar esto, utilizaremos un concepto central de los métodos de evaluación, la matriz de confusión. Esta matriz nos muestra el número de elementos positivos (que fueron clasificados como "gato" o 1) y los elementos negativos ("que fueron clasificados como "no gato" o 0) tanto de la clasificación de la máquina, como la clasificación del supervisor.

Negativos (S) Positivos (S)
Negativos (LM) Verdaderos Negativos (TN) Falsos Negativos (FN)
Positivos (LM) Falsos Positivos (FP) Verdaderos Positivos (TP)

La matriz de confusión nos da los siguientes elementos:

  • TN: los elementos clasificados como negativos por la máquina que en verdad eran negativos (acierto).
  • FN: los elementos clasificados como negativos por la máquina pero que eran positivos (error).
  • FP: los elementos clasificados como positivos por la máquina pero que eran negativos (error).
  • TP: los elementos clasificados como positivos por la máquina que en verdad eran positivos (acierto).
In [11]:
#Paqutería necesaria
from sklearn.metrics import confusion_matrix

#Visualización de la matriz de confusión
pd.DataFrame(data=confusion_matrix(Y_test,Y_LM), 
             index=['Negativos (S)','Positivos (S)'], columns=['Negativos (LM)','Positivos (LM)'])
Out[11]:
Negativos (LM) Positivos (LM)
Negativos (S) 1 2
Positivos (S) 0 2

Aquí S es el supervisor, la categoría real, y LM la clasificación que hace la máquina. El cruce de estos dos elementos nos indica cuántos elementos positivos reales fueron clasificados comompositivos por la máquina. De igual forma para los negativos. Cada entrada de la matriz nos dice cuántos elementos positivos o negativos del supervisor fueron clasificados de esta forma por la máquina.

Selección de métrica de evaluación¶

Para obtener una evaluación numérica podemos utilizar los resultados de la matriz de confusión. Existen diferentes métricas de evaluación. Una forma muy común de evaluar es la Accuracy o Exactitud, que está determinada como:

$$Acc = \frac{TP + TN}{TN+FN+FP+TP}$$

Otras medidas de evaluación son la precisión y el recall:

$$Prec = \frac{TP}{TP+FP}$$$$Rec = \frac{TP}{TP+FN}$$

Finalmente la métrica $F_1$ nos da un promedio geométrico de ambas métricas:

$$F_1 = 2\cdot \frac{Prec \cdot Rec}{Prec + Rec}$$

Estas métricas pueden obtenerse de la paquetería de skleanr en el módulo de metrics.

In [12]:
#Paqutería necesaria
from sklearn.metrics import classification_report

#Reporte de clasificación
print(classification_report(Y_test,Y_LM))
              precision    recall  f1-score   support

           0       1.00      0.33      0.50         3
           1       0.50      1.00      0.67         2

    accuracy                           0.60         5
   macro avg       0.75      0.67      0.58         5
weighted avg       0.80      0.60      0.57         5

Además de estás métricas, la función de classification report nos da un promedio y un promedio ponderado. Ya que las métricas de precisión, recall y $F_1$ trabajan por cada una de las clases, estos promedios nos dan una noción general de estos. Son los promedios de las métricas a través de las clases.

El macro average es simplemente el promedio de cada métrica a través de las diferentes clases:

$$macro\_avg = \sum_{i=1}^n metric(i)$$

donde $metric(i)$ es cualquiera de las métricas (precision, recall o $f_1$ aplicada en la clase $i$.

Por su parte el weighted average es el promedio ponderado por la probabilidad (o el valor esperado) de esa métrica en una clase:

$$weighted\_avg = \sum_{i=1}^n p(i) \cdot metric(i)$$

donde $p(i)$ es el número de instancias (support) entre el total.