[PYTHON] Ich habe versucht, mit Theano tief zu lernen

Einführung

Da ich Theano vor kurzem als dritte Bibliothek nach Chainer und Caffe verwendet habe, werde ich die Implementierung des Convolutional Neural Network (CNN) mit Theano erläutern. Andere Artikel und Offizielles Tutorial sind für die grundlegende Verwendung von Theano leicht zu verstehen. Bitte beziehen Sie sich darauf. Dieser Artikel wird gemäß Deep Convolutional Network of Deep Learning Tutorial erläutert. Der Code wurde zur Erklärung geändert, aber der allgemeine Ablauf ist der gleiche. Es gibt keine Erklärung für Deep Learning selbst oder die grundlegende Verwendung von Theano in der Erklärung der CNN-Implementierung mit Theano. Weitere Informationen zu diesem Bereich finden Sie in anderen Artikeln. Auch wenn darin die Erklärung der Implementierung steht, gibt es viele Punkte, die ich nicht erreichen kann, da die Implementierungsfähigkeit der Person selbst nicht so hoch ist und sie einige Tage nach Beginn der Verwendung von Theano nicht gut Englisch kann (offizielles Tutorial ist Englisch). (Erwarten Sie nicht so viel, weil es sich wie ein Memorandum der Person anfühlt.)

LeNet Wir werden die kleinste handschriftliche Zeichenerkennung basierend auf LeNet gemäß dem Deep-Learning-Tutorial durchführen. LeNet ist ein Basis-CNN, das aus 2 Faltungsschichten, 2 Poolschichten und einer vollständig verbundenen Schicht besteht. In diesem Artikel unterscheiden sich die Details wie die Aktivierungsfunktion von den ursprünglichen, aber die Grundstruktur ist dieselbe. (Bitte lesen Sie das Originalpapier für eine detaillierte Erklärung von LeNet.)

Implementierung

Wir werden CNN basierend auf dem oben genannten LeNet implementieren. Als Voraussetzung wird angenommen, dass Folgendes importiert wird.

import theano
import theano.tensor as T
import numpy as np

Faltschicht

Zunächst werden wir die Faltungsschicht implementieren. Theano stellt T.nnet.conv.conv2d als Faltungssymbol bereit. Theano verwendet numpy und theano.shared, weil Sie die Gewichte und Vorurteile selbst schreiben müssen. Gewichte und Verzerrungen sind Werte, die durch Lernen aktualisiert werden. Daher definieren wir sie mit theano.shared. Da es schwierig ist, die Ebene jedes Mal zu beschreiben, wenn Sie eine Faltungsebene hinzufügen, definieren Sie sie als Klasse. Das Folgende ist ein Implementierungsbeispiel für die Faltungsschichtklasse.

class Conv2d(object):
    def __init__(self, input, out_c, in_c, k_size)
        self._input = input #Symbol eingegeben werden
        self._out_c = out_c #Anzahl der Ausgangskanäle
        self._in_c = in_c #Anzahl der Eingangskanäle
        w_shp = (out_c, in_c, k_size, k_size) #Gewichtsform
        w_bound = np.sqrt(6. / (in_c * k_size * k_size + \
                        out_c * k_size * k_size)) #Gewichtsbeschränkungen
        #Definition des Gewichts
        self.W = theano.shared( np.asarray(
                        np.random.uniform( #Mit Zufallszahlen initialisieren
                            low=-w_bound,
                            high=w_bound,
                            size=w_shp),
                        dtype=self._intype.dtype), name ='W', borrow=True)
        b_shp = out_c, #Vorspannungsform
        #Definition der Voreingenommenheit(Mit Null initialisieren)
        self.b = theano.shared(np.zeros(b_shp,
                        dtype=self._input.dtype), name ='b', borrow=True)
        #Definition von Faltungssymbolen
        self.output = T.nnet.conv.conv2d(self._input, self.W) \
                        + self.b.dimshuffle('x', 0, 'x', 'x')
        #Speichern Sie aktualisierte Parameter
        self.params = [self.W, self.b]

dimshuffle arbeitet so, dass es der Dimension des Bias-Terms von Vektor zu Tensor4 entspricht, der die Ausgabe von T.nnet.conv.conv2d ist. Es fühlt sich an wie eine Kombination aus Umformung und np.transponieren. Im Fall von ('x', 0, 'x', 'x') wird die Form von self.b zu (1, self.b.shape [0], 1, 1).

Aktivierungsfunktion

