Entrenamiento de modelos con PyTorch

Completado

PyTorch es un marco de aprendizaje automático que se usa habitualmente para entrenar modelos de aprendizaje profundo. En Azure Databricks, PyTorch está 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 para obtener un ejemplo de trabajo completo en el ejercicio más adelante en este módulo.

Definición de una red de PyTorch

En PyTorch, los modelos se basan en una red que defina. La red consta de varias capas, cada una con entradas y salidas especificadas. Además, el trabajo define una función de reenvío que aplica funciones a cada capa a medida que los datos se pasan a través de la red.

El código de ejemplo siguiente 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), pasando la salida de cada capa a la siguiente y, por último, devolviendo la salida de la última capa (que contiene el vector de predicción de etiqueta, 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 utilizado, puede optar por aplicar una función de activación, como un 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 para el entrenamiento, solo tiene que crear una instancia de la clase de red como esta:

myModel = MyNet()

Preparación de datos para el modelado

Las capas de PyTorch funcionan en los datos que tienen el formato de tensores : estructuras similares a matrices. Hay varias funciones para convertir otros formatos de datos comunes a tensores y puede definir un cargador de datos de PyTorch para leer tensores de datos en un modelo para el entrenamiento o la inferencia.

Al igual que con la mayoría de las técnicas de aprendizaje automático supervisados, debe definir conjuntos de datos independientes para el entrenamiento y la validación. Esta separación le permite validar que el modelo predice con precisión cuando se le presentan datos en los que no fue entrenado.

El código siguiente define dos cargadores de datos; uno para el entrenamiento y el otro para las pruebas. Se supone que los datos de origen de cada cargador de este ejemplo son una matriz Numpy de valores de características y una matriz Numpy de 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 de optimizador

El modelo se entrena mediante la alimentación de los datos de entrenamiento en la red, la medición de la pérdida (la diferencia agregada entre los valores predichos y reales) y la optimización de la red ajustando los pesos y saldos para minimizar la pérdida. Los detalles específicos de cómo se calcula la pérdida y minimización se rigen por el criterio de pérdida y el algoritmo de optimización que elija.

Criterios de pérdida

PyTorch admite varias funciones de criterios de pérdida, entre las que se incluyen (entre muchas otras):

  • cross_entropy: función que mide la diferencia agregada entre los valores predichos y reales de varias variables (normalmente se usa para medir la pérdida de probabilidades de clase en clasificación multiclase).
  • binary_cross_entropy: función que mide la diferencia entre las probabilidades predichas 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 media de errores cuadrados para los valores numéricos previstos y reales (normalmente se usan para la regresión).

Para especificar el criterio de pérdida que desea usar al entrenar el modelo, cree una instancia de la función adecuada; Así:

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 del optimizador

Después de calcular la pérdida, se usa un optimizador para determinar la mejor manera de ajustar los pesos y saldos con el fin de minimizarlo. 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):

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 que controle el tamaño de los ajustes realizados con cada optimización.

El código siguiente crea una instancia del optimizador de Adam .

import torch.optim as opt

learning_rate = 0.001
optimizer = opt.Adam(model.parameters(), lr=learning_rate)

Sugerencia

Para 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 pasando los datos de entrenamiento a través de la red, calculando la pérdida, optimizando los pesos y sesgos de red, y validando 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

Entrena el modelo durante varias épocas

Para entrenar un modelo de aprendizaje profundo, normalmente se ejecuta la función de entrenamiento varias veces (denominada épocas), con el objetivo de reducir la pérdida calculada a partir de los datos de entrenamiento 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 durante 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

Después de haber entrenado exitosamente un modelo, puede guardar los pesos y sesgos del modelo de esta 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 pesos y sesgos guardados.

model = myNet()
model.load_state_dict(torch.load(model_file))