Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους
Σε αυτό το άρθρο, θα σας πω πώς να ρυθμίσετε ένα περιβάλλον μηχανικής εκμάθησης σε 30 λεπτά, να δημιουργήσετε ένα νευρωνικό δίκτυο για την αναγνώριση εικόνας και, στη συνέχεια, να εκτελέσετε το ίδιο δίκτυο σε έναν επεξεργαστή γραφικών (GPU).

Αρχικά, ας ορίσουμε τι είναι ένα νευρωνικό δίκτυο.

Στην περίπτωσή μας, αυτό είναι ένα μαθηματικό μοντέλο, καθώς και η ενσωμάτωση λογισμικού ή υλικού του, που βασίζεται στην αρχή της οργάνωσης και της λειτουργίας των βιολογικών νευρωνικών δικτύων - δικτύων νευρικών κυττάρων ενός ζωντανού οργανισμού. Αυτή η ιδέα προέκυψε κατά τη μελέτη των διεργασιών που συμβαίνουν στον εγκέφαλο και την προσπάθεια να μοντελοποιηθούν αυτές οι διαδικασίες.

Τα νευρωνικά δίκτυα δεν είναι προγραμματισμένα με τη συνήθη έννοια της λέξης, είναι εκπαιδευμένα. Η ικανότητα μάθησης είναι ένα από τα κύρια πλεονεκτήματα των νευρωνικών δικτύων έναντι των παραδοσιακών αλγορίθμων. Τεχνικά, η μάθηση συνίσταται στην εύρεση των συντελεστών των συνδέσεων μεταξύ των νευρώνων. Κατά τη διαδικασία εκπαίδευσης, το νευρωνικό δίκτυο είναι σε θέση να εντοπίσει πολύπλοκες εξαρτήσεις μεταξύ δεδομένων εισόδου και δεδομένων εξόδου, καθώς και να πραγματοποιήσει γενίκευση.

Από την άποψη της μηχανικής μάθησης, ένα νευρωνικό δίκτυο είναι μια ειδική περίπτωση μεθόδων αναγνώρισης προτύπων, διακριτικής ανάλυσης, μεθόδων ομαδοποίησης και άλλων μεθόδων.

Оборудование

Αρχικά, ας δούμε τον εξοπλισμό. Χρειαζόμαστε έναν διακομιστή με εγκατεστημένο το λειτουργικό σύστημα Linux. Ο εξοπλισμός που απαιτείται για τη λειτουργία συστημάτων μηχανικής μάθησης είναι αρκετά ισχυρός και, ως εκ τούτου, ακριβός. Για όσους δεν έχουν καλό μηχάνημα στο χέρι, συνιστώ να δώσουν προσοχή στις προσφορές των παρόχων cloud. Μπορείτε να νοικιάσετε γρήγορα τον απαιτούμενο διακομιστή και να πληρώσετε μόνο για το χρόνο χρήσης.

Σε έργα όπου είναι απαραίτητο να δημιουργήσω νευρωνικά δίκτυα, χρησιμοποιώ τους διακομιστές ενός από τους Ρώσους παρόχους cloud. Η εταιρεία προσφέρει διακομιστές cloud προς ενοικίαση ειδικά για μηχανική εκμάθηση με ισχυρούς επεξεργαστές γραφικών Tesla V100 (GPU) από την NVIDIA. Εν ολίγοις: η χρήση διακομιστή με GPU μπορεί να είναι δεκάδες φορές πιο αποτελεσματική (γρήγορη) σε σύγκριση με έναν διακομιστή παρόμοιου κόστους που χρησιμοποιεί μια CPU (τη γνωστή κεντρική μονάδα επεξεργασίας) για υπολογισμούς. Αυτό επιτυγχάνεται χάρη στα χαρακτηριστικά της αρχιτεκτονικής GPU, η οποία αντιμετωπίζει τους υπολογισμούς πιο γρήγορα.

Για να εφαρμόσουμε τα παραδείγματα που περιγράφονται παρακάτω, αγοράσαμε τον ακόλουθο διακομιστή για αρκετές ημέρες:

  • Δίσκος SSD 150 GB
  • RAM 32 GB
  • Επεξεργαστής Tesla V100 16 Gb με 4 πυρήνες

Εγκαταστήσαμε το Ubuntu 18.04 στον υπολογιστή μας.

Ρύθμιση του περιβάλλοντος

Τώρα ας εγκαταστήσουμε όλα τα απαραίτητα για εργασία στον διακομιστή. Δεδομένου ότι το άρθρο μας είναι κυρίως για αρχάριους, θα μιλήσω για μερικά σημεία που θα τους φανούν χρήσιμα.

