[PYTHON] Tiefes Lernen / Tiefes Lernen von Grund auf 2-Versuchen Sie, GRU zu bewegen

1. Zuallererst

Ich lese ein Meisterwerk, ** "Deep Learning from Zero 2" **. ** GRU ** wird im Anhang dieses Buches vorgestellt, aber es gibt keine tatsächliche Szene, um es zu verschieben. Dieses Mal habe ich, wie in Kapitel 6, die ** BetterRnnlm-Klasse ** verwendet, um ** GRU ** die Reihenfolge der Wörter im ** PTB-Datensatz ** und den Grad des Lernens mit Ratlosigkeit lernen zu lassen. Ich würde gerne messen.

2. Implementierung der GRU

スクリーンショット 2020-06-02 18.24.01.png

Dies ist ein GRU-Berechnungsdiagramm. Es gibt keine Speicherzellen im LSTM und nur h im verborgenen Zustand breitet sich in Zeitrichtung aus. Es gibt zwei Gates, ** Reset Gate ** und ** Update Gate **.

Das ** Reset-Gate ** bestimmt, wie viel vergangene versteckte Zustände ignoriert werden sollen. Wenn r Null ist, wird $ h_ {hat} $ nur aus der Eingabe bestimmt, wobei vergangene verborgene Zustände ignoriert werden.

Das ** Update-Gate ** fungiert gleichzeitig als LSTM-Vergessens-Gate und Eingangs-Gate. Der Teil, der als Vergessensgatter fungiert, ist $ (1-z) \ odot h_ {t-1} $. Diese Berechnung löscht Informationen, die aus früheren verborgenen Zuständen vergessen werden sollten.

Und es ist der Teil $ z \ odot h_ {hat} $, der als Eingangsgatter fungiert. Diese Berechnung gewichtet die neu hinzugefügten Informationen.

Lassen Sie uns nun die Gewichte und Verzerrungen vor der Implementierung sortieren.

スクリーンショット 2020-06-02 18.35.09.png Wxz, Wxr, Wxh zusammen ** Wx ** (T × 3H), Whz, Whr, Whh zusammen ** Wh ** (H × 3H), bz, br, bh zusammen ** b * * (3H).

from common.np import *  # import numpy as np (or import cupy as np)
from common.layers import *
from common.functions import softmax, sigmoid

class GRU:
    def __init__(self, Wx, Wh, b):  

        self.params = [Wx, Wh, b]  
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]  ###
        self.cache = None

    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        H = Wh.shape[0]
        Wxz, Wxr, Wxh = Wx[:, :H], Wx[:, H:2 * H], Wx[:, 2 * H:]
        Whz, Whr, Whh = Wh[:, :H], Wh[:, H:2 * H], Wh[:, 2 * H:]
        bz, br, bh = b[:H], b[H:2 * H], b[2 * H:]  

        z = sigmoid(np.dot(x, Wxz) + np.dot(h_prev, Whz) + bz)
        r = sigmoid(np.dot(x, Wxr) + np.dot(h_prev, Whr) + br)
        h_hat = np.tanh(np.dot(x, Wxh) + np.dot(r*h_prev, Whh) + bh)
        h_next = (1-z) * h_prev + z * h_hat

        self.cache = (x, h_prev, z, r, h_hat)

        return h_next

Die Parameter werden in Form von ** self.params ** und die Farbverläufe in Form von ** self.grad ** behandelt, damit sie so behandelt werden können, wie sie in der BetterRnnlm-Klasse sind. Einzelne Werte können durch die Breite H geteilt werden.

Jetzt ist es eine etwas komplizierte Rückausbreitung. Erstens ist es der Teil, der ** self.params ** zerlegt, um jeden Wert zu erhalten, und die anderen Zustände aus dem ** Cache ** wiederherstellt.

    def backward(self, dh_next):
        Wx, Wh, b = self.params  
        H = Wh.shape[0]
        Wxz, Wxr, Wxh = Wx[:, :H], Wx[:, H:2 * H], Wx[:, 2 * H:]
        Whz, Whr, Whh = Wh[:, :H], Wh[:, H:2 * H], Wh[:, 2 * H:]
        x, h_prev, z, r, h_hat = self.cache

