Chainer ist eine Bibliothek zur Implementierung neuronaler Netze, die von Preferred Networks entwickelt wurde. Die Funktionen sind wie folgt (von der Homepage).
Persönlich möchte ich noch eines erwähnen: "Einfach zu installieren". Die meisten Deep-Learning-Frameworks sind mühsam zu installieren, aber Chainer hat nur wenige abhängige Bibliotheken und war einfach zu installieren ... aber ich habe mit Cython ab 1.5.0 angefangen und es war ein bisschen mühsam. wurde. Informationen zur Installationsmethode finden Sie im Folgenden.
Darüber hinaus ist die Notation von Chainer wie oben beschrieben intuitiv und einfach, sodass sie einen weiten Bereich von einfachen Netzwerken bis hin zu komplexeren, sogenannten Deep-Learning-Bereichen abdecken kann. Andere Deep-Learning-Bibliotheken sind völlig überspezifiziert, wenn sie nicht tief sind, und andererseits sind einfache Bibliotheken (wie PyBrain) schwierig, wenn sie tief sind. Ich denke, dies ist auch ein großer Vorteil.
Dieses Mal werde ich erklären, wie man einen so attraktiven Chainer benutzt, aber um mit Chainer umgehen zu können, ist das Wissen über (relativ tiefe) neuronale Netze unabdingbar. Infolgedessen kommt es häufig vor, dass das Wissen auf der Seite des neuronalen Netzes unzureichend ist (ich habe es verstanden).
Daher möchte ich zunächst kurz den Mechanismus des neuronalen Netzwerks erläutern und dann erläutern, wie es in einem späteren Stadium mit Chainer implementiert werden kann.
Die Struktur des neuronalen Netzwerks ist wie folgt (abgesehen davon ist das Zeichnen von Linien zwischen Knoten nicht jedes Mal ein Problem).
Schauen wir uns genauer an, wie die Eingabe von Eingabe zu Ausgabe gelangt. Die folgende Abbildung macht es leicht zu sehen, wie Eingaben in den ersten Knoten der verborgenen Ebene vorgenommen werden.
Sie können sehen, dass vier Eingänge übertragen werden. Die Eingabe wird nicht direkt übertragen, sondern gewichtet. Ein neuronales Netzwerk ahmt die Zusammensetzung der Neuronen im Gehirn nach, betrachtet es jedoch als geschwächt oder gestärkt, wenn sich der Input (Stimulus) ausbreitet. Mathematisch ausgedrückt, wenn die Eingabe $ x $ ist, wird sie als $ a $ wie $ ax $ gewichtet.
Jetzt haben wir die Eingabe "ax" erhalten, aber der Knoten übergibt diesen Wert nicht wie an die nächste Ebene. Es scheint, dass es im Gehirn einen Mechanismus gibt, bei dem nur die Eingabe, die einen bestimmten Schwellenwert überschreitet, an die nächste Schicht weitergegeben wird, und auch hier imitiert sie diese und wandelt die empfangene Eingabe in die Ausgabe in die nächste Schicht um. Mathematisch ausgedrückt ist die Funktion, die die Eingabe in die Ausgabe in die nächste Ebene konvertiert, $ h $, und der Ausgabewert kann als $ h (ax) $ ausgedrückt werden. Diese Funktion $ h $ wird als Aktivierungsfunktion bezeichnet.
Zusammenfassend gibt es zwei wichtige Faktoren für die Wertausbreitung in einem neuronalen Netzwerk:
Kurz gesagt, das neuronale Netzwerk gewichtet einfach die empfangene Eingabe und gibt sie aus. Daher ist ein einschichtiges neuronales Netzwerk fast gleichbedeutend mit linearer oder logistischer Regression.
In diesem Sinne wird klar, was die Operationen an der Anzahl der Knoten und der Anzahl der Schichten bedeuten.
Wenn es um ein neuronales Netzwerk geht, denke ich, dass die Anzahl der Knoten und die Anzahl der Schichten angemessen durcheinander gebracht werden können, aber es ist auch wichtig, die Daten fest zu zeichnen und die geeignete Anzahl von Knoten und Schichten zu finden.
Um ein neuronales Netzwerk zu trainieren, verwenden wir eine Technik, die als Rückausbreitung bezeichnet wird. Der Fehler ist die Differenz zwischen dem vom neuronalen Netz ausgegebenen Wert und dem tatsächlichen Wert. Backpropagation ist eine Methode, um diesen Fehler von hinten zu verbreiten (Ausgabeschicht = Ausgabeschicht), wie der Name schon sagt, und das Gewicht jeder Schicht anzupassen.
Die Details der Backpropagation werden hier nicht im Detail besprochen, da es verschiedene andere Erklärungen gibt, aber die folgenden zwei Punkte sind wichtig.
Es gibt verschiedene Methoden zur Verwendung der obigen Trainingsdaten, um die obige Operation "Berechnen des Fehlers und Aktualisieren des Gewichts" durchzuführen.
Der Zyklus von 1 Epoche besteht darin, die Aktualisierung der verwendeten Trainingsdaten abzuschließen. Normalerweise werden Sie diese Epoche mehrmals lernen. Es ist jedoch nicht so gut, wenn es einfach wiederholt wird, so dass die Trainingsdaten in jeder Epoche gemischt werden und im Fall eines Mini-Batches die Erfassungsposition des Mini-Batches verschoben oder zufällig abgetastet wird.
Diese Epoche ist eine wichtige Einheit beim Lernen eines neuronalen Netzwerks, z. B. beim Überprüfen des Lernfortschritts und beim Neuanpassen von Parametern.
Hier ist eine Zusammenfassung des Inhalts der Erklärung des neuronalen Netzwerks.
Schauen wir uns nun die Implementierung in Chainer und die obigen Punkte an.
In Chainer besteht das neuronale Netzwerk aus "Chain" (Funktionseinstellung bis 1.4). Das Folgende ist eine Definition des neuronalen Netzwerks vom Typ 4-3-2, das bisher in der Erklärung verwendet wurde.
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
class MyChain(Chain):
def __init__(self):
super(MyChain, self).__init__(
l1=L.Linear(4, 3),
l2=L.Linear(3, 2)
)
def __call__(self, x):
h = F.sigmoid(self.l1(x))
o = self.l2(h)
return o
Hinweis
Es scheint, dass Sie "Chain" nicht erben müssen, aber Sie können keine CPU / GPU-Migration und Modellspeicherung durchführen, ohne "Chain" zu erben. Ich denke, Sie sollten es gehorsam erben. Wenn es sich um eine einfache vollständige Verknüpfung handelt, muss keine Klasse erstellt werden, und es scheint, dass "Kette (l1 = ..., l2 = ...)" in Ordnung ist.
Ab 1.5 sind Funktionen mit Parametern (= Optimierungsziele) klar von Link und reine Funktionen (Sigmoid usw.) klar von Funktionen getrennt.
Ich denke, es ist am besten für diejenigen, die sich gefragt haben, ob die verborgene Ebene nicht einmal eine Ebene ist. Bitte beziehen Sie sich auf die Abbildung unten für die obigen "l1" und "l2".
In Anbetracht der Ausbreitung zwischen Schichten auf diese Weise besteht der Mechanismus darin, dass es zwei Schichten gibt. Tatsächlich hält "L.Linear" das Gewicht für die Ausbreitung und ist dafür verantwortlich, dieses Gewicht auf die Eingabe anzuwenden.
Die Weitergabeverarbeitung wird wie oben beschrieben in __call__
der Chain-Klasse implementiert.
def __call__(self, x):
h = F.sigmoid(self.l1(x))
o = self.l2(h)
return o
Hinweis
Der Prozess, der als "forward" bis 1.4 geschrieben wurde, wird in "call" geschrieben (Wenn Sie in Python "call" definieren, z. B. wenn Sie "model ()" aus einer Instanz namens "model" schreiben), Sie können den in __call__
) geschriebenen Prozess aufrufen.
Hier wird die Eingabe "x" gewichtet ("self.l1 (x)") und der Wert über die Sigmoidfunktion, die häufig als Aktivierungsfunktion verwendet wird, an die nächste Schicht übergeben ("h ="). F. sigmoid (self.l1 (x)) ). Die endgültige Ausgabe erfordert keine Verarbeitung, um zur nächsten Schicht überzugehen, daher wird die Aktivierungsfunktion nicht verwendet (
o = self.l2 (h)`)
Beim Training müssen Sie zuerst den Fehler zwischen dem vorhergesagten Wert und dem tatsächlichen Wert berechnen. Sie können dies einfach als Funktion implementieren (der Name "lossfun" ist in Chainer üblich), aber bei Klassifizierungsproblemen ist es einfacher, "Classifier" zu verwenden.
from chainer.functions.loss.mean_squared_error import mean_squared_error
model = L.Classifier(MyChain(), lossfun=mean_squared_error)
Tatsächlich ist "Klassifikator" auch "Link", dh eine Funktion mit Parametern, und berechnet den Fehler zwischen dem von "MyChain" ausgegebenen Wert und den Lehrerdaten in "call" ("Funktion" für die Berechnung ist natürlich. Es kann angegeben werden (mean_squared_error oben).
In 1.5 ist der Punkt, an dem dieser "Link" verbunden werden kann, sehr groß und die Wiederverwendbarkeit des Modells ist viel höher. Selbst oben können Sie sehen, dass das Modell des Hauptkörpers und der Prozess der Berechnung des Fehlers damit separat geschrieben werden können.
Optimieren Sie das Modell nach der Berechnung des Fehlers, um ihn zu minimieren (Backpropagation oben). Es ist der "Optimierer", der diese Rolle spielt, und der lernende Teil des MNIST-Beispiels lautet wie folgt. Es ist geworden.
# Setup optimizer
optimizer = optimizers.Adam()
optimizer.setup(model)
...(Unterlassung)...
# Learning loop
for epoch in six.moves.range(1, n_epoch + 1):
print('epoch', epoch)
# training
perm = np.random.permutation(N)
sum_accuracy = 0
sum_loss = 0
for i in six.moves.range(0, N, batchsize):
x = chainer.Variable(xp.asarray(x_train[perm[i:i + batchsize]]))
t = chainer.Variable(xp.asarray(y_train[perm[i:i + batchsize]]))
# Pass the loss function (Classifier defines it) and its arguments
optimizer.update(model, x, t)
Es gibt drei grundlegende Schritte:
optimizers.Adam ()
)optimizer.setup (model)
)optimizer.update (model, x, t)
)Im Mittelpunkt steht die Aktualisierung von optimizer.update
. Ab 1.5 wird durch Übergeben von lossfun als Argument die Fehlerberechnung und Weitergabe durch den übergebenen lossfun automatisch durchgeführt. Natürlich ist es auch möglich, den Gradienten mit "model.zerograds ()" zu initialisieren und dann den Fehler selbst zu berechnen und zu verbreiten (loss.backward) und "optimizer.update" wie zuvor aufzurufen.
Wie Sie sehen können, ist Chainer so konzipiert, dass Sie Ihr Modell nach der Definition problemlos optimieren können (Define-and-Run
).
Und das trainierte Modell kann einfach mit dem Serializer gespeichert / wiederhergestellt werden (der Optimierer kann auch gespeichert werden).
serializers.save_hdf5('my.model', model)
serializers.load_hdf5('my.model', model)
Danach finden Sie hier einige Tipps zur tatsächlichen Implementierung.
Vielleicht bleibt als erstes hauptsächlich Tippfehler hängen. Ich weiß nicht, ob Chainer mit einem Typ beginnt und mit einem Typ endet, aber es besteht kein Zweifel, dass er mit einem Typ beginnt. Seien Sie also in diesem Punkt vorsichtig und verwenden Sie ihn.
Recommended Posts