Ursprünglich ist die Aktivierungsfunktion von LeNet tanh, aber dieses Mal werden wir relu verwenden. relu ist eine Aktivierungsfunktion, die durch eine einfache Formel namens max (0, x) ausgedrückt wird. Da Theano kein Relu-Symbol hat, müssen Sie es selbst definieren. Das T.max () -Symbol von Theano ist etwas speziell geschrieben, da es keine reellen Werte enthalten kann (obwohl es einen Weg gibt) und die if-Anweisung nicht für das Symbol verwendet werden kann. Das Folgende ist ein Beispiel für die Implementierung von relu.

class relu(object):
    def __init__(self, input):
        self._input = input
        self.output  = T.switch(self._input < 0, 0, self._input)

Pooling-Schicht

Die Pooling-Ebene wird in Theano platziert und das Symbol wird in theano.tensor.signal.pool.pool_2d definiert. Die Pooling-Ebene ist einfach zu schreiben, da im Gegensatz zum Falten keine Symbole für die Aktualisierung wie Gewichte und Verzerrungen vorbereitet werden müssen. Unten finden Sie ein Beispiel für die Implementierung der Pooling-Schicht.

from theano.tensor.signal import pool

class Pool2d(object):
    def __init__(self, input, k_size, st, pad=0, mode='max'):
        self._input = input
        #Definition von Pooling-Layer-Symbolen
        self.output = pool.pool_2d(self._input, 
                            (k_size, k_size), #Kernelgröße
                            ignore_border=True, #Kantenbearbeitung(Grundsätzlich wahr und ok,Einzelheiten finden Sie im offiziellen Dokument)
                            st=(st, st), #schreiten
                            padding=(pad, pad), #Polsterung
                            mode=mode) #Arten von Pooling('max', 'sum', 'average_inc_pad', 'average_exc_pad')

Vollständig verbundene Schicht

Die vollständig verbundene Schicht wird von mir beschrieben, weil das Symbol nicht in theano erstellt wurde, sondern durch die Berechnung des inneren Produkts der Matrix ausgedrückt werden kann und das Symbol für das innere Produkt von T.dot () angegeben wird, was nicht besonders schwierig ist. Wie bei der Faltungsschicht gibt es Gewichte und Vorspannungen, so dass jede definiert ist. Das Folgende ist ein Implementierungsbeispiel für die vollständig verbundene Schicht.

class FullyConnect(object):
    def __init__(self, input, inunit, outunit):
        self._input = input
        #Definition des Gewichts
        W = np.asarray(
            np.random.uniform(
            low=-np.sqrt(6. / (inunit + outunit)),
            high=np.sqrt(6. / (inunit + outunit)),
            size=(inunit, outunit)
            ),
            dtype=theano.config.floatX)
        self.W = theano.shared(value=W, name='W', borrow=True)
        #Definition der Voreingenommenheit
        b = np.zeros((outunit,), dtype=theano.config.floatX) #Mit Null initialisieren
        self.b = theano.shared(value=b, name='b', borrow=True)
        #Definition von vollständig verbundenen Ebenensymbolen
        self.output = T.dot(self._input, self.W) + self.b
        #Speichern Sie aktualisierte Parameter
        self.params = [self.W, self.b]

Verlustfunktion

Die Verlustfunktion verwendet die Softmax-Kreuzentropie, um die 10-Klassen-Klassifikation von Mnist zu lösen. Das Softmax-Symbol befindet sich in T.nnet.softmax () in Theano. Verwenden Sie dieses Symbol. Das Folgende ist ein Implementierungsbeispiel.

class softmax(object):
    def __init__(self, input, y):
        self._input = input
        #Softmax-Symboldefinition
        self.output = nnet.softmax(self._input)
        #Symboldefinition der Kreuzentropie(Die Formel ist Summe, aber hier verwenden wir Mittelwert.)
        self.cost = -T.mean(T.log(self.output)[T.arange(y.shape[0]), y])

y steht für das Lehrerbezeichnungssymbol. [T.arange (y.shape [0]), y] bedeutet, von y [0] zu y [y.shape [0] -1] zu addieren, wenn T.mean ausgeführt wird.

LeNet

Datensatz

Nachdem wir jede Ebene definiert haben, fahren wir mit der LeNet-Implementierung fort. Bereiten Sie zunächst die wichtigsten Daten vor. Die pkl-Daten sind unter http://www.iro.umontreal.ca/~lisa/deep/data/mnist/mnist.pkl.gz verfügbar. Laden Sie sie daher in den entsprechenden Ordner herunter. Extrahieren Sie hier Zug-, Validierungs- und Testdaten. Unten finden Sie ein Beispiel für das Laden von Daten.

