Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger
In diesem Artikel erkläre ich Ihnen, wie Sie in 30 Minuten eine Umgebung für maschinelles Lernen einrichten, ein neuronales Netzwerk für die Bilderkennung erstellen und dann dasselbe Netzwerk auf einem Grafikprozessor (GPU) ausführen.

Lassen Sie uns zunächst definieren, was ein neuronales Netzwerk ist.

In unserem Fall handelt es sich um ein mathematisches Modell sowie seine Software- oder Hardware-Verkörperung, die auf dem Organisations- und Funktionsprinzip biologischer neuronaler Netze – Netzwerken von Nervenzellen eines lebenden Organismus – basiert. Dieses Konzept entstand, als man die im Gehirn ablaufenden Prozesse untersuchte und versuchte, diese Prozesse zu modellieren.

Neuronale Netze werden nicht im herkömmlichen Sinne programmiert, sondern trainiert. Die Lernfähigkeit ist einer der Hauptvorteile neuronaler Netze gegenüber herkömmlichen Algorithmen. Technisch gesehen besteht Lernen darin, die Koeffizienten der Verbindungen zwischen Neuronen zu ermitteln. Während des Trainingsprozesses ist das neuronale Netzwerk in der Lage, komplexe Abhängigkeiten zwischen Eingabedaten und Ausgabedaten zu erkennen und eine Generalisierung durchzuführen.

Aus Sicht des maschinellen Lernens ist ein neuronales Netzwerk ein Sonderfall von Mustererkennungsmethoden, Diskriminanzanalysen, Clustering-Methoden und anderen Methoden.

Ausrüstung

Schauen wir uns zunächst die Ausstattung an. Wir benötigen einen Server, auf dem das Linux-Betriebssystem installiert ist. Die für den Betrieb maschineller Lernsysteme erforderliche Ausrüstung ist recht leistungsstark und daher teuer. Wer keine gute Maschine zur Hand hat, dem empfehle ich, auf die Angebote von Cloud-Anbietern zu achten. Sie können den benötigten Server schnell mieten und zahlen nur für die Nutzungszeit.

Bei Projekten, bei denen es notwendig ist, neuronale Netze aufzubauen, nutze ich die Server eines der russischen Cloud-Anbieter. Das Unternehmen bietet Cloud-Server zur Miete speziell für maschinelles Lernen mit leistungsstarken Tesla V100-Grafikprozessoren (GPU) von NVIDIA an. Kurz gesagt: Die Verwendung eines Servers mit einer GPU kann zehnmal effizienter (schneller) sein als ein Server mit ähnlichen Kosten, der eine CPU (die bekannte Zentraleinheit) für Berechnungen verwendet. Dies wird durch die Besonderheiten der GPU-Architektur erreicht, die Berechnungen schneller bewältigt.

Для выполнения примеров описанных далее, мы приобрели на несколько дней такой сервер:

  • SSD-Festplatte 150 GB
  • RAM 32 GB
  • Tesla V100 16 GB Prozessor mit 4 Kernen

Wir haben Ubuntu 18.04 auf unserem Rechner installiert.

Einrichten der Umgebung

Jetzt installieren wir alles, was für die Arbeit auf dem Server notwendig ist. Da sich unser Artikel in erster Linie an Anfänger richtet, werde ich auf einige Punkte eingehen, die für sie nützlich sein werden.

Ein Großteil der Arbeit beim Einrichten einer Umgebung wird über die Befehlszeile erledigt. Die meisten Benutzer verwenden Windows als Arbeitsbetriebssystem. Die Standardkonsole in diesem Betriebssystem lässt viel zu wünschen übrig. Daher werden wir ein praktisches Tool verwenden Befehl/. Laden Sie die Mini-Version herunter und führen Sie Cmder.exe aus. Als nächstes müssen Sie sich per SSH mit dem Server verbinden:

ssh root@server-ip-or-hostname

Geben Sie anstelle von server-ip-or-hostname die IP-Adresse oder den DNS-Namen Ihres Servers an. Geben Sie als Nächstes das Passwort ein und wenn die Verbindung erfolgreich ist, sollten wir eine Nachricht ähnlich dieser erhalten.

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

