CURSO

Python para Ciencia de Datos

Ph.D. Antonio Escamilla P.
4 sections to go

Capítulo 6: Introducción al Machine Learning con Python

¿Qué es el Machine Learning?

El Machine Learning (Aprendizaje Automático) es una rama de la Inteligencia Artificial que proporciona a los sistemas la capacidad de aprender automáticamente y mejorar a partir de la experiencia sin ser programados explícitamente para cada tarea.

En palabras de Arthur Samuel, quien acuñó el término en 1959, el Machine Learning es "el campo de estudio que da a las computadoras la capacidad de aprender sin ser programadas explícitamente".

Intro to Machine Learning
Introducción al Machine Learning

El Machine Learning se ha convertido en una herramienta indispensable en la ciencia de datos, permitiendo:

Principales Tipos de Machine Learning

El aprendizaje automático se divide generalmente en tres categorías principales:

Tipos de Machine Learning
Tipos de Machine Learning

Aprendizaje Supervisado

En el aprendizaje supervisado, el algoritmo aprende de datos etiquetados (ejemplos de entrada-salida). El objetivo es aprender una función que, dada una entrada, predice correctamente la salida.

Principales tareas:

Aprendizaje No Supervisado

En el aprendizaje no supervisado, el algoritmo aprende patrones a partir de datos no etiquetados, sin tener una "respuesta correcta" predefinida.

Principales tareas:

Aprendizaje por Refuerzo

En el aprendizaje por refuerzo, un agente aprende a tomar decisiones mediante la interacción con un entorno, recibiendo recompensas o penalizaciones según sus acciones.

En este curso, nos centraremos principalmente en técnicas de aprendizaje supervisado y no supervisado, que son las más utilizadas en proyectos de ciencia de datos.

Flujo de Trabajo en un Proyecto de Machine Learning

Esta sección está basada en el Apéndice B (Machine Learning Project Checklist) del libro Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems 2nd Edition de Aurélien Géron, con algunas adaptaciones y adiciones personales basadas en mi experiencia y en otros materiales didácticos.

El libro está acompañado por un repositorio con Jupyter Notebooks: https://github.com/ageron/handson-ml2

Un proyecto de Machine Learning sigue un proceso estructurado que permite desarrollar modelos efectivos y confiables. A continuación, presento una guía paso a paso que puede servir como lista de verificación para tus proyectos.

Flujo de trabajo en Machine Learning
Flujo de trabajo típico en un proyecto de Machine Learning

1. Definición del Problema

El primer paso es entender claramente qué problema estamos tratando de resolver:

Dedicar tiempo suficiente a esta fase es crucial. Un problema bien definido está parcialmente resuelto. Es preferible invertir tiempo en esta etapa que rehacer todo el trabajo por un enfoque erróneo.

2. Obtención de los Datos

Una vez definido el problema, debemos obtener los datos necesarios:

Es fundamental separar un conjunto de prueba al inicio del proceso y mantenerlo intacto. Si se utiliza para ajustar modelos, no será útil para evaluar el rendimiento real.

3. Exploración de los Datos (EDA)

El análisis exploratorio de datos nos permite entender mejor la información con la que contamos:

Existen varias librerías en Python que facilitan el EDA:


# Ejemplos de librerías para EDA
import pandas as pd
import pandas_profiling  # Generación automática de reportes EDA
import sweetviz         # Visualizaciones automáticas 
import dtale            # Interfaz interactiva para exploración
import matplotlib.pyplot as plt
import seaborn as sns

# Ejemplo básico
# Cargar datos
df = pd.read_csv('datos.csv')

# Resumen estadístico
print(df.describe())

# Verificar valores nulos
print(df.isnull().sum())

# Crear un informe completo con pandas_profiling
from pandas_profiling import ProfileReport
profile = ProfileReport(df, title="Informe de Datos")
profile.to_file("informe_eda.html")
    
La exploración de datos es un proceso iterativo. A medida que se adquiere más conocimiento sobre los datos, pueden surgir nuevas preguntas y vías de exploración.

4. Preparación de los Datos

Esta fase es crucial para exponer mejor los patrones en los datos y prepararlos para los algoritmos de ML:


# Ejemplo de preparación de datos en Python
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline

# Definir transformadores para variables numéricas
num_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Definir transformadores para variables categóricas
cat_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

# Combinar transformadores
preprocessor = ColumnTransformer(
    transformers=[
        ('num', num_transformer, numerical_cols),
        ('cat', cat_transformer, categorical_cols)
    ])

# Crear un pipeline para la preparación de datos
data_pipeline = Pipeline(steps=[('preprocessor', preprocessor)])

# Aplicar las transformaciones
X_processed = data_pipeline.fit_transform(X)
    
Es recomendable encapsular todas las transformaciones en funciones o pipelines, para poder aplicarlas fácilmente a nuevos datos y asegurar la consistencia en el procesamiento.