import gzip
import cPickle

def shared_dataset(data_xy):
    data_x, data_y = data_xy
    set_x = theano.shared(np.asarray(data_x,
                  dtype=theano.config.floatX).reshape(-1,1,28,28),
                  borrow=True)
    set_y = T.cast(theano.shared(np.asarray(data_y,
                  dtype=theano.config.floatX), borrow=True), 'int32')
    return set_x, set_y

with open('/path/to/mnist.pkl.gz', 'rb') as f:
    train_set, valid_set, test_set = cPickle.load(f)

train_set_x, train_set_y = shared_dataset(train_set)
valid_set_x, valid_set_y = shared_dataset(valid_set)
test_set_x, test_set_y = shared_dataset(test_set)

Zur Vereinfachung der Implementierung werden die Daten hier als Symbol für theano.shared definiert, die Eingabe kann jedoch ein Array von numpy sein. Wenn Sie es jedoch in theano.shared definieren, wird die Implementierung etwas sauberer. Definieren Sie als Nächstes die Eingabedaten und das Symbol der Lehrerbezeichnung. Da die Eingabedaten 4 Dimensionen haben (Anzahl der Stapel, Anzahl der Kanäle, Länge, Breite), ist es T.tensor4 (), und da die Lehrerbezeichnung ein Vektor von eindimensionalen ganzzahligen Werten ist, ist es T.ivector ().

x = T.tensor4() #Eingabedatensymbol
y = T.ivector() #Ausgabedatensymbol

Ebenendefinition

Ab hier wird es zur Definition jeder Schicht. Ich habe jede Klasse oben erstellt, damit es etwas einfacher zu schreiben ist.

conv1 = Conv2d(x, 20, 1, 5) #Mit x als Eingang beträgt der Ausgang 20 Kanäle, der Eingang 1 Kanal, die Kernelgröße 5
relu1 = relu(conv1.output) #Nehmen Sie die Ausgabe von conv1 als Eingabe
pool1 = Pool2d(relu1.output, 2, 2) #Nehmen Sie den Ausgang von relu1 als Eingang,Kernel Größe 2, Schritt 2

conv2 = Conv2d(pool1.output, 50, 20, 5) #Der Ausgang von poo1 wird als Eingang genommen, der Ausgang ist 50 Kanäle, der Eingang ist 20 Kanäle und die Kernelgröße ist 5.
relu2 = relu(conv2.output) #Nehmen Sie die Ausgabe von conv2 als Eingabe
pool2 = Pool2d(relu2.output, 2, 2) #Nehmen Sie den Ausgang von relu2 als Eingang,Kernel Größe 2, Schritt 2

fc1_input = pool2.output.flatten(2) #Das Ausgabesymbol von Pool2 ist T..abflachen für Tensor4()Zum Anpassen des Eingabesymbols der vollständig verbundenen Ebene mit
fc1 = FullyConnect(fc1_input, 50*4*4, 500) #50 Eingabeeinheiten*4*4(Anzahl der Kanäle*Vertikal*Seite)Die Anzahl der Ausgabeeinheiten beträgt 500
relu3 = relu(fc1.output)
fc2 = FullyConnect(relu3.output, 500, 10) #500 Eingabeeinheiten, 10 Ausgabeeinheiten(Für 10 Klassifizierung)
loss = softmax(fc2.output, y)

Jetzt haben Sie die Definition von LeNet geschrieben. Durch Ändern des Ausgabesymbols jeder Ebene in das Eingabesymbol der nächsten Ebene werden alle Symbole verbunden und die Gradientenberechnung kann gleichzeitig durchgeführt werden. Das ist, N ・ ・ T.nnet.conv.conv2d (pool.pool2d (T.nnet.conv.conv2d ())) ・ ・ ・ Dies bedeutet, dass ein langes Symbol wie definiert ist. Wenn Sie T.grad () das letzte Symbol geben (diesmal verlust.Kosten), ist es daher möglich, den Gradienten aller Schichten einfach zu berechnen.

Lernen

Schließlich definieren wir die Funktion von Training und Evaluierung durch Validierungsdaten und Testdaten. Bis zu diesem Punkt haben wir nur Symbole definiert, daher können wir keine tatsächlichen Werte für das Lernen eingeben. Daher definieren wir das Symbol als theano.function. Das Lernen kann durch Definieren der Aktualisierung der Parameter zu diesem Zeitpunkt erfolgen. Dieses Mal werden wir die Parameter mit SGD aktualisieren. Das Folgende ist ein Implementierungsbeispiel für diese Funktion zum Lernen.

