Januar 2019

Band 34, Nummer 1

[Machine Learning]

Einführung in PyTorch unter Windows

Von James McCaffrey

Es ist möglich, wenn auch recht schwierig, neuronale Netze aus Rohcode zu erstellen. Glücklicherweise sind viele Open-Source-Codebibliotheken vorhanden, die Sie verwenden können, um den Vorgang zu beschleunigen. Diese Bibliotheken umfassen CNTK (Microsoft), TensorFlow (Google) und scikit-learn. Die meisten neuronalen Netzwerkbibliotheken sind aus Leistungsgründen in C++ geschrieben, verfügen aber aus Gründen der Benutzerfreundlichkeit über eine Python-API.

In diesem Artikel zeige ich Ihnen, wie Sie erste Schritte mit der beliebten PyTorch-Bibliothek ausführen können. Im Vergleich zu vielen anderen neuronalen Netzwerkbibliotheken arbeitet PyTorch auf einer niedrigeren Abstraktionsebene. Dies gibt Ihnen mehr Kontrolle über Ihren Code und ermöglicht Ihnen eine einfachere Anpassung, auch wenn Sie zusätzlichen Code schreiben müssen.

Wenn Sie erfahren möchten, womit sich dieser Artikel beschäftigt, werfen Sie am besten einen Blick auf das Demoprogramm in Abbildung 1. Das Demoprogramm liest das bekannte Iris-Dataset in den Speicher. Ziel ist es, die Art einer Schwertlilie (Iris Setosa, Iris Versicolor oder Iris Virginica) aus vier Prädiktorwerten vorherzusagen: Kelchblattlänge, Kelchblattbreite, Blütenblattlänge und Blütenblattbreite. Ein Kelchblatt ist eine blattartige Struktur.

Das Iris-Datasetbeispiel mit PyTorch
Abbildung 1: Das Iris-Datasetbeispiel mit PyTorch

Das vollständige Iris-Dataset verfügt über 150 Elemente. Das Demoprogramm verwendet 120 Elemente für das Training und 30 Elemente für das Testen. Das Demoprogramm erstellt zunächst ein neuronales Netz mit PyTorch und trainiert das Netzwerk dann mit 600 Iterationen. Nach dem Training wird das Modell mithilfe der Testdaten ausgewertet. Das trainierte Modell weist eine Genauigkeit von 90,00 Prozent auf, was bedeutet, dass das Modell die Art von 27 der 30 Testelemente richtig vorhersagt.

Das Demoprogramm schließt mit der Vorhersage der Art für eine neue, bisher unbekannte Schwertlilie ab, die Kelchblatt- und Blütenblattwerte von (6,1, 3,1, 5,1, 1,1) besitzt. Die Vorhersagewahrscheinlichkeiten sind (0,0454, 0,6798, 0,2748), was einer Vorhersage von Versicolor entspricht.

Dieser Artikel geht davon aus, dass Sie über mittlere oder fortgeschrittene Programmierkenntnisse mit einer Sprache der C-Familie verfügen, setzt aber nicht voraus, dass Sie etwas über PyTorch wissen. Der gesamte Democode wird in diesem Artikel vorgestellt. Der Quellcode und die beiden von der Demo verwendeten Datendateien sind auch im Download verfügbar, der diesen Artikel begleitet. Die gesamte normale Fehlerprüfung wurde entfernt, um die Hauptideen so klar wie möglich darzustellen.

Installieren von PyTorch

Die Installation von PyTorch umfasst zwei Schritte. Zuerst installieren Sie Python und mehrere erforderliche Hilfspakete wie NumPy und SciPy, dann installieren Sie PyTorch als Add-On-Paket. Auch wenn es möglich ist, Python und die für die Ausführung von PyTorch erforderlichen Pakete separat zu installieren, ist es wesentlich empfehlenswerter, eine Python-Distribution zu installieren. Ich empfehle dringend, die Anaconda-Distribution von Python zu verwenden, die alle Pakete enthält, die Sie für die Ausführung von PyTorch benötigen, sowie viele weitere nützliche Pakete. In diesem Artikel wird die Installation auf einem Windows 10-Computer durchgeführt. Die Installation auf macOS- und Linux-Systemen ist ähnlich.

