Dela via


Träna bildklassificerarens modell med PyTorch

Anmärkning

För större funktioner kan PyTorch också användas med DirectML i Windows.

I föregående steg i den här självstudien hämtade vi den datamängd som vi ska använda för att träna vår bildklassificerare med PyTorch. Nu är det dags att använda dessa data.

Om du vill träna bildklassificeraren med PyTorch måste du utföra följande steg:

  1. Läs in data. Om du har gjort föregående steg i den här självstudien har du redan hanterat detta.
  2. Definiera ett neuralt convolution-nätverk.
  3. Definiera en förlustfunktion.
  4. Träna modellen på träningsdata.
  5. Testa nätverket på testdatan.

Definiera ett neuralt convolution-nätverk.

Om du vill skapa ett neuralt nätverk med PyTorch använder torch.nn du paketet. Det här paketet innehåller moduler, utökningsbara klasser och alla nödvändiga komponenter för att skapa neurala nätverk.

Här skapar du ett grundläggande neuralt convolution-nätverk (CNN) för att klassificera bilderna från CIFAR10 datauppsättningen.

En CNN är en klass av neurala nätverk som definieras som neurala nätverk med flera lager som är utformade för att identifiera komplexa funktioner i data. De används oftast i applikationer för datorseende.

Vårt nätverk kommer att struktureras med följande 14 lager:

Conv -> BatchNorm -> ReLU -> Conv -> BatchNorm -> ReLU -> MaxPool -> Conv -> BatchNorm -> ReLU -> Conv -> BatchNorm -> ReLU -> Linear.

Konvolutionslagret

Convolution-lagret är ett huvudlager i CNN som hjälper oss att identifiera funktioner i bilder. Vart och ett av lagren har flera kanaler för att identifiera specifika funktioner i bilder och ett antal kärnor för att definiera storleken på den identifierade funktionen. Därför skulle ett faltningslager med 64 kanaler och kernelstorleken 3 x 3 upptäcka 64 distinkta funktioner, var och en av storlek 3 x 3. När du definierar ett convolution-lager anger du antalet kanaler, antalet utkanaler och kernelstorleken. Antalet utkanaler i lagret fungerar som antalet inkanaler till nästa lager.

Till exempel: Ett convolution-lager med in-channels=3, out-channels=10 och kernel-size=6 får RGB-avbildningen (3 kanaler) som indata, och den tillämpar 10 funktionsdetektorer på bilderna med kernelstorleken 6x6. Mindre kernelstorlekar minskar beräkningstiden och viktdelningen.

Andra lager

Följande andra lager ingår i vårt nätverk:

  • Lagret ReLU är en aktiveringsfunktion för att definiera att alla inkommande funktioner ska vara 0 eller större. När du använder det här lagret ändras ett tal som är mindre än 0 till noll, medan andra behålls på samma sätt.
  • BatchNorm2d lagret tillämpar normalisering på indata för att ha noll medelvärde och enhetsavvikelse och öka nätverksnoggrannheten.
  • Lagret MaxPool hjälper oss att se till att platsen för ett objekt i en bild inte påverkar det neurala nätverkets förmåga att identifiera dess specifika funktioner.
  • Lagret Linear är de sista lagren i vårt nätverk, som beräknar poängen för var och en av klasserna. I den CIFAR10 datauppsättningen finns det tio klasser med etiketter. Etiketten med högsta poäng är den som modellen förutspår. I det linjära lagret måste du ange antalet indatafunktioner och antalet utdatafunktioner som ska motsvara antalet klasser.

Hur fungerar ett neuralt nätverk?

CNN är ett feed-forward-nätverk. Under träningsprocessen bearbetar nätverket indata genom alla lager, beräknar förlusten för att förstå hur långt den förutsagda etiketten för bilden avviker från den korrekta och sprider gradienterna tillbaka till nätverket för att uppdatera lagrens vikter. Genom att iterera över en enorm datamängd med indata kommer nätverket att "lära sig" att ange sina vikter för att uppnå bästa resultat.

En framåtfunktion beräknar värdet av förlustfunktionen, och backward-funktionen beräknar gradienterna för de inlärbara parametrarna. När du skapar vårt neurala nätverk med PyTorch behöver du bara definiera funktionen framåt. Bakåtfunktionen definieras automatiskt.

  1. Kopiera följande kod till PyTorchTraining.py filen i Visual Studio för att definiera CCN.
import torch
import torch.nn as nn
import torchvision
import torch.nn.functional as F