Von hier aus werden wir die Backpropagation in vier Teilen implementieren. Erstens der Teil, der nichts mit Tanh und den beiden Sigmoiden zu tun hat.

スクリーンショット 2020-06-03 08.49.21.png

        dh_hat =dh_next * z
        dh_prev = dh_next * (1-z)

Es ist eine einfache Kombination von + und ×. Als nächstes ist um Tanh.

スクリーンショット 2020-06-03 08.50.31.png

        # tanh
        dt = dh_hat * (1 - h_hat ** 2)
        dbh = np.sum(dt, axis=0) 
        dWhh = np.dot((r * h_prev).T, dt)
        dhr = np.dot(dt, Whh.T)
        dWxh = np.dot(x.T, dt)
        dx = np.dot(dt, Wxh.T)
        dh_prev += r * dhr

Da dh_prev früher berechnet wurde, werden wir es von hier aus mit dh_prev + = zum Ergebnis hinzufügen. Als nächstes ist ungefähr z des Update-Gates.

スクリーンショット 2020-06-03 09.08.57.png

        # update gate(z)
        dz = dh_next * h_hat - dh_next * h_prev
        dt = dz * z * (1-z)
        dbz = np.sum(dt, axis=0) 
        dWhz = np.dot(h_prev.T, dt)
        dh_prev += np.dot(dt, Whz.T)
        dWxz = np.dot(x.T, dt)
        dx += np.dot(dt, Wxz.T)

Da dx bereits mit tanh berechnet wurde, wird von nun an dx + = zum Ergebnis hinzugefügt. Weiter ist um r des Reset-Gates.

スクリーンショット 2020-06-03 8.53.55.png

        # rest gate(r)
        dr = dhr * h_prev
        dt = dr * r * (1-r)
        dbr = np.sum(dt, axis=0) 
        dWhr = np.dot(h_prev.T, dt)
        dh_prev += np.dot(dt, Whr.T)
        dWxr = np.dot(x.T, dt)
        dx += np.dot(dt, Wxr.T)

Nachdem die Berechnung jedes Gradienten abgeschlossen ist, werden wir ihn in Grad zusammenfassen.

        self.dWx = np.hstack((dWxz, dWxr, dWxh))
        self.dWh = np.hstack((dWhz, dWhr, dWhh))
        self.db = np.hstack((dbz, dbr, dbh)) 
        
        self.grads[0][...] = self.dWx
        self.grads[1][...] = self.dWh
        self.grads[2][...] = self.db  

        return dx, dh_prev

Zu diesem Zeitpunkt ist die GRU-Implementierung abgeschlossen.

3. Implementierung von TimeGRU

スクリーンショット 2020-06-03 17.20.13.png

Für die Vorwärtsausbreitung von ** TimeGRU ** werden 3D-Daten ** xs ** ausgeschnitten und stündlich in ** GRU ** eingegeben, und die Ausgabe von ** GRU ** wird erneut in 3D-Daten ** ausgegeben. Es ist in hs ** zusammengefasst.

class TimeGRU:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None
        self.h, self.dh = None, None
        self.stateful = stateful

    def forward(self, xs):
        Wx, Wh, b = self.params
        H = Wh.shape[0]        
        N, T, D = xs.shape
        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = GRU(*self.params)
            self.h = layer.forward(xs[:, t, :], self.h)
            hs[:, t, :] = self.h
            self.layers.append(layer)

        return hs

Bereiten Sie eine Box ** hs ** (N, T, H) vor, um die Ausgabe zu speichern. Bereiten Sie bei Bedarf auch eine Nullmatrix ** self.h ** (N, H) vor. Schneiden Sie dann eine Stunde aus den Daten ** xs ** aus, geben Sie sie in ** GRU ** ein und speichern Sie die Ausgabe ** self.h ** von GRU in ** hs **. Fügen Sie gleichzeitig die Ebene für die Zeit T Minuten hinzu (dies wird rückwärts verwendet).