Die Koordination kompatibler Versionen von Python, erforderlicher Hilfspakete und von PyTorch ist eine nicht triviale Herausforderung. Fast alle Installationsfehler, die ich gesehen habe, sind auf Versionsinkompatibilitäten zurückzuführen. Zum Zeitpunkt, an dem ich diesen Artikel schreibe, verwende ich Anaconda3 5.2.0 (bestehend aus Python 3.6.5, NumPy 1.14.3 und SciPy 1.1.0) und PyTorch 0.4.1. Diese Versionen sind alle recht stabil, aber da PyTorch relativ neu ist und ständig weiterentwickelt wird, kann es eine neuere Version geben, wenn Sie diesen Artikel lesen.

Bevor Sie beginnen, empfehle ich Ihnen, alle vorhandenen Python-Systeme auf Ihrem Computer über „Windows-Systemsteuerung > Programme und Funktionen“ zu deinstallieren. Ich schlage auch vor, ein Verzeichnis „C:\PyTorch“ zu erstellen, um Installationsdateien und Projektdateien (Code und Daten) zu speichern.

Um die Anaconda-Distribution zu installieren, navigieren Sie zu repo.continuum.io/archive, und suchen Sie nach der Datei Anaconda3-5.2.0-Windows-x86_64.exe. Dies ist eine selbstentpackende ausführbare Datei. Wenn Sie auf den Link klicken, wird ein Dialogfeld mit Schaltflächen zum Auszuführen oder Speichern angezeigt. Sie können auf die Schaltfläche „Run“ (Ausführen) klicken.

Das Installationsprogramm von Anaconda ist sehr benutzerfreundlich. Es werden acht Bildschirme des Installations-Assistenten angezeigt. Sie können alle Standardeinstellungen übernehmen und einfach auf jedem Bildschirm auf die Schaltfläche „Next“ (Weiter) klicken, mit einer Ausnahme. Wenn Sie zu dem Bildschirm gelangen, in dem Sie gefragt werden, ob Sie Python Ihrer PATH-Umgebungsvariablen des Systems hinzufügen möchten, ist die Standardeinstellung nicht aktiviert („No“). Ich empfehle, diese Option zu aktivieren, damit Sie Ihren Systempfad nicht manuell bearbeiten müssen. Die Standardeinstellungen speichern den Python-Interpreter und mehr als 500 kompatible Pakete im Verzeichnis „C:\Users\<user>\AppData\Local\ Continuum\Anaconda3“.

Um die PyTorch-Bibliothek zu installieren, navigieren Sie zu pytorch.org und suchen nach dem Link „Previous versions of PyTorch“ (Vorherige Versionen von PyTorch). Klicken Sie auf diesen Link. Suchen Sie nach einer Datei namens „torch-0.4.1-cp36-cp36m-win_amd64.whl“. Dies ist eine „Wheeldatei“ von Python. Eine WHL-Datei ähnelt in etwa einer MSI-Datei von Windows. Wenn Sie auf den Link klicken, wird eine Option zum Öffnen oder Speichern angezeigt. Verwenden Sie „Save as“ (Speichern unter), und speichern Sie die WHL-Datei in Ihrem Verzeichnis „C:\PyTorch“. Wenn Sie die WHL-Datei von PyTorch nicht finden können, versuchen Sie es unter bit.ly/2SUiAuj. Dort befand sich die Datei, als ich diesen Artikel geschrieben habe.

Sie können PyTorch mit dem pip-Hilfsprogramm von Python installieren, das Sie mit der Anaconda-Distribution erhalten. Öffnen Sie eine Windows-Befehlsshell, und navigieren Sie zu dem Verzeichnis, in dem Sie die WHL-Datei von PyTorch gespeichert haben. Geben Sie dann den folgenden Befehl ein:

