© Pasko Maksim/Shutterstock.com
Entwicklung, Training und Deployment von neuronalen Netzwerken

Neuronale Netzwerke mit PyTorch


PyTorch [1] ist zur Zeit eins der populärsten Frameworks zur Entwicklung und zum Trainieren von neuronalen Netzwerken. Es zeichnet sich vor allem durch seine hohe Flexibilität und die Möglichkeit aus, Standard-Python-Debugger einzusetzen. Dabei müssen keinerlei Abstriche bezüglich der Trainingsperformance gemacht werden.

Aufgrund der genannten Eigenschaften ist PyTorch vor allem bei Deep-Learning-Forschern und bei Entwicklern im Bereich Natural Language Processing (NLP) sehr beliebt. Auch im Bereich Integration und Deployment wurden in der letzten Version, dem ersten offiziellen Release 1.0, wesentliche Neuerungen eingeführt.

Tensoren

Die elementare Datenstruktur zur Repräsentation und Verarbeitung von Daten in PyTorch ist torch.Tensor. Der mathematische Begriff Tensor steht für eine Generalisierung von Vektoren und Matrizen. In PyTorch werden Tensoren in Form von multidimensionalen Arrays implementiert. Ein Vektor ist dabei nichts anderes als ein eindimensionaler Tensor (oder ein Tensor mit Rang 1), dessen Elemente Zahlen eines bestimmten Datentyps (z. B. torch.float64 oder torch.int32) sein können. Eine Matrix ist somit ein zweidimensionaler Tensor (Rang 2) und ein Skalar ein nulldimensionaler Tensor (Rang 0). Tensoren noch höherer Dimensionen besitzen keine speziellen Namen mehr (Abb. 1).

nguyen_pytorch_1.tif_fmt1.jpgAbb. 1: Tensoren

Das Interface für PyTorch-Tensoren lehnt sich stark an das Design von multidimensionalen Arrays in NumPy [2] an. Genauso wie NumPy stellt PyTorch vordefinierte Methoden bereit, mit denen man Tensoren manipulieren und Operationen der linearen Algebra durchführen kann. Einige Beispiele sind in Listing 1 dargestellt.

Listing 1

# Generierung eines eindimensionalen Tensors mit  # 8 (uninitialisierten) Elementen (float32) x = torch.Tensor(8) x.double() # Konvertierung nach float64 Tensor x.int() # Konvertierung nach int32 Datentyp # 2D-Long-Tensor vorinitialisiert mit Nullen x = torch.zeros([2, 2]) # 2D-Long-Tensor vorinitialisiert mit Einsen  # und anschließende Konvertierung nach int64 y = torch.ones([2, 3]).long() # Zusammenfügen zweier Tensoren entlang Dimension 1 x = torch.cat([x, y], 1) x.sum() # Summe über alle Elemente x.mean() # Durchschnitt über alle Elemente # Matrixmultiplikation x.mm(y) # Transponieren x.t() # Inneres Produkt zweier Tensoren torch.dot(x, y) # Berechnet Eigenwerte und -vektoren torch.eig(x) # Gibt Tensor mit dem Sinus der Elemente zurück torch.sin(x)

Die Anwendung optimierter Bibliotheken wie BLAS [3], LAPACK [4] und MKL [5] erlaubt eine höchst performante Ausführung von Tensoroperationen auf der CPU (vor allem mit Intel-Prozessoren). Zusätzlich unterstützt PyTorch (im Gegensatz zu NumPy) auch die Ausführung der Operationen auf NVIDIA-Grafikkarten mit Hilfe des CUDA-Toolkits [6] und der cuDNN-Bibliothek [7]. Listing 2 zeigt an einem Beispiel, wie man Tensorobjekte in den Speicher der Grafikkarte verschiebt, um dort optimierte Tensoroperationen durchzuführen.

Listing 2

# 1D Tensoren x = torch.ones(1) y = torch.zeros(1) # Tensoren in den GPU-Speicher verschieben x = x.cuda() y = y.cuda() # oder: device = torch.device("cuda") x = x.to(device) y = y.to(device) # Die Additionsoperation wird nun auf der GPU durchgeführt x + y # wie torch.add(x, y) # Zurückkopieren auf die CPU x = x.cpu() y = y.cpu()

Da NumPy-Arrays quasi als Standarddatenstrukturen in der Python-Data-Science-Community gelten, ist in der Praxis ein häufiges Konvertieren von PyTorch nach NumPy und zurück nötig. Diese Konvertierungen können unkompliziert und effizient durchgeführt werden (Listing 3), da dabei der gleiche Speicherbereich geteilt wird, sodass kein Kopieren von Speicherinhalten durchgeführt werden muss.

Listing 3

# Konvertierung nach NumPy x = x.numpy() # Konvertierung zurück als PyTorch-Tensor y = torch.from_numpy(x) # y zeigt jetzt auf den gleichen Speicherbereich wie x # eine Änderung von y ändert gleichzeitig auch x

Netzwerkmodule

Die Bibliothek torch.nn enthält viele Tools und vordefinierte Module zur Generierung neuronaler Netzwerkarchitekturen. In der Praxis definiert man seine eigenen Netzwerke durch die Ableitung der abstrakten Klasse torch.nn.Module. In Listing 4 ist die Implementierung eines einfachen Feed-Forward-Netzwerks mit einem Hidden Layer und einer Tanh-Aktivierung aufgeführt.

Listing 4

import torch import torch.nn as nn class Net(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super(Net, self).__init__()  # Hier generierst du Instanzen aller Submodule des Netzwerks self.fc1 = nn.Linear(input_dim, hidden_dim) self.act1 = nn.Tanh() self.fc2 = nn.Linear(hidden_dim, output_dim) def forward(self, x):  # Hier definierst du die Vorwärtssequenz  # torch.autograd generiert dynamisch einen Graphen # bei jedem Durchlauf x = self.fc1(x) x = self.act1(x) x = self.fc2(x) return x

Dabei wird eine Netzwerkklasse von der abstrakten Klasse nn.Module abgeleitet. Die Methoden __init__ () und forward() müssen dabei definiert werden. In __ init__ () sollten alle benötigten Elemente, aus denen das gesamte Netzwerk zusammengebaut ist, instanziiert und initiiert werden. In unserem Falle generieren wir drei Elemente:

  1. fc1 – mit nn.Linear(input_dim, hidden_dim) wird ein Fully Connected Layer mit einer Eingabedimension von input_dim und einer Ausgabedimension von hidden_dim erzeugt

  2. act1 – eine Tanh-Aktivierungsfunktion

  3. fc2 – ein weiterer Fully Connected Layer mit einer Eingabedimension von hidden_dim und einer Ausgabedimension von output_dim.

Die Reihenfolge in __init()__ ist im Grunde egal, aber aus stilistischen Gründen sollte man sie möglichst in der Reihenfolge generieren, in der sie in der Methode forward() aufgerufen werden. Entscheidend für die Prozessierung ist die Reihenfolge in der forward()-Methode, in der man die Sequenzen des Vorwärtsdurchlaufs festlegt. An dieser Stelle kann man sogar beliebige bedingte Abfragen und Verzweigungen einbauen, da bei jedem Lauf ein Berechnungsgraph dynamisch generiert wird (Listing 5). Das is...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang