Modelos de comboio com PyTorch

Concluído

O PyTorch é uma estrutura de aprendizado de máquina comumente usada para treinar modelos de aprendizado profundo. No Azure Databricks, o PyTorch é pré-instalado em clusters de ML .

Nota

Os trechos de código nesta unidade são fornecidos como exemplos para enfatizar pontos-chave. Você terá a chance de executar o código para um exemplo completo e funcional no exercício mais adiante neste módulo.

Definir uma rede PyTorch

No PyTorch, os modelos são baseados em uma rede que você define. A rede consiste em várias camadas, cada uma com entradas e saídas especificadas. Além disso, o trabalho define uma função forward que aplica funções a cada camada à medida que os dados são passados pela rede.

O código de exemplo a seguir define uma rede.

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

Embora o código possa parecer complexo no início, essa classe define uma rede relativamente simples com três camadas:

  • Uma camada de entrada que aceita quatro valores de entrada e gera cinco valores de saída para a próxima camada.
  • Uma camada que aceita cinco entradas e gera cinco saídas.
  • Uma camada de saída final que aceita cinco entradas e gera três saídas.

A função forward aplica as camadas aos dados de entrada (x), passando a saída de cada camada para a próxima e, finalmente, retornando a saída da última camada (que contém o vetor de previsão de rótulo, y). Uma função de ativação de unidade linear retificada (ReLU) é aplicada às saídas das camadas 1 e 2 para restringir os valores de saída a números positivos.

Nota

Dependendo do tipo de critério de perda usado, você pode optar por aplicar uma função de ativação, como um log_softmax , ao valor de retorno para forçá-lo ao intervalo de 0 a 1. No entanto, alguns critérios de perda (como CrossEntropyLoss, que é comumente usado para classificação multiclasse) aplicam automaticamente uma função adequada.

Para criar um modelo para treinamento, você só precisa criar uma instância da classe de rede como esta:

myModel = MyNet()

preparar dados para modelação

As camadas PyTorch funcionam em dados formatados como tensores - estruturas semelhantes a matrizes. Existem várias funções para converter outros formatos de dados comuns em tensores, e você pode definir um carregador de dados PyTorch para ler tensores de dados em um modelo para treinamento ou inferência.

Como acontece com a maioria das técnicas de aprendizado de máquina supervisionado, você deve definir conjuntos de dados separados para treinamento e validação. Essa separação permite validar que o modelo prevê com precisão quando apresentado com dados sobre os quais não foi treinado.

O código a seguir define dois carregadores de dados; um para formação e outro para testes. Os dados de origem para cada carregador neste exemplo são assumidos como uma matriz Numpy de valores de recurso e uma matriz Numpy de valores de rótulo correspondentes.

# 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)

Os carregadores neste exemplo dividem os dados em lotes de 30, que são passados para a função forward durante o treinamento ou inferência.

Escolha um critério de perda e um algoritmo otimizador

O modelo é treinado alimentando os dados de treinamento na rede, medindo a perda (a diferença agregada entre os valores previstos e reais) e otimizando a rede ajustando os pesos e balanças para minimizar a perda. Os detalhes específicos de como a perda é calculada e minimizada são regidos pelo critério de perda e pelo algoritmo otimizador escolhido.

Critérios de perda

O PyTorch suporta múltiplas funções de critérios de perda, incluindo (entre muitas outras):

  • cross_entropy: Uma função que mede a diferença agregada entre valores previstos e reais para múltiplas variáveis (normalmente usada para medir a perda de probabilidades de classe na classificação multiclasse).
  • binary_cross_entropy: Uma função que mede a diferença entre probabilidades previstas e reais (normalmente usada para medir a perda de probabilidades de classe na classificação binária).
  • mse_loss: Uma função que mede a perda de erro quadrado médio para valores numéricos previstos e reais (normalmente usada para regressão).

Para especificar o critério de perda que você deseja usar ao treinar seu modelo, crie uma instância da função apropriada; Assim:

import torch.nn as nn

loss_criteria = nn.CrossEntropyLoss

Gorjeta

Para obter mais informações sobre os critérios de perda disponíveis no PyTorch, consulte Funções de perda na documentação do PyTorch.

Algoritmos otimizadores

Tendo calculado a perda, um otimizador é usado para determinar a melhor forma de ajustar os pesos e balanças, a fim de minimizá-la. Os otimizadores são implementações específicas de uma abordagem de descida de gradiente para minimizar uma função. Os otimizadores disponíveis no PyTorch incluem (entre outros):

Para usar qualquer um desses algoritmos para treinar um modelo, você precisa criar uma instância do otimizador e definir todos os parâmetros necessários. Os parâmetros específicos variam dependendo do otimizador escolhido, mas a maioria exige que você especifique uma taxa de aprendizado que governe o tamanho dos ajustes feitos com cada otimização.

O código a seguir cria uma instância do otimizador Adam .

import torch.optim as opt

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

Gorjeta

Para obter mais informações sobre otimizadores disponíveis no PyTorch, consulte Algoritmos na documentação do PyTorch.

Criar funções de trem e teste

Depois de definir uma rede e preparar os dados para ela, você pode usar os dados para treinar e testar um modelo passando os dados de treinamento pela rede, calculando a perda, otimizando os pesos e vieses da rede e validando o desempenho da rede com os dados de teste. É comum definir uma função que passa dados pela rede para treinar o modelo com os dados de treinamento e uma função separada para testar o modelo com os dados de teste.

Criar uma função de comboio

O exemplo a seguir mostra uma função para treinar um 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

O exemplo a seguir mostra uma função para testar o 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

Treine o modelo ao longo de várias épocas

Para treinar um modelo de aprendizagem profunda, você normalmente executa a função de treinamento várias vezes (referidas como épocas), com o objetivo de reduzir a perda calculada a partir dos dados de treinamento de cada época. Você pode usar sua função de teste para validar que a perda dos dados de teste (nos quais o modelo não foi treinado) também está reduzindo de acordo com a perda de treinamento - em outras palavras, que o treinamento do modelo não está produzindo um modelo que está sobreajustado aos dados de treinamento.

Gorjeta

Você não precisa executar a função de teste para todas as épocas. Você pode optar por executá-lo a cada segunda época, ou uma vez no final. No entanto, testar o modelo à medida que é treinado pode ser útil para determinar depois de quantas épocas um modelo começa a ficar sobreajustado.

O código a seguir treina um modelo com mais 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)

Salvar o estado do modelo treinado

Depois de treinar um modelo com sucesso, você pode salvar seus pesos e vieses como este:

model_file = '/dbfs/my_model.pkl'
torch.save(model.state_dict(), model_file)

Para carregar e usar o modelo posteriormente, crie uma instância da classe de rede na qual o modelo se baseia e carregue os pesos e vieses economizados.

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