C:\PyTorch> pip install torch-0.4.1-cp36-cp36m-win_amd64.whl

Die Installation ist schnell, aber es kann auch eine Menge schiefgehen. Wenn bei der Installation ein Fehler auftritt, untersuchen Sie die Fehlermeldungen in der Shell sorgfältig. Das Problem wird mit großer Wahrscheinlichkeit ein Problem der Versionskompatibilität sein.

Um sicherzustellen, dass Python und PyTorch erfolgreich installiert wurden, öffnen Sie eine Befehlsshell, und geben Sie „python“ ein, um den Python-Interpreter zu starten. Die Python-Eingabeaufforderung „>>>“ wird angezeigt. Geben Sie dann die folgenden Befehle ein (beachten Sie, dass zwei aufeinander folgende Unterstriche im Versionsbefehl vorhanden sind):

C:\>python
>>> import torch as T
>>> T.__version__
'0.4.1'
>>> exit()
C:\>

Wenn Sie die hier gezeigte Ausgabe sehen, Sie sind bereit, mit PyTorch Machine Learning-Code für neuronale Netze zu schreiben. Herzlichen Glückwunsch!

Vorbereiten des Iris-Datasets

Das Iris-Rohdataset finden Sie unter bit.ly/1N5br3h. Die Daten sehen folgendermaßen aus:

5.1, 3.5, 1.4, 0.2, Iris-setosa
4.9, 3.0, 1.4, 0.2, Iris-setosa
...
7.0, 3.2, 4.7, 1.4, Iris-versicolor
6.4, 3.2, 4.5, 1.5, Iris-versicolor
...
6.2, 3.4, 5.4, 2.3, Iris-virginica
5.9, 3.0, 5.1, 1.8, Iris-virginica

Die ersten vier Werte in jeder Zeile geben die Länge der Kelchblätter einer Blüte, die Breite der Kelchblätter, die Länge der Blütenblätter und die Breite der Blütenblätter an. Das fünfte Element ist die vorherzusagende Art. Die Rohdaten bestehen aus 50 Iris Setosa, gefolgt von 50 Versicolor, gefolgt von 50 Virginica. Die Trainingsdatei besteht aus den ersten 40 jeder Art (120 Elemente) und die Testdatei aus den letzten 10 jeder Art (30 Elemente). Da es vier Prädiktorvariablen gibt, ist es nicht möglich, das Dataset grafisch darzustellen. Eine grobe Vorstellung von der Struktur der Daten können Sie sich jedoch anhand der Grafik in Abbildung 2 verschaffen.

Partielle Iris-Daten
Abbildung 2: Partielle Iris-Daten

Neuronale Netze verstehen nur Zahlen, daher muss jede Art codiert werden. Bei den meisten neuronalen Netzwerkbibliotheken würden Sie Setosa durch (1, 0, 0), Versicolor durch (0, 1, 0) und Virginica durch (0, 0, 1) ersetzen. Dies wird als 1-aus-N-Codierung oder One-Hot-Codierung bezeichnet. PyTorch führt jedoch One-Hot-Codierung hinter den Kulissen durch und erwartet 0, 1 oder 2 für die drei Klassen. Daher sehen die codierten Daten für PyTorch folgendermaßen aus:

5.1, 3.5, 1.4, 0.2, 0
4.9, 3.0, 1.4, 0.2, 0
...
7.0, 3.2, 4.7, 1.4, 1
6.4, 3.2, 4.5, 1.5, 1
...
6.2, 3.4, 5.4, 2.3, 2
5.9, 3.0, 5.1, 1.8, 2

In den meisten Fällen sollten Sie die Prädiktorvariablen unter Verwendung der sogenannten Min-Max-Normalisierung normalisieren, typischerweise durch Skalierung, sodass alle Werte zwischen 0,0 und 1,0 liegen. Ich habe die Iris-Daten nicht normalisiert, um dieses Demoprogramm etwas einfacher zu halten. Wenn ich mit neuronalen Netzen arbeite, erstelle ich normalerweise einen Stammordner für das Problem (z.B. „C:\PyTorch\Iris“) und dann ein Unterverzeichnis namens „Data“, um die Datendateien zu speichern.

