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

Ihr erstes neuronales Netzwerk auf einer Grafikkarte (GPU). Anleitung für Anfänger
In diesem Artikel erkläre ich, wie Sie in 30 Minuten eine Umgebung für maschinelles Lernen einrichten, ein neuronales Netzwerk für die Bilderkennung erstellen und dasselbe Netzwerk auf einer Grafikkarte (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 um dessen programmatische oder hardwaretechnische Umsetzung, die nach dem Prinzip der Organisation und Funktionsweise biologischer neuronaler Netze — Nervenleitungen lebender Organismen — aufgebaut ist. Dieses Konzept entstand bei der Untersuchung von Prozessen im Gehirn und bei dem Versuch, diese Prozesse zu modellieren.

Neuronale Netze werden nicht im herkömmlichen Sinne programmiert; sie werden trainiert. Die Möglichkeit des Trainings ist eines der Hauptmerkmale, die neuronale Netze gegenüber traditionellen Algorithmen begünstigen. Technisch gesehen besteht das Training darin, die Koeffizienten der Verbindungen zwischen den Neuronen zu bestimmen. Während des Trainings kann das neuronale Netzwerk komplexe Abhängigkeiten zwischen den Eingabedaten und den Ausgabedaten aufdecken und auch Verallgemeinerungen durchführen.

Aus der Sicht des maschinellen Lernens ist ein neuronales Netzwerk eine spezielle Form von Methoden zur Mustererkennung, Diskriminanzanalyse, Clusteranalysen und anderen Methoden.

Ausrüstung

Zuerst klären wir die technische Ausstattung. Wir benötigen einen Server mit dem darauf installierten Betriebssystem Linux. Die Hardware, die für maschinelles Lernen benötigt wird, muss ausreichend leistungsfähig und deshalb teuer sein. Allen, die nicht über einen leistungsstarken Computer verfügen, empfehle ich, ein Auge auf die Angebote von Cloud-Anbietern zu werfen. Den benötigten Server kann man schnell mieten und nur für die Nutzungsdauer bezahlen.

In Projekten, in denen neuronale Netzwerke erstellt werden müssen, nutze ich die Server eines russischen Cloud-Anbieters. Das Unternehmen bietet Cloud-Server zur Miete an, die speziell für maschinelles Lernen mit leistungsstarken Tesla V100-Grafikprozessoren (GPU) von NVIDIA ausgestattet sind. Kurz gesagt: Die Nutzung eines Servers mit GPU kann im Vergleich zu einem Server mit ähnlichen Kosten, der CPU verwendet, um ein Vielfaches effizienter (schneller) sein. Dies wird durch die Architektur der GPU erreicht, die Berechnungen schneller ausführt.

Um die im Folgenden beschriebenen Beispiele auszuführen, haben wir für einige Tage einen solchen Server gemietet:

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

Auf dem Rechner wurde Ubuntu 18.04 installiert.

Umgebung einrichten

Jetzt installieren wir alles Notwendige für die Arbeit auf dem Server. Da unser Artikel in erster Linie für Anfänger gedacht ist, werde ich einige Punkte erläutern, die für sie nützlich sein werden.

Ein Großteil der Arbeit bei der Einrichtung der Umgebung erfolgt über die Befehlszeile. Die meisten Benutzer verwenden Windows als Arbeitsbetriebssystem. Die Standardkonsole in diesem Betriebssystem lässt zu wünschen übrig. Daher nutzen wir ein praktisches Tool Cmder/. Laden Sie die Mini-Version herunter und starten Sie Cmder.exe. Danach müssen wir uns über das SSH-Protokoll mit dem Server verbinden:

ssh root@server-ip-or-hostname

Statt server-ip-or-hostname geben Sie die IP-Adresse oder den DNS-Namen Ihres Servers an. Dann geben Sie das Passwort ein und bei erfolgreicher Verbindung sollten wir eine solche Nachricht erhalten.

Willkommen bei Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)

Die Hauptprogrammiersprache für die Entwicklung von ML-Modellen ist Python. Die beliebteste Plattform zu seiner Verwendung unter Linux ist Anaconda.

Wir installieren es auf unserem Server.

Wir beginnen mit der Aktualisierung des lokalen Paketmanagers:

sudo apt-get update

Wir installieren curl (ein Dienstprogramm für die Befehlszeile):

sudo apt-get install curl

Wir laden die neueste Version der Anaconda Distribution herunter:

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

Wir starten die Installation:

bash Anaconda3-2019.10-Linux-x86_64.sh

Während der Installation müssen Sie dem Lizenzvertrag zustimmen. Bei erfolgreicher Installation sollten Sie dies sehen:

Vielen Dank für die Installation von Anaconda3!

Es gibt jetzt viele Frameworks zur Entwicklung von ML-Modellen, wir arbeiten mit den beliebtesten: PyTorch und Tensorflow.

Die Verwendung des Frameworks ermöglicht eine schnellere Entwicklung und die Nutzung bereits vorhandener Werkzeuge für Standardaufgaben.

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

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

