Trenowanie modeli za pomocą biblioteki PyTorch

Ukończone

PyTorch to powszechnie używana platforma uczenia maszynowego do trenowania modeli uczenia głębokiego. W usłudze Azure Databricks usługa PyTorch jest wstępnie zainstalowana w klastrach uczenia maszynowego .

Uwaga

Fragmenty kodu w tej lekcji są udostępniane jako przykłady podkreślające kluczowe kwestie. Będziesz mieć możliwość uruchomienia kodu dla pełnego, działającego przykładu w ćwiczeniu w dalszej części tego modułu.

Definiowanie sieci PyTorch

W narzędziu PyTorch modele są oparte na zdefiniowanej sieci. Sieć składa się z wielu warstw, z których każda ma określone dane wejściowe i wyjściowe. Ponadto praca definiuje funkcję przesyłania dalej , która stosuje funkcje do każdej warstwy, ponieważ dane są przekazywane przez sieć.

Poniższy przykładowy kod definiuje sieć.

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

Chociaż kod może wydawać się złożony na początku, ta klasa definiuje stosunkowo prostą sieć z trzema warstwami:

  • Warstwa wejściowa, która akceptuje cztery wartości wejściowe i generuje pięć wartości wyjściowych dla następnej warstwy.
  • Warstwa, która akceptuje pięć danych wejściowych i generuje pięć danych wyjściowych.
  • Końcowa warstwa danych wyjściowych, która akceptuje pięć danych wejściowych i generuje trzy dane wyjściowe.

Funkcja forward stosuje warstwy do danych wejściowych (x), przekazując dane wyjściowe z każdej warstwy do następnej i na koniec zwracając dane wyjściowe z ostatniej warstwy (która zawiera wektor przewidywania etykiety, y). Funkcja aktywacji jednostki liniowej reLU jest stosowana do danych wyjściowych warstw 1 i 2 w celu ograniczenia wartości wyjściowych do liczb dodatnich.

Uwaga

W zależności od używanego kryterium utraty można zastosować funkcję aktywacji, taką jak log_softmax do wartości zwracanej, aby wymusić ją w zakresie od 0 do 1. Jednak niektóre kryteria utraty (takie jak CrossEntropyLoss, które są często używane do klasyfikacji wieloklasowej) automatycznie stosują odpowiednią funkcję.

Aby utworzyć model do trenowania, wystarczy utworzyć wystąpienie klasy sieciowej w następujący sposób:

myModel = MyNet()

Przygotowywanie danych do modelowania

Warstwy PyTorch działają na danych sformatowanych jako tensory — struktury podobne do macierzy. Istnieją różne funkcje umożliwiające konwertowanie innych typowych formatów danych na tensory. Można zdefiniować moduł ładujący danych PyTorch w celu odczytywania tensorów danych do modelu na potrzeby trenowania lub wnioskowania.

Podobnie jak w przypadku większości nadzorowanych technik uczenia maszynowego, należy zdefiniować oddzielne zestawy danych na potrzeby trenowania i walidacji. To rozdzielenie umożliwia sprawdzenie, czy model przewidział dokładnie podczas prezentowania danych, na których nie został wytrenowany.

Poniższy kod definiuje dwa moduły ładujących dane; jeden do trenowania, a drugi do testowania. Dane źródłowe dla każdego modułu ładującego w tym przykładzie zakłada się, że jest tablicą Numpy wartości funkcji i tablicą Numpy odpowiadających wartości etykiet.

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

Moduły ładujących w tym przykładzie dzielą dane na partie 30, które są przekazywane do funkcji przesyłania dalej podczas trenowania lub wnioskowania.

Wybieranie kryterium utraty i algorytmu optymalizatora

Model jest trenowany przez przekazywanie danych treningowych do sieci, mierzenie utraty (zagregowana różnica między wartościami przewidywanymi i rzeczywistymi) oraz optymalizowanie sieci przez dostosowanie wag i sald w celu zminimalizowania utraty. Szczegółowe informacje na temat sposobu obliczania i minimalizowania straty podlegają kryterium utraty i wybranemu algorytmowi optymalizatora.

Kryteria utraty