Das Demoprogramm

Das vollständige Demoprogramm (mit einigen kleinen Bearbeitungen, um Platz zu sparen) wird in Abbildung 3 gezeigt. Anstatt der üblichen vier Leerzeichen habe ich einen Einzug von zwei Leerzeichen verwendet, um Platz zu sparen. Ich habe den Editor verwendet, um das Demoprogramm zu bearbeiten, aber es gibt Dutzende von Python-Editoren, die über erweiterte Funktionen verfügen. Beachten Sie, dass Python das Zeichen „\“ für die Zeilenfortsetzung verwendet. 

Abbildung 3: Das Demoprogramm für das Iris-Dataset

# iris_nn.py
# PyTorch 0.4.1 Anaconda3 5.2.0 (Python 3.6.5)
import numpy as np
import torch as T
# -----------------------------------------------------------
class Batch:
  def __init__(self, num_items, bat_size, seed=0):
    self.num_items = num_items; self.bat_size = bat_size
    self.rnd = np.random.RandomState(seed)
  def next_batch(self):
    return self.rnd.choice(self.num_items, self.bat_size,
      replace=False)
# -----------------------------------------------------------
class Net(T.nn.Module):
  def __init__(self):
    super(Net, self).__init__()
    self.fc1 = T.nn.Linear(4, 7)
    T.nn.init.xavier_uniform_(self.fc1.weight)  # glorot
    T.nn.init.zeros_(self.fc1.bias)
    self.fc2 = T.nn.Linear(7, 3)
    T.nn.init.xavier_uniform_(self.fc2.weight)
    T.nn.init.zeros_(self.fc2.bias)
  def forward(self, x):
    z = T.tanh(self.fc1(x))
    z = self.fc2(z)  # see CrossEntropyLoss() below
    return z
# -----------------------------------------------------------
def accuracy(model, data_x, data_y):
  X = T.Tensor(data_x)
  Y = T.LongTensor(data_y)
  oupt = model(X)
  (_, arg_maxs) = T.max(oupt.data, dim=1)
  num_correct = T.sum(Y==arg_maxs)
  acc = (num_correct * 100.0 / len(data_y))
  return acc.item()
# -----------------------------------------------------------
def main():
  # 0. get started
  print("\nBegin Iris Dataset with PyTorch demo \n")
  T.manual_seed(1);  np.random.seed(1)
  # 1. load data
  print("Loading Iris data into memory \n")
  train_file = ".\\Data\\iris_train.txt"
  test_file = ".\\Data\\iris_test.txt"
  train_x = np.loadtxt(train_file, usecols=range(0,4),
    delimiter=",",  skiprows=0, dtype=np.float32)
  train_y = np.loadtxt(train_file, usecols=[4],
    delimiter=",", skiprows=0, dtype=np.float32)
  test_x = np.loadtxt(test_file, usecols=range(0,4),
    delimiter=",",  skiprows=0, dtype=np.float32)
  test_y = np.loadtxt(test_file, usecols=[4],
    delimiter=",", skiprows=0, dtype=np.float32)
  # 2. define model
  net = Net()
# -----------------------------------------------------------
  # 3. train model
  net = net.train()  # set training mode
  lrn_rate = 0.01; b_size = 12
  max_i = 600; n_items = len(train_x)
  loss_func = T.nn.CrossEntropyLoss()  # applies softmax()
  optimizer = T.optim.SGD(net.parameters(), lr=lrn_rate)
  batcher = Batch(num_items=n_items, bat_size=b_size)
  print("Starting training")
  for i in range(0, max_i):
    if i > 0 and i % (max_i/10) == 0:
      print("iteration = %4d" % i, end="")
      print("  loss = %7.4f" % loss_obj.item(), end="")
      acc = accuracy(net, train_x, train_y)
      print("  accuracy = %0.2f%%" % acc)
    curr_bat = batcher.next_batch()
    X = T.Tensor(train_x[curr_bat])
    Y = T.LongTensor(train_y[curr_bat])
    optimizer.zero_grad()
    oupt = net(X)
    loss_obj = loss_func(oupt, Y)
    loss_obj.backward()
    optimizer.step()
  print("Training complete \n")
  # 4. evaluate model
  net = net.eval()  # set eval mode
  acc = accuracy(net, test_x, test_y)
  print("Accuracy on test data = %0.2f%%" % acc) 
  # 5. save model
  # TODO
