Modelos de comboio com PyTorch
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):
- Adadelta: Um otimizador baseado no algoritmo Adaptive Learning Rate .
- Adam: Um otimizador computacionalmente eficiente baseado no algoritmo Adam .
- SGD: Um otimizador baseado no algoritmo de descida de gradiente estocástico.
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))