März 2019

Band 34, Nummer 3

Dieser Artikel wurde maschinell übersetzt.

[Test Run]

Neuronale Regression mithilfe von PyTorch

Durch James McCaffrey

James McCaffreyDas Ziel eines Regressionsproblems besteht darin, einen einzelnen numerischen Wert vorherzusagen. Beispielsweise empfiehlt es sich, den Preis eines Hauses basierend auf Quadratmeterzahl, Alter, Postleitzahl und usw. vorherzusagen. In diesem Artikel zeige ich, wie ein neuronales Regressionsmodell, das über die Codebibliothek PyTorch zu erstellen. Die beste Möglichkeit, zu verstehen, in diesem Artikel geht auf einen Blick auf das Demoprogramm in ist Abbildung1.  

Neuronale Regression, die mit einem ausführenden PyTorch-Demo
Abbildung 1 neuronale Regression mit einem ausführenden PyTorch-Demo

Das Demoprogramm erstellt ein Vorhersagemodell, das auf Basis des Boston Housing Datasets, die zum Ziel hat, den Medianwert Haus Preis eines 506 Stadt in der Nähe Boston vorherzusagen. Die Daten stammen aus den frühen 1970er Jahren. Jedes Datenelement hat 13 prädiktorvariablen, z. B. Crime-Index, der Stadt, die durchschnittliche Anzahl von Räumen pro Haus in der Stadt und so weiter. Es gibt nur eine Ausgabe-Wert, da das Ziel darin, einen einzelnen numerischen Wert vorherzusagen.

Die Demo 404 trainingselemente und 102 Testelemente in den Arbeitsspeicher geladen und erstellt dann ein 13-(10-10)-1-neuronales Netzwerk. Das neuronale Netzwerk besitzt zwei verborgene Ebenen, die jeweils 10 Knoten aufweisen. Die Anzahl der Eingabe-und Ausgabeknoten wird durch die Daten bestimmt, aber die Anzahl der verborgenen Schichten und die Anzahl von Knoten in jeder sind freie Parameter, die durch Versuch und Irrtum ermittelt werden müssen.

Das Demoprogramm trainiert das neuronale Netz, d. h. die Werte der Gewichtungen und Bias, die das Verhalten des neuronalen Netzwerks zu definieren sind mithilfe von Trainingsdaten, die richtige Eingabe bezeichnet und Ausgabewerte berechnet. Nach dem Training berechnet die Demo die Genauigkeit des Modells für die Testdaten (75.49 Prozent, 77 von 102 korrekt). Die testgenauigkeit wird ein Ungefähres Maß an, wie gut das Modell mit neuen, zuvor nicht gesehenen Daten erwarten.

Die Demo schließt mit einer Vorhersage für den ersten Test Stadt. Der unformatierte 13 Eingabewerte sind (0.09266, 34.0,... 8.67). Wenn das neuronale Regressionsmodell trainiert wurde, normalisierte Daten (skaliert, damit alle zwischen 0,0 und 1,0 Werte) verwendet wurde, damit beim Bereitstellen einer Vorhersage mussten die Demo normalisierte Daten verwenden, handelt es sich (0.00097, 0.34,... 0.191501). Das Modell sagt voraus, dass der Median Haus Preis $24,870.07, Recht nahe an der Median der tatsächliche Preis von $26,400.

In diesem Artikel wird davon ausgegangen, dass Sie über mittlere oder fortgeschrittene besseres Programmierungsmodell Kenntnisse in einer C-Sprache und grundlegende Kenntnisse von Machine Learning verfügen. Der gesamte Democode wird in diesem Artikel vorgestellt. Den Quellcode und die beiden Datendateien der Demo sind auch im begleitenden Download verfügbar. Die gesamte normale Fehlerprüfung wurde entfernt, um die Hauptideen so klar wie möglich darzustellen.

Installieren von PyTorch