# -----------------------------------------------------------
  # 6. make a prediction
  unk = np.array([[6.1, 3.1, 5.1, 1.1]], dtype=np.float32)
  unk = T.tensor(unk)  # to Tensor
  logits = net(unk)  # values do not sum to 1.0
  probs_t = T.softmax(logits, dim=1)  # as Tensor
  probs = probs_t.detach().numpy()    # to numpy array
  print("\nSetting inputs to:")
  for x in unk[0]: print("%0.1f " % x, end="")
  print("\nPredicted: (setosa, versicolor, virginica)")
  for p in probs[0]: print("%0.4f " % p, end="")
  print("\n\nEnd Iris demo")
if __name__ == "__main__":
  main()

Die Struktur eines PyTorch-Programms unterscheidet sich etwas von der anderer Bibliotheken. In der Demo liefert die vom Programm definierte Batch-Klasse eine bestimmte Anzahl von Trainingselementen für das Training. Die Net-Klasse definiert ein neuronales 4-7-3-Netz. Die accuracy-Funktion berechnet die Klassifizierungsgenauigkeit (Prozentsatz der richtigen Vorhersagen) von Daten unter Verwendung eines angegebenen Modells/Netzwerks. Die gesamte Programmsteuerungslogik ist in einer main-Funktion enthalten.

Da PyTorch und Python so schnell entwickelt werden, sollten Sie einen Kommentar hinzufügen, der angibt, welche Versionen verwendet werden. Viele Programmierer, für die Python neu ist, sind überrascht zu erfahren, dass Basis-Python keine Arrays unterstützt. NumPy-Arrays werden von PyTorch verwendet, sodass Sie fast immer das NumPy-Paket importieren müssen.

Definieren des neuronalen Netzes

Die Definition des neuronalen Netzwerks beginnt wie folgt:

class Net(T.nn.Module):
  def __init__(self):
    super(Net, self).__init__()
    self.fc1 = T.nn.Linear(4, 7)
    T.nn.init.xavier_uniform_(self.fc1.weight)
    T.nn.init.zeros_(self.fc1.bias)
...

Die erste Zeile des Codes zeigt an, dass die Klasse von einer T.nn.Module-Klasse erbt, die Funktionen zum Erstellen eines neuronalen Netzes enthält. Sie können sich die Funktion __init__ als Klassenkonstruktor vorstellen. Das Objekt fc1 („fully connected layer 1“, vollständig verbundene Schicht 1) ist die verborgene Schicht des Netzwerks, die vier Eingabewerte (die Prädiktorwerte) erwartet und sieben Verarbeitungsknoten aufweist. Die Anzahl der verborgenen Knoten ist ein Hyperparameter und muss durch Versuch und Irrtum bestimmt werden. Die Gewichtungen der verborgenen Schichten werden mit dem einheitlichen Xavier-Algorithmus initialisiert, der in den meisten anderen Bibliotheken einheitlicher Glorot-Algorithmus genannt wird. Die Bias der verborgenen Schicht werden alle mit Null initialisiert.

Die Ausgabeschicht des Netzwerks wird wie folgt definiert:

self.fc2 = T.nn.Linear(7, 3)
T.nn.init.xavier_uniform_(self.fc2.weight)
T.nn.init.zeros_(self.fc2.bias)

