2.6 - Notas de Luis - Nuestro primer modelo predictivo¶
Este documento detalla el desarrollo de nuestro primer proyecto de software de inteligencia artificial, centrado en la predicción de necesidades de mascarillas. El proceso se estructura en los siguientes pasos:
- Importación de bibliotecas necesarias.
- Preparación y preprocesamiento de los datos.
- Definición de los componentes del modelo.
- Implementación del bucle de entrenamiento.
- Visualización y análisis de los resultados.
- Aplicación del modelo para realizar predicciones.
Paso 1: Importación de bibliotecas necesarias¶
Para iniciar, procederemos a importar las bibliotecas de Python esenciales para este proyecto. Se han seleccionado dos bibliotecas principales por su idoneidad y simplicidad.
numpy: Biblioteca fundamental para la computación numérica en Python. Se utilizará para la manipulación de arreglos de datos y operaciones matemáticas.matplotlib: Biblioteca para la creación de visualizaciones estáticas, interactivas y animadas en Python. Se empleará para visualizar los datos y los resultados del modelo.
# Bloque de Código 1: Importando nuestras bibliotecas
import numpy as np
import matplotlib.pyplot as plt
# Una rápida impresión para confirmar que todo está cargado y listo. ¡Buena práctica!
print("Kit de herramientas listo. NumPy y Matplotlib están cargados y listos.")
Kit de herramientas listo. NumPy y Matplotlib están cargados y listos.
Paso 2: Preparación y preprocesamiento de los datos¶
Nuestro modelo necesita datos para aprender. Mientras Marta y Víctor trabajan con la base de datos real del hospital, yo estoy creando un conjunto de datos "sintéticos". Actuará como si fuera real, imitando la relación entre pacientes (x) y mascarillas necesarias (y), pero con un toque aleatorio para que parezca real.
# Bloque de Código 2: Creando un conjunto de datos sintético y realista
# Estoy configurando una 'semilla aleatoria' aquí. Este es un paso crucial en la ingeniería de software
# para el aprendizaje automático. Asegura que nuestros números 'aleatorios' sean los mismos cada
# vez que ejecutamos este código, haciendo que nuestros resultados sean reproducibles.
np.random.seed(42)
# Simulemos datos para 50 días.
m = 50 # 'm' es el nombre de variable estándar para el número de ejemplos de entrenamiento.
# X = Número de Pacientes. Creemos 50 valores aleatorios entre 50 y 150.
# La forma (m, 1) lo convierte en un vector columna.
X = 50 + 100 * np.random.rand(m, 1)
# y = Número de Mascarillas Usadas.
# Creemos una relación: y = 25 (base) + 0.8 * X + algo de ruido aleatorio.
# Esto simula un escenario del mundo real donde la relación no es perfectamente lineal.
y = 25 + 0.8 * X + np.random.randn(m, 1) * 10
# Lo primero que debe hacer un desarrollador con los datos es mirarlos.
# Hagamos la gráfica para confirmar visualmente que se ven como esperamos.
plt.figure(figsize=(12, 7))
plt.scatter(X, y, label='Datos Diarios Simulados')
plt.title("Hospital Minermont - Datos Sintéticos")
plt.xlabel("Número de Pacientes (Característica X)")
plt.ylabel("Mascarillas Usadas (Objetivo y)")
plt.grid(True)
plt.legend()
plt.show()
Paso 3: Definición de los componentes del modelo¶
Marta nos dio el plano, ahora definamos las piezas centrales en nuestro código:
- La Hipótesis ($h(x)$): Esta es nuestra función de predicción. Para la regresión lineal, es solo la ecuación de una línea. La llamaré
prediction(). $$prediction(x) = w * x + b$$ $w$ (peso) y $b$ (sesgo) son los parámetros que nuestro modelo aprenderá. Estos son los "mandos" que ajustaremos. - La Función de Coste ($J(w, b$)): Esto nos dice cómo de "equivocadas" están las predicciones de nuestro modelo. Cuanto menor, mejor. Usaremos el Error Cuadrático Medio (MSE), una elección bastante estándar. $$cost = averageOf( (prediction - actualvalue)^2 )$$
- Descenso del Gradiente: Este es el proceso de aprendizaje en sí. Ajusta automáticamente $w$ y $b$ para minimizar el coste. Descubre la "pendiente" de la función de coste y da pasos cuesta abajo.
Paso 4: Implementación del bucle de entrenamiento¶
Este es el corazón de nuestro programa. Inicializaremos nuestros parámetros y luego ejecutaremos un bucle que los refinará repetidamente utilizando el Descenso del Gradiente.
Al tratarse de una muestra de datos muy pequeña con solo 50 elementos y para mantener el código sencillo, utilizaremos el descenso por lotes utilizando las 50 entradas en cada iteración.
Antes del bucle, escalamos la entrada. El número de pacientes va de 50 a 150, mientras que el sesgo se multiplica por un 1 constante. Con los valores sin escalar, el gradiente de $w$ es unas cien veces mayor que el de $b$: solo una tasa de aprendizaje diminuta mantiene estable $w$ y, con ella, $b$ apenas se mueve, así que el bucle se detiene mucho antes de llegar a la mejor recta. Como advierten las notas de Luis, el descenso del gradiente es sensible a la escala de las variables, y el remedio es normalizar los datos. Estandarizamos $x$ (le restamos su media y lo dividimos por su desviación típica), entrenamos con los valores escalados y al final devolvemos $w$ y $b$ a mascarillas por paciente.
Las actualizaciones usan los gradientes del error cuadrático medio, con el factor $2/m$ de las ecuaciones de actualización de Luis, donde $\alpha$ es la tasa de aprendizaje:
$$w := w - \alpha \, \frac{2}{m} \sum_{i=1}^{m} (\hat{y}_i - y_i)\, x_i \qquad b := b - \alpha \, \frac{2}{m} \sum_{i=1}^{m} (\hat{y}_i - y_i)$$
# Bloque de Código 3: Construyendo y Entrenando el Modelo (con Parada Temprana)
# --- 1. Escalado de la Característica ---
# El número de pacientes (de 50 a 150) es un número grande comparado con el 1
# constante que multiplica al sesgo. Lo estandarizamos: media 0 y desviación típica 1.
X_mean = X.mean()
X_std = X.std()
X_scaled = (X - X_mean) / X_std
# --- 2. Inicialización de Parámetros ---
# Necesitamos darle a nuestro modelo un punto de partida. Empecemos w y b en 0.
# Es una posición inicial simple y neutral.
w = 0.0
b = 0.0
# --- 3. Configuración de Hiperparámetros ---
# Estos son ajustes para el algoritmo de aprendizaje, no aprendidos de los datos.
# Tenemos que configurarlos nosotros mismos.
learning_rate = 0.01 # Cómo de grande es el paso en cada actualización (alfa en las notas de Luis).
num_iterations = 10000 # El número máximo de iteraciones.
tol = 0.001 # Tolerancia: cambio mínimo en el coste para seguir adelante (umbral de parada temprana).
# --- 4. El Bucle de Entrenamiento ---
# Almacenaremos el coste en cada paso para observar cómo aprende nuestro modelo.
cost_log = []
print("Iniciando la secuencia de entrenamiento...")
for i in range(num_iterations):
# a) Realizar predicciones para TODOS nuestros puntos de datos con los w y b actuales.
y_pred = w * X_scaled + b
# b) Calcular el coste (MSE) para estas predicciones.
# Esto nos dice cómo de bien lo estamos haciendo en esta iteración.
cost = np.mean((y_pred - y)**2)
cost_log.append(cost)
# --- Condición de Parada Temprana ---
# Si el coste no ha cambiado mucho en comparación con el paso anterior,
# significa que hemos alcanzado una región plana, no estamos aprendiendo mucho más.
if i > 0 and abs(cost_log[-2] - cost_log[-1]) < tol:
print(f"Parada temprana en la iteración {i}: el coste apenas cambia.")
break
# c) Calcular los gradientes. Este es el núcleo del aprendizaje.
# Estas fórmulas provienen de la derivada de la función de coste.
# Nos dicen la pendiente de nuestra "colina" de coste para w y b.
dw = (2/m) * np.sum((y_pred - y) * X_scaled) # Gradiente para el peso
db = (2/m) * np.sum(y_pred - y) # Gradiente para el sesgo
# d) Actualizar los parámetros.
# Movemos w y b en la dirección *opuesta* a su gradiente.
# Este es el paso "cuesta abajo".
w = w - learning_rate * dw
b = b - learning_rate * db
# Un registro simple para seguir el proceso en tiempo real.
# Imprimimos la evolución del error cada 100 iteraciones, empezando por la 1
# (la iteración 0 es el punto de partida, w = b = 0).
if ((i-1) % 100) == 0:
print(f"Iteración {i}: Coste = {cost:.6f}")
# --- 5. De Vuelta a las Unidades Originales ---
# El modelo ha aprendido con los pacientes escalados. Deshacemos el escalado para que
# w sea "mascarillas por paciente" y b el número base de mascarillas.
b = b - w * X_mean / X_std
w = w / X_std
# imprimir el número de iteraciones y el error final
print("\n--- Entrenamiento Completo ---")
print(f"Número de iteraciones: {i}")
print(f"Error final: {cost:.6f}")
print(f"Peso aprendido final (w): {w:.4f}")
print(f"Sesgo aprendido final (b): {b:.4f}")
Iniciando la secuencia de entrenamiento... Iteración 1: Coste = 10285.499154 Iteración 101: Coste = 261.760311 Iteración 201: Coste = 85.463328 Iteración 301: Coste = 82.362626 Parada temprana en la iteración 322: el coste apenas cambia. --- Entrenamiento Completo --- Número de iteraciones: 322 Error final: 82.330876 Peso aprendido final (w): 0.7765 Sesgo aprendido final (b): 27.0436
Paso 5: Visualización y análisis de los resultados¶
Muy bien, ya hemos construido el motor. Pero, ¿realmente está funcionando? En aprendizaje automático, tenemos que visualizar.
Primero, hagamos una gráfica de la curva de aprendizaje. Esto muestra cómo el coste disminuye con el tiempo. Si esta gráfica no baja, algo hemos hecho mal.
Luego, haremos la gráfica de la línea final que nuestro modelo aprendió sobre los datos originales. Esta es la verificación visual definitiva para ver cómo de bien se ajusta.
# Bloque de Código 4: Visualizando el Rendimiento del Modelo
# Crea una figura con dos gráficos lado a lado para un informe agradable.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 7))
# --- Gráfico 1: La Curva de Aprendizaje ---
ax1.plot(cost_log)
ax1.set_title("Curva de Aprendizaje del Modelo")
ax1.set_xlabel("Iteración")
ax1.set_ylabel("Coste (Error Cuadrático Medio)")
ax1.grid(True)
# --- Gráfico 2: La Línea de Regresión Final ---
# Primero, añade a la gráfica los puntos de datos originales
ax2.scatter(X, y, label='Datos Originales')
# Luego, añade a la gráfica la línea que ha aprendido el modelo
# Generamos los valores y para la línea usando nuestros w y b finales
ax2.plot(X, w * X + b, color='red', linewidth=3, label='Modelo Aprendido')
ax2.set_title("Ajuste del Modelo Final")
ax2.set_xlabel("Número de Pacientes")
ax2.set_ylabel("Mascarillas Usadas")
ax2.grid(True)
ax2.legend()
plt.show()
Paso 6: Aplicación del modelo para realizar predicciones¶
¡Muy bien, el objetivo era construir algo útil! Ahora que nuestro modelo está entrenado, usémoslo para predecir las necesidades de mascarillas para nuevos escenarios.
Esta es la recompensa: tomar nuestros w y b entrenados y conectarlos a nuestra ecuación de la recta.
# Bloque de Código 5: Haciendo una Predicción
# Digamos que queremos saber cuántas mascarillas debemos presupuestar si esperamos 100 pacientes mañana.
patients_tomorrow = 100
# Usamos nuestros w y b finales entrenados para calcular la predicción.
predicted_masks = w * patients_tomorrow + b
print("\n--- Haciendo una Nueva Predicción ---")
print(f"Para un día con {patients_tomorrow} pacientes...")
print(f"Nuestro modelo predice que necesitaremos aproximadamente {predicted_masks:.0f} mascarillas.")
# Probemos otra para un día más tranquilo.
patients_quiet_day = 65
predicted_masks_quiet = w * patients_quiet_day + b
print(f"\nPara un día con {patients_quiet_day} pacientes...")
print(f"Nuestro modelo predice que necesitaremos aproximadamente {predicted_masks_quiet:.0f} mascarillas.")
--- Haciendo una Nueva Predicción --- Para un día con 100 pacientes... Nuestro modelo predice que necesitaremos aproximadamente 105 mascarillas. Para un día con 65 pacientes... Nuestro modelo predice que necesitaremos aproximadamente 78 mascarillas.
Conclusión y Próximos Pasos¶
Hemos implementado con éxito un modelo predictivo inicial basado en regresión lineal para estimar la necesidad de mascarillas. Lo hemos desarrollado desde cero, paso a paso.
Este primer prototipo, aunque conceptualmente simple, es robusto y escalable. Su implementación interna nos permite comprender a fondo su funcionamiento. Este modelo es el punto de partida para desarrollos más complejos y precisos.