PyTorch zu installieren, umfasst zwei Schritte nötig. Zunächst können Sie Python und mehreren erforderlichen zusätzlichen Pakete wie NumPy und SciPy installieren, und klicken Sie dann Sie PyTorch als Add-On-Python-Paket installieren.

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. Für mein Demo, die ich installiert die Anaconda3 5.2.0 Verteilung (einschließlich Python 3.6.5) und PyTorch 1.0.0. Wenn Sie mit Python vertraut sind, denken Sie daran, dass die Installation und Verwaltung von paketabhängigkeiten Add-on nicht trivialen ist.

Nach der Installation von Python über die Anaconda-Distribution, kann das PyTorch-Paket mit der Pip-Hilfsprogramm-Funktion und eine .whl ("Rad")-Datei installiert werden. PyTorch ist in einer nur-CPU-Version und in einer GPU-Version. Ich habe die reine CPU-Version.

Verstehen der Daten

Boston Housing Dataset stammen aus einer Forschungsarbeit im Jahr 1978 entwickelt wurde, die Luftverschmutzung studiert geschrieben. In vielen Stellen im Internet finden Sie verschiedene Versionen des Datasets. Das erste Datenelement ist:

0.00632, 18.00, 2.310, 0, 0.5380, 6.5750, 65.20
4.0900, 1, 296.0, 15.30, 396.90, 4.98, 24.00

Jedes Datenelement verfügt über 14 Werte und stellt eine der 506 Stadt in der Nähe von Boston dar. Die ersten 13 Zahlen sind die Werte der vorhersagevariablen und der letzte Wert ist der mittlere House-Preis in der Stadt (geteilt durch 1.000). Kurz gesagt, die 13 prädiktorvariablen sind: Kriminalitätsrate im Stadt, große Menge Prozentsatz, Prozentsatz für die Industrie, Nähe zu Charles River, Verschmutzung, die durchschnittliche Anzahl Räume pro Haus, House Age-Informationen, Entfernung nach Boston, Zugriff auf Autobahnen, Speicherarray steuern Rate, Schultag-Lehrer-Verhältnis, Anteil der schwarze Bürger und den Prozentsatz der Low-Status-Bürger.  

Da sind 14 Variablen, es nicht möglich ist, um das Dataset visuell darzustellen, aber Sie eine grobe Vorstellung von den Daten im Diagramm in erhalten abbildung2. Das Diagramm zeigt die durchschnittliche Haus Preis als Funktion der Prozentsatz der Branche für die 102 Elemente im testdataset Speicherarray Stadt.

Partielle Boston Bereich Haus Dataset
Abbildung 2 teilweise Boston Bereich Haus Dataset

Beim Arbeiten mit neuronalen Netzwerken müssen Sie nicht-numerische Daten codieren, und Sie sollten numerische Daten normalisieren, sodass große Werte, z. B. ein Schultag-Lehrer-Verhältnis von 20.0, nicht die kleinen Werten, z. B. ein Lesen der Verunreinigung des 0.538 überschwemmen. Die Charles River-Variable ist ein kategorischer Wert gespeichert, entweder als 0 (Stadt ist nicht benachbarten) oder 1 (neben). Diese Werte wurden als-1 und + 1 neu codiert. Die 12 vorhersagevariablen sind numerisch. Für jede Variable, aus, den ich der Minimalwert und der maximale Wert berechnet, und klicken Sie dann für jeden Wert X, normalisiert sie als (x - min) / (max - min). Nach der min-Max-Normalisierung werden alle Werte zwischen 0,0 und 1,0 sein.

Die mittlere House-Werte in die unformatierten Daten wurden bereits durch den Wert durch 1.000 dividieren, sodass die Werte, die einem Bereich von 5.0 zu 50,0, mit am häufigsten auf etwa 25.0 normalisiert. Ich haben eine weitere Normalisierung der Preise durch 10 geteilt, damit alle Median Haus Preise zwischen 0,5 und 5.0, wobei die meisten etwa 2,5 wurden angewendet.

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. Python verwendet, und die ' \'-Zeichen für die zeilenfortsetzung. Ich Editor verwendet, um das Programm zu bearbeiten, aber viele meiner Kollegen bevorzugen, Visual Studio oder Visual Studio Code, der beide verfügen über eine hervorragende Unterstützung für Python.