Die Ausgabeschicht erwartet sieben Eingaben (aus der verborgenen Schicht) und generiert drei Ausgabewerte: einen Wert für jede mögliche Art. Beachten Sie, dass die verborgene Schicht und die Ausgabeschicht an diesem Punkt nicht logisch verbunden sind. Die Verbindung wird durch die erforderliche forward-Funktion eingerichtet:

def forward(self, x):
  z = T.tanh(self.fc1(x))
  z = self.fc2(z)  # no softmax!
  return z

Die Funktion akzeptiert x, die Prädiktoreingabewerte. Diese Werte werden an die verborgene Schicht übergeben, und die Ergebnisse werden dann an die tanh-Aktivierungsfunktion übergeben. Dieses Ergebnis wird an die Ausgabeschicht übergeben, und die endgültigen Ergebnisse werden zurückgegeben. Im Gegensatz zu vielen anderen neuronalen Netzwerkbibliotheken wenden Sie mit PyTorch keine Softmax-Aktivierung auf die Ausgabeschicht an, da Softmax automatisch von der Trainingsverlustfunktion angewendet wird. Wenn Sie Softmax auf die Ausgabeschicht angewendet hätten, würde Ihr Netzwerk immer noch funktionieren, aber das Training wäre langsamer, weil Sie Softmax zwei Mal anwenden würden.

Laden der Daten in den Arbeitsspeicher

Wenn Sie PyTorch verwenden, laden Sie Daten in NumPy-Arrays in den Arbeitsspeicher und konvertieren die Arrays dann in PyTorch Tensor-Objekte. Sie können sich einen Tensor ungefähr als ein komplexes Array vorstellen, das von einem GPU-Prozessor verarbeitet werden kann.

Es gibt mehrere Möglichkeiten, Daten in ein NumPy-Array zu laden. Unter meinen Kollegen ist die gebräuchlichste Technik die Verwendung des Python Pandas-Pakets (ursprünglich „Panel Data“, jetzt „Python-Datenanalyse“). Pandas erfordert jedoch eine gewisse Einarbeitung, sodass das Demoprogramm der Einfachheit halber die loadtxt-Funktion von NumPy verwendet. Die Trainingsdaten werden folgendermaßen geladen:

train_file = ".\\Data\\iris_train.txt"
train_x = np.loadtxt(train_file, usecols=range(0,4),
  delimiter=",",  skiprows=0, dtype=np.float32)
train_y = np.loadtxt(train_file, usecols=[4],
  delimiter=",", skiprows=0, dtype=np.float32)

PyTorch erwartet, dass die Prädiktorwerte in einer Matrix im Array-aus-Arrays-Stil vorliegen und sich die vorherzusagenden Klassenwerte in einem Array befinden. Nachdem diese Anweisungen ausgeführt wurden, weist die Matrix train_x 120 Zeilen und vier Spalten auf, und train_y ist ein Array mit 120 Werten. Die meisten neuronalen Netzwerkbibliotheken (einschließlich PyTorch) verwenden float32-Daten als Standarddatentyp, da die durch Verwendung von 64-Bit-Variablen erzielte Genauigkeit nicht die entstandene Leistungseinbuße wert ist.

Trainieren des neuronalen Netzwerks

Das Demoprogramm erstellt das neuronale Netz und bereitet das Training dann mit den folgenden Anweisungen vor:

net = Net()
net = net.train()  # set training mode
lrn_rate = 0.01; b_size = 12
max_i = 600; n_items = len(train_x)
loss_func = T.nn.CrossEntropyLoss()  # applies softmax()
optimizer = T.optim.SGD(net.parameters(), lr=lrn_rate)
batcher = Batch(num_items=n_items, bat_size=b_size)

Für das Demoprogramm ist es nicht erforderlich, das Netzwerk in den Trainingsmodus zu versetzen, da das Training keine Dropout- oder Batchnormalisierung verwendet, die unterschiedliche Ausführungsabläufe für Training und Auswertung verlangt. Die Lernrate (0,01), die Batchgröße (12) und die maximale Anzahl von Trainingsiterationen (600) sind Hyperparameter. Das Demoprogramm verwendet Iterationen statt Epochen, da sich eine Epoche in der Regel auf die jeweils einmalige Verarbeitung aller Trainingselemente bezieht. Hier bedeutet eine Iteration nur die Verarbeitung von 12 der Trainingselemente.