Die Hauptsprache für die Entwicklung von ML-Modellen ist Python. Und die beliebteste Plattform für den Einsatz unter Linux ist Anaconda.

Lassen Sie es uns auf unserem Server installieren.

Wir beginnen mit der Aktualisierung des lokalen Paketmanagers:

sudo apt-get update

Curl (Befehlszeilenprogramm) installieren:

sudo apt-get install curl

Laden Sie die neueste Version von Anaconda Distribution herunter:

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

Beginnen wir mit der Installation:

bash Anaconda3-2019.10-Linux-x86_64.sh

Während des Installationsvorgangs werden Sie aufgefordert, die Lizenzvereinbarung zu bestätigen. Nach erfolgreicher Installation sollten Sie Folgendes sehen:

Thank you for installing Anaconda3!

Für die Entwicklung von ML-Modellen sind mittlerweile viele Frameworks entstanden; wir arbeiten mit den beliebtesten: PyTorch и Tensorflow.

Durch den Einsatz des Frameworks können Sie die Entwicklungsgeschwindigkeit erhöhen und vorgefertigte Tools für Standardaufgaben nutzen.

In diesem Beispiel werden wir mit PyTorch arbeiten. Lass es uns installieren:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Jetzt müssen wir Jupyter Notebook starten, ein beliebtes Entwicklungstool für ML-Spezialisten. Sie können damit Code schreiben und sofort die Ergebnisse seiner Ausführung sehen. Jupyter Notebook ist in Anaconda enthalten und bereits auf unserem Server installiert. Sie müssen von unserem Desktop-System aus eine Verbindung herstellen.

Dazu starten wir Jupyter zunächst auf dem Server und geben dabei Port 8080 an:

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

Als nächstes öffnen wir eine weitere Registerkarte in unserer Cmder-Konsole (oberes Menü – Dialog „Neue Konsole“) und verbinden uns über Port 8080 per SSH mit dem Server:

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

Wenn wir den ersten Befehl eingeben, werden uns Links zum Öffnen von Jupyter in unserem Browser angeboten:

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

Verwenden wir den Link für localhost:8080. Kopieren Sie den vollständigen Pfad und fügen Sie ihn in die Adressleiste des lokalen Browsers Ihres PCs ein. Das Jupyter-Notizbuch wird geöffnet.

Erstellen wir ein neues Notebook: Neu – Notebook – Python 3.

Lassen Sie uns die korrekte Funktion aller von uns installierten Komponenten überprüfen. Geben wir den PyTorch-Beispielcode in Jupyter ein und führen die Ausführung aus (Schaltfläche „Ausführen“):

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

Das Ergebnis sollte etwa so aussehen:

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Wenn Sie ein ähnliches Ergebnis haben, haben wir alles richtig konfiguriert und können mit der Entwicklung eines neuronalen Netzwerks beginnen!

Erstellen eines neuronalen Netzwerks

Wir werden ein neuronales Netzwerk zur Bilderkennung erstellen. Nehmen wir dies als Grundlage руководство.

Wir werden den öffentlich verfügbaren CIFAR10-Datensatz verwenden, um das Netzwerk zu trainieren. Es gibt Klassen: „Flugzeug“, „Auto“, „Vogel“, „Katze“, „Hirsch“, „Hund“, „Frosch“, „Pferd“, „Schiff“, „Lastwagen“. Bilder in CIFAR10 sind 3x32x32, also 3-Kanal-Farbbilder mit 32x32 Pixeln.

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger
Für die Arbeit verwenden wir das von PyTorch erstellte Paket für die Arbeit mit Bildern – Torchvision.

Wir werden die folgenden Schritte der Reihe nach ausführen:

  • Laden und Normalisieren von Trainings- und Testdatensätzen
  • Definition eines neuronalen Netzwerks
  • Netzwerktraining anhand von Trainingsdaten
  • Netzwerktests anhand von Testdaten
  • Wiederholen wir das Training und Testen mit der GPU

Wir werden den gesamten Code unten in Jupyter Notebook ausführen.

Laden und Normalisieren von CIFAR10

Kopieren Sie den folgenden Code und führen Sie ihn in Jupyter aus:


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

Die Antwort sollte lauten:

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

Lassen Sie uns zum Testen mehrere Trainingsbilder anzeigen:


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

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Definition eines neuronalen Netzwerks