Abbildung 3: die Demoprogramm untergebracht Boston

# boston_dnn.py
# Boston Area House Price dataset regression
# Anaconda3 5.2.0 (Python 3.6.5), PyTorch 1.0.0
import numpy as np
import torch as T  # non-standard alias
# ------------------------------------------------------------
def accuracy(model, data_x, data_y, pct_close):
  n_items = len(data_y)
  X = T.Tensor(data_x)  # 2-d Tensor
  Y = T.Tensor(data_y)  # actual as 1-d Tensor
  oupt = model(X)       # all predicted as 2-d Tensor
  pred = oupt.view(n_items)  # all predicted as 1-d
  n_correct = T.sum((T.abs(pred - Y) < T.abs(pct_close * Y)))
  result = (n_correct.item() * 100.0 / n_items)  # scalar
  return result 
# ------------------------------------------------------------
class Net(T.nn.Module):
  def __init__(self):
    super(Net, self).__init__()
    self.hid1 = T.nn.Linear(13, 10)  # 13-(10-10)-1
    self.hid2 = T.nn.Linear(10, 10)
    self.oupt = T.nn.Linear(10, 1)
    T.nn.init.xavier_uniform_(self.hid1.weight)  # glorot
    T.nn.init.zeros_(self.hid1.bias)
    T.nn.init.xavier_uniform_(self.hid2.weight)
    T.nn.init.zeros_(self.hid2.bias)
    T.nn.init.xavier_uniform_(self.oupt.weight)
    T.nn.init.zeros_(self.oupt.bias)
  def forward(self, x):
    z = T.tanh(self.hid1(x))
    z = T.tanh(self.hid2(z))
    z = self.oupt(z)  # no activation, aka Identity()
    return z