Nun müssen wir Jupyter Notebook starten – ein bei ML-Spezialisten beliebtes Entwicklungswerkzeug. Es erlaubt, Code zu schreiben und sofort die Ergebnisse seiner Ausführung zu sehen. Jupyter Notebook ist Teil von Anaconda und bereits auf unserem Server installiert. Wir müssen uns von unserem Desktop-System aus mit ihm verbinden.

Dazu starten wir zuerst Jupyter auf dem Server und geben den Port 8080 an:

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

Öffnen Sie dann in unserer Konsole Cmder einen weiteren Tab (obere Menüleiste – Dialog Neue Konsole) und verbinden Sie sich über SSH mit dem Server über den Port 8080:

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

Wenn Sie den ersten Befehl eingeben, werden Ihnen Links zur Verfügung gestellt, um Jupyter in Ihrem Browser zu öffnen:

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

Wir verwenden den Link für localhost:8080. Kopieren Sie den vollständigen Pfad und fügen Sie ihn in die Adresszeile Ihres lokalen Browsers ein. Jupyter Notebook wird geöffnet.

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

Überprüfen wir die ordnungsgemäße Funktion aller Komponenten, die wir installiert haben. Geben Sie in Jupyter ein Beispiel für PyTorch-Code ein und starten Sie die Ausführung (Taste Ausführen):

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

Das Ergebnis sollte ungefähr so aussehen:

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

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

Erstellen eines neuronalen Netzwerks

Wir werden ein neuronales Netzwerk zur Bilderkennung erstellen. Wir nehmen hierfür dieses Anleitung.

Für das Training des Netzwerks verwenden wir den öffentlichen Datensatz CIFAR10. Er umfasst Klassen: „Flugzeug“, „Auto“, „Vogel“, „Katze“, „Reh“, „Hund“, „Frosch“, „Pferd“, „Schiff“, „Lastwagen“. Bilder im CIFAR10 haben die Größe 3x32x32, das heißt 3-kanalige Farbbilder mit der Größe von 32×32 Pixeln.

Ihr erstes neuronales Netzwerk auf einer Grafikkarte (GPU). Anleitung für Anfänger
Für die Arbeit verwenden wir das erstellte PyTorch-Paket zur Bildbearbeitung – torchvision.

Wir werden die nächsten Schritte der Reihenfolge nach durchführen:

  • Laden und Normalisieren der Trainings- und Testdatensätze
  • Bestimmung des neuronalen Netzwerks
  • Training des Netzwerks mit den Trainingsdaten
  • Testen des Netzwerks mit den Testdaten
  • Wir wiederholen das Training und Testen unter Verwendung von GPU

Den gesamten folgenden Code führen wir in Jupyter Notebook aus.

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 folgendermaßen aussehen:

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 einige Trainingsbilder zur Überprüfung anzeigen:


import matplotlib.pyplot as plt
import numpy as np

# Funktionen zum Anzeigen eines Bildes

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

# Einige zufällige Trainingsbilder erhalten
dataiter = iter(trainloader)
images, labels = dataiter.next()

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

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

Bestimmung des neuronalen Netzwerks

Betrachten wir zunächst, wie ein neuronales Netzwerk zur Bildklassifizierung funktioniert. Es handelt sich um ein einfaches Feedforward-Netzwerk. Es nimmt Eingabedaten entgegen, leitet sie nacheinander durch mehrere Schichten und gibt schließlich eine Ausgabe zurück.

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

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

Definieren wir auch die Verlustfunktion und den Optimierer


import torch.optim as optim

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

Training des Netzwerks mit den Trainingsdaten

Wir beginnen mit dem Training unseres neuronalen Netzwerks. Ich weise darauf hin, dass Sie nach dem Starten dieses Codes einige Zeit warten müssen, bis die Ausführung abgeschlossen ist. Bei mir hat es 5 Minuten gedauert. Um das Netzwerk zu trainieren, benötigt man Zeit.

 für epoch in range(2): # Schleife über das Dataset mehrere Male

 laufender_verlust = 0.0
 für i, daten in enumerate(trainloader, 0):
 # Eingaben abrufen; daten ist eine Liste von [eingaben, etiketten]
 eingaben, etiketten = daten

 # Gradienten der Parameter nullen
 optimizer.zero_grad()

 # vorwärts + rückwärts + optimieren
 ausgaben = net(eingaben)
 verlust = criterion(ausgaben, etiketten)
 verlust.backward()
 optimizer.step()

 # Statistiken drucken
 laufender_verlust += verlust.item()
 wenn i % 2000 == 1999: # jede 2000. Mini-Batch drucken
 print('[%d, ] Verlust: %.3f' %
 (epoch + 1, i + 1, laufender_verlust / 2000))
 laufender_verlust = 0.0

print('Training abgeschlossen')

Wir erhalten folgendes Ergebnis:

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

Wir speichern unser trainiertes Modell:

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

Testen des Netzwerks mit den Testdaten

Wir haben das Netzwerk mit einem Datensatz trainiert. Aber wir müssen überprüfen, ob das Netzwerk überhaupt gelernt hat.