Μεγάλο μέρος της εργασίας κατά τη ρύθμιση ενός περιβάλλοντος γίνεται μέσω της γραμμής εντολών. Οι περισσότεροι χρήστες χρησιμοποιούν τα Windows ως λειτουργικό λειτουργικό σύστημα. Η τυπική κονσόλα σε αυτό το λειτουργικό σύστημα αφήνει πολλά να είναι επιθυμητά. Επομένως, θα χρησιμοποιήσουμε ένα βολικό εργαλείο Cmder/. Κατεβάστε τη μίνι έκδοση και εκτελέστε το Cmder.exe. Στη συνέχεια, πρέπει να συνδεθείτε στον διακομιστή μέσω SSH:

ssh root@server-ip-or-hostname

Αντί για server-ip-or-hostname, καθορίστε τη διεύθυνση IP ή το όνομα DNS του διακομιστή σας. Στη συνέχεια, πληκτρολογήστε τον κωδικό πρόσβασης και εάν η σύνδεση είναι επιτυχής, θα λάβουμε ένα παρόμοιο μήνυμα.

Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)

Η κύρια γλώσσα για την ανάπτυξη μοντέλων ML είναι η Python. Και η πιο δημοφιλής πλατφόρμα για χρήση στο Linux είναι ανακόνδας.

Ας το εγκαταστήσουμε στον διακομιστή μας.

Ξεκινάμε ενημερώνοντας τον τοπικό διαχειριστή πακέτων:

sudo apt-get update

Εγκατάσταση curl (βοηθητικό πρόγραμμα γραμμής εντολών):

sudo apt-get install curl

Κατεβάστε την πιο πρόσφατη έκδοση του Anaconda Distribution:

cd /tmp
curl –O https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.sh

Ας ξεκινήσουμε την εγκατάσταση:

bash Anaconda3-2019.10-Linux-x86_64.sh

Κατά τη διαδικασία εγκατάστασης, θα σας ζητηθεί να επιβεβαιώσετε την άδεια χρήσης. Μετά την επιτυχή εγκατάσταση θα πρέπει να δείτε αυτό:

Thank you for installing Anaconda3!

Πολλά πλαίσια έχουν πλέον δημιουργηθεί για την ανάπτυξη μοντέλων ML· συνεργαζόμαστε με τα πιο δημοφιλή: PyTorch и Τάση ροής.

Η χρήση του πλαισίου σάς επιτρέπει να αυξήσετε την ταχύτητα ανάπτυξης και να χρησιμοποιήσετε έτοιμα εργαλεία για τυπικές εργασίες.

Σε αυτό το παράδειγμα θα εργαστούμε με το PyTorch. Ας το εγκαταστήσουμε:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Τώρα πρέπει να λανσάρουμε το Jupyter Notebook, ένα δημοφιλές εργαλείο ανάπτυξης για ειδικούς ML. Σας επιτρέπει να γράψετε κώδικα και να δείτε αμέσως τα αποτελέσματα της εκτέλεσής του. Το Jupyter Notebook περιλαμβάνεται στο Anaconda και είναι ήδη εγκατεστημένο στον διακομιστή μας. Πρέπει να συνδεθείτε σε αυτό από το επιτραπέζιο σύστημα μας.

Για να γίνει αυτό, θα εκκινήσουμε πρώτα το Jupyter στον διακομιστή που καθορίζει τη θύρα 8080:

jupyter notebook --no-browser --port=8080 --allow-root

Στη συνέχεια, ανοίγοντας μια άλλη καρτέλα στην κονσόλα Cmder (επάνω μενού - Διάλογος Νέα κονσόλα) θα συνδεθούμε μέσω της θύρας 8080 στον διακομιστή μέσω SSH:

ssh -L 8080:localhost:8080 root@server-ip-or-hostname

Όταν εισάγουμε την πρώτη εντολή, θα μας προσφερθούν σύνδεσμοι για να ανοίξουμε το Jupyter στο πρόγραμμα περιήγησής μας:

To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-18788-open.html
    Or copy and paste one of these URLs:
        http://localhost:8080/?token=cca0bd0b30857821194b9018a5394a4ed2322236f116d311
     or http://127.0.0.1:8080/?token=cca0bd0b30857821194b9018a5394a4ed2322236f116d311

Ας χρησιμοποιήσουμε τον σύνδεσμο για τον localhost:8080. Αντιγράψτε την πλήρη διαδρομή και επικολλήστε την στη γραμμή διευθύνσεων του τοπικού προγράμματος περιήγησης του υπολογιστή σας. Το σημειωματάριο Jupyter θα ανοίξει.

