Parte III: Aprendizaje supervisado con scikit-learn

1. Introducción

  • Queremos construir un modelo usando los datos de entrenamiento que pueda generalizar datos que el modelo "no haya visto"
  • Si un modelo es capaz de hacer predicciones precisas en datos no vistos decimos que ha generalizado a partir de los datos de entrenamiento
  • Una forma de estimar qué tan buena será la generalización es dividir nuestro conjuntos de datos en dos: uno para entrenamiento (training set) y otro para evaluación (test set)

1. Introducción (II)

  • Otro factor a tomar en cuenta es la complejidad del modelo (que depende del espacio de hipótesis $H$)
  • Un modelo muy complejo es capaz de "memorizar los datos" por lo que se desempeñará bien en los datos de entrenamiento pero no en los de evaluación (este problema se conoce como overfitting
  • Por otro lado, un modelo muy simple no será capaz de capturar toda la información contenida en los datos por lo que el desempeño será malo tnato en los datos de entrenamiento como en los de evaluación (underfitting)

2. Entrenando un primer modelo

  • Usaremos el dataset iris, hacer un clasificador con este conjunto de datos es como el Hello World! en programación
  • El conjunto de datos contiene 150 observaciones de 3 tipos de flores con medidas de altura y ancho para pétalo y sépalo, así como la clase a la que pertenece la flor (setosa, versicolour o virginica)
  • Para mayor información sobre los datos ver: http://mlr.cs.umass.edu/ml/machine-learning-databases/iris/

2. Entrenando un primer modelo (III)


In [1]:
# configuramos matplotlib para incluir las gráficas en jupyter e importamos pandas
%matplotlib inline
import pandas as pd

In [2]:
# cargamos los datos en un data frame de pandas
url = 'http://mlr.cs.umass.edu/ml/machine-learning-databases/iris/iris.data'
names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
df = pd.read_csv(url, names=names)

# veamos como lucen las primeras observaciones
df.head()


Out[2]:
sepal_length sepal_width petal_length petal_width class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa

In [3]:
# obtenemos los valores del data frame, esto nos regresará
# arreglos de numpy
X = df[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']].values
y = df['class'].values

from sklearn.model_selection import train_test_split

# partimos los datos en entrenamiento y evaluación
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

In [4]:
from sklearn.linear_model import LogisticRegression

clf = LogisticRegression()
clf.fit(X_train, y_train)


Out[4]:
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)

2. Entrenando un primer modelo (IV)


In [5]:
print("Test set accuracy: {:.2f}".format(clf.score(X_test, y_test)))


Test set accuracy: 0.98

In [6]:
y_pred = clf.predict(X_test)
y_score = clf.predict_proba(X_test)

from sklearn_evaluation import ClassifierEvaluator

ce = ClassifierEvaluator(clf, y_test, y_pred, y_score,
                         df.columns,
                        ['setosa', 'versicolor', 'virginica'])

In [7]:
cm = ce.confusion_matrix



In [8]:
ce.precision_recall


Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x11d6ba630>

2. Entrenando un primer modelo (V)

  • En este primer ejemplo ignoramos muchas cuestiones que se tienen que tomar en cuenta en proyectos reales
  • Los datos casi nunca están en un formato aceptable por lo que hay que cambiar formato, exportarlos a una base de datos, etc
  • Una vez que tenemos los datos en formato aceptable el siguiente paso importante es limpiarlos, por lo general los datos van a contener errores (mal formato, nombres de columnas incorrectos, errores de captura, etc).
  • Cuando tenemos los datos limpios necesitamos trabajar en otro problema: datos faltantes. Es normal que en nuestros datos haya faltantes y existen muchas técnicas para trabajar con ellos, desde borrar filas/columnas con datos faltantes hasta algoritmos para llenarlos, algunos algoritmos de ML pueden trabajar con datos faltantes pero otros no
  • Una vez que tenemos los datos limpios y completos se suele recurrir a un preprocesamiento, entre las operaciones más comunes se encuentra transformar la escala de los datos, esto ayuda a ciertos algoritmos a tener un mejor desempeño

3. Un ejemplo un poco más difícil

  • Ahora veremos otro ejemplo de aprendizaje supervisado con datos que se asemejan un poco más a lo que encontraríamos en un proyecto real
  • Trabajaremos con los datos del Titanic disponibles en https://www.kaggle.com/c/titanic

3.1 Datos

  • En este conjunto de datos tenemos obervaciones sobre algunos de los pasajeros del Titanic incluida una variable que indica si el pasajero sobrevivió o no
  • Usaremos los datos para modelar la sobrevivencia dada la información que tenemos sobre el pasajero
  • Más información sobre los datos disponible en https://www.kaggle.com/c/titanic/data

3.2 Limpieza de datos


In [18]:
df = pd.read_csv("titanic.csv")
df.head()


Out[18]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

In [16]:
# Tenemos algunos datos faltantes
df.isnull().sum()


Out[16]:
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64
  • Vamos a descartar PassenderId pues no nos da información útil para el modelo (el dato es único para cada pasajero), vemos que también tenemos
  • También tenemos columnas con texto, a pesar de que podemos extraer información de ellas y usarlas en el modelo no lo haremos por simplicidad

In [20]:
# Obtenemos un subconjunto de las columnas
df = df[['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Embarked']]
df.head()


Out[20]:
Survived Pclass Sex Age SibSp Parch Embarked
0 0 3 male 22.0 1 0 S
1 1 1 female 38.0 1 0 C
2 1 3 female 26.0 0 0 S
3 1 1 female 35.0 1 0 S
4 0 3 male 35.0 0 0 S

In [30]:
# Tenemos ahora que trabajar con los datos faltantes en Embarked y Age
# para el caso de Embarked (variable categórica) usaremos la moda como el valor imputado
# para Age usaremos la media como valor inputado
df.loc[df.Age.isnull(), 'Age'] = df.Age.mean()

In [36]:
df.Embarked.value_counts()


Out[36]:
S    646
C    168
Q     77
Name: Embarked, dtype: int64

In [35]:
df.loc[df.Embarked.isnull(), 'Embarked'] = 'S'

In [40]:
# Ahora que tenemos datos completos tenemos que convertir las variables expresadas como strings a numéros
# pues muchos algoritmos solo aceptan este tipo de entrada, en nuestro caso hay que convertir Sex y Embarked
print('Sex values: ', df.Sex.unique())
print('Embarked values: ', df.Embarked.unique())


Sex values:  ['male' 'female']
Embarked values:  ['S' 'C' 'Q']

In [45]:
df.Sex.replace({'male': 0, 'female': 1}, inplace=True)
df.Embarked.replace({'S': 0, 'C': 1, 'Q': 2}, inplace=True)

In [46]:
print('Sex values: ', df.Sex.unique())
print('Embarked values: ', df.Embarked.unique())


Sex values:  [0 1]
Embarked values:  [0 1 2]

3.2 Entrenamiento


In [50]:
X = df[['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Embarked']].values
y = df['Survived'].values

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

clf = LogisticRegression()
clf.fit(X_train, y_train)

print("Test set accuracy: {:.2f}".format(clf.score(X_test, y_test)))


Test set accuracy: 0.80

3.3 Cross-validation

  • Los modelos de ML tienen hiperparámetros que se tienen que elegir manualmente
  • Debido a esto, podría darse el caso e que al estar optimizando dichos hiperparámetros causemos overfitting en el modelo por lo que nuestra estimación del error de generalización será muy optimista
  • Para resolver este problema se suelen dividir los datos en tres conjuntos: entrenamiento (training), validación (validation) y prueba (test)
  • El algoritmo se entrena con los datos de entrenamiento, el error se estima con el conjunto de validación y cuando se ha seleccionado el modelo final, se estima el error de generalización con el conjunto de prueba

3.3 Cross-validation (II)

  • Desafortunadamente al hacer estas tres particiones reducimos el número de observaciones que se usarán para el entrenamiento del modelo
  • Una solución a este problema se conoce como cross-validation (CV)
  • Cuando se usa CV un test de prueba se usa para la evaluación final, pero el conjunto de validación ya no es necesario

3.3 Cross-validation (III)

  • En una de sus variantes conocida como k-fold CV, se siguen estos pasos:
    • Se divide el conjunto de entrenamiento en k partes
    • El modelo se entrena con k-1 partes
    • El modelo se valida con la parte que no se usó para entrenamiento
    • Se repite el proceso hasta que se haya validado el modelo con las k partes y se reporta el promedio de la métrica que estemos usando para evaluar el modelo

In [51]:
from sklearn.model_selection import cross_val_score

clf = LogisticRegression()
scores = cross_val_score(clf, X_train, y_train, cv=5)
scores


Out[51]:
array([ 0.81,  0.82,  0.82,  0.75,  0.77])

In [52]:
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))


Accuracy: 0.79 (+/- 0.06)

3.4 Bias-Variance tradeoff

3.5 Entrando más modelos


In [ ]:
from sklearn.ensemble import AdaBoostClassifier, BaggingClassifier, RandomForestClassifier, ExtraTreesClassifier

3.6 Optimizando hiperparámetros

3.7 Feature engineering

Fuentes

Cross-validation: evaluating estimator performance. http://scikit-learn.org/stable/modules/cross_validation.html

Lectura recomendada

Domingos, P. A Few Useful Things to Know about Machine Learning

https://homes.cs.washington.edu/~pedrod/papers/cacm12.pdf