スクリーンショット 2020-06-03 17.00.02.png

Nun die Rückausbreitung von TimeGRU. Während der Backpropagation wird $ dh_t + dh_ {next} $ in die GRU-Ebene eingegeben.

    def backward(self, dhs):
        Wx, Wh, b = self.params   
        N, T, H = dhs.shape
        D = Wx.shape[0]

        dxs = np.empty((N, T, D), dtype='f')

        dh = 0
        grads = [0, 0, 0]  
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh = layer.backward(dhs[:, t, :] + dh)
            dxs[:, t, :] = dx
            
            for i, grad in enumerate(layer.grads):  
                grads[i] += grad     
        
        for i, grad in enumerate(grads):    
            self.grads[i][...] = grad       
        
        self.dh = dh
        return dxs

    def set_state(self, h):
        self.h = h

    def reset_state(self):
        self.h = None

Bereiten Sie eine Box *** dxs *** (N, T, D) vor, um die Backpropagation-Ausgabe zu speichern. Bereiten Sie außerdem eine Liste ** Absolventen ** vor, um die Farbverläufe vorübergehend zu speichern.

** Eine Stunde von dhs ausschneiden + Gradient von einem zukünftigen dh ** wird eingegeben, und die durch vorwärts angehängte GRU-Ebene wird in umgekehrter Reihenfolge aufgerufen und rückwärts angewendet. Dann wird das Rückwärtsergebnis ** dx ** in ** dxs ** gespeichert.

Im Ausdruck dx, dh = layer.backward (dhs [:, t,:] + dh) ist dh auf der rechten Seite $ dh_ {next} $ und dh auf der linken Seite ist $ dh_ {prev} $.

Fügen Sie dann die Gewichtsverläufe bei jeder Schicht hinzu und fassen Sie das Endergebnis in ** self.grads ** zusammen.

Nachdem die Implementierung von GRU und TimeGRU abgeschlossen ist, erstellen Sie einen Ordner mit dem Namen "ch09" und speichern Sie ihn unter dem Dateinamen "time_layers_gru.py".

4. Korrigieren Sie better_rnnlm

Ändern Sie als Nächstes better_rnnlm.py, um das Netzwerkmodell zu generieren.

import sys
sys.path.append('..')
from common.time_layers import TimeEmbedding, TimeAffine, TimeSoftmaxWithLoss, TimeDropout  #Geben Sie die zu lesende Ebene an
from time_layers_gru import *  #Von hier wird nur GRU gelesen
from common.np import *  # import numpy as np
from common.base_model import BaseModel

class BetterRnnlm(BaseModel):

    def __init__(self, vocab_size=10000, wordvec_size=650, 
                 hidden_size=650, dropout_ratio=0.5):
        V, D, H = vocab_size, wordvec_size, hidden_size
        rn = np.random.randn

        embed_W = (rn(V, D) / 100).astype('f')
        gru_Wx1 = (rn(D, 3 * H) / np.sqrt(D)).astype('f')
        gru_Wh1 = (rn(H, 3 * H) / np.sqrt(H)).astype('f')
        gru_b1 = np.zeros(3 * H).astype('f')
        gru_Wx2 = (rn(H, 3 * H) / np.sqrt(H)).astype('f')
        gru_Wh2 = (rn(H, 3 * H) / np.sqrt(H)).astype('f')
        gru_b2 = np.zeros(3 * H).astype('f')
        affine_b = np.zeros(V).astype('f')

        self.layers = [
            TimeEmbedding(embed_W),
            TimeDropout(dropout_ratio),
            TimeGRU(gru_Wx1, gru_Wh1, gru_b1, stateful=True),  
            TimeDropout(dropout_ratio),
            TimeGRU(gru_Wx2, gru_Wh2, gru_b2, stateful=True),  
            TimeDropout(dropout_ratio),
            TimeAffine(embed_W.T, affine_b)  
        ]
        self.loss_layer = TimeSoftmaxWithLoss()
        self.gru_layers = [self.layers[2], self.layers[4]]  
        self.drop_layers = [self.layers[1], self.layers[3], self.layers[5]]

        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

    def predict(self, xs, train_flg=False):
        for layer in self.drop_layers:
            layer.train_flg = train_flg

        for layer in self.layers:
            xs = layer.forward(xs)
        return xs

    def forward(self, xs, ts, train_flg=True):
        score = self.predict(xs, train_flg)
        loss = self.loss_layer.forward(score, ts)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout

    def reset_state(self):
        for layer in self.gru_layers:  
            layer.reset_state()

