Entrenamiento de modelos con PyTorch
PyTorch es un marco de aprendizaje automático que se usa habitualmente para entrenar modelos de aprendizaje profundo. En Azure Databricks, PyTorch viene preinstalado en clústeres de ML.
Nota:
Los fragmentos de código de esta unidad se proporcionan como ejemplos para resaltar los puntos clave. Tendrá la oportunidad de ejecutar código de ejemplo totalmente funcional en un ejercicio posterior de este módulo.
Definición de una red de PyTorch
En PyTorch, los modelos se basan en una red definida por usted. La red consta de varias capas, cada una con entradas y salidas específicas. Además, el trabajo define una función forward que aplica funciones a cada capa a medida que se pasan los datos a través de la red.
El siguiente código de ejemplo define una red.
import torch
import torch.nn as nn
import torch.nn.functional as F
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__()
self.layer1 = nn.Linear(4, 5)
self.layer2 = nn.Linear(5, 5)
self.layer3 = nn.Linear(5, 3)
def forward(self, x):
layer1_output = torch.relu(self.layer1(x))
layer2_output = torch.relu(self.layer2(layer1_output))
y = self.layer3(layer2_output)
return y
Aunque el código puede parecer complejo al principio, esta clase define una red relativamente sencilla con tres capas:
- Una capa de entrada que acepta cuatro valores de entrada y genera cinco valores de salida para la capa siguiente.
- Una capa que acepta cinco entradas y genera cinco salidas.
- Una capa de salida final que acepta cinco entradas y genera tres salidas.
La función forward aplica las capas a los datos de entrada (x), pasa la salida de cada capa a la siguiente y, por último, devuelve la salida de la última capa (que contiene el vector de predicción de etiquetas, y). Se aplica una función de activación de unidad lineal rectificada (ReLU) a las salidas de las capas 1 y 2 para restringir los valores de salida a números positivos.
Nota:
Según el tipo de criterio de pérdida empleado, puede optar por aplicar una función de activación, como log_softmax, al valor devuelto para forzarlo en el intervalo de 0 a 1. Sin embargo, algunos criterios de pérdida (como CrossEntropyLoss, que se usa normalmente para la clasificación multiclase) aplican automáticamente una función adecuada.
Para crear un modelo de entrenamiento, solo tiene que crear una instancia de la clase de red de la siguiente manera:
myModel = MyNet()
Preparación de datos para el modelado
Las capas de PyTorch funcionan con datos que tienen formato de tensores (estructuras similares a matrices). Existen varias funciones para convertir otros formatos de datos comunes en tensores, y puede definir un cargador de datos de PyTorch para leer tensores de datos en un modelo para entrenamiento o inferencia.
Igual que con la mayoría de las técnicas de aprendizaje automático supervisado, debe definir conjuntos de datos independientes para el entrenamiento y la validación. Esta separación permite validar que el modelo predice de manera precisa cuando recibe datos con los que no se ha entrenado.
El código siguiente define dos cargadores de datos: uno para el entrenamiento y el otro para las pruebas. Se da por hecho que los datos de origen de cada cargador en este ejemplo son una matriz Numpy con valores de características y una matriz Numpy con los valores de etiqueta correspondientes.
# Create a dataset and loader for the training data and labels
train_x = torch.Tensor(x_train).float()
train_y = torch.Tensor(y_train).long()
train_ds = td.TensorDataset(train_x,train_y)
train_loader = td.DataLoader(train_ds, batch_size=20,
shuffle=False, num_workers=1)
# Create a dataset and loader for the test data and labels
test_x = torch.Tensor(x_test).float()
test_y = torch.Tensor(y_test).long()
test_ds = td.TensorDataset(test_x,test_y)
test_loader = td.DataLoader(test_ds, batch_size=20,
shuffle=False, num_workers=1)
Los cargadores de este ejemplo dividen los datos en lotes de 30, que se pasan a la función forward durante el entrenamiento o la inferencia.
Elección de un criterio de pérdida y un algoritmo optimizador
El modelo se entrena introduciendo los datos de entrenamiento en la red, midiendo la pérdida (la diferencia agregada entre los valores de predicción y los reales) y optimizando la red mediante el ajuste de las ponderaciones y los balances para minimizar la pérdida. Los detalles específicos de cómo se calcula y cómo se minimiza la pérdida se rigen por el criterio de pérdida y el algoritmo optimizador que elija.
Criterios de pérdida
PyTorch admite varias funciones de criterios de pérdida, entre las que se incluyen:
- cross_entropy: función que mide la diferencia agregada entre los valores de predicción y los valores reales de múltiples variables (normalmente se usa para medir la pérdida de probabilidades de clase en la clasificación multiclase).
- binary_cross_entropy: función que mide la diferencia entre las probabilidades previstas y reales (normalmente se usa para medir la pérdida de probabilidades de clase en la clasificación binaria).
- mse_loss: función que mide la pérdida del error cuadrático medio para los valores numéricos previstos y reales (normalmente se usa para la regresión).
Para especificar el criterio de pérdida que quiere usar al entrenar el modelo, cree una instancia de la función adecuada de la siguiente forma:
import torch.nn as nn
loss_criteria = nn.CrossEntropyLoss
Sugerencia
Para obtener más información sobre los criterios de pérdida disponibles en PyTorch, consulte Funciones de pérdida en la documentación de PyTorch.
Algoritmos optimizadores
Después de calcular la pérdida, se usa un optimizador para determinar cómo ajustar mejor las ponderaciones y los balances con el fin de minimizar la pérdida. Los optimizadores son implementaciones específicas de un enfoque de descenso de gradiente para minimizar una función. Los optimizadores disponibles en PyTorch incluyen (entre otros):
- Adadelta: optimizador basado en el algoritmo de velocidad del aprendizaje adaptativo.
- Adam: optimizador eficiente a nivel computacional basado en el algoritmo Adam.
- SGD: optimizador basado en el algoritmo de descenso de gradiente estocástico.
Para usar cualquiera de estos algoritmos para entrenar un modelo, debe crear una instancia del optimizador y establecer los parámetros necesarios. Los parámetros específicos varían en función del optimizador elegido, pero la mayoría requiere que especifique una velocidad de aprendizaje, la cual rige el tamaño de los ajustes realizados con cada optimización.
El código siguiente crea una instancia del optimizador Adam:
import torch.optim as opt
learning_rate = 0.001
optimizer = opt.Adam(model.parameters(), lr=learning_rate)
Sugerencia
Para obtener más información sobre los optimizadores disponibles en PyTorch, consulte Algoritmos en la documentación de PyTorch.
Creación de funciones de entrenamiento y prueba
Después de definir una red y preparar los datos para ella, puede usar los datos para entrenar y probar un modelo. Para ello, pase los datos de entrenamiento a través de la red, calcule la pérdida, optimice las ponderaciones y los sesgos de la red, y valide el rendimiento de la red con los datos de prueba. Es habitual definir una función que pasa datos a través de la red para entrenar el modelo con los datos de entrenamiento y una función independiente para probar el modelo con los datos de prueba.
Creación de una función de entrenamiento
En el ejemplo siguiente se muestra una función para entrenar un modelo.
def train(model, data_loader, optimizer):
# Use GPU if available, otherwise CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
# Set the model to training mode (to enable backpropagation)
model.train()
train_loss = 0
# Feed the batches of data forward through the network
for batch, tensor in enumerate(data_loader):
data, target = tensor # Specify features and labels in a tensor
optimizer.zero_grad() # Reset optimizer state
out = model(data) # Pass the data through the network
loss = loss_criteria(out, target) # Calculate the loss
train_loss += loss.item() # Keep a running total of loss for each batch
# backpropagate adjustments to weights/bias
loss.backward()
optimizer.step()
#Return average loss for all batches
avg_loss = train_loss / (batch+1)
print('Training set: Average loss: {:.6f}'.format(avg_loss))
return avg_loss
En el ejemplo siguiente se muestra una función para probar el modelo.
def test(model, data_loader):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
# Switch the model to evaluation mode (so we don't backpropagate)
model.eval()
test_loss = 0
correct = 0
# Pass the data through with no gradient computation
with torch.no_grad():
batch_count = 0
for batch, tensor in enumerate(data_loader):
batch_count += 1
data, target = tensor
# Get the predictions
out = model(data)
# calculate the loss
test_loss += loss_criteria(out, target).item()
# Calculate the accuracy
_, predicted = torch.max(out.data, 1)
correct += torch.sum(target==predicted).item()
# Calculate the average loss and total accuracy for all batches
avg_loss = test_loss/batch_count
print('Validation set: Average loss: {:.6f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
avg_loss, correct, len(data_loader.dataset),
100. * correct / len(data_loader.dataset)))
return avg_loss
Entrenamiento del modelo en varias épocas
Para entrenar un modelo de aprendizaje profundo, normalmente se ejecuta la función de entrenamiento varias veces (denominadas épocas) con el objetivo de reducir la pérdida calculada a partir de los datos de entrenamiento de cada época. Puede usar la función de prueba para validar que la pérdida de los datos de prueba (con los que no se entrenó el modelo) también se reduce en línea con la pérdida de entrenamiento; es decir, que el entrenamiento del modelo no genera un modelo sobreajustado a los datos de entrenamiento.
Sugerencia
No es necesario ejecutar la función de prueba para cada época. Puede optar por ejecutarla cada dos épocas o una vez al final. Pero puede resultar útil probar el modelo a medida que se entrena para determinar después de cuántas épocas comienza a sobreajustarse.
El código siguiente entrena un modelo con más de 50 épocas.
epochs = 50
for epoch in range(1, epochs + 1):
# print the epoch number
print('Epoch: {}'.format(epoch))
# Feed training data into the model to optimize the weights
train_loss = train(model, train_loader, optimizer)
print(train_loss)
# Feed the test data into the model to check its performance
test_loss = test(model, test_loader)
print(test_loss)
Guardado del estado de un modelo entrenado
Una vez que haya entrenado correctamente un modelo, puede guardar sus ponderaciones y sesgos de la siguiente manera:
model_file = '/dbfs/my_model.pkl'
torch.save(model.state_dict(), model_file)
Para cargar y usar el modelo más adelante, cree una instancia de la clase de red en la que se basa el modelo y cargue los valores de ponderación y sesgo guardados.
model = myNet()
model.load_state_dict(torch.load(model_file))