# ------------------------------------------------------------
def main():
  # 0. Get started
  print("\nBoston regression using PyTorch 1.0 \n")
  T.manual_seed(1);  np.random.seed(1)
  # 1. Load data
  print("Loading Boston data into memory ")
  train_file = ".\\Data\\boston_train.txt"
  test_file = ".\\Data\\boston_test.txt"
  train_x = np.loadtxt(train_file, delimiter="\t",
    usecols=range(0,13), dtype=np.float32)
  train_y = np.loadtxt(train_file, delimiter="\t",
    usecols=[13], dtype=np.float32)
  test_x = np.loadtxt(test_file, delimiter="\t",
    usecols=range(0,13), dtype=np.float32)
  test_y = np.loadtxt(test_file, delimiter="\t",
    usecols=[13], dtype=np.float32)
  # 2. Create model
  print("Creating 13-(10-10)-1 DNN regression model \n")
  net = Net()  # all work done above
  # 3. Train model
  net = net.train()  # set training mode
  bat_size = 10
  loss_func = T.nn.MSELoss()  # mean squared error
  optimizer = T.optim.SGD(net.parameters(), lr=0.01)
  n_items = len(train_x)
  batches_per_epoch = n_items // bat_size
  max_batches = 1000 * batches_per_epoch
  print("Starting training")
  for b in range(max_batches):
    curr_bat = np.random.choice(n_items, bat_size,
      replace=False)
    X = T.Tensor(train_x[curr_bat])
    Y = T.Tensor(train_y[curr_bat]).view(bat_size,1)
    optimizer.zero_grad()
    oupt = net(X)
    loss_obj = loss_func(oupt, Y)
    loss_obj.backward()
    optimizer.step()
    if b % (max_batches // 10) == 0:
      print("batch = %6d" % b, end="")
      print("  batch loss = %7.4f" % loss_obj.item(), end="")
      net = net.eval()
      acc = accuracy(net, train_x, train_y, 0.15)
      net = net.train()
      print("  accuracy = %0.2f%%" % acc)      
  print("Training complete \n")
  # 4. Evaluate model
  net = net.eval()  # set eval mode
  acc = accuracy(net, test_x, test_y, 0.15)
  print("Accuracy on test data = %0.2f%%" % acc)
  # 5. Save model - TODO
  # 6. Use model
  raw_inpt = np.array([[0.09266, 34, 6.09, 0, 0.433, 6.495, 18.4,
    5.4917, 7, 329, 16.1, 383.61, 8.67]], dtype=np.float32)
  norm_inpt = np.array([[0.000970, 0.340000, 0.198148, -1,
    0.098765, 0.562177, 0.159629, 0.396666, 0.260870, 0.270992,
    0.372340, 0.966488, 0.191501]], dtype=np.float32)
  X = T.Tensor(norm_inpt)
  y = net(X)
  print("For a town with raw input values: ")
  for (idx,val) in enumerate(raw_inpt[0]):
    if idx % 5 == 0: print("")
    print("%11.6f " % val, end="")
  print("\n\nPredicted median house price = $%0.2f" %
    (y.item()*10000))
if __name__=="__main__":
  main()

Das Demoprogramm das gesamte PyTorch-Paket importiert und weist ihm ein Alias "t" Eine Alternative ist, importieren Sie nur die Module und Funktionen, die erforderlich sind.

Die Demo definiert eine Hilfsfunktion namens Genauigkeit. Wenn Sie ein Regressionsmodell zu verwenden, gibt es keine inhärenten Definition von der Genauigkeit einer Vorhersage. Sie müssen definieren, wie weit ein vorhergesagter Wert einen Zielwert muss sein, um als eine richtige Vorhersage gewertet. Das Demoprogramm zählt einen vorhergesagte Median Haus Preis als richtig, wenn es innerhalb von 15 Prozent der der Wert "true" ist.

Die steuernde Logik für das Demoprogramm ist in einer einzigen Hauptfunktion enthalten. Ausführung des Programms beginnt mit dem Festlegen der globalen NumPy und PyTorch zufällige Ausgangswerte, damit die Ergebnisse reproduziert werden können.

Laden von Daten in den Arbeitsspeicher

Die Demo lädt die Daten im Arbeitsspeicher, die mithilfe der NumPy Loadtxt-Funktion:

train_x = np.loadtxt(train_file, delimiter="\t",
  usecols=range(0,13), dtype=np.float32)
train_y = np.loadtxt(train_file, delimiter="\t",
  usecols=[13], dtype=np.float32)
test_x = np.loadtxt(test_file, delimiter="\t",
  usecols=range(0,13), dtype=np.float32)
test_y = np.loadtxt(test_file, delimiter="\t",
  usecols=[13], dtype=np.float32)

Der Code wird davon ausgegangen, dass die Daten in ein Unterverzeichnis namens Daten gespeichert sind. Die Demodaten wurde vorverarbeitet, indem sie in Trainings- und Testsätze aufteilen. Datenanalysen nicht vom Konzept her schwierig, aber es ist fast immer recht zeitaufwendig und lästig. Viele meiner Kollegen möchten das Pandas-Paket (Analyse der Python-Daten) zu verwenden, um die Daten bearbeiten.

Definieren des neuronalen Netzes

Die Demo definiert das 13 neuronale-(10-10)-1-Netzwerk in einer programmdefinierte Klasse mit dem Namen Net, die von der Nn erbt. Modul-Modul. Sie können die Python-__init__-Funktion als Konstruktor einer Klasse vorstellen. Beachten Sie, dass Sie explizit eine Eingabeebene definieren nicht, da die Eingabewerte direkt in der ersten verborgenen Schicht gemeldet werden.

Das Netzwerk verfügt (13 * 10) + (10 * 10) + (10 * 1) = 240 Gewichtungen. Jedes Gewicht wird auf einen kleinen, mit dem Algorithmus Xavier Uniform random Wert initialisiert. Das Netzwerk verfügt über 10 + 10 + 1 = 21 Bias. Jeder Wert für den Zeitunterschied wird auf 0 (null) initialisiert.

Die Net-Klasse-forward-Funktion definiert, wie die Schichten Ausgabe zu berechnen. Die Demo verwendet (hyperbolische Tangensfunktion)-Aktivierungsfunktion Tanh für die zwei verdeckten Schichten und keine Aktivierung für die Ausgabeschicht:

def forward(self, x):
  z = T.tanh(self.hid1(x))
  z = T.tanh(self.hid2(z))
  z = self.oupt(z)
  return z

Für die Aktivierung der verborgenen Schicht die wichtigsten Alternative ist die Aktivierung der relu linear Unit (Rectified), aber es gibt viele andere Funktionen.

Da PyTorch auf einer relativ niedrigen Ebene der Abstraktion arbeitet, stehen viele alternative Entwurfsmuster für die Sie verwenden können. Beispielsweise anstelle von Definieren einer Net-Klasse mit dem __init__ und forward-Funktionen, und klicken Sie dann Instanziieren von mit Net = Net(), können Sie die sequenzielle-Funktion, wie folgt:

net = T.nn.Sequential(
  T.nn.Linear(13,10),
  T.nn.Tanh(),
  T.nn.Linear(10,10),
  T.nn.Tanh(),
  T.nn.Linear(10,1))

Der sequenzielle Ansatz ist viel einfacher, aber beachten Sie, dass Sie nicht über direkte Kontrolle über die initialisierungsalgorithmen gewichtungs- und biaswerten verfügen. Die enorme Flexibilität erhalten Sie bei Verwendung von PyTorch ist ein Vorteil, sobald Sie mit der Bibliothek vertraut sind.

Trainieren des Modells

Trainieren des Modells beginnt mit diesen sieben Anweisungen:

net = net.train()  # Set training mode
bat_size = 10
loss_func = T.nn.MSELoss()  # Mean squared error
optimizer = T.optim.SGD(net.parameters(), lr=0.01)
n_items = len(train_x)
batches_per_epoch = n_items // bat_size
max_batches = 1000 * batches_per_epoch

PyTorch verfügt über zwei Modi: trainieren und eval. Der Standardmodus ist trainieren, aber meiner Meinung nach ist es empfiehlt sich, explizit den Modus festzulegen. Die Batchgröße (oft als "minibatchtraining" bezeichnet) ist ein Hyperparameter. Für ein regressionsproblem bedeutet, dass quadratischer Fehler am häufigsten verwendeten verlustfunktion. Der stochastischen gradientenverfahren (SGD)-Algorithmus ist der elementarsten Technik und in vielen Situationen bietet des Adam-Algorithmus die bessere Ergebnisse.

Das Demoprogramm verwendet einen einfachen Ansatz für die Batchverarbeitung trainingselemente. Für die Demo ungefähr 400 Elemente zu trainieren, daher ist die Batchgröße auf 10, durchschnittlich Zugriff auf jedes trainingselements einmal vorhanden sind (Dies wird in der Regel eine Epoche in Machine Learning-Terminologie bezeichnet) müssen 400 / 10 = 40 batches. Aus diesem Grund wird um das Äquivalent von 1.000 Epochen zu trainieren, das Demoprogramm 1000 * 40 = 40.000 Batches benötigt.

Die Core-Training-Anweisungen sind:

for b in range(max_batches):
  curr_bat = np.random.choice(n_items, bat_size,
    replace=False)
  X = T.Tensor(train_x[curr_bat])
  Y = T.Tensor(train_y[curr_bat]).view(bat_size,1)
  optimizer.zero_grad()
  oupt = net(X)
  loss_obj = loss_func(oupt, Y)
  loss_obj.backward()  # Compute gradients
  optimizer.step()     # Update weights and biases

Die Choice-Funktion wählt 10 zufällige Indizes aus der trainingselemente 404 verfügbar. Die Elemente werden von NumPy-Arrays in PyTorch Tensors konvertiert. Sie können eine Tensor als ein mehrdimensionales Array vorstellen, die von einer GPU effizienter verarbeitet werden können, (obwohl das Demoprogramm eine GPU nutzen nicht). Die Shellobjekts Ansichtsfunktion umformt die eindimensionale Zielwerte in einem zweidimensionalen Tensor. Konvertieren von NumPy-Arrays in PyTorch Tensors sowie den Umgang mit Formen Tensor "und" Array "ist eine der hauptherausforderungen beim Arbeiten mit PyTorch.

Sobald alle 4.000 zeigt die Demo-Programms batches Quadrat der Wert des Mittelwerts Fehler Datenverlust für den aktuellen Batch von 10 trainingselementen und die vorhersagegenauigkeit des Modells, mit die aktuellen gewichtungs- und Biaswerte für das gesamte Trainingsdataset mit 404-Element:

if b % (max_batches // 10) == 0:
  print("batch = %6d" % b, end="")
  print("  batch loss = %7.4f" % loss_obj.item(), end="")
  net = net.eval()
  acc = accuracy(net, train_x, train_y, 0.15)
  net = net.train()
  print("  accuracy = %0.2f%%" % acc)

Die "/ /"-Operators ist Ganzzahldivision in Python. Die Demo legt das Netzwerk vor dem Aufrufen der Genauigkeit Programm definierte Funktion, eval-Modus fest. Technisch gesehen ist dies nicht erforderlich, da Trainings- und eval Modi nur unterschiedliche Ergebnisse liefern, wenn das Netzwerk Dropout oder Layer-Batch-Normalisierung verwendet.

Evaluieren und mithilfe des trainierten Modells

Nach dem Training abgeschlossen ist, wertet des Demoprogramms der vorhersagegenauigkeit des Modells für den Test-Datasets:

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

Die eval-Funktion gibt einen Verweis auf das Modell auf dem er angewendet wird; Sie können ohne die zuweisungsanweisung aufgerufen wurden.

In den meisten Fällen nach dem Modell zu trainieren Sie das Modell für die spätere Verwendung speichern möchten. Speichern eines trainierten Modells für PyTorch etwas außerhalb des Bereichs dieses Artikels ist, aber einige Beispiele finden Sie in der PyTorch-Dokumentation.

Der ganze Sinn des Trainings eines Regressionsmodells ist es zum Treffen einer Vorhersage verwenden. Das Demoprogramm stellt eine Vorhersage mit das erste Datenelement aus den 102 Testelementen:

raw_inpt = np.array([[0.09266, 34, 6.09, 0, 0.433, 6.495, 18.4,
  5.4917, 7, 329, 16.1, 383.61, 8.67]], dtype=np.float32)
norm_inpt = np.array([[0.000970, 0.340000, 0.198148, -1,
  0.098765, 0.562177, 0.159629, 0.396666, 0.260870, 0.270992,
  0.372340, 0.966488, 0.191501]], dtype=np.float32)

Wenn Sie neue Daten verfügen, müssen Sie auf die gleiche Weise die prädiktorwerte normalisieren, dass die Trainingsdaten normalisiert wurde. Für die min-Max-Normalisierung bedeutet dies, dass es sich bei müssen Sie speichern die Minimal- und Maximalwert für jede Variable, der normalisiert wurde.

Die Demo schließt mit vornehmen und die Vorhersage angezeigt:

...
  X = T.Tensor(norm_inpt)
  y = net(X)
  print("Predicted = $%0.2f" % (y.item()*10000))
if __name__=="__main__":
  main()

Der vorhergesagte Wert wird als eine Tensor durch einen einzelnen Wert zurückgegeben. Die Item-Funktion wird verwendet, auf den Wert zugreifen, damit sie angezeigt werden kann.

Zusammenfassung

Die PyTorch-Bibliothek ist etwas ausgereifter als alternativen TensorFlow, Keras und CNTK, insbesondere im Hinblick auf Beispielcode. Aber unter meinen Kollegen, die Verwendung von PyTorch wächst sehr schnell. Ich erwarte, dass dieser Trend fortgesetzt, und qualitativ hochwertige Beispiele werden Ihnen immer zur Verfügung.


Dr. James McCaffreyist in Redmond (Washington, USA) für Microsoft Research tätig. Er arbeitet in mehreren wichtigen Microsoft-Produkten wie 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: Chris Lee, Ricky Loynd