Zu Beginn wird nur die angegebene Ebene aus "common / time_layers.py" importiert, und die GRU wird so geändert, dass sie aus "time_layers_gru.py" importiert wird, das zuvor im aktuellen Verzeichnis gespeichert wurde.

Ändern Sie danach den LSTM-Teil des Codes in GRU. Das Gewicht wird von 4 auf 3 reduziert, so zum Beispiel "(D, 3 *) in" gru_Wx1 = (rn (D, 3 * H) / np.sqrt (D)). Astype ("f") " Vergessen Sie nicht, den Teil in Bezug auf die Anzahl der Gewichte wie H) `zu ändern.

Speichern Sie diesen Code im Ordner ch09 als better_rnnlm_gru.py.

5. Code lernen

Ändern Sie basierend auf dem Lerncode in Kapitel 6 das von better_rnnlm import BetterRnnlm am Anfang in von better_rnnlm_gru import BetterRnnlm und speichern Sie es im Ordner ch09 mit dem Dateinamen train_better_rnnlm.py.

Wenn ich es mit dem Hyperparameter lr = 20 ausführte, gab es in den frühen Stadien große Unterschiede in der Perplixität, also habe ich es in lr = 10 geändert und es erneut ausgeführt.

import sys
sys.path.append('..')
from common import config
#Wenn Sie mit einer GPU arbeiten, löschen Sie den folgenden Kommentar (Cupy erforderlich).
# ==============================================
config.GPU = True
# ==============================================
from common.optimizer import SGD
from common.trainer import RnnlmTrainer
from common.util import eval_perplexity, to_gpu
from dataset import ptb
from better_rnnlm_gru import BetterRnnlm  #Veränderung

#Hyper-Parametereinstellungen
batch_size = 20
wordvec_size = 650
hidden_size = 650
time_size = 35
lr = 10  
max_epoch = 40  
max_grad = 0.25
dropout = 0.5

#Trainingsdaten lesen
corpus, word_to_id, id_to_word = ptb.load_data('train')
corpus_val, _, _ = ptb.load_data('val')
corpus_test, _, _ = ptb.load_data('test')

if config.GPU:
    corpus = to_gpu(corpus)
    corpus_val = to_gpu(corpus_val)
    corpus_test = to_gpu(corpus_test)

vocab_size = len(word_to_id)
xs = corpus[:-1]
ts = corpus[1:]

model = BetterRnnlm(vocab_size, wordvec_size, hidden_size, dropout)
optimizer = SGD(lr)
trainer = RnnlmTrainer(model, optimizer)

best_ppl = float('inf')
for epoch in range(max_epoch):
    trainer.fit(xs, ts, max_epoch=1, batch_size=batch_size,
                time_size=time_size, max_grad=max_grad)

    model.reset_state()
    ppl = eval_perplexity(model, corpus_val)
    print('valid perplexity: ', ppl)

    if best_ppl > ppl:
        best_ppl = ppl
        model.save_params()
    else:
        lr /= 4.0
        optimizer.lr = lr

    model.reset_state()
    print('-' * 50)


#Auswertung mit Testdaten
model.reset_state()
ppl_test = eval_perplexity(model, corpus_test)
print('test perplexity: ', ppl_test)