PyTorch obsługuje wiele funkcji kryteriów utraty, w tym (między innymi):

  • cross_entropy: Funkcja, która mierzy zagregowaną różnicę między wartościami przewidywanymi i rzeczywistymi dla wielu zmiennych (zwykle używana do mierzenia utraty prawdopodobieństwa klas w klasyfikacji wieloklasowej).
  • binary_cross_entropy: funkcja, która mierzy różnicę między przewidywanymi i rzeczywistymi prawdopodobieństwami (zwykle używana do mierzenia utraty prawdopodobieństwa klas w klasyfikacji binarnej).
  • mse_loss: funkcja, która mierzy średnią kwadratową utratę błędu dla przewidywanych i rzeczywistych wartości liczbowych (zwykle używanych do regresji).

Aby określić kryterium utraty, którego chcesz użyć podczas trenowania modelu, należy utworzyć wystąpienie odpowiedniej funkcji; Jak to:

import torch.nn as nn

loss_criteria = nn.CrossEntropyLoss

Napiwek

Aby uzyskać więcej informacji na temat dostępnych kryteriów utraty w usłudze PyTorch, zobacz Funkcje utraty w dokumentacji PyTorch.

Algorytmy optymalizatora

Po obliczeniu straty optymalizator jest używany do określenia, jak najlepiej dostosować wagi i salda w celu zminimalizowania go. Optymalizatory to konkretne implementacje podejścia spadku gradientu w celu zminimalizowania funkcji. Dostępne optymalizatory w usłudze PyTorch obejmują (między innymi):

  • Adadelta: optymalizator oparty na algorytmie adaptacyjnego szybkości Edukacja.
  • Adam: Wydajny obliczeniowo optymalizator oparty na algorytmie Adama.
  • SGD: Optymalizator oparty na algorytmie spadku gradientu stochastycznego.

Aby użyć dowolnego z tych algorytmów do wytrenowania modelu, należy utworzyć wystąpienie optymalizatora i ustawić wszystkie wymagane parametry. Określone parametry różnią się w zależności od wybranego optymalizatora, ale większość wymaga określenia szybkości nauki, która określa rozmiar korekt dokonanych przy każdej optymalizacji.

Poniższy kod tworzy wystąpienie optymalizatora Adama .

import torch.optim as opt

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

Napiwek

Aby uzyskać więcej informacji na temat dostępnych optymalizatorów w usłudze PyTorch, zobacz Algorytmy w dokumentacji PyTorch.

Tworzenie funkcji trenowania i testowania

Po zdefiniowaniu sieci i przygotowaniu dla niej danych można użyć danych do trenowania i testowania modelu, przekazując dane treningowe za pośrednictwem sieci, obliczając utratę, optymalizując wagi sieci i uprzedzenia oraz sprawdzając wydajność sieci przy użyciu danych testowych. Często definiowanie funkcji przekazującej dane przez sieć w celu wytrenowania modelu przy użyciu danych treningowych oraz oddzielnej funkcji do testowania modelu przy użyciu danych testowych.

Tworzenie funkcji train

W poniższym przykładzie pokazano funkcję trenowania modelu.

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

W poniższym przykładzie pokazano funkcję do testowania modelu.

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

Trenowanie modelu w wielu epokach

Aby wytrenować model uczenia głębokiego, zazwyczaj uruchamiasz funkcję trenowania wiele razy (nazywaną epokami), a celem jest zmniejszenie utraty obliczonej na podstawie danych treningowych każdej epoki. Możesz użyć funkcji testowania, aby sprawdzić, czy utrata danych testowych (na których model nie został wytrenowany) również zmniejsza się zgodnie z utratą trenowania — innymi słowy, że trenowanie modelu nie generuje modelu, który jest nadmiernie dopasowany do danych treningowych.

Napiwek

Nie musisz uruchamiać funkcji testowej dla każdej epoki. Możesz uruchomić ją co sekundę lub raz na końcu. Jednak testowanie modelu w miarę trenowania może być pomocne podczas określania, ile epok zaczyna być nadmiernie dopasowanych.

Poniższy kod szkoli model ponad 50 epok.

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)

Zapisywanie wytrenowanego stanu modelu

Po pomyślnym wytrenowanym modelu możesz zaoszczędzić jego wagi i uprzedzenia w następujący sposób:

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

Aby załadować i użyć modelu w późniejszym czasie, utwórz wystąpienie klasy sieciowej, na której jest oparty model, i załaduj zapisane wagi i uprzedzenia.

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