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