Betrachten wir zunächst, wie ein neuronales Netzwerk zur Bilderkennung funktioniert. Dies ist ein einfaches Punkt-zu-Punkt-Netzwerk. Es nimmt Eingabedaten, leitet sie nacheinander durch mehrere Schichten und erzeugt schließlich Ausgabedaten.

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Lassen Sie uns ein ähnliches Netzwerk in unserer Umgebung erstellen:


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

Wir definieren auch eine Verlustfunktion und einen Optimierer


import torch.optim as optim

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

Netzwerktraining anhand von Trainingsdaten

Beginnen wir mit dem Training unseres neuronalen Netzwerks. Bitte beachten Sie, dass Sie nach der Ausführung dieses Codes einige Zeit warten müssen, bis die Arbeit abgeschlossen ist. Bei mir hat es 5 Minuten gedauert. Es braucht Zeit, das Netzwerk zu trainieren.

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

Wir erhalten folgendes Ergebnis:

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Wir speichern unser trainiertes Modell:

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

Netzwerktests anhand von Testdaten

Wir haben das Netzwerk anhand einer Reihe von Trainingsdaten trainiert. Wir müssen aber prüfen, ob das Netzwerk überhaupt etwas gelernt hat.

Wir werden dies testen, indem wir die vom neuronalen Netzwerk ausgegebene Klassenbezeichnung vorhersagen und testen, ob sie wahr ist. Wenn die Vorhersage richtig ist, fügen wir die Stichprobe zur Liste der richtigen Vorhersagen hinzu.
Lassen Sie uns ein Bild aus dem Testsatz zeigen:

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

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Bitten wir nun das neuronale Netzwerk, uns zu sagen, was auf diesen Bildern zu sehen ist:


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

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Die Ergebnisse scheinen ziemlich gut zu sein: Das Netzwerk hat drei von vier Bildern korrekt identifiziert.

Sehen wir uns an, wie sich das Netzwerk im gesamten Datensatz verhält.


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

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Es sieht so aus, als ob das Netzwerk etwas weiß und funktioniert. Würde er die Klassen zufällig bestimmen, läge die Genauigkeit bei 10 %.

Schauen wir uns nun an, welche Klassen das Netzwerk besser erkennt:

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

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Es scheint, dass das Netzwerk Autos und Schiffe am besten identifizieren kann: 71 % Genauigkeit.

Das Netzwerk funktioniert also. Versuchen wir nun, seine Arbeit auf den Grafikprozessor (GPU) zu übertragen und zu sehen, was sich ändert.

Training eines neuronalen Netzwerks auf der GPU

Zunächst werde ich kurz erklären, was CUDA ist. CUDA (Compute Unified Device Architecture) ist eine von NVIDIA entwickelte Parallel-Computing-Plattform für allgemeines Computing auf Grafikprozessoren (GPUs). Mit CUDA können Entwickler Computeranwendungen erheblich beschleunigen, indem sie die Leistung von GPUs nutzen. Diese Plattform ist bereits auf unserem von uns erworbenen Server installiert.

Definieren wir zunächst unsere GPU als das erste sichtbare Cuda-Gerät.

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 )

Ihr erstes neuronales Netzwerk auf einer Grafikverarbeitungseinheit (GPU). Ratgeber für Anfänger

Senden des Netzwerks an die GPU:

net.to(device)

Außerdem müssen wir bei jedem Schritt Eingaben und Ziele an die GPU senden:

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

Lassen Sie uns das Netzwerk auf der GPU neu trainieren:

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 и традиционного процессора буде возрастать.

Das scheint alles zu sein. Was wir geschafft haben:

  • Wir haben uns angesehen, was eine GPU ist, und den Server ausgewählt, auf dem sie installiert ist.
  • Wir haben eine Softwareumgebung eingerichtet, um ein neuronales Netzwerk zu erstellen.
  • Wir haben ein neuronales Netzwerk zur Bilderkennung erstellt und trainiert;
  • Wir haben das Netzwerktraining mit der GPU wiederholt und eine Geschwindigkeitssteigerung erhalten.

Fragen beantworte ich gerne in den Kommentaren.

Source: habr.com

Kommentar hinzufügen