#Listen Sie alle zu lernenden Parameter auf
params = conv1.params + conv2.params + fc1.params + fc2.params 
#Berechnen Sie das Differential für jeden Parameter
grads = T.grad(loss.cost, params)
#Definition der Lernrate
learning_rate = 0.001
#Update-Ausdruck definieren
updates = [(param_i, param_i - learning_rate * grad_i) for param_i, grad_i in zip(params, grads)]
#Theano lernen.Funktion definieren
index = T.lscalar()
batch_size = 128
train_model = theano.function(inputs=[index], #Eingabe ist der Index der Trainingsdaten
                       outputs=loss.cost, #Ausgabe ist Verlust.cost
                       updates=updates, #Erneuerungsformel
                       givens={
                            x: train_set_x[index: index + batch_size], #Trainiere auf x in der Eingabe_set_Gib x
                            y: train_set_y[index: index + batch_size] #Trainiere, um y einzugeben_set_Gib y
                       })

Erstens ist params eine Liste von Gewichts- und Bias-Symbolen für jede Ebene (da dies das Hinzufügen der Listen ist). Bei einer gegebenen Liste von Variablen gibt T.grads () eine Liste von Symbolen zurück, die durch jede Variable differenziert sind. Gradds ist also eine Liste mit Symbolen, die durch jeden Parameter von loss.cost differenziert sind. Updates ist auch eine Liste von Update-Ausdrücken für jeden Parameter. Als nächstes folgt die Definition von train_model. Wie oben erwähnt, sind conv1 to loss.cost ein Symbol, das x und y als Eingaben verwendet. In train_model erhält x den Wert von train_set_x und y den Wert von train_set_y. train_set_x und train_set_y empfangen den Index und verweisen auf die Daten für batch_size aus dem empfangenen Index. Wenn Sie train_model nur index als Argument geben, werden die Werte von train_set_x und train_set_y von index zu index + batch_size für x und y angegeben. Danach können Sie lernen, indem Sie dieses train_model wiederholt mit einer for-Anweisung oder ähnlichem aufrufen.

for i in range(0, train_set_y.get_value().shape[0], batch_size):
    train_model(i)

Auswertung

Definieren Sie abschließend die Funktion theano., um die Genauigkeit des Trainingsmodells zu bewerten. Da der Parameter in der Auswertung nicht aktualisiert wird, wird die Ausgabe auf Fehlerrate gesetzt. Da es in loss.output ein Softmax-Symbol gibt, definieren Sie damit das Symbol, das die Fehlerrate berechnet, und definieren Sie die Funktion, die anhand des Fehlerraten-Symbols ausgewertet wird.

pred = T.argmax(loss.output, axis=1) #Gibt die Klasse mit der höchsten vorhergesagten Wahrscheinlichkeit zurück
error = T.mean(T.neq(pred,y)) #Vergleichen Sie die vorhergesagte Klasse mit der richtigen Bezeichnung
test_model = theano.function(inputs=[index],
                             outputs=error,
                             givens={
                             x: test_set_x[index: index + batch_size],
                             y: test_set_y[index: index + batch_size]
                             })

val_model = theano.function(inputs=[index],
                             outputs=error,
                             givens={
                             x: test_set_x[index: index + batch_size],
                             y: test_set_y[index: index + batch_size]
                             })

Nachdem die Auswertungsfunktion für Validierungs- und Testdaten definiert wurde, kann sie mithilfe der for-Anweisung auf dieselbe Weise wie train_model ausgewertet werden.

