Entrenar un modelo de regresión lineal en PyTorch implica elegir una función de pérdida, configurar un optimizador y construir un bucle de entrenamiento que actualice los parámetros con cada época. Si estás dando tus primeros pasos en deep learning, este flujo te servirá como base para modelos más complejos.
La idea es simple: defines cómo medir el error, escoges un algoritmo que ajuste los pesos y dejas que el modelo aprenda iterando sobre los datos. PyTorch te entrega los bloques listos a través de torch.nn y torch.optim.
Qué función de pérdida usar para regresión lineal
La función de pérdida es la fórmula que mide qué tan lejos están tus predicciones de los valores reales. En PyTorch tienes acceso a un catálogo amplio dentro del módulo nn, y la documentación oficial muestra la definición matemática, los parámetros y ejemplos de uso de cada una.
Para este caso usamos torch.nn.L1Loss, también conocida como Mean Absolute Error (MAE). Funciona muy bien con datos tabulares, una X y una Y claras, y valores continuos como los que tenemos aquí [02:55].
¿Qué es L1Loss en PyTorch? Es una función de pérdida que calcula el promedio de la diferencia absoluta entre las predicciones y los valores reales. Se usa comúnmente en problemas de regresión con valores continuos.
python
fn_perdida = torch.nn.L1Loss()
Por qué importa elegir bien la pérdida
La función de pérdida define qué significa "mejorar" para tu modelo. Si la eliges mal, el modelo puede entrenar bien numéricamente pero resolver el problema equivocado. Por eso vale la pena revisar la documentación antes de comprometerte con una sola opción.
Cómo configurar el optimizador SGD y la tasa de aprendizaje
El optimizador es el algoritmo que ajusta los pesos del modelo en cada paso. Aquí usamos Stochastic Gradient Descent (SGD) desde torch.optim, porque es el más típico y un buen punto de partida [03:35].
El SGD necesita dos cosas: los parámetros que va a optimizar y una tasa de aprendizaje (learning rate). La tasa de aprendizaje controla qué tan agresivo es cada cambio.
Si es muy alta, el modelo da saltos grandes y puede salirse del mínimo que buscas.
Si es muy baja, el modelo mejora lento y tal vez no llegue a tiempo.
Una buena práctica es comenzar con 0.01 y experimentar desde ahí.
¿Qué es la tasa de aprendizaje? Es un hiperparámetro que define cuánto cambian los pesos del modelo en cada actualización del gradiente. Valores típicos arrancan en 0.01 y se ajustan por prueba y error.
La tasa de aprendizaje y el número de épocas son hiperparámetros, no se aprenden: tú los defines y los ajustas según el comportamiento del modelo.
Cuáles son los pasos del bucle de entrenamiento en PyTorch
Antes de entrenar, fijamos la semilla con torch.manual_seed(42) para obtener resultados reproducibles, definimos epocas = 100 y creamos dos listas vacías para registrar la pérdida de entrenamiento y la de prueba en cada vuelta [05:20].
Una época es una pasada completa por los datos de entrenamiento. Con 100 épocas, el modelo verá los datos cien veces y ajustará sus pesos en cada una.
Los cinco pasos dentro de cada época
Dentro del bucle for epoca in range(epocas), el entrenamiento sigue una secuencia fija que conviene memorizar:
Pon el modelo en modo entrenamiento con model_1.train(). Esto activa el cálculo de gradientes.
Calcula la predicción con y_pred = model_1(X_train). Internamente PyTorch ejecuta el método forward.
Calcula la pérdida comparando predicción contra valores reales: perdida = fn_perdida(y_pred, y_train).
Reinicia los gradientes con optimizador.zero_grad(). Si no lo haces, los gradientes se acumulan de épocas anteriores y contaminan el cálculo.
Ejecuta perdida.backward() para calcular los nuevos gradientes vía retropropagación, y luego optimizador.step() para que el SGD aplique la actualización a los pesos.
El paso backward saca la derivada de la pérdida respecto a los parámetros: te dice si la pérdida sube o baja cuando mueves cada peso. El step usa esa información junto con la tasa de aprendizaje para acercarte a un punto donde la pérdida sea menor [08:10].
Cómo evaluar el modelo en cada época
Después de entrenar, cambias el modelo a modo evaluación con model_1.eval(). En este modo ya no se calculan gradientes, solo haces inferencia [09:15].
Generas predicciones con los datos de prueba: prueba_pred = model_1(X_test).
Calculas la pérdida en prueba comparando contra y_test.
Conviertes los tensores a float para prevenir errores de tipo de datos.
¿Por qué separar entrenamiento y evaluación? Porque en entrenamiento el modelo ajusta pesos usando los datos vistos, mientras que en evaluación mides su desempeño en datos nuevos para detectar si está generalizando o memorizando.
Con estas dos pérdidas guardadas por época, tendrás insumos para graficar cómo desciende el error en entrenamiento y prueba, y detectar si tu modelo está aprendiendo de forma sana o si necesita ajustes en la tasa de aprendizaje, el número de épocas o incluso la función de pérdida.
¿Qué hiperparámetros has tenido que ajustar más en tus propios entrenamientos? Cuéntame en los comentarios.
Para los que no comprendan aun bien que es el Stochastic Gradient Descent
El Stochastis Gradient Descent que es el optimizador que utilizamos aqui es uno de los optimizadores mas comunes, simplemente es hacer un Gradient Descent pero en vez de tomar todos los datos y hacer calculos a partir de ellos, tomamos pequenos lotes, es decir una muestra de los datos para hacer los calculos sobre esto, que nos ahorra mucho el tiempo de entrenamiento del modelo.
Si no entiendes que es el Gradient Descent
Nota: Gradient o gradiente es una forma de decirle a las derivadas parciales de una funcion, a la manera más simple, siempre que leas gradiente entiende muchas derivadas de una funcion.
💡 De manera simple es ir viendo como bajamos la funcion de error, por medio de ver que tan lejos estamos del numero optimo de error que buscamos, y vamos modificando por iteracion. El gradiente no es mas que derivar, ver si hay que subir o bajar el gradiente y entonces le restamos este gradiente al parametro actual.
Por que restamos? porque el gradiente calcula hacia donde va la funcion y para moverla al lado contrario con tal de llegar a 0. Es decir, si lo vemos como una brujula, el gradiente nos dice que vamos subiendo, cuando en realidad queremos bajar entonces por eso la resta.
Videos (En ingles) que puede funcionar entender a profundidad estos temas
Gradient Descent
Stochastic Gradien Descent
¿Pero por cual función nos estamos moviendo con el descenso de gradiente? por la función de error, en este caso MAE. En otros casos se usa la función de error Entropia Cruzada.
Lo digo porque tenia esa confusión. Todos dicen, hay que hacer descenso de gradiente. Ok, se saca la derivada de la función, eso nos entrega la pendiente de la tangente en ese punto y nos movemos en dirección contraria de la pendiente el numero de unidides lamda (o tasa de aprendizaje)... Pero ¡¡¿cual función voy a derivar (bueno la libreria deriva)?!! Pues la función error panita!
Es la funcion de perdida amigo
En PyTorch, el proceso de entrenamiento de un modelo implica definir una **función de pérdida** (loss function) y un **optimizador**, que se encargan de actualizar los pesos del modelo en función del error entre las predicciones del modelo y los valores reales. Aquí te explico cada uno de estos elementos y te doy un ejemplo práctico.
### Entrenamiento en PyTorch: Conceptos Clave
1. **Función de Pérdida (Loss Function):**
La función de pérdida mide la diferencia entre las predicciones del modelo y los valores verdaderos. Su valor se minimiza durante el entrenamiento. Algunas funciones comunes son:
- nn.MSELoss: Para problemas de regresión (Minimiza el error cuadrático medio).
- nn.CrossEntropyLoss: Para problemas de clasificación múltiple.
- nn.BCELoss: Para problemas de clasificación binaria.
2. **Optimizador:**
El optimizador es el algoritmo que ajusta los pesos del modelo para reducir la función de pérdida. Un optimizador popular es **Stochastic Gradient Descent (SGD)**, pero PyTorch también ofrece otros optimizadores como **Adam**.
- torch.optim.SGD: Descenso de gradiente estocástico.
- torch.optim.Adam: Un optimizador más avanzado, que a menudo funciona mejor en redes más complejas.
3. **Ciclo de Entrenamiento:**
- **Paso 1:** Pasar los datos de entrada a través del modelo.
- **Paso 2:** Calcular la pérdida entre las predicciones y los valores reales.
- **Paso 3:** Retropropagar el error (backpropagation).
- **Paso 4:** Actualizar los pesos utilizando el optimizador.
### Ejemplo Completo
Este es un ejemplo de entrenamiento de un modelo simple con una función de pérdida y un optimizador.
import torch
import torch.nn as nn
import torch.optim as optim
\# Definición del modelo (similar al ejemplo anterior)classSimpleNN(nn.Module):  def \_\_init\_\_(self, input\_size, hidden\_size, output\_size):  super(SimpleNN, self).\_\_init\_\_()  self.fc1 = nn.Linear(input\_size, hidden\_size) # Capa oculta  self.fc2 = nn.Linear(hidden\_size, output\_size) # Capa de salida  self.relu = nn.ReLU() # Función de activación ReLU  def forward(self, x):  x = self.fc1(x)  x = self.relu(x)  x = self.fc2(x)  return x\# Parámetros del modelo
input\_size =10hidden\_size =5output\_size =1learning\_rate =0.01\# Crear una instancia del modelo
model = SimpleNN(input\_size, hidden\_size, output\_size)
\# Definir la función de pérdida y el optimizador
criterion = nn.MSELoss()# Pérdida para regresiónoptimizer = optim.SGD(model.parameters(), lr=learning\_rate)# Optimizador SGD
\# Datos ficticios para entrenamiento
inputs = torch.randn(100,input\_size)# 100 ejemplos, 10 característicastargets = torch.randn(100, output\_size)# 100 etiquetas de salida\# Entrenamiento del modelo
num\_epochs =100for epoch inrange(num\_epochs):  \# Paso hacia adelante (Forward pass): calcular predicciones  outputs = model(inputs)     \# Calcular la pérdida  loss = criterion(outputs, targets)     \# Paso hacia atrás (Backward pass): retropropagación  optimizer.zero\_grad() # Resetear los gradientes  loss.backward() # Calcular los gradientes     \# Actualizar los pesos  optimizer.step() # Actualizar los parámetros del modelo     \# Imprimir la pérdida en intervalos  if (epoch+1) % 10 == 0:  print(f'Epoch \[{epoch+1}/{num\_epochs}], Loss: {loss.item():.4f}')
\# Predicción con el modelo entrenado
test\_input = torch.randn(1,input\_size)predicted\_output = model(test\_input)print(f'Predicted Output: {predicted\_output}')
### Explicación Paso a Paso
1. **Modelo:** Usamos una red neuronal simple con una capa oculta y una función de activación ReLU.
2. **Función de pérdida (criterion):** Se utiliza MSELoss (Error Cuadrático Medio) para este ejemplo de regresión.
3. **Optimizador (optimizer):** Se usa SGD (Stochastic Gradient Descent) con una tasa de aprendizaje de 0.01.
4. **Datos de Entrenamiento:** Generamos datos ficticios aleatorios para los ejemplos y etiquetas.
5. **Ciclo de Entrenamiento:**
- Se calculan las predicciones del modelo.
- Se calcula la pérdida comparando las predicciones con las etiquetas reales.
- Se retropropaga el error para calcular los gradientes.
- Se actualizan los pesos del modelo con el optimizador.
6. **Impresión del Progreso:** Se imprime la pérdida cada 10 épocas.
### Otras Funciones de Pérdida y Optimizadores
#### Funciones de Pérdida Comunes:
- nn.CrossEntropyLoss: Para clasificación múltiple.
- nn.BCELoss: Para clasificación binaria.
#### Optimizadores Comunes:
- torch.optim.Adam: Un optimizador avanzado que usa tasas de aprendizaje adaptativas.
- torch.optim.RMSprop: Similar a Adam, pero con algunas diferencias en cómo actualiza los parámetros.
Este es el flujo básico del entrenamiento de modelos en PyTorch. ¿Te gustaría ver más ejemplos o detalles sobre algún otro optimizador o tipo de función de pérdida?
{"Función de Pérdida (Loss Function)":"La función de pérdida mide la diferencia entre las predicciones del modelo y los valores verdaderos.","Optimizador":"El optimizador ajusta los pesos del modelo para reducir la función de pérdida."}
Los 5 pasos del entrenamiento mencionados son comunes en muchos modelos de aprendizaje automático, incluidos aquellos que utilizan PyTorch. Estos pasos incluyen:
Modo entrenamiento: Preparar el modelo para entrenar.
Predicción: Calcular la salida del modelo con datos de entrada.
Cálculo de la pérdida: Comparar las predicciones con las etiquetas reales.
Cálculo de gradientes: Ajustar los gradientes para optimizar los parámetros.
Optimización: Actualizar los parámetros del modelo.
Aunque la estructura puede variar, estos principios fundamentales son aplicables a la mayoría de los modelos de deep learning.
Creo que hay un error en la guía, el MAE es el error absoluto medio, no cuadrático
importtorchfrom torch import nn
import torch.optimas optim
import matplotlib.pyplotas plt
# ============================================================# 0)MODELO:Regresión lineal simple y = w*x + b
# ============================================================classModeloRegresion(nn.Module): def __init__(self):super().__init__() # Defino los parámetros entrenables del modelo
# volumen representa el peso(w) y sesgo el bias(b) self.volumen= nn.Parameter(torch.rand(1, dtype=torch.float, requires_grad=True)) self.sesgo= nn.Parameter(torch.rand(1, dtype=torch.float, requires_grad=True)) def forward(self,x: torch.Tensor)-> torch.Tensor: # Implemento la ecuación de la recta: y = w*x + b
return self.volumen* x + self.sesgo# ============================================================# 1)INICIALIZACIÓN: semilla y modelo
# ============================================================torch.manual_seed(42)modelo1 =ModeloRegresion()# ============================================================# 2)FUNCIÓNDEPÉRDIDAYOPTIMIZADOR# ============================================================# UsoL1Loss(MAE) para medir el error absoluto promedio
fn_perdida = nn.L1Loss()# Configuro el optimizador SGD con tasa de aprendizaje de 0.025optimizador = optim.SGD(modelo1.parameters(), lr=0.025)# ============================================================# 3)LISTASPARAGUARDARHISTORIALDEPÉRDIDAS(para graficar)# ============================================================train_losses =[]test_losses =[]# ============================================================# 4)ENTRENAMIENTO# ============================================================epochs =400for epoch inrange(epochs): # ----------------------------- # MODOENTRENAMIENTO # ----------------------------- modelo1.train() # Genero las predicciones del modelo con los datos de entrenamiento
y_pred =modelo1(X_train) # Calculo la pérdida comparando predicciones con valores reales
perdida =fn_perdida(y_pred,Y_train) # Reseteo los gradientes antes del backward pass
optimizador.zero_grad() # Calculo los gradientes mediante backpropagation
perdida.backward() # Actualizo los parámetros usando los gradientes calculados
optimizador.step() # ----------------------------- # MODOEVALUACIÓN(TEST) # ----------------------------- modelo1.eval()with torch.no_grad(): y_pred_prueba =modelo1(X_test) perdida_prueba =fn_perdida(y_pred_prueba,Y_test.float()) # Almaceno las pérdidas para análisis posterior
train_losses.append(perdida.item()) test_losses.append(perdida_prueba.item()) # Muestro el progreso cada 20 iteraciones
if epoch %20==0:print(f"Epoch {epoch} | Train Loss: {perdida.item():.5f} | Test Loss: {perdida_prueba.item():.5f}")# ============================================================# 5)GRÁFICA1:Curva de aprendizaje(loss vs épocas)# ============================================================plt.figure()plt.plot(train_losses, label="Train Loss (MAE)")plt.plot(test_losses, label="Test Loss (MAE)")plt.xlabel("Épocas")plt.ylabel("Pérdida (MAE)")plt.title("Curva de aprendizaje")plt.legend()plt.show()# ============================================================# 6)GRÁFICA2:Datos reales vs predicción final del modelo
# ============================================================modelo1.eval()with torch.no_grad(): predicciones =modelo1(X_test)# Convierto los tensores a numpy para visualización
x =X_test.detach().cpu().numpy()y_real =Y_test.detach().cpu().numpy()y_pred = predicciones.detach().cpu().numpy()plt.figure()plt.scatter(x, y_real, label="Datos reales")plt.plot(x, y_pred, label="Modelo aprendido")plt.xlabel("X")plt.ylabel("Y")plt.title("Regresión lineal aprendida (y = w*x + b)")plt.legend()plt.show()# ============================================================# 7)EXTRA:Imprimir parámetros finales aprendidos
# ============================================================print("Peso aprendido (w / volumen):", modelo1.volumen.item())print("Sesgo aprendido (b):", modelo1.sesgo.item())
Con SGD, lo que pasa es que si pudieramos visualizar es que la "convergencia" hacia el minimo global se hace mas "saltarin" coin respecto al Gradient Descendent - el cual actualiza los pesos en toda el dataset de entrenamiento. Si SGD lo combinamos con el momentun (otro parametro del optimizador), lo que hace es que eventualmente en funciones de perdida con valles , el SGD + momentum , hace que "brinque" los minimos locales y siga buscando el minimo global
■ Resumen:
La elección de la función de pérdida y el optimizador debe estar guiada por la naturaleza del problema, la arquitectura del modelo, la estabilidad y la convergencia deseada, y los recursos disponibles. Para la mayoría de los casos SGD funciona bien, pero dataset más complejos, precisarán de otras funciones.
Seleccionaremos una función de pérdida y el optimizador para el entrenamiento de modelo. La función de pérdida mide que tan equivocadas están las predicciones del modelo, y_pred en comparación con las etiquetas.
El optimizador le indica a los modelos como actualizar sus parámetros internos para reducir la pérdida.
Para el siguiente ejemplo podremos usar el Error cuadrático medio, como la función de pérdida torch.nn-L1Loss para medir la diferencia absoluta entre dos puntos y tomar la media en todos los ejemplos.
Para el optimizador usaremos Stocastic Gradient Descent SGD como optimizador donde los parámetros serán el volume y el bias, lr es la tasa de aprendizaje para que el optimizador actualice los pesos. (O cuales sean tus parámetros)
El descenso de gradiente estocástico (SGD) es como bajar una montaña a ciegas. En cada paso, sientes la pendiente justo debajo de ti y das un paso en esa dirección. Repites esto muchas veces hasta llegar al punto más bajo (o casi). Es rápido pero a veces puede no ser la ruta más directa.