Wir werden das überprüfen, indem wir die Klasse vorhersagen, die das neuronale Netzwerk ausgibt, und dies auf Richtigkeit überprüfen. Wenn die Vorhersage korrekt ist, fügen wir das Beispiel der Liste der korrekten Vorhersagen hinzu.
Lassen Sie uns ein Bild aus dem Testdatensatz anzeigen:

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

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

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

Jetzt bitten wir das neuronale Netzwerk, uns zu sagen, was auf diesen Bildern ist:


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

outputs = net(images)

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

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

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

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

Lassen Sie uns sehen, wie das Netzwerk im gesamten Datensatz abschneidet.


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('Genauigkeit des Netzwerks bei den 10000 Testbildern: %d %%' % (
    100 * correct / total))

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

Es scheint, als wüsste das Netzwerk etwas und funktioniert. Wenn es die Klassen zufällig bestimmen würde, läge die Genauigkeit bei 10%.

Lassen Sie uns nun sehen, welche Klassen das Netzwerk besser identifiziert:

klasse_korrekt = list(0. für i in range(10))
klasse_gesamt = list(0. für i in range(10))
mit torch.no_grad():
 für daten in testloader:
 bilder, etiketten = daten
 ausgaben = net(bilder)
 _, vorhergesagt = torch.max(ausgaben, 1)
 c = (vorhergesagt == etiketten).squeeze()
 für i in range(4):
 etikett = etiketten[i]
 klasse_korrekt[etikett] += c[i].item()
 klasse_gesamt[etikett] += 1


für i in range(10):
 print('Genauigkeit von %5s : %%' % (
 klassen[i], 100 * klasse_korrekt[i] / klasse_gesamt[i]))

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

Es scheint, dass das Netzwerk Fahrzeuge und Schiffe am besten erkennt: 71% Genauigkeit.

Also funktioniert das Netzwerk. Lass uns nun versuchen, seine Funktion auf die Grafikkarte (GPU) zu übertragen und sehen, was sich ändert.

Training des neuronalen Netzwerks auf der GPU

Zuerst erkläre ich kurz, was CUDA ist. CUDA (Compute Unified Device Architecture) ist eine von NVIDIA entwickelte Plattform für parallele Berechnungen, die allgemeine Berechnungen auf Grafikkarten (GPU) ermöglicht. Mit CUDA können Entwickler Berechnungsanwendungen erheblich beschleunigen, indem sie die Leistungsfähigkeit von Grafikkarten nutzen. Auf unserem Server, den wir erworben haben, ist diese Plattform bereits installiert.

Lass uns zunächst unsere GPU als das erste sichtbare CUDA-Gerät definieren.

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Angenommen, wir befinden uns auf einer CUDA-Maschine, sollte dies ein CUDA-Gerät ausgeben:
print(device)

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

Das Netzwerk auf die GPU übertragen:

net.to(device)

Wir müssen auch die Eingaben und Ziele in jedem Schritt auf die GPU übertragen:

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

Lass uns das Netzwerk nun auf der GPU erneut trainieren:

import torch.optim als optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
für epoch in range(2): # Schleife über das Dataset mehrere Male

 laufender_verlust = 0.0
 für i, daten in enumerate(trainloader, 0):
 # Eingaben abrufen; daten ist eine Liste von [eingaben, etiketten]
 eingaben, etiketten = daten[0].to(device), daten[1].to(device)

 # Gradienten der Parameter nullen
 optimizer.zero_grad()

 # vorwärts + rückwärts + optimieren
 ausgaben = net(eingaben)
 verlust = criterion(ausgaben, etiketten)
 verlust.backward()
 optimizer.step()

 # Statistiken drucken
 laufender_verlust += verlust.item()
 wenn i % 2000 == 1999: # jede 2000. Mini-Batch drucken
 print('[%d, ] Verlust: %.3f' %
 (epoch + 1, i + 1, laufender_verlust / 2000))
 laufender_verlust = 0.0

print('Training abgeschlossen')

Dieses Mal dauerte das Training des Netzwerks etwa 3 Minuten. Erinnern wir uns, dass derselbe Schritt auf einer herkömmlichen CPU 5 Minuten erforderte. Der Unterschied ist nicht signifikant, weil unser Netzwerk nicht sehr groß ist. Bei der Verwendung größerer Datensätze für das Training wird der Geschwindigkeitsunterschied zwischen der GPU und der herkömmlichen CPU zunehmen.

Damit scheint alles gesagt zu sein. Was wir erreicht haben:

  • Wir haben betrachtet, was eine GPU ist und haben den Server ausgewählt, auf dem sie installiert ist;
  • Wir haben die Softwareumgebung für die Erstellung des neuronalen Netzwerks eingerichtet;
  • Wir haben ein neuronales Netzwerk für die Bilderkennung erstellt und es trainiert;
  • Wir haben das Training des Netzwerks unter Verwendung der GPU wiederholt und einen Geschwindigkeitszuwachs erzielt.

Ich freue mich darauf, Fragen in den Kommentaren zu beantworten.

Quelle: habr.com

60GB SSD 8Gb DDR4