Die CrossEntropyLoss-Funktion wird verwendet, um den Fehler bei Klassifizierungsproblemen mit mehreren Klassen zu messen, bei denen mindestens drei Klassen vorherzusagen sind. Ein häufiger Fehler ist es, sie für die binäre Klassifizierung zu testen und zu verwenden. Die Demo verwendet stochastischen Gradientenabstieg (Stochastic Gradient Descent, SGD), die rudimentärste Form der Trainingsoptimierung. Für realistische Probleme unterstützt PyTorch ausgefeilte Algorithmen wie Adam (Adaptive Moment Estimation), Adagrad (Adaptive Gradient) und RMSprop (Resilient Mean Squared Propagation).

Die vom Programm definierte Batch-Klasse implementiert den denkbar einfachsten Batchmechanismus. Bei jedem Aufruf der next_batch-Funktion werden 12 zufällig ausgewählte Indizes aus den 120 möglichen Trainingsdatenindizes zurückgegeben. Dieser Ansatz garantiert nicht, dass alle Trainingselemente gleich häufig verwendet werden. In einem Nicht-Demo-Szenario würden Sie wahrscheinlich einen ausgefeilteren Batcher implementieren, der zufällig verschiedene Indizes auswählt, bis alle Indizes ein Mal ausgewählt wurden, und sich dann zurücksetzt.

Das Training erfolgt genau 600 Mal. Alle 600/10 = 60 Iterationen zeigt die Demo Statusinformationen an:

for i in range(0, max_i):
  if i > 0 and i % (max_i/10) == 0:
    print("iteration = %4d" % i, end="")
    print("  loss = %7.4f" % loss_obj.item(), end="")
    acc = accuracy(net, train_x, train_y)
    print("  accuracy = %0.2f%%" % acc)

Der durchschnittliche Kreuzentropieverlust bzw. -fehlerwert für den aktuellen Batch von 12 Trainingselementen kann über die Item-Funktion des Objekts abgerufen werden. Im Allgemeinen ist der Kreuzentropieverlust während des Trainings schwer zu interpretieren, aber Sie sollten ihn überwachen, um sicherzustellen, dass er allmählich abnimmt, was bedeutet, dass das Training funktioniert.

Etwas ungewöhnlich: Zum Zeitpunkt, an dem ich diesen Artikel schreibe, verfügt PyTorch über keine integrierte Funktion, um Ihnen Klassifizierungsgenauigkeit zu ermöglichen. Die vom Programm definierte Genauigkeitsfunktion berechnet die Klassifizierungsgenauigkeit des Modells unter Verwendung der aktuellen Gewichtungen und Biaswerte. Genauigkeit ist viel einfacher zu interpretieren als Verlust oder Fehler, ist aber auch eine gröbere Metrik.

Innerhalb der Trainingsschleife wird ein Batch von Elementen aus dem Dataset mit 120 Elementen ausgewählt und in Tensor-Objekte konvertiert:

curr_bat = batcher.next_batch()
X = T.Tensor(train_x[curr_bat])
Y = T.LongTensor(train_y[curr_bat])

Erinnern Sie sich daran, dass curr_bat ein Array von 12 Indizes in den Trainingsdaten ist, also weist train_x[curr_bat] 12 Zeilen und vier Spalten auf. Diese Matrix wird in PyTorch Tensor-Objekte konvertiert, indem die Matrix an die Tensor-Funktion übergeben wird. Für ein Klassifizierungsproblem müssen Sie die codierten Klassenbezeichnungswerte in LongTensor-Objekte und nicht in Tensor-Objekte konvertieren.

Das eigentliche Training wird durch diese fünf Anweisungen durchgeführt:

optimizer.zero_grad()
oupt = net(X)
loss_obj = loss_func(oupt, Y)
loss_obj.backward()
optimizer.step()