# Define a convolution neural network
class Network(nn.Module):
    def __init__(self):
        super(Network, self).__init__()
        
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=5, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(12)
        self.conv2 = nn.Conv2d(in_channels=12, out_channels=12, kernel_size=5, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(12)
        self.pool = nn.MaxPool2d(2,2)
        self.conv4 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=5, stride=1, padding=1)
        self.bn4 = nn.BatchNorm2d(24)
        self.conv5 = nn.Conv2d(in_channels=24, out_channels=24, kernel_size=5, stride=1, padding=1)
        self.bn5 = nn.BatchNorm2d(24)
        self.fc1 = nn.Linear(24*10*10, 10)

    def forward(self, input):
        output = F.relu(self.bn1(self.conv1(input)))      
        output = F.relu(self.bn2(self.conv2(output)))     
        output = self.pool(output)                        
        output = F.relu(self.bn4(self.conv4(output)))     
        output = F.relu(self.bn5(self.conv5(output)))     
        output = output.view(-1, 24*10*10)
        output = self.fc1(output)

        return output

# Instantiate a neural network model 
model = Network()

Anmärkning

Vill du veta mer om neurala nätverk med PyTorch? Kolla in PyTorch-dokumentationen

Definiera en förlustfunktion

En förlustfunktion beräknar ett värde som beräknar hur långt borta utdata är från målet. Huvudmålet är att minska förlustfunktionens värde genom att ändra viktvektorvärdena genom backpropagation i neurala nätverk.

Förlustvärdet skiljer sig från modellens noggrannhet. Förlustfunktionen ger oss förståelse för hur väl en modell beter sig efter varje iteration av optimering på träningsuppsättningen. Modellens noggrannhet beräknas på testdata och visar procentandelen av rätt förutsägelse.

I PyTorch innehåller det neurala nätverkspaketet olika förlustfunktioner som utgör byggstenarna i djupa neurala nätverk. I den här handledningen kommer du att använda en klassificeringsförlustfunktion med Klassificering Cross-Entropy-förlust och en Adam-optimizer. Inlärningsfrekvensen (lr) anger kontrollen över hur mycket du justerar nätverkets vikter med avseende på förlustgradienten. Du sätter den till 0,001. Ju lägre den är, desto långsammare blir träningen.

  1. Kopiera följande kod till PyTorchTraining.py filen i Visual Studio för att definiera förlustfunktionen och en optimerare.
from torch.optim import Adam
 
# Define the loss function with Classification Cross-Entropy loss and an optimizer with Adam optimizer
loss_fn = nn.CrossEntropyLoss()
optimizer = Adam(model.parameters(), lr=0.001, weight_decay=0.0001)

Träna modellen på träningsdata.

För att träna modellen måste du loopa över vår data iterator, mata in indata till nätverket och optimera. PyTorch har inget dedikerat bibliotek för GPU-användning, men du kan definiera körningsenheten manuellt. Enheten kommer att vara en Nvidia GPU om den finns på din dator, annars blir det din processor.

  1. Lägg till följande kod i PyTorchTraining.py filen
from torch.autograd import Variable

# Function to save the model
def saveModel():
    path = "./myFirstModel.pth"
    torch.save(model.state_dict(), path)

# Function to test the model with the test dataset and print the accuracy for the test images
def testAccuracy():
    
    model.eval()
    accuracy = 0.0
    total = 0.0
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            # run the model on the test set to predict labels
            outputs = model(images.to(device))
            # the label with the highest energy will be our prediction
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            accuracy += (predicted == labels.to(device)).sum().item()
    
    # compute the accuracy over all test images
    accuracy = (100 * accuracy / total)
    return(accuracy)


# Training function. We simply have to loop over our data iterator and feed the inputs to the network and optimize.
def train(num_epochs):
    
    best_accuracy = 0.0

    # Define your execution device
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("The model will be running on", device, "device")
    # Convert model parameters and buffers to CPU or Cuda
    model.to(device)

    for epoch in range(num_epochs):  # loop over the dataset multiple times
        running_loss = 0.0
        running_acc = 0.0

        for i, (images, labels) in enumerate(train_loader, 0):
            
            # get the inputs
            images = Variable(images.to(device))
            labels = Variable(labels.to(device))

            # zero the parameter gradients
            optimizer.zero_grad()
            # predict classes using images from the training set
            outputs = model(images)
            # compute the loss based on model output and real labels
            loss = loss_fn(outputs, labels)
            # backpropagate the loss
            loss.backward()
            # adjust parameters based on the calculated gradients
            optimizer.step()

            # Let's print statistics for every 1,000 images
            running_loss += loss.item()     # extract the loss value
            if i % 1000 == 999:    
                # print every 1000 (twice per epoch) 
                print('[%d, %5d] loss: %.3f' %
                      (epoch + 1, i + 1, running_loss / 1000))
                # zero the loss
                running_loss = 0.0

        # Compute and print the average accuracy fo this epoch when tested over all 10000 test images
        accuracy = testAccuracy()
        print('For epoch', epoch+1,'the test accuracy over the whole test set is %d %%' % (accuracy))
        
        # we want to save the model if the accuracy is the best
        if accuracy > best_accuracy:
            saveModel()
            best_accuracy = accuracy

Testa modellen på testdata.

Nu kan du testa modellen med batch av bilder från vår testuppsättning.

  1. Lägg till följande kod i PyTorchTraining.py-filen:
import matplotlib.pyplot as plt
import numpy as np

# Function to show the images
def imageshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# Function to test the model with a batch of images and show the labels predictions
def testBatch():
    # get batch of images from the test DataLoader  
    images, labels = next(iter(test_loader))

    # show all images as one image grid
    imageshow(torchvision.utils.make_grid(images))
   
    # Show the real labels on the screen 
    print('Real labels: ', ' '.join('%5s' % classes[labels[j]] 
                               for j in range(batch_size)))
  
    # Let's see what if the model identifiers the  labels of those example
    outputs = model(images)
    
    # We got the probability for every 10 labels. The highest (max) probability should be correct label
    _, predicted = torch.max(outputs, 1)
    
    # Let's show the predicted labels on the screen to compare with the real ones
    print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] 
                              for j in range(batch_size)))

Slutligen ska vi lägga till huvudkoden. Detta initierar modellträning, sparar modellen och visar resultatet på skärmen. Vi kör bara två iterationer [train(2)] över träningsuppsättningen, så träningsprocessen tar inte för lång tid.

  1. Lägg till följande kod i PyTorchTraining.py-filen:
if __name__ == "__main__":
    
    # Let's build our model
    train(5)
    print('Finished Training')

    # Test which classes performed well
    testAccuracy()
    
    # Let's load the model we just created and test the accuracy per label
    model = Network()
    path = "myFirstModel.pth"
    model.load_state_dict(torch.load(path))

    # Test with batch of images
    testBatch()

Nu kör vi testet! Kontrollera att de nedrullningsbara menyerna i det övre verktygsfältet är inställda på Felsök. Ändra Solution Platform till x64 för att köra projektet på den lokala datorn om enheten är 64-bitars eller x86 om den är 32-bitars.

Om du väljer epoknumret (antalet fullständiga pass genom träningsdatamängden) som är lika med två ([train(2)]) kommer det att resultera i att iterera två gånger genom hela testdatamängden med 10 000 bilder. Det tar cirka 20 minuter att slutföra träningen på 8:e generationens Intel CPU, och modellen bör uppnå mer eller mindre 65% av framgångsgraden i klassificeringen av tio etiketter.

  1. Om du vill köra projektet klickar du på knappen Starta felsökning i verktygsfältet eller trycker på F5.

Konsolfönstret visas, och du kommer att kunna se träningsprocessen.

Som du har definierat kommer förlustvärdet att skrivas ut för var 1 000:e batch av bilder, eller fem gånger för varje genomgång av träningsuppsättningen. Du förväntar dig att förlustvärdet minskar för varje loop.

Du ser också modellens noggrannhet efter varje iteration. Modellnoggrannhet skiljer sig från förlustvärdet. Förlustfunktionen ger oss förståelse för hur väl en modell beter sig efter varje iteration av optimering på träningsuppsättningen. Modellens noggrannhet beräknas på testdata och visar procentandelen av rätt förutsägelse. I vårt fall kommer det att berätta hur många bilder från testuppsättningen på 10 000 bilder som vår modell kunde klassificera korrekt efter varje tränings-iteration.

När träningen är klar bör du förvänta dig att se utdata som liknar nedanstående. Dina siffror kommer inte att vara exakt desamma - trianing beror på många faktorer och returnerar inte alltid identificala resultat - men de bör se liknande ut.

Utdata från den inledande modellträningen

Efter att bara ha kört 5 epoker är modellens framgångsgrad 70%. Detta är ett bra resultat för en grundläggande modell som tränats under kort tid!

Genom att testa med bildbatchen lyckades modellen identifiera 7 bilder korrekt av totalt 10. Inte dåligt alls och överensstämmer med modellens framgångsgrad.

Framgångsrikt klassificerade bilder

Du kan kontrollera vilka klasser vår modell kan förutsäga bäst. Lägg enkelt till körningskoden nedan:

  1. Valfritt – lägg till följande testClassess funktion i PyTorchTraining.py filen, lägg till ett anrop för den här funktionen – testClassess() inuti huvudfunktionen - __name__ == "__main__".
# Function to test what classes performed well
def testClassess():
    class_correct = list(0. for i in range(number_of_labels))
    class_total = list(0. for i in range(number_of_labels))
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            outputs = model(images)
            _, predicted = torch.max(outputs, 1)
            c = (predicted == labels).squeeze()
            for i in range(batch_size):
                label = labels[i]
                class_correct[label] += c[i].item()
                class_total[label] += 1

    for i in range(number_of_labels):
        print('Accuracy of %5s : %2d %%' % (
            classes[i], 100 * class_correct[i] / class_total[i]))

Utdata är följande:

Inledande klassificeringsnoggrannhet

Nästa steg

Nu när vi har en klassificeringsmodell är nästa steg att konvertera modellen till ONNX-formatet