Introducción al mundo del aprendizaje automático
El aprendizaje automático representa una de las ramas más fascinantes de la inteligencia artificial, donde los sistemas computacionales adquieren la capacidad de detectar patrones y tomar decisiones sin haber sido programados explícitamente para cada tarea. En esta guía, exploraremos desde los conceptos fundamentales hasta la implementación avanzada de modelos.
El lenguaje Python como ecosistema de datos
Python se ha consolidado como el estándar de facto en ciencia de datos gracias a su sintaxis clara, su comunidad activa y un ecosistema de bibliotecas extraordinariamente robusto. Las bibliotecas esenciales incluyen:
- NumPy: Operaciones numéricas de alto rendimiento con arreglos multidimensionales
- Pandas: Manipulación y análisis de datos estructurados mediante DataFrames
- Matplotlib y Seaborn: Visualización de datos con gráficos estáticos e interactivos
- Scikit-learn: Implementación de algoritmos clásicos de aprendizaje automático
- TensorFlow y PyTorch: Construcción y entrenamiento de redes neuronales profundas
Preparación del entorno de desarrollo
La configuración adecuada del entorno es el primer paso indispensable. Recomendamos utilizar un gestor de entornos virtuales que garantice aislamiento entre proyectos.
Instalación con Anaconda
Anaconda es una distribución especializada que incluye Python, el gestor de paquetes conda y numersoas bibliotecas preinstaladas. Una vez instalado, podemos crear entornos aislados para cada proyecto:
conda create --name proyecto_ml python=3.10
conda activate proyecto_ml
conda install numpy pandas scikit-learn matplotlib seaborn jupyter
Jupyter Notebook
Jupyter proporciona un entorno interactivo donde podemos combinar código ejecutable, texto en formato Markdown y visualizaciones en un solo documento. Para iniciarlo:
jupyter notebook
Fundamentos de NumPy para computación numérica
NumPy es la piedra angular del ecosistema científico de Python. Su objeto central, el arreglo n-dimensional (ndarray), ofrece operaciones vectorizadas con rendimiento comparable al código C nativo.
Creación y manipulación de arreglos
import numpy as np
# Generación de arreglos con distintos métodos
datos_enteros = np.arange(0, 20, 2)
puntos_lineales = np.linspace(-5.0, 5.0, num=11)
matriz_vacia = np.zeros((4, 6))
identidad = np.eye(5)
aleatorio = np.random.standard_normal((3, 4))
# Propiedades fundamentales
print(f"Dimensiones: {aleatorio.ndim}")
print(f"Forma: {aleatorio.shape}")
print(f"Tipo de dato: {aleatorio.dtype}")
Indexación, segmentación y difusión
# Indexación booleana
valores = np.array([12, 7, 35, 2, 19, 8, 41])
mascara = valores > 15
seleccion = valores[mascara]
print(f"Valores mayores a 15: {seleccion}")
# Difusión (broadcasting)
matriz = np.ones((3, 4))
vector_columna = np.array([[10], [20], [30]])
resultado = matriz + vector_columna
print(resultado)
Pandas para análisis de datos estructurados
Pandas introduce dos estructuras de datos fundamentales: Series para datos unidimensionales y DataFrame para datos tabulares bidimensionales, similar a una hoja de cálculo con etiquetas.
Exploración inicial de datos
import pandas as pd
# Carga de datos desde un archivo CSV
registros = pd.read_csv('dataset_ejemplo.csv')
# Inspección rápida
print(registros.head(10))
print(registros.info())
print(registros.describe())
# Verificar valores faltantes
total_nulos = registros.isnull().sum()
porcentaje = (total_nulos / len(registros)) * 100
print(porcentaje[porcentaje > 0])
Selección, filtrado y agrupamiento
# Selección con loc e iloc
fila_especifica = registros.loc[5]
subset = registros.iloc[10:20, :3]
# Filtrado condicional
filtro = registros[(registros['precio'] > 500) & (registros['categoria'] == 'tecnologia')]
# Agrupamiento y agregación
resumen = registros.groupby('region').agg(
ventas_totales=('monto', 'sum'),
cantidad_ventas=('monto', 'count'),
ticket_promedio=('monto', 'mean')
).sort_values('ventas_totales', ascending=False)
print(resumen)
Visualización de datos
La visualización permite transformar datos tabulares en representaciones gráficas que revelan patrones ocultos, distribuciones y relaciones entre variables.
import matplotlib.pyplot as plt
import seaborn as sns
# Configuración de estilo
sns.set_theme(style="whitegrid")
fig, ejes = plt.subplots(2, 2, figsize=(14, 10))
# Histograma de distribución
sns.histplot(data=registros, x='monto', bins=30, kde=True, ax=ejes[0, 0])
ejes[0, 0].set_title('Distribución de montos')
# Gráfico de dispersión
sns.scatterplot(data=registros, x='edad_cliente', y='monto', hue='region', ax=ejes[0, 1])
ejes[0, 1].set_title('Relación edad vs monto')
# Gráfico de barras
top_productos = registros.groupby('producto')['monto'].sum().nlargest(10)
top_productos.plot(kind='barh', ax=ejes[1, 0])
ejes[1, 0].set_title('Top 10 productos por ventas')
# Mapa de calor de correlaciones
columnas_numericas = registros.select_dtypes(include=[np.number])
sns.heatmap(columnas_numericas.corr(), annot=True, cmap='RdYlBu_r', ax=ejes[1, 1])
ejes[1, 1].set_title('Matriz de correlación')
plt.tight_layout()
plt.show()
Preprocesamiento y preparación de datos
El preprocesamiento consume típicamente entre el 60% y el 80% del tiempo total de un proyecto de aprendizaje automático. La calidad de los datos de entrada determina directamente el techo de rendimiento de cualquier modelo.
Manejo de valores faltantes
from sklearn.impute import SimpleImputer, KNNImputer
# Imputación con la mediana (robusta ante valores extremos)
imputador_mediana = SimpleImputer(strategy='median')
columnas_num = registros.select_dtypes(include=[np.number]).columns
registros[columnas_num] = imputador_mediana.fit_transform(registros[columnas_num])
# Imputación con KNN (más sofisticada)
imputador_knn = KNNImputer(n_neighbors=5)
registros_knn = pd.DataFrame(
imputador_knn.fit_transform(registros[columnas_num]),
columns=columnas_num
)
Codificación de variables categóricas
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder
# Codificación ordinal para variables con orden natural
orden_educacion = ['primaria', 'secundaria', 'universidad', 'posgrado']
encoder_ordinal = OrdinalEncoder(categories=[orden_educacion])
registros['nivel_educativo_cod'] = encoder_ordinal.fit_transform(registros[['nivel_educativo']])
# Codificación one-hot para variables nominales
registros_encoded = pd.get_dummies(registros, columns=['ciudad', 'genero'], drop_first=True)
Escalado de características numéricas
from sklearn.preprocessing import StandardScaler, RobustScaler
# Estandarización (media=0, desviación=1)
escalador_std = StandardScaler()
X_estandarizado = escalador_std.fit_transform(X)
# Escalado robusto (menos sensible a valores atípicos)
escalador_rob = RobustScaler()
X_robusto = escalador_rob.fit_transform(X)
Ingeniería de características
# Creación de nuevas características a partir de existentes
registros['ratio_precio_cantidad'] = registros['precio'] / (registros['cantidad'] + 1)
registros['antiguedad_meses'] = (pd.Timestamp.now() - pd.to_datetime(registros['fecha_registro'])).dt.days / 30
registros['es_fin_de_semana'] = pd.to_datetime(registros['fecha']).dt.dayofweek.isin([5, 6]).astype(int)
Evaluación y selección de modelos
Antes de entrenar cualquier algoritmo, es fundamental establecer métricas de evaluación y una metodología rigurosa para estimar el rendimiento en datos no vistos.
División de conjuntos
from sklearn.model_selection import train_test_split
X_entrenamiento, X_prueba, y_entrenamiento, y_prueba = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
Métricas para clasificación
from sklearn.metrics import (confusion_matrix, classification_report,
roc_auc_score, average_precision_score,
roc_curve, precision_recall_curve)
def evaluar_clasificador(nombre, modelo, X_eval, y_eval):
predicciones = modelo.predict(X_eval)
probabilidades = modelo.predict_proba(X_eval)[:, 1]
print(f"\n{'='*50}")
print(f"Resultados de: {nombre}")
print(f"{'='*50}")
print(classification_report(y_eval, predicciones))
auc_roc = roc_auc_score(y_eval, probabilidades)
auc_pr = average_precision_score(y_eval, probabilidades)
print(f"AUC-ROC: {auc_roc:.4f}")
print(f"AUC-PR: {auc_pr:.4f}")
return predicciones, probabilidades
Métricas para regresión
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
def evaluar_regresor(nombre, modelo, X_eval, y_eval):
predicciones = modelo.predict(X_eval)
mse = mean_squared_error(y_eval, predicciones)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_eval, predicciones)
r2 = r2_score(y_eval, predicciones)
print(f"\nRegresor: {nombre}")
print(f" RMSE: {rmse:.4f}")
print(f" MAE: {mae:.4f}")
print(f" R²: {r2:.4f}")
Validación cruzada
from sklearn.model_selection import cross_val_score, StratifiedKFold
# Validación cruzada estratificada de 5 pliegues
estrategia_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
puntuaciones = cross_val_score(
modelo, X_entrenamiento, y_entrenamiento,
cv=estrategia_cv, scoring='accuracy'
)
print(f"Precisión CV: {puntuaciones.mean():.4f} ± {puntuaciones.std():.4f}")
Optimización de hiperparámetros
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint
espacio_parametros = {
'n_estimators': randint(50, 300),
'max_depth': randint(3, 15),
'learning_rate': uniform(0.01, 0.3),
'subsample': uniform(0.6, 0.4),
'colsample_bytree': uniform(0.6, 0.4)
}
busqueda_aleatoria = RandomizedSearchCV(
estimator=modelo_base,
param_distributions=espacio_parametros,
n_iter=50,
cv=estrategia_cv,
scoring='roc_auc',
random_state=42,
n_jobs=-1
)
busqueda_aleatoria.fit(X_entrenamiento, y_entrenamiento)
print(f"Mejores parámetros: {busqueda_aleatoria.best_params_}")
print(f"Mejor AUC-ROC CV: {busqueda_aleatoria.best_score_:.4f}")
Algoritmos de aprendizaje supervisado: clasificación
Regresión logística
La regresión logística modela la probabilidad de pertenencia a una clase mediante la función sigmoide. Es un clasificador lineal que produce salidas probabilísticas, lo que la hace ideal como modelo de referencia inicial.
from sklearn.linear_model import LogisticRegression
clasificador_lr = LogisticRegression(
penalty='l2',
C=1.0,
solver='lbfgs',
max_iter=1000,
random_state=42
)
clasificador_lr.fit(X_entrenamiento_esc, y_entrenamiento)
evaluar_clasificador("Regresión Logística", clasificador_lr, X_prueba_esc, y_prueba)
Vecinos más cercanos (KNN)
KNN clasifica un punto nuevo según la mayoría de etiquetas de sus k vecinos más próximos. Requiere obligatoriamente escalado de características y su rendimiento se degrada con datos de alta dimensionalidad.
from sklearn.neighbors import KNeighborsClassifier
clasificador_knn = KNeighborsClassifier(n_neighbors=7, metric='minkowski', p=2)
clasificador_knn.fit(X_entrenamiento_esc, y_entrenamiento)
evaluar_clasificador("KNN", clasificador_knn, X_prueba_esc, y_prueba)
Máquinas de vectores de soporte (SVM)
SVM busca el hiperplano que maximiza el margen de separación entre clases. Mediante el truco del kernel, puede manejar fronteras de decisión no lineales. El kernel RBF es el más versátil para problemas generales.
from sklearn.svm import SVC
clasificador_svm = SVC(
kernel='rbf',
C=10.0,
gamma='scale',
probability=True,
random_state=42
)
clasificador_svm.fit(X_entrenamiento_esc, y_entrenamiento)
evaluar_clasificador("SVM (RBF)", clasificador_svm, X_prueba_esc, y_prueba)
Árboles de decisión y bosques aleatorios
Los árboles de decisión particionan recursivamente el espacio de características siguiendo reglas interpretables del tipo "si-entonces". Los bosques aleatorios combinan cientos de árboles entrenados con distintas submuestras y subconjuntos de características, reduciendo drásticamente la varianza.
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
# Bosque aleatorio
clasificador_rf = RandomForestClassifier(
n_estimators=200,
max_depth=10,
min_samples_leaf=5,
random_state=42,
n_jobs=-1
)
clasificador_rf.fit(X_entrenamiento, y_entrenamiento)
evaluar_clasificador("Bosque Aleatorio", clasificador_rf, X_prueba, y_prueba)
# Importancia de características
importancias = pd.Series(
clasificador_rf.feature_importances_,
index=X.columns
).sort_values(ascending=False)
print("Características más relevantes:")
print(importancias.head(10))
Algoritmos de aprendizaje supervisado: regresión
Regresión lineal y regularización
from sklearn.linear_model import Ridge, Lasso, ElasticNet
# Ridge (L2) - Reduce coeficientes pero no los lleva a cero
reg_ridge = Ridge(alpha=1.0)
reg_ridge.fit(X_entrenamiento_esc, y_entrenamiento)
# Lasso (L1) - Puede eliminar características (coeficientes a cero)
reg_lasso = Lasso(alpha=0.1)
reg_lasso.fit(X_entrenamiento_esc, y_entrenamiento)
caracteristicas_seleccionadas = np.sum(reg_lasso.coef_ != 0)
# ElasticNet - Combina L1 y L2
reg_elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)
reg_elastic.fit(X_entrenamiento_esc, y_entrenamiento)
Regresión con métodos de conjunto
from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor
import xgboost as xgb
# Impulso de gradiente
reg_gb = GradientBoostingRegressor(
n_estimators=150,
learning_rate=0.08,
max_depth=4,
subsample=0.8,
random_state=42
)
reg_gb.fit(X_entrenamiento, y_entrenamiento)
evaluar_regresor("Gradient Boosting", reg_gb, X_prueba, y_prueba)
# XGBoost
reg_xgb = xgb.XGBRegressor(
n_estimators=200,
learning_rate=0.05,
max_depth=5,
reg_lambda=1.0,
random_state=42
)
reg_xgb.fit(X_entrenamiento, y_entrenamiento)
evaluar_regresor("XGBoost", reg_xgb, X_prueba, y_prueba)
Algoritmos de aprendizaje no supervisado
Agrupamiento con K-Means
from sklearn.cluster import KMeans, DBSCAN
from sklearn.metrics import silhouette_score
# Método del codo y coeficiente de silueta
rango_k = range(2, 11)
inercia_lista = []
silueta_lista = []
for k in rango_k:
modelo_km = KMeans(n_clusters=k, n_init=10, random_state=42)
etiquetas = modelo_km.fit_predict(X_escala)
inercia_lista.append(modelo_km.inertia_)
silueta_lista.append(silhouette_score(X_escala, etiquetas))
# Visualización
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
ax1.plot(rango_k, inercia_lista, 'bo-')
ax1.set_xlabel('Número de clusters')
ax1.set_ylabel('Inercia')
ax1.set_title('Método del codo')
ax2.plot(rango_k, silueta_lista, 'ro-')
ax2.set_xlabel('Número de clusters')
ax2.set_ylabel('Coeficiente de silueta')
ax2.set_title('Puntuación de silueta')
plt.tight_layout()
plt.show()
DBSCAN: agrupamiento basado en densidad
from sklearn.datasets import make_moons
# Datos no lineales
X_lunas, _ = make_moons(n_samples=300, noise=0.08, random_state=42)
modelo_dbscan = DBSCAN(eps=0.25, min_samples=5)
etiquetas_db = modelo_dbscan.fit_predict(X_lunas)
plt.figure(figsize=(8, 6))
plt.scatter(X_lunas[:, 0], X_lunas[:, 1], c=etiquetas_db, cmap='Spectral', s=50)
plt.title('Agrupamiento DBSCAN')
plt.show()
Reducción de dimensionalidad con PCA
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# Estandarización previa (imprescindible para PCA)
escalador_pca = StandardScaler()
X_std = escalador_pca.fit_transform(X_numerico)
# PCA con retención del 95% de la varianza
pca_reductor = PCA(n_components=0.95)
X_reducido = pca_reductor.fit_transform(X_std)
print(f"Dimensiones originales: {X_numerico.shape[1]}")
print(f"Dimensiones tras PCA: {pca_reductor.n_components_}")
print(f"Varianza explicada acumulada: {pca_reductor.explained_variance_ratio_.sum():.4f}")
# Gráfico de varianza acumulada
plt.figure(figsize=(8, 5))
plt.plot(np.cumsum(PCA().fit(X_std).explained_variance_ratio_), 'g-o')
plt.axhline(y=0.95, color='r', linestyle='--', label='Umbral 95%')
plt.xlabel('Número de componentes')
plt.ylabel('Varianza explicada acumulada')
plt.title('Gráfico de sedimentación')
plt.legend()
plt.grid(True)
plt.show()
Proyecto práctico: detección de transacciones fraudulentas
Este caso de uso real demuestra cómo abordar un problema de clasificación con datos altamente desbalanceados, donde las transacciones fraudulentas constituyen menos del 0.2% del total.
Comprensión del desbalance de clases
import pandas as pd
import numpy as np
from sklearn.preprocessing import RobustScaler
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split
# Carga del dataset
transacciones = pd.read_csv('creditcard.csv')
# Análisis de desbalance
distribucion_clases = transacciones['Class'].value_counts()
print(f"Transacciones legítimas: {distribucion_clases[0]}")
print(f"Transacciones fraudulentas: {distribucion_clases[1]}")
print(f"Ratio de fraude: {distribucion_clases[1] / distribucion_clases.sum() * 100:.4f}%")
# Escalado robusto de monto y tiempo
esc_robusto = RobustScaler()
transacciones['monto_escalado'] = esc_robusto.fit_transform(transacciones[['Amount']])
transacciones['tiempo_escalado'] = esc_robusto.fit_transform(transacciones[['Time']])
transacciones.drop(['Amount', 'Time'], axis=1, inplace=True)
Aplicación de SMOTE y entrenamiento
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, average_precision_score
# Preparación de datos
atributos = transacciones.drop('Class', axis=1)
objetivo = transacciones['Class']
# División manteniendo la proporción de clases
X_train, X_test, y_train, y_test = train_test_split(
atributos, objetivo, test_size=0.2, random_state=42, stratify=objetivo
)
# SMOTE solo en el conjunto de entrenamiento
generador_smote = SMOTE(random_state=42, sampling_strategy=0.5)
X_train_bal, y_train_bal = generador_smote.fit_resample(X_train, y_train)
print(f"Antes de SMOTE: {y_train.value_counts().to_dict()}")
print(f"Después de SMOTE: {pd.Series(y_train_bal).value_counts().to_dict()}")
# Entrenamiento con múltiples modelos
modelos = {
'Reg. Logística': LogisticRegression(solver='lbfgs', max_iter=1000),
'Bosque Aleatorio': RandomForestClassifier(n_estimators=100, random_state=42, n_jobs=-1)
}
for nombre, mod in modelos.items():
mod.fit(X_train_bal, y_train_bal)
pred = mod.predict(X_test)
probas = mod.predict_proba(X_test)[:, 1]
auc_pr = average_precision_score(y_test, probas)
print(f"\n--- {nombre} ---")
print(classification_report(y_test, pred, target_names=['Legítima', 'Fraudulenta']))
print(f"AUC-PR: {auc_pr:.4f}")
Interpretabilidad con SHAP
import shap
# Crear explicador para el modelo de bosque aleatorio
explicador = shap.TreeExplainer(modelos['Bosque Aleatorio'])
valores_shap = explicador.shap_values(X_test)
# Gráfico resumen de importancia de características
shap.summary_plot(valores_shap[1], X_test, plot_type="bar")
# Explicación de una predicción individual
shap.force_plot(
explicador.expected_value[1],
valores_shap[1][0, :],
X_test.iloc[0, :]
)
Proyecto práctico: análisis de sentimientos en texto
El procesamiento de lenguaje natural requiere transformar texto sin estructura en representaciones numéricas que los modelos puedan procesar.
Preprocesamiento textual
import re
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
nltk.download('punkt')
nltk.download('stopwords')
conjunto_stopwords = set(stopwords.words('english'))
def limpiar_texto(texto):
texto = texto.lower()
texto = re.sub(r'<[^>]+>', '', texto)
texto = re.sub(r'[^a-zA-Z\s]', '', texto)
texto = re.sub(r'\s+', ' ', texto).strip()
tokens = word_tokenize(texto)
tokens_filtrados = [t for t in tokens if t not in conjunto_stopwords and len(t) > 2]
return ' '.join(tokens_filtrados)
opiniones['texto_limpio'] =opiniones['texto_original'].apply(limpiar_texto)
Vectorización y modelado
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.pipeline import Pipeline
# Pipeline completo: vectorización + clasificación
flujo_trabajo = Pipeline([
('vectorizador', TfidfVectorizer(max_features=10000, ngram_range=(1, 2))),
('clasificador', LogisticRegression(C=1.0, max_iter=1000))
])
X_texto_train, X_texto_test, y_texto_train, y_texto_test = train_test_split(
opiniones['texto_limpio'], opiniones['sentimiento'],
test_size=0.2, random_state=42
)
flujo_trabajo.fit(X_texto_train, y_texto_train)
predicciones_texto = flujo_trabajo.predict(X_texto_test)
print(classification_report(y_texto_test, predicciones_texto))
Introducción a las redes neuronales
Las redes neuronales artificiales se inspiran en la estructura del cerebro humano. Una neurona artificial recibe entradas ponderadas, las suma, aplica una función de activación no lineal y produce una salida.
Construcción con Keras/TensorFlow
import tensorflow as tf
from tensorflow.keras import layers, models, callbacks
# Arquitectura de red neuronal multicapa
def crear_red(input_dim):
modelo = models.Sequential([
layers.Dense(128, activation='relu', input_shape=(input_dim,)),
layers.BatchNormalization(),
layers.Dropout(0.3),
layers.Dense(64, activation='relu'),
layers.BatchNormalization(),
layers.Dropout(0.3),
layers.Dense(32, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
modelo.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
loss='binary_crossentropy',
metrics=['accuracy', tf.keras.metrics.AUC(name='auc')]
)
return modelo
red_neuronal = crear_red(X_entrenamiento_esc.shape[1])
# Callbacks para entrenamiento eficiente
lista_callbacks = [
callbacks.EarlyStopping(monitor='val_auc', patience=10, restore_best_weights=True, mode='max'),
callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5)
]
historial = red_neuronal.fit(
X_entrenamiento_esc, y_entrenamiento,
validation_split=0.15,
epochs=100,
batch_size=256,
callbacks=lista_callbacks,
verbose=1
)
Visualización del historial de entrenamiento
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
ax1.plot(historial.history['loss'], label='Entrenamiento')
ax1.plot(historial.history['val_loss'], label='Validación')
ax1.set_title('Pérdida durante el entrenamiento')
ax1.set_xlabel('Época')
ax1.set_ylabel('Pérdida')
ax1.legend()
ax2.plot(historial.history['auc'], label='Entrenamiento')
ax2.plot(historial.history['val_auc'], label='Validación')
ax2.set_title('AUC durante el entrenamiento')
ax2.set_xlabel('Época')
ax2.set_ylabel('AUC')
ax2.legend()
plt.tight_layout()
plt.show()
Despliegue de modelos en producción
Persistencia del modelo
import joblib
# Guardar modelo entrenado
joblib.dump(mejor_modelo, 'modelo_fraude_v2.joblib', compress=3)
# Cargar modelo en otro entorno
modelo_cargado = joblib.load('modelo_fraude_v2.joblib')
prediccion_nueva = modelo_cargado.predict(nuevos_datos)
Servicio API con FastAPI
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List
import joblib
import numpy as np
aplicacion = FastAPI(title="API Detección de Fraude")
# Carga del modelo al iniciar el servicio
modelo_servicio = joblib.load('modelo_fraude_v2.joblib')
class SolicitudTransaccion(BaseModel):
caracteristicas: List[float]
@aplicacion.post("/predecir")
def predecir_fraude(solicitud: SolicitudTransaccion):
arreglo_datos = np.array(solicitud.caracteristicas).reshape(1, -1)
prediccion = modelo_servicio.predict(arreglo_datos)
probabilidades = modelo_servicio.predict_proba(arreglo_datos)
return {
"es_fraude": int(prediccion[0]),
"prob_fraude": float(probabilidades[0][1]),
"prob_legitima": float(probabilidades[0][0])
}
Contenedorización con Docker
# Dockerfile
FROM python:3.10-slim
WORKDIR /aplicacion
COPY requisitos.txt .
RUN pip install --no-cache-dir -r requisitos.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:aplicacion", "--host", "0.0.0.0", "--port", "8000"]
Panorama de la inteligencia artificial moderna
Arquitecturas de aprendizaje profundo
Las redes neuronales convolucionales (CNN) dominan el reconocimiento visual al explotar la estructura espacial de las imágenes mediante filtros compartidos. Las redes recurrentes (LSTM/GRU) capturan dependencias temporales en secuencias. El### Aprendizaje por refuerzo
En esta modalidad, un agente aprende políticas de acción óptimas mediante interacción con un entorno, maximizando la recompensa acumulada a lo largo del tiempo. Algoritmos como DQN, PPO y SAC han logrado superar el rendimiento humano en juegos, robótica y optimización de sistemas.
Redes neuronales sobre grafos
Las GNN extienden las redes neuronales a datos con estructura de grafo (redes sociales, moléculas, infraestructura), propagando información entre nodos vecinos mediante el mecanismo de paso de mensajes.
Aprendizaje federado
Permite entrenar modelos de forma colaborativa entre múltiples dispositivos sin compartir datos crudos, preservando la privacidad. Cada participante entrena localmente y solo comparte actualizaciones de parámetros con un servidor central.
Conclusión ----------
El dominio del aprendizaje automático exige un equilibrio constante entre la comprensión teórica y la experimentación práctica. Cada algoritmo presentado aquí es una herramienta con fortalezas y limitaciones específicas. La verdadera maestría radica en saber cuándo y por qué seleccionar cada una.
El campo evoluciona a un ritmo acelerado. Mantenga una rutina de lectura de artículos académicos, participe en competencias de ciencia de datos, contribuya a proyectos de código abierto y nunca deje de experimentar con nuevos enfoques sobre problemas reales.