result.PNG Die Testverwirrung des LSTM-Modells in Kapitel 6 lag in den hohen 70er Jahren, aber das GRU-Modell scheint in den niedrigen 80er Jahren zu bleiben. Für einen langen Korpus von über 900.000 Wörtern wie diesen Datensatz scheint das LSTM-Modell mit Speicherzellen vorteilhafter zu sein.

Recommended Posts

Tiefes Lernen / Tiefes Lernen von Grund auf 2-Versuchen Sie, GRU zu bewegen
Deep Learning von Grund auf neu
Deep Learning von Grund auf 1-3 Kapitel
Tiefes Lernen von Grund auf neu (Kostenberechnung)
Deep Learning Memo von Grund auf neu gemacht
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 7]
Tiefes Lernen von Grund auf neu (Vorwärtsausbreitung)
Deep Learning / Deep Learning von Grund auf neu Kapitel 6 Memo
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 5]
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 6]
"Deep Learning von Grund auf neu" mit Haskell (unvollendet)
Deep Learning / Deep Learning von Grund auf neu Kapitel 7 Memo
[Windows 10] Aufbau einer "Deep Learning from Scratch" -Umgebung
Lernbericht über das Lesen von "Deep Learning von Grund auf neu"
[Deep Learning von Grund auf neu] Über die Optimierung von Hyperparametern
"Deep Learning from Grund" Memo zum Selbststudium (Teil 12) Deep Learning
[Lernnotiz] Deep Learning von Grund auf neu gemacht [~ Kapitel 4]
Selbststudien-Memo "Deep Learning from Grund" (Nr. 13) Verwenden Sie Google Colaboratory
Selbststudien-Memo "Deep Learning from Grund" (unlesbares Glossar)
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 9) MultiLayerNet-Klasse
Deep Learning von Grund auf neu ① Kapitel 6 "Lerntechniken"
GitHub des guten Buches "Deep Learning von Grund auf neu"
Deep Learning von Grund auf neu Kapitel 2 Perceptron (Memo lesen)
[Lernnotiz] Deep Learning von Grund auf ~ Implementierung von Dropout ~
Python vs Ruby "Deep Learning von Grund auf neu" Zusammenfassung
"Deep Learning from Grund" Memo zum Selbststudium (10) MultiLayerNet-Klasse
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 11) CNN
[Deep Learning von Grund auf neu] Ich habe die Affine-Ebene implementiert
Versuchen Sie es mit TensorFlow
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 19) Datenerweiterung
Deep Learning / LSTM Scratch Code
Versuchen Sie Deep Learning mit FPGA
Anwendung von Deep Learning 2 von Grund auf neu Spam-Filter
Ich habe versucht, Dropout zu erklären
[Deep Learning von Grund auf neu] Implementierung der Momentum-Methode und der AdaGrad-Methode
Versuchen Sie, ein Deep Learning / Neuronales Netzwerk mit Scratch aufzubauen
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 5
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 2
Erstellen Sie mit Docker eine Umgebung für "Deep Learning von Grund auf neu"
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 3
Ein Amateur stolperte in Deep Learning von Grund auf neu. Hinweis: Kapitel 7
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 5
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 1
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 4
Selbststudien-Memo "Deep Learning from Grund" (Nr. 18) Eins! Miau! Grad-CAM!
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 4
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 2
Ich habe versucht, Perceptron Teil 1 [Deep Learning von Grund auf neu] zu implementieren.
Selbststudien-Memo "Deep Learning from Grund" (Nr. 15) TensorFlow-Anfänger-Tutorial
Deep Learning / Deep Learning von Grund auf neu 2 Kapitel 4 Memo
Probieren Sie Deep Learning mit FPGA-Select-Gurken aus
Deep Learning / Deep Learning von Grund auf neu Kapitel 3 Memo
Versuchen Sie es mit TensorFlow Part 2
Probieren Sie das Regressionsmodell von sklearn von Grund auf aus
Deep Learning / Deep Learning von Null 2 Kapitel 7 Memo
Deep Learning / Deep Learning von Null 2 Kapitel 8 Memo
Probieren Sie das offizielle Django-Tutorial von Anfang an aus