En el vertiginoso mundo actual, las palabras de moda como Machine Learning (ML), Inteligencia Artificial (IA), Procesamiento del Lenguaje Natural (NLP) e IA generativa están por todas partes. Es fácil sentirse abrumado, sobre todo cuando parece que todo el mundo es ya un experto. Tanto si eres un estudiante que se adentra en la IA/ML por primera vez, un entusiasta con conocimientos teóricos que quiere ensuciarse las manos o alguien que simplemente siente curiosidad por este apasionante campo, este artículo es para ti.
Recorreremos juntos algunos de los modelos de ML más fundamentales, pasaremos a los intermedios y, por último, exploraremos técnicas avanzadas. Utilizaremos conjuntos de datos existentes para ver cómo estos modelos pueden ayudarnos a dar sentido a los datos y hacer predicciones.
Tabla de contenidos
Introducción a los modelos de Machine Learning
El Machine Learning consiste en enseñar a los ordenadores a aprender de los datos. En esencia, el aprendizaje automático consiste en proporcionar datos a algoritmos que pueden realizar predicciones o tomar decisiones sin estar explícitamente programados para realizar la tarea. Comprender los diferentes tipos de modelos de ML y cuándo utilizarlos es crucial para resolver problemas del mundo real.
El conjunto de datos: Predicción del precio de la vivienda
Utilizaremos el conjunto de datos California Housing Prices, que contiene información sobre la vivienda en California a partir del censo de 1990. El conjunto de datos incluye características como la renta media, la antigüedad de la vivienda, el número medio de habitaciones, etc. Nuestro objetivo es predecir el valor medio de la vivienda basándonos en estas características.
Objetivo: Utilizar diferentes modelos ML para predecir el precio de la vivienda basándonos en las características proporcionadas.
Modelos básicos
Empecemos por lo básico. Exploraremos modelos sencillos pero potentes que constituyen la base del Machine Learning.
Regresión lineal
La regresión lineal es una de las técnicas estadísticas más sencillas y utilizadas para el modelado predictivo. Modela la relación entre una variable dependiente (objetivo) y una o más variables independientes (características) ajustando una ecuación lineal a los datos observados.
¿Por qué utilizar la regresión lineal?
- Simplicidad: Fácil de aplicar e interpretar.
- Eficacia: Funciona bien con datos relacionados linealmente.
- Casos prácticos: Predicción de precios, previsión de tendencias, evaluación de riesgos.
Aplicación de la regresión lineal
Analicemos el código para ver cómo funciona la regresión lineal con el conjunto de datos California Housing.
# Import necessary libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Load the dataset
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
# Create a DataFrame
data = pd.DataFrame(housing.data, columns=housing.feature_names)
data['MedHouseVal'] = housing.target
# Display the first few rows
print(data.head())
Análisis exploratorio de datos
En primer lugar, realizamos un análisis exploratorio de datos (EDA) para comprender los datos.
# Scatter plot of MedInc vs MedHouseVal
plt.figure(figsize=(8, 6))
sns.scatterplot(x='MedInc', y='MedHouseVal', data=data, alpha=0.3)
plt.title('Median Income vs. Median House Value')
plt.xlabel('Median Income (in $10,000)')
plt.ylabel('Median House Value (in $100,000)')
plt.show()
Explicación:
- El diagrama de dispersión muestra una relación lineal positiva entre la renta media y el valor medio de la vivienda.
- A medida que aumenta la renta media, tiende a aumentar el valor medio de la vivienda.
División de los datos
Dividimos los datos en conjuntos de entrenamiento y de prueba para evaluar el rendimiento del modelo en datos no vistos.
from sklearn.model_selection import train_test_split
X = data[['MedInc']] # Independent variable
y = data['MedHouseVal'] # Dependent variable
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
Entrenamiento del modelo
from sklearn.linear_model import LinearRegression
# Initialize and train the model
lr_model = LinearRegression()
lr_model.fit(X_train, y_train)
Explicación:
- Inicializamos el modelo LinearRegression y lo ajustamos utilizando los datos de entrenamiento.
- El modelo aprende la relación entre MedInc y MedHouseVal.
Predicción y evaluación
from sklearn.metrics import mean_squared_error, r2_score
# Make predictions
y_pred = lr_model.predict(X_test)
# Evaluate the model
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"Linear Regression MSE: {mse:.2f}")
print(f"Linear Regression R² Score: {r2:.2f}")
Output
Linear Regression MSE: 0.53
Linear Regression R² Score: 0.44
Explicación:
- Error cuadrático medio (MSE): Mide la diferencia cuadrática media entre los valores predichos y los reales. Un MSE más bajo indica un mejor rendimiento del modelo.
- Puntuación R²: Representa en qué medida la variable independiente explica la varianza de la variable dependiente. Una puntuación R² cercana a 1 indica un mejor ajuste.
En este caso, la puntuación R² es de 0,44, lo que significa que aproximadamente el 44% de la varianza del valor medio de la vivienda puede explicarse sólo por la mediana de los ingresos.
Visualización de la línea de regresión
# Plotting the regression line
plt.figure(figsize=(8, 6))
plt.scatter(X_test, y_test, color='blue', alpha=0.3, label='Actual')
plt.plot(X_test, y_pred, color='red', linewidth=2, label='Predicted')
plt.title('Linear Regression: Actual vs Predicted')
plt.xlabel('Median Income (in $10,000)')
plt.ylabel('Median House Value (in $100,000)')
plt.legend()
plt.show()
Explicación:
- La línea roja representa los valores de las viviendas predichos por nuestro modelo.
- Los puntos azules son los valores reales del conjunto de pruebas.
- El modelo capta la tendencia general, pero no tiene en cuenta toda la variabilidad.
Conclusión clave:
- La regresión lineal es una forma sencilla de modelizar la relación entre variables.
- Se limita a relaciones lineales y puede no captar patrones complejos en los datos.
Árboles de decisión
Un árbol de decisión es una estructura similar a un diagrama de flujo en la que cada nodo interno representa una característica (atributo), cada rama representa una regla de decisión y cada nodo hoja representa el resultado.
¿Por qué utilizar árboles de decisión?
- Interpretabilidad: Fáciles de entender y visualizar.
- No paramétricos: Sin suposiciones sobre la distribución de los datos.
- Casos de uso: Tareas de clasificación y regresión, selección de características.
Implementación de árboles de decisión
Seguiremos utilizando el conjunto de datos California Housing pero incluiremos más características.
# Selecting multiple features
X = data[['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']]
y = data['MedHouseVal']
# Splitting the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
Entrenamiento del modelo
from sklearn.tree import DecisionTreeRegressor
from sklearn import tree
# Initialize and train the model
dt_model = DecisionTreeRegressor(max_depth=5)
dt_model.fit(X_train, y_train)
Explicación:
- Inicializamos el DecisionTreeRegressor con una profundidad máxima de 5 para evitar el sobreajuste.
- El modelo aprende reglas de decisión a partir de las características para predecir el valor medio de la vivienda.
Predicción y evaluación
# Make predictions
y_pred = dt_model.predict(X_test)
# Evaluate the model
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"Decision Tree MSE: {mse:.2f}")
print(f"Decision Tree R² Score: {r2:.2f}")
Output:
Decision Tree MSE: 0.46
Decision Tree R² Score: 0.52
Explicación:
- El modelo de Árbol de decisión tiene un MSE más bajo y una puntuación R² más alta en comparación con el modelo de Regresión lineal.
- Esto indica que el modelo de Árbol de decisión se ajusta mejor a los datos y explica más varianza.
Visualización del Árbol de decisión
# Plotting the decision tree
plt.figure(figsize=(20, 10))
tree.plot_tree(dt_model, feature_names=X.columns, filled=True)
plt.show()
Explicación:
- La visualización muestra cómo el modelo toma decisiones basándose en los valores de las características.
- Cada nodo representa un punto de decisión, y las hojas representan los valores predichos.
Conclusión clave:
- Los árboles de decisión pueden captar relaciones e interacciones no lineales entre características.
- Son fáciles de interpretar, pero pueden sobreajustarse si no se controlan adecuadamente (por ejemplo, estableciendo max_depth).
Modelos intermedios
Ahora vamos a explorar modelos más complejos que pueden manejar conjuntos de datos más grandes y capturar patrones intrincados.
Random Forest
Random Forest es un método de aprendizaje conjunto que construye múltiples árboles de decisión durante el entrenamiento y genera la predicción media de los árboles individuales.
¿Por qué utilizar Random Forest?
- Mayor precisión: Reduce el sobreajuste al promediar múltiples árboles.
- Robustez: Maneja grandes conjuntos de datos con mayor dimensionalidad.
- Casos de uso: Regresión, clasificación, jerarquización de características.
Implementación de Random Forest
Continuaremos con el conjunto de datos California Housing:
from sklearn.ensemble import RandomForestRegressor
# Initialize and train the model
rf_model = RandomForestRegressor(n_estimators=100, max_depth=7)
rf_model.fit(X_train, y_train)
Predicción y evaluación
# Make predictions
y_pred = rf_model.predict(X_test)
# Evaluate the model
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"Random Forest MSE: {mse:.2f}")
print(f"Random Forest R² Score: {r2:.2f}")
Output:
Random Forest MSE: 0.33
Random Forest R² Score: 0.66
Explicación:
- El modelo Random Forest tiene un MSE más bajo y una puntuación R² más alta en comparación con el modelo Decision Tree.
- Esto indica que Random Forest mejora el rendimiento al reducir el sobreajuste.
Visualización de la importancia de las características
# Get feature importances
importances = rf_model.feature_importances_
feature_names = X.columns
# Create a bar plot
plt.figure(figsize=(10, 6))
sns.barplot(x=importances, y=feature_names)
plt.title('Feature Importances in Random Forest')
plt.show()
Explicación:
- El gráfico muestra qué características contribuyen más a las predicciones del modelo.
- MedInc (renta media) es la característica más importante, seguida de la latitud y la longitud.
Conclusión clave:
- Random Forest es eficaz para tareas de regresión, especialmente cuando el sobreajuste es un problema.
- Proporciona una mayor precisión e información sobre la importancia de las características.
Support Vector Machines (SVM)
Support Vector Machines (SVM) es un algoritmo de aprendizaje automático supervisado que puede utilizarse para tareas de clasificación o regresión. Funciona encontrando el hiperplano que mejor divide un conjunto de datos en clases.
¿Por qué utilizar SVM?
- Eficaz en altas dimensiones: Funciona bien con un claro margen de separación.
- Versátil: Se pueden especificar diferentes funciones de kernel.
- Casos de uso: Categorización de textos, clasificación de imágenes, bioinformática.
Conjunto de datos: Clasificación de especies de iris
Utilizaremos el clásico conjunto de datos Iris, que contiene mediciones de flores de iris y sus especies. Nuestro objetivo es clasificar las especies basándonos en las mediciones.
Implementando SVM
# Import necessary libraries
from sklearn.datasets import load_iris
from sklearn.svm import SVC
# Load the dataset
iris = load_iris()
X = iris.data
y = iris.target
# Splitting the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
Entrenando el modelo
# Initialize and train the model
svm_model = SVC(kernel='linear')
svm_model.fit(X_train, y_train)
Predicción y evaluación
from sklearn.metrics import accuracy_score
# Make predictions
y_pred = svm_model.predict(X_test)
# Evaluate the model
accuracy = accuracy_score(y_test, y_pred)
print(f"SVM Accuracy: {accuracy:.2f}")
Output
SVM Accuracy: 1.00
Explicación:
- El modelo SVM alcanza una precisión del 100% en el conjunto de pruebas.
- Esto indica que el modelo clasificó perfectamente todas las muestras de la prueba.
Visualización de los límites de decisión
# Since visualizing in higher dimensions is complex, we'll use only two features
X_vis = X[:, :2] # Using only the first two features
y_vis = y
# Splitting the data
X_train_vis, X_test_vis, y_train_vis, y_test_vis = train_test_split(X_vis, y_vis, test_size=0.2)
# Training the model
svm_model_vis = SVC(kernel='linear')
svm_model_vis.fit(X_train_vis, y_train_vis)
# Plotting decision boundaries
def plot_decision_boundaries(X, y, model):
h = .02 # step size in the mesh
# Create color maps
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ['#FF0000', '#00FF00', '#0000FF']
# Meshgrid
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# Predict class for each point in mesh
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# Plot contours
plt.figure(figsize=(8, 6))
plt.contourf(xx, yy, Z, cmap=cmap_light)
# Plot training points
sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=y, palette=cmap_bold, edgecolor='k')
plt.xlabel(iris.feature_names[0])
plt.ylabel(iris.feature_names[1])
plt.title('SVM Decision Boundary')
plt.show()
plot_decision_boundaries(X_vis, y_vis, svm_model_vis)
Explicación:
- El gráfico muestra cómo el modelo SVM separa las diferentes clases utilizando un hiperplano.
- Cada color representa una región de clase diferente en el espacio de características.
Conclusión clave:
- SVM es potente para tareas de clasificación, especialmente con datos de alta dimensión.
- Funciona bien cuando hay un claro margen de separación entre las clases.
Modelos avanzados
Por último, exploraremos modelos avanzados que se utilizan a menudo en aplicaciones de vanguardia.
Gradient Boosting Machines (XGBoost)
XGBoost (eXtreme Gradient Boosting) es una librería distribuida y optimizada de aumento de gradiente diseñada para ser altamente eficiente, flexible y portátil.
¿Por qué utilizar XGBoost?
- Rendimiento: A menudo consigue mejor rendimiento que otros modelos.
- Velocidad: Rápida velocidad de entrenamiento y uso eficiente de la memoria.
- Casos de uso: Soluciones ganadoras en concursos de ML, predicciones de alto rendimiento.
Implementación de XGBoost
Utilizaremos de nuevo el conjunto de datos California Housing:
import xgboost as xgb
# Splitting the data
X = data.drop('MedHouseVal', axis=1)
y = data['MedHouseVal']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Initialize and train the model
xgb_model = xgb.XGBRegressor(objective='reg:squarederror', n_estimators=100, max_depth=6)
xgb_model.fit(X_train, y_train)
Predicción y evaluación
# Make predictions
y_pred = xgb_model.predict(X_test)
# Evaluate the model
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"XGBoost MSE: {mse:.2f}")
print(f"XGBoost R² Score: {r2:.2f}")
Output:
XGBoost MSE: 0.25
XGBoost R² Score: 0.76
Explicación:
- XGBoost consigue un MSE menor y una puntuación R² mayor en comparación con los modelos anteriores.
- Esto indica que XGBoost ofrece un mejor rendimiento en este conjunto de datos.
Visualización de la importancia de las características
# Plotting feature importance
xgb.plot_importance(xgb_model)
plt.rcParams['figure.figsize'] = [10, 7]
plt.title('XGBoost Feature Importance')
plt.show()
Explicación:
- El gráfico muestra qué características son las más importantes en el modelo.
- La latitud y la longitud son muy significativas, lo que indica que la situación geográfica influye mucho en el precio de la vivienda.
Conclusión clave:
- XGBoost es potente para manejar conjuntos de datos complejos y lograr una gran precisión.
- Capta con eficacia tanto las relaciones lineales como las no lineales.
Redes neuronales
¿Qué es una red neuronal?
Las redes neuronales son modelos computacionales inspirados en el cerebro humano, formados por unidades interconectadas (neuronas) que procesan información mediante respuestas dinámicas de estado a entradas externas.
¿Por qué utilizar redes neuronales?
- Capacidad: Pueden modelizar relaciones complejas y no lineales.
- Flexibilidad: Aplicable tanto a la regresión como a la clasificación.
- Casos prácticos: Reconocimiento de imágenes y voz, procesamiento del lenguaje natural, previsión de series temporales.
Conjunto de datos: Reconocimiento de dígitos manuscritos (MNIST)
Utilizaremos el conjunto de datos MNIST, una gran base de datos de dígitos manuscritos utilizada habitualmente para entrenar diversos sistemas de procesamiento de imágenes.
Implementación de redes neuronales
# Import necessary libraries
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.optimizers import Adam
# Load the dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# Normalize the data
X_train = X_train / 255.0
X_test = X_test / 255.0
# One-hot encode the labels
y_train_enc = to_categorical(y_train)
y_test_enc = to_categorical(y_test)
Visualizar los datos
# Display the first image
plt.imshow(X_train[0], cmap='gray')
plt.title(f'Label: {y_train[0]}')
plt.show()
Explicación:
- El conjunto de datos MNIST consta de imágenes en escala de grises de 28×28 píxeles de dígitos manuscritos del 0 al 9.
- La visualización de los datos nos ayuda a comprender lo que aprenderá el modelo.
Construcción del modelo
# Initialize the model
nn_model = Sequential()
# Add layers
nn_model.add(Flatten(input_shape=(28, 28)))
nn_model.add(Dense(128, activation='relu'))
nn_model.add(Dense(64, activation='relu'))
nn_model.add(Dense(10, activation='softmax'))
# Compile the model
nn_model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
Explicación:
Utilizamos una red neuronal simple con dos capas ocultas.
La capa de salida utiliza la activación softmax para la clasificación multiclase.
Entrenamiento del modelo
# Train the model
history = nn_model.fit(X_train, y_train_enc, epochs=5, batch_size=128, validation_split=0.1)
Explicación:
- Entrenamos el modelo durante 5 épocas con un tamaño de lote de 128.
- La validación del 10% se utiliza para controlar el rendimiento del modelo en datos no vistos durante el entrenamiento.
Evaluación del modelo
# Evaluate the model
loss, accuracy = nn_model.evaluate(X_test, y_test_enc)
print(f"Neural Network Accuracy: {accuracy:.2f}")
Output:
Neural Network Accuracy: 0.98
Explicación:
- El modelo alcanza una precisión del 98% en el conjunto de pruebas.
- Esto indica un excelente rendimiento en el reconocimiento de dígitos manuscritos.
Visualización del historial de entrenamiento
# Plot training & validation accuracy values
plt.figure(figsize=(8, 6))
plt.plot(history.history['accuracy'], label='Train')
plt.plot(history.history['val_accuracy'], label='Validation')
plt.title('Model Accuracy Over Epochs')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend()
plt.show()
Explicación:
- El gráfico muestra que tanto la precisión de entrenamiento como la de validación mejoran a lo largo de las épocas.
- El modelo no se sobreajusta, ya que la precisión de validación se aproxima a la precisión de entrenamiento.
Visualización de predicciones
# Make predictions
y_pred = nn_model.predict(X_test)
y_pred_classes = np.argmax(y_pred, axis=1)
# Display some predictions
fig, axes = plt.subplots(2, 5, figsize=(10, 5))
for i, ax in enumerate(axes.flatten()):
ax.imshow(X_test[i], cmap='gray')
ax.set_title(f'Predicted: {y_pred_classes[i]}')
ax.axis('off')
plt.tight_layout()
plt.show()
Explicación:
- El modelo predice correctamente los dígitos de las imágenes de prueba.
- La visualización de las predicciones ayuda a evaluar el rendimiento del modelo en muestras individuales.
Conclusión clave:
- Las redes neuronales son versátiles y potentes para manejar tipos de datos complejos.
- Requieren más recursos informáticos y datos, pero pueden lograr una gran precisión.
Conclusión
Hemos pasado de modelos de ML básicos a avanzados utilizando el conjunto de datos California Housing y otros. Cada modelo tiene sus propias ventajas y es adecuado para distintos tipos de problemas.
- Regresión lineal: Lo mejor para comprender relaciones lineales.
- Árboles de decisión: Ideal para modelos interpretables en tareas de regresión.
- Random Forest: Eficaz para mejorar la precisión y reducir el sobreajuste.
- SVM: Potente para la clasificación con datos de alta dimensión.
- XGBoost: ideal para aplicaciones de rendimiento crítico.
- Redes neuronales: Capaces de modelar patrones complejos en los datos.
La elección del modelo adecuado depende del problema específico, la naturaleza de los datos y el resultado deseado.
Puntos clave
- Empieza por lo sencillo: Comience con modelos básicos para comprender los patrones subyacentes.
- Utiliza conjuntos de datos adecuados: Seleccione conjuntos de datos que destaquen los puntos fuertes de cada modelo.
- Visualiza los datos y los resultados: La visualización facilita la comprensión y la interpretación.
- Experimentar e iterar: Pruebe varios modelos y ajústelos para mejorar su rendimiento.
- Comprender las compensaciones: Equilibrio entre complejidad del modelo, rendimiento e interpretabilidad.
Gracias por embarcarte conmigo en este viaje por el ML. Espero que este artículo haya desmitificado algunas de las complejidades de los modelos de aprendizaje automático y te haya inspirado para profundizar en ellos.