5. Selección de Modelos

En esta fase, evaluamos diferentes algoritmos para identificar los más prometedores:

Validación Cruzada (Cross-Validation)

Al evaluar diferentes modelos, es fundamental utilizar validación cruzada para obtener una estimación confiable de su rendimiento. Esta técnica divide los datos en varios subconjuntos o "pliegues" (folds) y realiza múltiples iteraciones de entrenamiento y evaluación.

En la validación cruzada de k-pliegues (k-fold cross-validation), el proceso es el siguiente:

  1. Se divide el conjunto de datos en k subconjuntos de igual tamaño
  2. Para cada uno de los k subconjuntos:
    • Se entrena el modelo usando los k-1 subconjuntos restantes
    • Se evalúa el modelo en el subconjunto reservado
  3. Se promedian las k métricas de rendimiento para obtener una estimación más robusta
Esquema de validación cruzada
Ilustración del proceso de validación cruzada (Fuente: Scikit-learn)
La validación cruzada es especialmente importante cuando:
  • El conjunto de datos es pequeño
  • Se desea maximizar el uso de los datos disponibles
  • Se busca una estimación más precisa del rendimiento del modelo
# Ejemplo de evaluación de modelos en Python
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier

# Definir modelos a evaluar
models = {
    'LogisticRegression': LogisticRegression(),
    'RandomForest': RandomForestClassifier(),
    'SVC': SVC(),
    'KNeighbors': KNeighborsClassifier(),
    'DecisionTree': DecisionTreeClassifier()
}

# Evaluar cada modelo con validación cruzada
results = {}
for name, model in models.items():
    cv_scores = cross_val_score(model, X_train, y_train, cv=5, scoring='accuracy')
    results[name] = {
        'mean_score': cv_scores.mean(),
        'std_score': cv_scores.std()
    }
    print(f"{name}: {cv_scores.mean():.4f} (±{cv_scores.std():.4f})")
    

6. Afinamiento de Modelos

Una vez identificados los modelos prometedores, optimizamos sus hiperparámetros:

Optimización de Hiperparámetros

Un paso crucial en el afinamiento de modelos es la optimización de hiperparámetros, que consiste en encontrar la configuración óptima de parámetros externos al modelo que afectan su rendimiento y capacidad de generalización.

Los hiperparámetros son configuraciones que no se aprenden durante el entrenamiento, sino que deben especificarse previamente. Ejemplos comunes incluyen:

Existen principalmente dos enfoques para la búsqueda de hiperparámetros:

Grid Search Evalúa exhaustivamente todas las combinaciones posibles de valores de hiperparámetros especificados. Es completa pero computacionalmente costosa cuando hay muchos parámetros.
Random Search Muestrea aleatoriamente combinaciones del espacio de hiperparámetros. Suele ser más eficiente que Grid Search, especialmente cuando no todos los hiperparámetros son igualmente importantes.

# Ejemplo de afinamiento de hiperparámetros
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform

# Definir espacio de búsqueda para Random Forest
param_dist = {
    'n_estimators': randint(100, 500),
    'max_depth': randint(5, 30),
    'min_samples_split': randint(2, 20),
    'min_samples_leaf': randint(1, 10),
    'max_features': ['auto', 'sqrt', 'log2', None]
}

# Crear el modelo base
rf = RandomForestClassifier()

# Configurar la búsqueda aleatoria
random_search = RandomizedSearchCV(
    rf, 
    param_distributions=param_dist,
    n_iter=100,
    cv=5,
    scoring='accuracy',
    n_jobs=-1,
    verbose=1
)

# Ejecutar la búsqueda
random_search.fit(X_train, y_train)

# Obtener y mostrar los mejores parámetros
print("Mejores parámetros:", random_search.best_params_)
print("Mejor puntuación:", random_search.best_score_)

# Evaluar en el conjunto de prueba
best_model = random_search.best_estimator_
test_score = best_model.score(X_test, y_test)
print(f"Puntuación en el conjunto de prueba: {test_score:.4f}")
    
El afinamiento excesivo de hiperparámetros puede llevar al sobreajuste (overfitting) en el conjunto de validación. Siempre evalúa el rendimiento final en un conjunto de prueba separado.

7. Interpretabilidad del Modelo

Entender por qué y cómo funciona el modelo es esencial para generar confianza y detectar posibles problemas:


# Ejemplo de análisis de interpretabilidad
import shap
import matplotlib.pyplot as plt
from sklearn.inspection import permutation_importance

# Para modelos basados en árboles, podemos ver directamente la importancia de características
feature_importances = best_model.feature_importances_
feature_names = X_train.columns

# Visualizar importancia de características
plt.figure(figsize=(10, 6))
sorted_idx = np.argsort(feature_importances)
plt.barh(range(len(sorted_idx)), feature_importances[sorted_idx])
plt.yticks(range(len(sorted_idx)), np.array(feature_names)[sorted_idx])
plt.xlabel('Importancia de Característica')
plt.title('Importancia de Características del Modelo')
plt.tight_layout()
plt.show()

# Para análisis más avanzados, podemos usar SHAP
explainer = shap.TreeExplainer(best_model)
shap_values = explainer.shap_values(X_test)

# Resumen de valores SHAP
shap.summary_plot(shap_values, X_test, feature_names=feature_names)

# Gráfico de dependencia para ver relaciones específicas
shap.dependence_plot("feature_name", shap_values, X_test)
    
La interpretabilidad es particularmente importante en aplicaciones críticas como finanzas, medicina o sistemas legales, donde las decisiones del modelo pueden tener consecuencias significativas.

8. Presentación de la Solución

Comunicar efectivamente los resultados es clave para el éxito del proyecto:

Las visualizaciones son fundamentales para comunicar resultados complejos. Un gráfico bien diseñado puede transmitir información que sería difícil de entender en formato textual o numérico.

9. Despliegue, Monitoreo y Mantenimiento

La implementación en producción y el mantenimiento continuo son esenciales para el éxito a largo plazo:


# Ejemplo de cómo guardar un modelo para producción
import joblib
from sklearn.pipeline import Pipeline

# Crear un pipeline completo que incluya preprocesamiento y modelo
full_pipeline = Pipeline([
    ('preprocessor', preprocessor),  # El preprocesador definido anteriormente
    ('model', best_model)            # El mejor modelo encontrado
])

# Entrenar el pipeline completo
full_pipeline.fit(X_train_raw, y_train)  # Datos sin preprocesar

# Guardar el pipeline para uso en producción
joblib.dump(full_pipeline, 'modelo_produccion.pkl')

# Ejemplo de carga y uso del modelo en producción
loaded_model = joblib.load('modelo_produccion.pkl')
predictions = loaded_model.predict(new_data)  # Se puede usar directamente con datos sin procesar
    
Nunca subestimes la importancia del monitoreo y mantenimiento. Muchos proyectos de Machine Learning fallan no por el modelo inicial, sino por la falta de seguimiento y adaptación a los cambios en los datos y requisitos.

Scikit-learn: La Librería Principal para Machine Learning en Python

Para implementar este flujo de trabajo, scikit-learn es la herramienta más utilizada en Python. Ofrece una API consistente para diferentes algoritmos y utilidades de preprocesamiento, evaluación y selección de modelos.

Mapa de Machine Learning
Mapa de algoritmos de Machine Learning (Fuente: Scikit-learn)

En los próximos capítulos exploraremos en detalle cómo utilizar scikit-learn y otras librerías para implementar cada paso de este flujo de trabajo, centrándonos en regresión, clasificación y clustering.

Conceptos Clave Aplicados en Scikit-learn

Para trabajar efectivamente con Machine Learning en Python, es fundamental comprender dos conceptos centrales en scikit-learn: la diferencia entre transformadores y estimadores, y cómo utilizar pipelines para crear flujos de trabajo eficientes y reproducibles.

Transformadores vs Predictores en Scikit-learn

En el ecosistema de scikit-learn, todos los objetos son estimadores, pero existen diferentes tipos especializados que cumplen funciones específicas en el flujo de trabajo de Machine Learning.

Diferencia entre Transformadores y Predictores
Esquema conceptual de la diferencia entre Transformadores y Predictores

Transformadores (Transformers)

Los transformadores son un tipo especial de estimador que se usa para transformar datos. Implementan métodos adicionales para modificar los datos de entrada.

Características principales de los transformadores:

Predictores (Predictors)

Los predictores son estimadores que implementan el método predict() para hacer predicciones sobre nuevos datos. A diferencia de los transformadores que modifican los datos, los predictores aprenden patrones y relaciones para generar predicciones.

Características principales de los predictores:

Pipelines en Scikit-learn

Un Pipeline en scikit-learn es una herramienta que permite encadenar múltiples pasos de procesamiento y modelado en un flujo secuencial. Esta es una de las características más poderosas de scikit-learn para crear workflows reproducibles y eficientes.

Diagrama de Pipeline de Scikit-learn
Flujo de datos en un Pipeline de scikit-learn

¿Por qué usar Pipelines?

Los pipelines resuelven varios problemas comunes en Machine Learning:

Estructura de un Pipeline

Un pipeline está compuesto por:

Material de Práctica

Para consolidar los conceptos aprendidos en este capítulo, te invito a realizar el siguiente ejercicio:

Ejercicio práctico: Identifica un problema de tu interés que pueda resolverse con Machine Learning. Utiliza la lista de verificación presentada para definir el problema, determinar qué datos necesitarías y qué enfoque utilizarías. Completa al menos las primeras dos etapas del flujo de trabajo (definición del problema y planificación de obtención de datos).

Referencias

Para profundizar en Machine Learning con Python, recomendamos consultar estos valiosos recursos: