🤖 Automatización de Procesos para el Flujo de Trabajo en Machine Learning¶
Machine Learning Avanzado — NRC-8772 — Semana 7¶
Grupo: Grupo 10
Integrantes:
- Alexander Oviedo Fadul (Ponente)
- Maria Fernanda Ruiz Paipilla
- Neheman Samir Jaller Cerchiaro
- William David Obando Lopez
Enlace del Notebook en Google Colab: Abrir en Google Colab
Programa: Especialización en Inteligencia Artificial, Corporación Universitaria Minuto de Dios (UNIMINUTO)
Curso: NRC-8772 — Machine Learning Avanzado
Profesor: Leonardo Valderrama García
Fecha: 21 de junio de 2026
Descripción de la actividad¶
En este proyecto se implementa un flujo de trabajo automatizado (Pipeline) de Machine Learning para predecir la supervivencia en el dataset del Titanic.
El objetivo principal es demostrar cómo la automatización de procesos mediante scikit-learn previene la fuga de datos (data leakage), optimiza hiperparámetros de manera integrada mediante GridSearchCV y permite una evaluación de modelos rigurosa a través de validación cruzada multimétrica.
Estructura del notebook¶
| Sección | Contenido |
|---|---|
| 1 | Respuestas a Preguntas Orientadoras |
| 2 | Importación de Librerías |
| 3 | Descarga e Ingesta Automática del Dataset |
| 4 | Análisis Exploratorio de Datos (EDA) |
| 5 | Ingeniería de Características Personalizada |
| 6 | Automatización del Preprocesamiento y Clasificadores (Pipelines) |
| 7 | Optimización de Hiperparámetros (GridSearchCV) |
| 8 | Evaluación Multimétrica con Validación Cruzada |
| 9 | Evaluación en Conjunto de Prueba y Matrices de Confusión |
| 10 | Dilemas Éticos y Sesgo Algorítmico |
| 11 | Conclusiones y Referencias |
1. Respuestas a las Preguntas Orientadoras¶
Pregunta 1: ¿Cómo puede la automatización de procesos en los flujos de trabajo de Machine Learning aumentar la eficiencia y la precisión de los modelos?¶
La automatización de procesos (MLOps / Pipelines) incrementa la eficiencia y la precisión a través de varios mecanismos clave:
- Prevención de la Fuga de Datos (Data Leakage): Al encapsular los pasos de preprocesamiento (como el escalado y la imputación de nulos) dentro de un objeto
Pipelinede scikit-learn, garantizamos que las estadísticas (como la media o mediana) se calculen únicamente en los datos de entrenamiento de cada fold durante la validación cruzada, y no en los datos de prueba o validación. Esto evita estimaciones de rendimiento demasiado optimistas y mejora la precisión real en producción. - Consistencia y Repetibilidad: Se reduce el error humano. El mismo flujo exacto se aplica tanto a los conjuntos de entrenamiento, validación y prueba, como a los datos nuevos que llegan en tiempo real.
- Optimización Integrada: Permite realizar búsquedas de hiperparámetros (
GridSearchCVoRandomizedSearchCV) sobre todo el flujo simultáneamente, encontrando combinaciones óptimas de preprocesamiento (ej. número de componentes en PCA) y del clasificador que de otra manera requerirían ciclos manuales ineficientes. - Agilidad en la Iteración: Permite probar múltiples algoritmos y configuraciones cambiando pocas líneas de código, acelerando el ciclo de vida del desarrollo de software (SDLC) en proyectos de IA.
Pregunta 2: ¿Cuáles son las herramientas y técnicas de automatización de procesos utilizadas en los flujos de trabajo de Machine Learning y cómo se aplican en diferentes casos de uso?¶
- Pipelines de scikit-learn (
PipelineyColumnTransformer): Técnica para encadenar transformadores y estimadores. Ideal para preprocesar datos tubulares de forma diferenciada según el tipo de variable (numérica o categórica). - MLflow: Herramienta para registrar parámetros de ejecución, métricas y artefactos de modelos. Se aplica en experimentación para comparar cientos de ejecuciones de modelos.
- Vertex AI / Kubeflow Pipelines: Plataformas de orquestación a gran escala que permiten ejecutar tareas de ML en contenedores aislados. Útil en entornos corporativos para reentrenar modelos periódicamente cuando se detecta deriva de datos (data drift).
- GridSearchCV / Optuna: Herramientas para la optimización automática de hiperparámetros. Se aplican en finetuning para exprimir el rendimiento de modelos complejos antes de su puesta en producción.
2. Importación de librerías¶
Se importan las librerías necesarias para el análisis de datos, visualización y el diseño de los flujos de preprocesamiento, modelado y validación cruzada.
# ===========================================================================
# 2. IMPORTACIÓN DE LIBRERÍAS
# ===========================================================================
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
import warnings
warnings.filterwarnings('ignore')
# Framework principal de Machine Learning (scikit-learn)
from sklearn.model_selection import (
train_test_split, cross_validate, GridSearchCV
)
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder, FunctionTransformer
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import (
classification_report, confusion_matrix, accuracy_score,
precision_score, recall_score, f1_score, roc_curve, auc
)
# Estilo visual de las gráficas
sns.set_theme(style="whitegrid")
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['font.size'] = 11
print("✅ Librerías cargadas exitosamente.")
✅ Librerías cargadas exitosamente.
3. Ingesta y descarga automática del dataset¶
Para asegurar la portabilidad y ejecución 100% web del notebook (por ejemplo en Google Colab), el dataset del Titanic se descarga directamente desde un repositorio público. Si se ejecuta en local, busca el archivo de respaldo titanic.csv.
# ===========================================================================
# 3. CARGA DE DATOS DESDE LA WEB
# ===========================================================================
URL_TITANIC = "https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
RUTA_LOCAL = "./titanic.csv"
if os.path.exists(RUTA_LOCAL):
print("📁 Cargando dataset desde la ruta local...")
df = pd.read_csv(RUTA_LOCAL)
else:
print("📥 Descargando dataset desde URL pública...")
try:
df = pd.read_csv(URL_TITANIC)
df.to_csv(RUTA_LOCAL, index=False)
print("💾 Dataset guardado localmente como 'titanic.csv'.")
except Exception as e:
print(f"❌ Error al descargar de la web: {e}")
print("Intentando cargar desde la carpeta de la semana anterior...")
df = pd.read_csv("../MA Semana 5/titanic.csv")
# Estandarizar nombres de columnas a minúsculas
df.columns = df.columns.str.lower()
# Resumen de dimensiones y columnas
print(f"Dimensiones del dataset: {df.shape[0]} filas, {df.shape[1]} columnas")
print("\nPrimeras 5 filas:")
display(df.head())
📥 Descargando dataset desde URL pública... 💾 Dataset guardado localmente como 'titanic.csv'. Dimensiones del dataset: 891 filas, 12 columnas Primeras 5 filas:
| 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 |
4. Análisis Exploratorio de Datos (EDA)¶
Se realiza un análisis gráfico inicial para comprender la distribución de la supervivencia y la relación de variables críticas como la clase del boleto (pclass), el género (sex), la tarifa (fare) y la edad (age).
# ===========================================================================
# 4. ANÁLISIS EXPLORATORIO DE DATOS (EDA)
# ===========================================================================
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
fig.suptitle('Análisis Exploratorio de Datos (Supervivencia en el Titanic)', fontsize=16, fontweight='bold')
# 1. Distribución de supervivencia
sns.countplot(ax=axes[0, 0], x='survived', data=df, palette='Set2')
axes[0, 0].set_title('Distribución de Supervivencia (0 = No, 1 = Sí)')
axes[0, 0].set_xlabel('Supervivencia')
axes[0, 0].set_ylabel('Cantidad')
# 2. Supervivencia por género
sns.countplot(ax=axes[0, 1], x='sex', hue='survived', data=df, palette='Set1')
axes[0, 1].set_title('Supervivencia según el Género')
axes[0, 1].set_xlabel('Género')
axes[0, 1].set_ylabel('Cantidad')
# 3. Supervivencia por clase de boleto
sns.countplot(ax=axes[1, 0], x='pclass', hue='survived', data=df, palette='viridis')
axes[1, 0].set_title('Supervivencia por Clase de Boleto')
axes[1, 0].set_xlabel('Clase de Boleto')
axes[1, 0].set_ylabel('Cantidad')
# 4. Distribución de edad según supervivencia
sns.kdeplot(ax=axes[1, 1], x='age', hue='survived', data=df, fill=True, common_norm=False, palette='coolwarm')
axes[1, 1].set_title('Distribución de Edad por Supervivencia')
axes[1, 1].set_xlabel('Edad')
axes[1, 1].set_ylabel('Densidad')
plt.tight_layout()
plt.savefig('eda_supervivencia_titanic.png', dpi=150, bbox_inches='tight')
plt.show()
print("Gráfica guardada: eda_supervivencia_titanic.png")
Gráfica guardada: eda_supervivencia_titanic.png
5. Ingeniería de Características Personalizada¶
Se implementa una función de ingeniería de características que genera nuevas variables lógicas a partir del texto y variables originales:
- family_size: Suma de hermanos/cónyuges (
sibsp) y padres/hijos (parch) más 1 (el propio pasajero). - is_alone: Variable binaria que indica si el pasajero viajaba solo (
family_size == 1). - title: Título de cortesía extraído del nombre (Mr, Mrs, Miss, Master, etc.), útil para imputar edades y como indicador social.
# ===========================================================================
# 5. INGENIERÍA DE CARACTERÍSTICAS
# ===========================================================================
def procesar_caracteristicas(data):
# Crear una copia para evitar SettingWithCopyWarning
df_new = data.copy()
# 1. Tamaño de la familia
df_new['family_size'] = df_new['sibsp'] + df_new['parch'] + 1
# 2. ¿Viaja solo?
df_new['is_alone'] = (df_new['family_size'] == 1).astype(int)
# 3. Extraer títulos del nombre
df_new['title'] = df_new['name'].str.extract(r' ([A-Za-z]+)\.', expand=False)
# Agrupar títulos poco comunes en categorias estándar
titulos_raros = ['Lady', 'Countess','Capt', 'Col', 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona']
df_new['title'] = df_new['title'].replace(titulos_raros, 'Rare')
df_new['title'] = df_new['title'].replace('Mlle', 'Miss')
df_new['title'] = df_new['title'].replace('Ms', 'Miss')
df_new['title'] = df_new['title'].replace('Mme', 'Mrs')
# Rellenar valores nulos de títulos
df_new['title'] = df_new['title'].fillna('Mr')
# Eliminar columnas que no usaremos en el modelado directo
df_new = df_new.drop(['passengerid', 'name', 'ticket', 'cabin'], axis=1)
return df_new
# Aplicar ingeniería de características al dataset completo
df_procesado = procesar_caracteristicas(df)
print("Columnas actuales del dataset procesado:")
print(list(df_procesado.columns))
print("\nPrimeras 5 filas con nuevas características:")
display(df_procesado.head())
Columnas actuales del dataset procesado: ['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'fare', 'embarked', 'family_size', 'is_alone', 'title'] Primeras 5 filas con nuevas características:
| survived | pclass | sex | age | sibsp | parch | fare | embarked | family_size | is_alone | title | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | 2 | 0 | Mr |
| 1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | 2 | 0 | Mrs |
| 2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | 1 | 1 | Miss |
| 3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | 2 | 0 | Mrs |
| 4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | 1 | 1 | Mr |
6. Automatización del Preprocesamiento y Modelado (Pipelines)¶
Para evitar la fuga de información y automatizar el flujo, se construye un preprocesador usando ColumnTransformer:
- Variables Numéricas (
age,fare,sibsp,parch,family_size): Imputación con la mediana y escalamiento estándar (StandardScaler). - Variables Categóricas (
sex,embarked,title): Imputación con el valor más frecuente y codificación One-Hot (OneHotEncoder). - Variables Binarias (
is_alone): Pasan directamente sin procesar.
A continuación, se definen los pipelines completos para dos algoritmos: Random Forest y Support Vector Machine (SVM).
# ===========================================================================
# 6. PIPELINES Y COLUMNTRANSFORMER
# ===========================================================================
# Separar características (X) y variable objetivo (y)
X = df_procesado.drop('survived', axis=1)
y = df_procesado['survived']
# Dividir en entrenamiento y prueba (80/20) con semilla aleatoria para replicabilidad
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Definir las columnas numéricas y categóricas
num_cols = ['age', 'fare', 'sibsp', 'parch', 'family_size']
cat_cols = ['sex', 'embarked', 'title']
bin_cols = ['is_alone']
# 1. Pipeline numérico: Imputador Mediana + Scaler
num_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 2. Pipeline categórico: Imputador Moda + One-Hot Encoding
cat_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])
# Combinar preprocesamiento
preprocessor = ColumnTransformer(transformers=[
('num', num_transformer, num_cols),
('cat', cat_transformer, cat_cols),
('bin', 'passthrough', bin_cols)
])
# 3. Pipelines completos de extremo a extremo
pipeline_rf = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(random_state=42, class_weight='balanced'))
])
pipeline_svm = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', SVC(probability=True, random_state=42, class_weight='balanced'))
])
print("✅ Pipelines de Random Forest y SVM creados exitosamente.")
✅ Pipelines de Random Forest y SVM creados exitosamente.
7. Optimización de Hiperparámetros (GridSearchCV)¶
Se implementa una búsqueda de cuadrícula (GridSearchCV) integrada en los pipelines para automatizar la selección de los mejores hiperparámetros para ambos clasificadores, usando la métrica F1-score para optimizar el balance entre precisión y exhaustividad.
# ===========================================================================
# 7. OPTIMIZACIÓN CON GRIDSEARCHCV
# ===========================================================================
# Cuadrículas de parámetros a probar
param_grid_rf = {
'classifier__n_estimators': [50, 100, 200],
'classifier__max_depth': [4, 6, 8, 10],
'classifier__min_samples_split': [2, 5, 10]
}
param_grid_svm = {
'classifier__C': [0.1, 1, 10],
'classifier__gamma': ['scale', 'auto', 0.01, 0.1]
}
print("🔎 Iniciando búsqueda de hiperparámetros para Random Forest...")
grid_rf = GridSearchCV(pipeline_rf, param_grid_rf, cv=5, scoring='f1', n_jobs=-1)
grid_rf.fit(X_train, y_train)
best_rf_model = grid_rf.best_estimator_
print(f"🥇 Mejores parámetros RF: {grid_rf.best_params_}")
print(f"📈 Mejor F1-score RF: {grid_rf.best_score_:.4f}")
print("\n🔎 Iniciando búsqueda de hiperparámetros para SVM...")
grid_svm = GridSearchCV(pipeline_svm, param_grid_svm, cv=5, scoring='f1', n_jobs=-1)
grid_svm.fit(X_train, y_train)
best_svm_model = grid_svm.best_estimator_
print(f"🥇 Mejores parámetros SVM: {grid_svm.best_params_}")
print(f"📈 Mejor F1-score SVM: {grid_svm.best_score_:.4f}")
🔎 Iniciando búsqueda de hiperparámetros para Random Forest...
🥇 Mejores parámetros RF: {'classifier__max_depth': 8, 'classifier__min_samples_split': 5, 'classifier__n_estimators': 50}
📈 Mejor F1-score RF: 0.7656
🔎 Iniciando búsqueda de hiperparámetros para SVM...
🥇 Mejores parámetros SVM: {'classifier__C': 10, 'classifier__gamma': 'auto'}
📈 Mejor F1-score SVM: 0.7574
8. Evaluación Multimétrica con Validación Cruzada¶
Se realiza una validación cruzada con 5 particiones (5-Fold CV) utilizando el conjunto de datos de entrenamiento para evaluar de forma robusta la capacidad de generalización de ambos modelos optimizados bajo múltiples métricas simultáneas.
# ===========================================================================
# 8. EVALUACIÓN ROBUSTA CON VALIDACIÓN CRUZADA MULTIMÉTRICA
# ===========================================================================
metricas = {
'accuracy': 'accuracy',
'precision': 'precision',
'recall': 'recall',
'f1': 'f1'
}
# Validación cruzada para el modelo optimizado de Random Forest
cv_results_rf = cross_validate(
best_rf_model, X_train, y_train, cv=5, scoring=metricas, return_train_score=True
)
# Validación cruzada para el modelo optimizado de SVM
cv_results_svm = cross_validate(
best_svm_model, X_train, y_train, cv=5, scoring=metricas, return_train_score=True
)
# Mostrar resultados promedio
def mostrar_resumen_cv(resultados, nombre):
print(f"=== Resultados de Validación Cruzada (Train vs Val) para {nombre} ===")
print(f"{'Métrica':<15} | {'Entrenamiento (Promedio)':<25} | {'Validación (Promedio)':<20}")
print("-" * 70)
for m in metricas.keys():
train_mean = resultados[f'train_{m}'].mean()
val_mean = resultados[f'test_{m}'].mean()
print(f"{m.capitalize():<15} | {train_mean:24.4f} | {val_mean:19.4f}")
print("=" * 70 + "\n")
mostrar_resumen_cv(cv_results_rf, "Random Forest")
mostrar_resumen_cv(cv_results_svm, "SVM")
=== Resultados de Validación Cruzada (Train vs Val) para Random Forest === Métrica | Entrenamiento (Promedio) | Validación (Promedio) ---------------------------------------------------------------------- Accuracy | 0.8936 | 0.8216 Precision | 0.8611 | 0.7717 Recall | 0.8617 | 0.7620 F1 | 0.8613 | 0.7656 ====================================================================== === Resultados de Validación Cruzada (Train vs Val) para SVM === Métrica | Entrenamiento (Promedio) | Validación (Promedio) ---------------------------------------------------------------------- Accuracy | 0.8416 | 0.8188 Precision | 0.8140 | 0.7810 Recall | 0.7610 | 0.7363 F1 | 0.7866 | 0.7574 ======================================================================
9. Evaluación en el Conjunto de Prueba y Matrices de Confusión¶
Evaluamos finalmente los mejores modelos en el conjunto de prueba (test set) no visto durante el entrenamiento y visualizamos las matrices de confusión y las curvas ROC correspondientes.
# ===========================================================================
# 9. EVALUACIÓN FINAL EN TEST SET Y MATRICES DE CONFUSIÓN
# ===========================================================================
# Predicciones
y_pred_rf = best_rf_model.predict(X_test)
y_pred_svm = best_svm_model.predict(X_test)
y_probs_rf = best_rf_model.predict_proba(X_test)[:, 1]
y_probs_svm = best_svm_model.predict_proba(X_test)[:, 1]
# Graficar matrices de confusión y curvas ROC
fig, axes = plt.subplots(2, 2, figsize=(16, 14))
# 1. Matriz de Confusión Random Forest
cm_rf = confusion_matrix(y_test, y_pred_rf)
sns.heatmap(cm_rf, annot=True, fmt='d', cmap='Blues', ax=axes[0, 0])
axes[0, 0].set_title('Matriz de Confusión - Random Forest')
axes[0, 0].set_xlabel('Predicción')
axes[0, 0].set_ylabel('Real')
# 2. Matriz de Confusión SVM
cm_svm = confusion_matrix(y_test, y_pred_svm)
sns.heatmap(cm_svm, annot=True, fmt='d', cmap='Oranges', ax=axes[0, 1])
axes[0, 1].set_title('Matriz de Confusión - SVM')
axes[0, 1].set_xlabel('Predicción')
axes[0, 1].set_ylabel('Real')
# 3. Curva ROC
fpr_rf, tpr_rf, _ = roc_curve(y_test, y_probs_rf)
fpr_svm, tpr_svm, _ = roc_curve(y_test, y_probs_svm)
auc_rf = auc(fpr_rf, tpr_rf)
auc_svm = auc(fpr_svm, tpr_svm)
axes[1, 0].plot(fpr_rf, tpr_rf, color='blue', lw=2, label=f'Random Forest (AUC = {auc_rf:.4f})')
axes[1, 0].plot(fpr_svm, tpr_svm, color='orange', lw=2, label=f'SVM (AUC = {auc_svm:.4f})')
axes[1, 0].plot([0, 1], [0, 1], color='navy', linestyle='--')
axes[1, 0].set_xlim([0.0, 1.0])
axes[1, 0].set_ylim([0.0, 1.05])
axes[1, 0].set_xlabel('Tasa de Falsos Positivos')
axes[1, 0].set_ylabel('Tasa de Verdaderos Positivos')
axes[1, 0].set_title('Curva ROC')
axes[1, 0].legend(loc="lower right")
# 4. Tabla comparativa de métricas en Test Set
metricas_test = {
'Modelo': ['Random Forest', 'SVM'],
'Accuracy': [accuracy_score(y_test, y_pred_rf), accuracy_score(y_test, y_pred_svm)],
'Precision': [precision_score(y_test, y_pred_rf), precision_score(y_test, y_pred_svm)],
'Recall': [recall_score(y_test, y_pred_rf), recall_score(y_test, y_pred_svm)],
'F1-score': [f1_score(y_test, y_pred_rf), f1_score(y_test, y_pred_svm)]
}
df_metricas = pd.DataFrame(metricas_test)
# Mostrar tabla como heatmap de texto en la gráfica
axes[1, 1].axis('off')
axes[1, 1].set_title('Métricas Finales en Conjunto de Prueba', fontsize=14, fontweight='bold')
tabla = axes[1, 1].table(
cellText=df_metricas.values,
colLabels=df_metricas.columns,
cellLoc='center',
loc='center'
)
tabla.set_fontsize(12)
tabla.scale(1, 2)
plt.tight_layout()
plt.savefig('resultados_comparativos_modelos.png', dpi=150, bbox_inches='tight')
plt.show()
print("Gráfica guardada: resultados_comparativos_modelos.png")
# Imprimir reportes en texto
print("\n" + "=" * 60)
print("REPORTE DE CLASIFICACIÓN: RANDOM FOREST")
print("=" * 60)
print(classification_report(y_test, y_pred_rf))
print("\n" + "=" * 60)
print("REPORTE DE CLASIFICACIÓN: SVM")
print("=" * 60)
print(classification_report(y_test, y_pred_svm))
Gráfica guardada: resultados_comparativos_modelos.png
============================================================
REPORTE DE CLASIFICACIÓN: RANDOM FOREST
============================================================
precision recall f1-score support
0 0.85 0.85 0.85 110
1 0.76 0.75 0.76 69
accuracy 0.82 179
macro avg 0.81 0.80 0.80 179
weighted avg 0.82 0.82 0.82 179
============================================================
REPORTE DE CLASIFICACIÓN: SVM
============================================================
precision recall f1-score support
0 0.85 0.87 0.86 110
1 0.79 0.75 0.77 69
accuracy 0.83 179
macro avg 0.82 0.81 0.82 179
weighted avg 0.83 0.83 0.83 179
10. Dilemas Éticos y Sesgo Algorítmico en la IA¶
El dataset del Titanic, si bien se utiliza comúnmente con fines educativos, expone con claridad varios de los problemas analizados en las lecturas de Carsten (2020) y Béranger (2018) sobre la ética de los algoritmos y la revolución digital:
- Sesgo en los Datos Históricos:
El análisis exploratorio demuestra que variables como
sex(género) ypclass(clase del billete) son predictores dominantes de la supervivencia. Esto refleja políticas y sesgos de la sociedad eduardiana de 1912 ("mujeres y niños primero", y prioridad absoluta a pasajeros de primera clase). Un modelo de IA entrenado con estos datos históricos simplemente "memoriza" y perpetúa esta discriminación estructurada. - Transferencia del Sesgo a Modelos de Decisiones: Si utilizáramos un algoritmo entrenado bajo este paradigma para tomar decisiones sobre quién debe ser rescatado en un siniestro marítimo actual, el algoritmo priorizaría sistemáticamente a mujeres ricas frente a hombres de clase trabajadora. Esto ejemplifica la "falsa neutralidad" de los algoritmos de IA: los modelos no son inherentemente éticos ni objetivos; solo codifican y automatizan las asimetrías presentes en sus conjuntos de datos de entrenamiento.
- El Rol de la Auditoría y Gobernanza Algorítmica: Béranger (2018) propone marcos éticos de control y transparencia para que los ingenieros de Machine Learning auditen periódicamente las variables de decisión. En nuestro flujo automatizado, esto implica analizar la importancia de características y controlar que variables sensibles (como el género o el estatus socioeconómico) no introduzcan sesgos discriminatorios inaceptables en sistemas de toma de decisiones humanas críticas.
11. Conclusiones y Referencias¶
Conclusiones¶
- La importancia de los Pipelines: El uso de
PipelineyColumnTransformerfacilitó la separación limpia del flujo de procesamiento para variables numéricas y categóricas, asegurando un entrenamiento libre de fugas de datos y garantizando que el modelo sea almacenable y exportable directamente a producción. - Validación Cruzada Multimétrica: La validación cruzada integrada con
cross_validatepermitió comparar el desempeño global de Random Forest y SVM no solo en base al Accuracy, sino también sobre el F1-Score, que resultó más robusto al balancear la precisión y el recall. - Optimización con GridSearchCV: La búsqueda de hiperparámetros en cuadricula permitió automatizar la sintonización de parámetros complejos (como la profundidad de árboles o los coeficientes C y gamma en SVM), reduciendo significativamente el tiempo de desarrollo manual.
- La Ética en la IA: Automatizar el modelado nos recuerda que los algoritmos son espejos de los datos. La supervisión humana y la gobernanza ética son esenciales para detectar y neutralizar sesgos discriminatorios heredados del pasado histórico.
Referencias Bibliográficas¶
- Béranger, J. (2018). The Framework for Algorithmic Processing. En The algorithmic code of ethics: Ethics at the bedside of the digital revolution (pp. 122-164). John Wiley & Sons, Incorporated.
- Campesato, O. (2020). Introduction to AI / Introduction to Machine Learning / Classifiers in Machine Learning. En Artificial intelligence, machine learning, and deep learning (pp. 1-66). Mercury Learning and Information.
- Carsten, B. (2020). Ethical Issues of AI / Addressing Ethical Issues in AI. En Artificial Intelligence for a Better Future: An Ecosystem Perspective on the Ethics of AI and Emerging Digital Technologies (pp. 35-64). Springer.
- Valderrama García, L. (2026, 20 de junio). Clase 7: Automatización de procesos para el flujo de trabajo en Machine Learning [Clase magistral]. NRC-8772 Machine Learning Avanzado, Corporación Universitaria Minuto de Dios.