Ich lese ein Meisterwerk, ** "Deep Learning from Zero 2" **. Diesmal ist ein Memo von Kapitel 6. Um den Code auszuführen, laden Sie den gesamten Code von Github herunter und verwenden Sie das Jupiter-Notizbuch in Kapitel 06.
Der Code für ch06 / train_rnnlm.py, der die Wortreihenfolge des PTB-Datensatzes lernt. Im einfachen RNN-Modell in Kapitel 5 haben wir nur die ersten 1000 Wörter des Trainingsdatensatzes gelernt, aber diesmal lernen wir ungefähr 900.000 Wörter für den gesamten Trainingsdatensatz.
Führen Sie mit time_size = 35, batch_size = 20, word_size = hidden_size = 100, max_eopch = 4 aus.
import sys
sys.path.append('..')
from common.optimizer import SGD
from common.trainer import RnnlmTrainer
from common.util import eval_perplexity
from dataset import ptb
from rnnlm import Rnnlm
#Hyper-Parametereinstellungen
batch_size = 20
wordvec_size = 100
hidden_size = 100 #Anzahl der Elemente im verborgenen Zustandsvektor von RNN
time_size = 35 #Die Größe für die Bereitstellung von RNN
lr = 20.0
max_epoch = 4
max_grad = 0.25
#Trainingsdaten lesen
corpus, word_to_id, id_to_word = ptb.load_data('train')
corpus_test, _, _ = ptb.load_data('test')
vocab_size = len(word_to_id)
xs = corpus[:-1]
ts = corpus[1:]
#Modellgenerierung
model = Rnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)
trainer = RnnlmTrainer(model, optimizer)
#Lernen Sie, indem Sie Gradientenausschnitte anwenden
trainer.fit(xs, ts, max_epoch, batch_size, time_size, max_grad,
eval_interval=20)
trainer.plot(ylim=(0, 500))
#Mit Testdaten auswerten
model.reset_state()
ppl_test = eval_perplexity(model, corpus_test)
print('test perplexity: ', ppl_test)
#Parameter speichern
model.save_params()
Es dauerte 33 Minuten, bis mein Macbook Air fertig war. Die Ratlosigkeit nach 4 Epochen betrug 111,47 und die Ratlosigkeit gemäß den Testdaten betrug 136,3. Wenn Sie das nächste Wort vorhersagen, haben Sie 136 Möglichkeiten. Die Bedingungen sind völlig anders als in Kapitel 5, daher kann ich sie nicht vergleichen, aber sie sind größer als erwartet.
Im einfachen RNN-Modell von Kapitel 5 tritt wahrscheinlich eine Gradientenexplosion auf. Im LSTM-Modell wird daher die Trainerklasse verwendet, um dies zu unterdrücken.Gradientenausschnitt( -threshold < ||
import sys
sys.path.append('..')
from common.time_layers import *
from common.base_model import BaseModel
class Rnnlm(BaseModel):
def __init__(self, vocab_size=10000, wordvec_size=100, hidden_size=100):
V, D, H = vocab_size, wordvec_size, hidden_size
rn = np.random.randn
#Gewichtsinitialisierung
embed_W = (rn(V, D) / 100).astype('f')
lstm_Wx = (rn(D, 4 * H) / np.sqrt(D)).astype('f')
lstm_Wh = (rn(H, 4 * H) / np.sqrt(H)).astype('f')
lstm_b = np.zeros(4 * H).astype('f')
affine_W = (rn(H, V) / np.sqrt(H)).astype('f')
affine_b = np.zeros(V).astype('f')
#Schichterzeugung
self.layers = [
TimeEmbedding(embed_W),
TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=True),
TimeAffine(affine_W, affine_b)
]
self.loss_layer = TimeSoftmaxWithLoss()
self.lstm_layer = self.layers[1]
#Listen Sie alle Gewichte und Verläufe auf
self.params, self.grads = [], []
for layer in self.layers:
self.params += layer.params
self.grads += layer.grads
def predict(self, xs):
for layer in self.layers:
xs = layer.forward(xs)
return xs
def forward(self, xs, ts):
score = self.predict(xs)
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):
self.lstm_layer.reset_state()
Die Schichtstruktur von ** Rnnlm ** ersetzt einfach die Zeit-RNN-Schicht des Modells in Kapitel 5 durch die Zeit-LSTM-Schicht. Bevor wir uns Time LSTM ansehen, schauen wir uns zunächst die dort verwendete LSTM-Ebene an.
class LSTM:
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, c_prev):
Wx, Wh, b = self.params
N, H = h_prev.shape
A = np.dot(x, Wx) + np.dot(h_prev, Wh) + b
f = A[:, :H]
g = A[:, H:2*H]
i = A[:, 2*H:3*H]
o = A[:, 3*H:]
f = sigmoid(f)
g = np.tanh(g)
i = sigmoid(i)
o = sigmoid(o)
c_next = f * c_prev + g * i
h_next = o * np.tanh(c_next)
self.cache = (x, h_prev, c_prev, i, f, g, o, c_next)
return h_next, c_next
Dies ist der Vorwärtsausbreitungsteil der ** LSTM-Schicht **. Da die Formeln für die Zellspeicherung und die Vorwärtsausbreitung der drei Gates alle dieselbe Form haben, berechnen die Gewichte $ W_x, W_h und b $ A zusammen und schneiden dann A, um es zu aktivieren. Es ist effizient, die Funktion zu übergeben.
def backward(self, dh_next, dc_next):
Wx, Wh, b = self.params
x, h_prev, c_prev, i, f, g, o, c_next = self.cache
tanh_c_next = np.tanh(c_next)
ds = dc_next + (dh_next * o) * (1 - tanh_c_next ** 2)
dc_prev = ds * f
di = ds * g
df = ds * c_prev
do = dh_next * tanh_c_next
dg = ds * i
di *= i * (1 - i)
df *= f * (1 - f)
do *= o * (1 - o)
dg *= (1 - g ** 2)
dA = np.hstack((df, dg, di, do))
dWh = np.dot(h_prev.T, dA)
dWx = np.dot(x.T, dA)
db = dA.sum(axis=0)
self.grads[0][...] = dWx
self.grads[1][...] = dWh
self.grads[2][...] = db
dx = np.dot(dA, Wx.T)
dh_prev = np.dot(dA, Wh.T)
return dx, dh_prev, dc_prev
Für die Rückausbreitung kann dA erhalten werden, indem df, dg, di und do gefunden und mit hstach verbunden werden. Danach kann, da es sich um die Rückausbreitung von MatMul handelt, dWx, dWh, db, dx, dh_prev erhalten werden.
5.TimeLSTM
class TimeLSTM:
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.c = None, None
self.dh = None
self.stateful = stateful
def forward(self, xs):
Wx, Wh, b = self.params
N, T, D = xs.shape
H = Wh.shape[0]
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')
if not self.stateful or self.c is None:
self.c = np.zeros((N, H), dtype='f')
for t in range(T):
layer = LSTM(*self.params)
self.h, self.c = layer.forward(xs[:, t, :], self.h, self.c)
hs[:, t, :] = self.h
self.layers.append(layer)
return hs
Es ist im Grunde das gleiche wie für TimeRNN. ** Time LSTM Layer ** ist ein Netzwerk, das T LSTM Layer verbindet. Hier können Sie anpassen, ob der Status h zwischen Blöcken mit dem Argument stateful geerbt werden soll. Es gibt auch Zelle C zum Speichern des Speichers.
Bereiten Sie für die Vorwärtsausbreitung zunächst einen Container hs (N, T, H) für die Ausgabe vor. Während die for-Schleife gedreht wird, werden die t-ten Daten durch xs [:, t,:] ausgeschnitten und in das normale LSTM eingegeben, und die Ausgabe befindet sich an der angegebenen Position des von hs [:, t,:] vorbereiteten Containers. Während wir es speichern, registrieren wir Ebenen in Ebenen.
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, dc = 0, 0
grads = [0, 0, 0]
for t in reversed(range(T)):
layer = self.layers[t]
dx, dh, dc = layer.backward(dhs[:, t, :] + dh, dc)
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, c=None):
self.h, self.c = h, c
def reset_state(self):
self.h, self.c = None, None
Die TimeLSTM-Vorwärtsausbreitung hat drei Ausgänge. Bei der Rückwärtsausbreitung wird also $ dh_t + dh_ {next} + dc_ {next} $, die Summe dieser drei, eingegeben.
Erstellen Sie zunächst einen Container dxs, der stromabwärts fließt, ermitteln Sie den Gradienten dx jedes Mal mit backward () der LSTM-Schicht in umgekehrter Reihenfolge der Vorwärtsausbreitung und ersetzen Sie ihn durch den entsprechenden Index von dxs. Der Parameter weight fügt die Gewichtsverläufe für jede Ebene hinzu und überschreibt das Endergebnis in self.grads.
Lassen Sie uns nun das verbesserte Modell train_better_rnnlm.py ausführen. Wie erwartet ist dies mit der CPU allein schwierig, daher habe ich cupy auf meinem Windows-Computer installiert.
Führen Sie mit time_size = 35, batch_size = 20, ** word_size = hidden_size = 650, max_epoch = 40 ** aus.
import sys
sys.path.append('..')
from common import config
#Löschen Sie bei der Ausführung auf einer GPU 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 import BetterRnnlm
#Hyper-Parametereinstellungen
batch_size = 20
wordvec_size = 650
hidden_size = 650
time_size = 35
lr = 20.0
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)
Es wurde in weniger als 3 Stunden auf der Windows-Maschine (GTX1060) abgeschlossen. Die Auswertung durch Testdaten nach 40 Personen hat sich auf Ratlosigkeit = 76-79 verbessert.
In diesem Code wird die Ratlosigkeit der Testdaten für jede Epch berechnet und der Lernkoeffizient lr wird nur verringert, wenn sich der Wert verschlechtert. Werfen wir einen Blick auf Better Rnnlm.
7.BetterRnnlm
import sys
sys.path.append('..')
from common.time_layers import *
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')
lstm_Wx1 = (rn(D, 4 * H) / np.sqrt(D)).astype('f')
lstm_Wh1 = (rn(H, 4 * H) / np.sqrt(H)).astype('f')
lstm_b1 = np.zeros(4 * H).astype('f')
lstm_Wx2 = (rn(H, 4 * H) / np.sqrt(H)).astype('f')
lstm_Wh2 = (rn(H, 4 * H) / np.sqrt(H)).astype('f')
lstm_b2 = np.zeros(4 * H).astype('f')
affine_b = np.zeros(V).astype('f')
self.layers = [
TimeEmbedding(embed_W),
TimeDropout(dropout_ratio),
TimeLSTM(lstm_Wx1, lstm_Wh1, lstm_b1, stateful=True),
TimeDropout(dropout_ratio),
TimeLSTM(lstm_Wx2, lstm_Wh2, lstm_b2, stateful=True),
TimeDropout(dropout_ratio),
TimeAffine(embed_W.T, affine_b) # weight tying!!
]
self.loss_layer = TimeSoftmaxWithLoss()
self.lstm_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.lstm_layers:
layer.reset_state()
BetterRnnlm Schichtstruktur. Es gibt drei Funktionen: mehrschichtige LSTM-Schicht (2), Verwendung von Dropout (2) und Gewichtsverteilung von Time Embedding und Time Affine.
Während die Ausdruckskraft durch Überlagerung von LSTM erhöht wird, ist Time Dropout (der Inhalt entspricht im Wesentlichen dem von Dropout) enthalten, um das Überlernen zu unterdrücken. Für die Gewichtsverteilung sind die Gewichte der Time Embeddinh-Schicht (V, H) und die Gewichte der Affine-Schicht (H, v), sodass die Trainingsparameter reduziert werden, indem die Gewichte der Time Embedding-Schicht als Gewichte der Affine-Schicht verwendet werden. Unterdrückt Überlernen und erleichtert das Lernen.
Recommended Posts