Sie können diese Anweisungen im Wesentlichen als magische PyTorch-Beschwörungen betrachten, die ein Training mithilfe von Rückpropagation durchführen. Sie müssen zunächst die Gewichtungs- und Biasgradientenwerte der vorherigen Iteration auf Null zurücksetzen. Der Aufruf der net-Funktion übergibt den aktuellen Batch von 12 Tensor-Objekten an das Netz und berechnet die 12 Ausgabewerte mit der forward-Funktion. Die Aufrufe von „backward“ und „step“ berechnen die Gradientenwerte und verwenden sie, um Gewichtungen und Bias zu aktualisieren.

Auswerten und Verwenden des Modells

Nach Abschluss des Trainings berechnet die Demo die Modellgenauigkeit für die Testdaten:

net = net.eval()  # set eval mode
acc = accuracy(net, test_x, test_y)
print("Accuracy on test data = %0.2f%%" % acc)

Wie zuvor ist es in diesem Beispiel nicht notwendig, das Modell in den Auswertungsmodus zu versetzen, aber es schadet nicht, dies explizit auszusprechen. Das Demoprogramm speichert das trainierte Modell nicht, aber in einem Nicht-Demoszenario möchten Sie es möglicherweise speichern. PyTorch unterstützt wie die meisten anderen neuronalen Netzwerkbibliotheken (mit der bemerkenswerten Ausnahme von TensorFlow) das ONNX-Format (Open Neural Network Exchange).

Die Demo verwendet das trainierte Modell, um die Arten einer neuen, bisher unbekannten Schwertlilie vorherzusagen:

unk = np.array([[6.1, 3.1, 5.1, 1.1]], dtype=np.float32)
unk = T.tensor(unk)  # to Tensor
logits = net(unk)  # values do not sum to 1.0
probs_t = T.softmax(logits, dim=1)  # as Tensor
probs = probs_t.detach().numpy()    # to numpy array

Der Aufruf der net-Funktion gibt drei Werte zurück, die nicht unbedingt 1,0 ergeben, z.B. (3,2, 4,5, 0,3), sodass die Demo Softmax anwendet, um die Ausgabewerte so zu beeinflussen, dass sie 1,0 ergeben und grob als Wahrscheinlichkeiten interpretiert werden können. Die Werte sind Tensor-Objekte, daher werden sie in ein NumPy-Array konvertiert, um sie leichter anzeigen zu können.

Zusammenfassung

Dieser Artikel hat kaum an der Oberfläche der PyTorch-Bibliothek gekratzt, sollte Ihnen aber alle Informationen vermitteln, die Sie benötigen, um mit dem Experimentieren zu beginnen. Wie dieser Artikel zeigt, unterscheidet sich PyTorch deutlich von CNTK, TensorFlow und scikit-learn und arbeitet auf einer niedrigeren Ebene als diese. Eine häufig gestellte Frage lautet: „Welche neuronale Netzwerkbibliothek ist die beste?“ In einer perfekten Welt können Sie sich Zeit nehmen und alle wichtigen Bibliotheken ausprobieren. Aber weil diese Bibliotheken ziemlich kompliziert sind, verwenden die meisten meiner Kollegen realistischerweise eine primäre Bibliothek. Meiner Meinung nach sind die drei besten Bibliotheken aus technischer Sicht CNTK, Keras/TensorFlow und PyTorch. Aber sie sind alle ausgezeichnet, und die bevorzugte Auswahl einer Bibliothek hängt wirklich hauptsächlich von Ihrem Programmierstil ab und davon, welche Bibliothek von Ihren Kollegen oder Ihrem Unternehmen am häufigsten genutzt wird.


Dr. James McCaffreyist in Redmond (Washington, USA) für Microsoft Research tätig. Er hat an verschiedenen Microsoft-Produkten mitgearbeitet, unter anderem an Internet Explorer und Bing. Dr. McCaffrey erreichen Sie unter jamccaff@microsoft.com.

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Brian Broll, Yihe Dong, Chris Lee