test_losses = [test_model(i)
               for i in range(0, test_set_y.get_value().shape[0], batch_size] #Speichern Sie den durchschnittlichen Verlust pro Charge in der Liste
mean_test_loss = np.mean(test_losses) #Berechnen Sie den Gesamtdurchschnitt

Mit dem Obigen wurde der Lern- und Bewertungscode von CNN unter Verwendung von theano geschrieben. Bitte überprüfen Sie das tatsächliche Lernergebnis, indem Sie den gesamten oben genannten Code verbinden (ich habe den Code, den ich für Quiita geschrieben habe, an einigen Stellen geändert und ohne Debugging geschrieben, sodass er möglicherweise nicht funktioniert lol). Wenn Sie Fragen, Code oder Erklärungen haben, teilen Sie uns dies bitte in den Kommentaren mit.

Zusammenfassung

Ich erklärte die Implementierung des Faltungsnetzwerks mit Theano. Auf den ersten Blick mag der Code lang und langweilig erscheinen, aber sobald Sie die Ebenenklassen für Ihre eigene Bequemlichkeit definiert haben, ist es einfach zu schreiben. Der Vorteil von Theano ist, dass Sie es selbst definieren können (obwohl es problematisch ist). Andere Implementierungen als LeNet können verschiedene Modelle erstellen, indem der Layer-Definitionsteil geändert wird. Darüber hinaus können Verlustfunktionen und Parameteraktualisierungsmethoden durch Definieren von Symbolen frei beschrieben werden. Ich hoffe, es wird für diejenigen nützlich sein, die Theano von nun an verwenden werden.

Recommended Posts

Ich habe versucht, mit Theano tief zu lernen
Ich habe versucht, tief zu lernen
Ich habe versucht, mit PyBrain verstärkt zu lernen
Ich habe versucht, ein Deep-Learning-Modell von TensorFlow mit TensorFlow Serving zu hosten
[Kaggle] Ich habe versucht, Ensemble mit LightGBM zu lernen
Ich habe versucht, parametrisiert zu verwenden
Ich habe versucht, Argparse zu verwenden
Ich habe versucht, Mimesis zu verwenden
Ich habe versucht, aiomysql zu verwenden
Ich habe versucht, Summpy zu verwenden
Ich habe versucht, Pipenv zu verwenden
Ich habe versucht, Matplotlib zu verwenden
Ich habe versucht, ESPCN zu verwenden
Ich habe versucht, openpyxl zu verwenden
Ich habe versucht, Ipython zu verwenden
Ich habe versucht, PyCaret zu verwenden
Ich habe versucht, Cron zu verwenden
Ich habe versucht, ngrok zu verwenden
Ich habe versucht, face_recognition zu verwenden
Ich habe versucht, Jupyter zu verwenden
Ich habe versucht, doctest zu verwenden
Ich habe versucht, Folium zu verwenden
Ich habe versucht, jinja2 zu verwenden
Ich habe versucht, Folium zu verwenden
Ich habe versucht, das Zeitfenster zu verwenden
Ein Amateur versuchte Deep Learning mit Caffe (Einführung)
Ein Amateur versuchte Deep Learning mit Caffe (Übung)
Ich habe versucht, Pytorchs Deep-Learning-Modell mit TorchServe auf Amazon SageMaker zu hosten
Ich habe die übliche Geschichte ausprobiert, Deep Learning zu verwenden, um den Nikkei-Durchschnitt vorherzusagen
Ich habe die gängige Geschichte der Vorhersage des Nikkei-Durchschnitts mithilfe von Deep Learning (Backtest) ausprobiert.
[Ich habe versucht, Pythonista 3 zu verwenden] Einführung
Ich habe versucht, easydict (Memo) zu verwenden.
Ich habe versucht, das Gesicht mit Face ++ zu erkennen
Ich habe versucht, RandomForest zu verwenden
Ich habe versucht, BigQuery ML zu verwenden
Ich habe versucht, Amazon Glacier zu verwenden
Ich habe versucht, Git Inspector zu verwenden
Ich habe versucht, Magenta / TensorFlow zu verwenden
Ich habe versucht, AWS Chalice zu verwenden
Ich habe versucht, Slack Emojinator zu verwenden
Ich habe versucht, meinen eigenen Datensatz mit Chainer Trainer zu lernen
Ich habe versucht, Dropout zu erklären
Ich habe versucht, das Bild mithilfe von maschinellem Lernen zu komprimieren
[Python] Deep Learning: Ich habe versucht, Deep Learning (DBN, SDA) ohne Verwendung einer Bibliothek zu implementieren.
Ich habe versucht, das Objekterkennungs-Tutorial mit dem neuesten Deep-Learning-Algorithmus auszuführen
Ich habe versucht, Deep VQE zu implementieren
Ich habe versucht, Rotrics Dex Arm zu verwenden
Ich habe versucht, GrabCut von OpenCV zu verwenden
Ich habe versucht, Tensorboard zu verwenden, ein Visualisierungstool für maschinelles Lernen
Ich habe versucht, Thonny (Python / IDE) zu verwenden.
[TF] Ich habe versucht, das Lernergebnis mit Tensorboard zu visualisieren
Ich habe versucht, mit dem Server-Client über tmux zu kommunizieren
Tiefes Lernen
Ich habe irgendwie versucht, ein Jupyter-Notebook zu verwenden
Ich habe versucht, Perceptron Teil 1 [Deep Learning von Grund auf neu] zu implementieren.
Ich habe versucht, LightGBM mit Yellowbrick zu lernen
[Kaggle] Ich habe versucht, mit unausgeglichenem Lernen zu unterabtasten