Ας δημιουργήσουμε ένα νέο σημειωματάριο: Νέο - Σημειωματάριο - Python 3.

Ας ελέγξουμε τη σωστή λειτουργία όλων των εξαρτημάτων που εγκαταστήσαμε. Ας εισαγάγουμε το παράδειγμα κώδικα PyTorch στο Jupyter και ας εκτελέσουμε την εκτέλεση (κουμπί Run):

from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)

Το αποτέλεσμα πρέπει να είναι κάπως έτσι:

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Αν έχετε παρόμοιο αποτέλεσμα, τότε έχουμε ρυθμίσει τα πάντα σωστά και μπορούμε να ξεκινήσουμε την ανάπτυξη ενός νευρωνικού δικτύου!

Δημιουργία νευρωνικού δικτύου

Θα δημιουργήσουμε ένα νευρωνικό δίκτυο για την αναγνώριση εικόνας. Ας το πάρουμε αυτό ως βάση οδηγός.

Θα χρησιμοποιήσουμε το δημοσίως διαθέσιμο σύνολο δεδομένων CIFAR10 για την εκπαίδευση του δικτύου. Έχει κατηγορίες: «αεροπλάνο», «αυτοκίνητο», «πουλί», «γάτα», «ελάφια», «σκύλος», «βάτραχος», «άλογο», «πλοίο», «φορτηγό». Οι εικόνες στο CIFAR10 είναι 3x32x32, δηλαδή έγχρωμες εικόνες 3 καναλιών των 32x32 pixel.

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους
Για εργασία, θα χρησιμοποιήσουμε το πακέτο που δημιουργήθηκε από την PyTorch για εργασία με εικόνες - torchvision.

Θα κάνουμε τα παρακάτω βήματα με τη σειρά:

  • Φόρτωση και κανονικοποίηση συνόλων δεδομένων εκπαίδευσης και δοκιμών
  • Ορισμός νευρωνικού δικτύου
  • Εκπαίδευση δικτύου σε δεδομένα εκπαίδευσης
  • Δοκιμή δικτύου σε δεδομένα δοκιμής
  • Ας επαναλάβουμε την εκπαίδευση και τη δοκιμή χρησιμοποιώντας GPU

Θα εκτελέσουμε όλο τον παρακάτω κώδικα στο Jupyter Notebook.

Φόρτωση και κανονικοποίηση CIFAR10

Αντιγράψτε και εκτελέστε τον ακόλουθο κώδικα στο Jupyter:


import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Η απάντηση θα πρέπει να είναι:

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified

Ας εμφανίσουμε πολλές εικόνες εκπαίδευσης για δοκιμή:


import matplotlib.pyplot as plt
import numpy as np

# functions to show an image

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

# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Ορισμός νευρωνικού δικτύου

Ας εξετάσουμε πρώτα πώς λειτουργεί ένα νευρωνικό δίκτυο για την αναγνώριση εικόνας. Αυτό είναι ένα απλό δίκτυο από σημείο σε σημείο. Λαμβάνει δεδομένα εισόδου, τα περνά μέσα από πολλά επίπεδα ένα προς ένα και στη συνέχεια παράγει τελικά δεδομένα εξόδου.

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Ας δημιουργήσουμε ένα παρόμοιο δίκτυο στο περιβάλλον μας:


import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

Ορίζουμε επίσης μια συνάρτηση απώλειας και έναν βελτιστοποιητή


import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

Εκπαίδευση δικτύου σε δεδομένα εκπαίδευσης

Ας αρχίσουμε να εκπαιδεύουμε το νευρωνικό μας δίκτυο. Λάβετε υπόψη ότι αφού εκτελέσετε αυτόν τον κωδικό, θα χρειαστεί να περιμένετε λίγο μέχρι να ολοκληρωθεί η εργασία. Μου πήρε 5 λεπτά. Χρειάζεται χρόνος για την εκπαίδευση του δικτύου.

 for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

Παίρνουμε το εξής αποτέλεσμα:

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Αποθηκεύουμε το εκπαιδευμένο μοντέλο μας:

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

Δοκιμή δικτύου σε δεδομένα δοκιμής

Εκπαιδεύσαμε το δίκτυο χρησιμοποιώντας ένα σύνολο δεδομένων εκπαίδευσης. Αλλά πρέπει να ελέγξουμε αν το δίκτυο έχει μάθει τίποτα καθόλου.

Θα το ελέγξουμε αυτό προβλέποντας την ετικέτα κλάσης που βγάζει το νευρωνικό δίκτυο και δοκιμάζοντας την για να δούμε αν είναι αλήθεια. Εάν η πρόβλεψη είναι σωστή, προσθέτουμε το δείγμα στη λίστα των σωστών προβλέψεων.
Ας δείξουμε μια εικόνα από το σετ δοκιμής:

dataiter = iter(testloader)
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Τώρα ας ζητήσουμε από το νευρωνικό δίκτυο να μας πει τι φαίνεται σε αυτές τις εικόνες:


net = Net()
net.load_state_dict(torch.load(PATH))

outputs = net(images)

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Τα αποτελέσματα φαίνονται αρκετά καλά: το δίκτυο εντόπισε σωστά τρεις στις τέσσερις εικόνες.

Ας δούμε πώς αποδίδει το δίκτυο σε ολόκληρο το σύνολο δεδομένων.


correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Φαίνεται ότι το δίκτυο γνωρίζει κάτι και λειτουργεί. Εάν καθόριζε τις τάξεις τυχαία, η ακρίβεια θα ήταν 10%.

Τώρα ας δούμε ποιες κλάσεις αναγνωρίζει καλύτερα το δίκτυο:

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


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

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Φαίνεται ότι το δίκτυο είναι το καλύτερο στον εντοπισμό αυτοκινήτων και πλοίων: 71% ακρίβεια.

Άρα το δίκτυο λειτουργεί. Τώρα ας προσπαθήσουμε να μεταφέρουμε το έργο του στον επεξεργαστή γραφικών (GPU) και να δούμε τι αλλάζει.

Εκπαίδευση νευρωνικού δικτύου σε GPU

Αρχικά, θα εξηγήσω εν συντομία τι είναι το CUDA. Το CUDA (Compute Unified Device Architecture) είναι μια πλατφόρμα παράλληλων υπολογιστών που αναπτύχθηκε από την NVIDIA για γενικούς υπολογισμούς σε μονάδες επεξεργασίας γραφικών (GPU). Με το CUDA, οι προγραμματιστές μπορούν να επιταχύνουν δραματικά τις υπολογιστικές εφαρμογές αξιοποιώντας την ισχύ των GPU. Αυτή η πλατφόρμα είναι ήδη εγκατεστημένη στον διακομιστή μας που αγοράσαμε.

Ας ορίσουμε πρώτα την GPU μας ως την πρώτη ορατή συσκευή cuda.

device = torch . device ( "cuda:0" if torch . cuda . is_available () else "cpu" )
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print ( device )

Το πρώτο σας νευρωνικό δίκτυο σε μονάδα επεξεργασίας γραφικών (GPU). Οδηγός για αρχάριους

Αποστολή του δικτύου στην GPU:

net.to(device)

Θα πρέπει επίσης να στέλνουμε εισόδους και στόχους σε κάθε βήμα στην GPU:

inputs, labels = data[0].to(device), data[1].to(device)

Ας εκπαιδεύσουμε ξανά το δίκτυο στη GPU:

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
    inputs, labels = data[0].to(device), data[1].to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

Αυτή τη φορά, η εκπαίδευση δικτύου διήρκεσε περίπου 3 λεπτά. Ας θυμηθούμε ότι το ίδιο στάδιο σε έναν συμβατικό επεξεργαστή διήρκεσε 5 λεπτά. Η διαφορά δεν είναι σημαντική, αυτό συμβαίνει επειδή το δίκτυό μας δεν είναι τόσο μεγάλο. Όταν χρησιμοποιείτε μεγάλες συστοιχίες για εκπαίδευση, η διαφορά μεταξύ της ταχύτητας της GPU και ενός παραδοσιακού επεξεργαστή θα αυξηθεί.

Αυτό φαίνεται να είναι όλο. Τι καταφέραμε:

  • Εξετάσαμε τι είναι μια GPU και επιλέξαμε τον διακομιστή στον οποίο είναι εγκατεστημένη.
  • Έχουμε δημιουργήσει ένα περιβάλλον λογισμικού για τη δημιουργία ενός νευρωνικού δικτύου.
  • Δημιουργήσαμε ένα νευρωνικό δίκτυο για την αναγνώριση εικόνων και το εκπαιδεύσαμε.
  • Επαναλάβαμε την εκπαίδευση δικτύου χρησιμοποιώντας τη GPU και λάβαμε αύξηση στην ταχύτητα.

Θα χαρώ να απαντήσω σε ερωτήσεις στα σχόλια.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο