[PYTHON] Deep Learning / Deep Learning à partir de Zero 2 Chapitre 6 Mémo

1.Tout d'abord

Je lis un chef-d'œuvre, ** "Deep Learning from Zero 2" **. Cette fois, c'est un mémo du chapitre 6. Pour exécuter le code, téléchargez le code complet depuis Github et utilisez le notebook jupyter dans ch06.

2. Modèle LSTM

Le code pour ch06 / train_rnnlm.py qui apprend l'ordre des mots de l'ensemble de données PTB. Dans le modèle RNN simple du chapitre 5, nous n'avons appris que les 1 000 premiers mots de l'ensemble de données d'apprentissage, mais cette fois, nous en apprendrons environ 900 000 pour l'ensemble de l'ensemble de données d'entraînement.

Exécutez avec time_size = 35, batch_size = 20, word_size = hidden_size = 100, max_eopch = 4.

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

#Paramètres des hyper paramètres
batch_size = 20
wordvec_size = 100
hidden_size = 100  #Nombre d'éléments dans le vecteur d'état caché de RNN
time_size = 35  #La taille pour déployer RNN
lr = 20.0
max_epoch = 4
max_grad = 0.25

#Lecture des données d'entraînement
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:]

#Génération de modèle
model = Rnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)
trainer = RnnlmTrainer(model, optimizer)

#Apprenez en appliquant un découpage en dégradé
trainer.fit(xs, ts, max_epoch, batch_size, time_size, max_grad,
            eval_interval=20)
trainer.plot(ylim=(0, 500))

#Évaluer avec des données de test
model.reset_state()
ppl_test = eval_perplexity(model, corpus_test)
print('test perplexity: ', ppl_test)

#Enregistrer les paramètres
model.save_params()

スクリーンショット 2020-05-22 19.20.28.png

Il a fallu 33 minutes pour terminer sur mon Macbook Air. La perplexité après 4 époques était de 111,47 et la perplexité selon les données de test était de 136,3. Si vous prédisez le mot suivant, vous avez 136 choix. Les conditions sont complètement différentes de ce que j'ai fait au chapitre 5, donc je ne peux pas le comparer, mais c'est plus grand que ce à quoi je m'attendais.

Dans le modèle RNN simple du chapitre 5, une explosion de gradient est susceptible de se produire, donc dans le modèle LSTM, afin de la supprimer, la classe d'entraînement est utilisée.Détourage de dégradé( -threshold < ||\hat{g}|| <seuil)Les fonctionnalités sont incluses.   3.Rnnlm

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

        #Initialisation du poids
        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')

        #Génération de couches
        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]

        #Lister tous les poids et dégradés
        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()

スクリーンショット 2020-05-22 13.31.47.png La structure de couche de ** Rnnlm ** remplace simplement la couche Time RNN du modèle du chapitre 5 par la couche Time LSTM. Avant de regarder Time LSTM, examinons d'abord la couche LSTM utilisée ici.

4. Couche LSTM

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

スクリーンショット 2020-05-22 16.33.56.png

Il s'agit de la partie propagation directe de la ** couche LSTM **. Étant donné que les formules pour le stockage des cellules et la propagation vers l'avant des trois portes ont toutes la même forme, les poids $ W_x, W_h, b $ calculent A ensemble, puis coupent A pour l'activer. Il est efficace de passer la fonction.

    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

スクリーンショット 2020-05-22 13.16.09.png Pour la rétropropagation, dA peut être obtenu en trouvant respectivement df, dg, di et do et en les connectant avec hstach. Après cela, puisqu'il s'agit de la propagation arrière de MatMul, dWx, dWh, db, dx, dh_prev peuvent être obtenus.

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

スクリーンショット 2020-05-22 18.06.23.png

C'est fondamentalement la même chose que pour TimeRNN. ** La couche Time LSTM ** est un réseau qui relie les couches T LSTM. Vous permet d'ajuster s'il faut hériter de l'état h entre les blocs avec l'argument avec état. Il y a aussi la cellule C pour stocker la mémoire.

Pour la propagation vers l'avant, préparez d'abord un conteneur hs (N, T, H) pour la sortie. Ensuite, tout en tournant la boucle for, les t-ièmes données sont coupées par xs [:, t ,:] et entrées dans le LSTM normal, et la sortie est à la position spécifiée du conteneur préparé par hs [:, t ,:]. Au fur et à mesure que nous le stockons, nous enregistrerons les couches par couches.

    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

La propagation vers l'avant TimeLSTM a trois sorties, donc dans le cas de la propagation arrière, $ dh_t + dh_ {next} + dc_ {next} $, qui est la somme de ces trois, est entrée.

Tout d'abord, créez un conteneur dxs qui coule en aval, trouvez le gradient dx à chaque fois avec backward () de la couche LSTM dans l'ordre inverse de la propagation directe, et remplacez-le par l'indice correspondant de dxs. Le paramètre poids ajoute les gradients de poids pour chaque calque et écrase le résultat final dans self.grads.

6. Modèle LSTM amélioré

Maintenant, exécutons le modèle amélioré train_better_rnnlm.py. Comme prévu, c'est difficile avec le processeur seul, j'ai donc installé cupy sur ma machine Windows.

Exécutez avec time_size = 35, batch_size = 20, ** word_size = hidden_size = 650, max_epoch = 40 **.

import sys
sys.path.append('..')
from common import config
#Lors de l'exécution sur GPU, supprimez le commentaire ci-dessous (cupy requis)
# ==============================================
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

#Paramètres des hyper paramètres
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

#Lecture des données d'entraînement
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)

#Évaluation avec données de test
model.reset_state()
ppl_test = eval_perplexity(model, corpus_test)
print('test perplexity: ', ppl_test)

キャプチャ2.PNG Il a été réalisé en moins de 3 heures sur la machine Windows (GTX1060). L'évaluation par les données de test après 40eopch s'est améliorée jusqu'à la perplexité = 76-79.

Dans ce code, la perplexité des données de test est calculée pour chaque epch, et le coefficient d'apprentissage lr n'est abaissé que lorsque la valeur se détériore. Jetons un coup d'œil à 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()

スクリーンショット 2020-05-23 16.17.02.png Structure de couche BetterRnnlm. Il existe trois fonctionnalités: couche LSTM multicouche (2), utilisation de Dropout (2) et partage de poids de Time Embedding et Time Affine.

Tout en augmentant l'expressivité en superposant LSTM, Time Dropout (le contenu est fondamentalement le même que Dropout) est inclus pour supprimer le surapprentissage. Pour le partage de poids, le poids de la couche Time Embeddinh est (V, H) et le poids de la couche Affine est (H, v). , Supprime le surapprentissage et facilite l'apprentissage.

Recommended Posts

Deep learning / Deep learning from scratch 2 Chapitre 4 Mémo
Deep learning / Deep learning made from scratch Chapitre 3 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 5 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 7 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 8 Mémo
Deep learning / Deep learning made from scratch Chapitre 4 Mémo
Deep learning / Deep learning from scratch 2 Chapitre 3 Mémo
Deep Learning / Deep Learning à partir de Zero 2 Chapitre 6 Mémo
[Mémo d'apprentissage] Le Deep Learning fait de zéro [Chapitre 7]
Deep learning / Deep learning made from scratch Chapitre 6 Mémo
[Mémo d'apprentissage] Deep Learning fait de zéro [Chapitre 5]
Deep learning / Deep learning made from scratch Chapitre 7 Mémo
[Mémo d'apprentissage] Deep Learning fait de zéro [~ Chapitre 4]
Deep Learning from scratch Chapter 2 Perceptron (lecture du mémo)
Apprentissage amélioré pour apprendre de zéro à profond
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 12) Deep learning
Apprentissage profond à partir de zéro
Mémo d'auto-apprentissage "Deep Learning from scratch" (glossaire illisible)
"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 9) Classe MultiLayerNet
[Mémo d'apprentissage] Apprentissage profond à partir de zéro ~ Mise en œuvre de l'abandon ~
Mémo d'auto-apprentissage «Deep Learning from scratch» (10) Classe MultiLayerNet
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 11) CNN
Apprentissage profond à partir de zéro 1 à 3 chapitres
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 19) Augmentation des données
Mémo d'apprentissage Python pour l'apprentissage automatique par Chainer du chapitre 2
Deep Learning 2 from scratch 1.3 Traitement du langage naturel 1.3 Résumé
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 1
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 5
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 2
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 3
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 7
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 5
Un amateur a trébuché dans le Deep Learning ❷ fait de zéro Note: Chapitre 1
Un amateur a trébuché dans le Deep Learning ❷ fait à partir de zéro Note: Chapitre 4
Un amateur a trébuché dans le Deep Learning à partir de zéro.
Mémo d'apprentissage profond créé à partir de zéro
Un amateur a trébuché dans le Deep Learning à partir de zéro Note: Chapitre 2
Mémo d'auto-apprentissage "Deep Learning from scratch" (n ° 15) Tutoriel pour débutants TensorFlow
Tutoriel d'apprentissage en profondeur de la construction d'environnement
"Deep Learning from scratch" Mémo d'auto-apprentissage (n ° 14) Exécutez le programme du chapitre 4 sur Google Colaboratory
Mémo d'auto-apprentissage "Deep Learning from scratch" (partie 8) J'ai dessiné le graphique du chapitre 6 avec matplotlib
Mémo d'auto-apprentissage "Deep Learning from scratch" (n ° 13) Essayez d'utiliser Google Colaboratory
Mémo d'auto-apprentissage «Deep Learning from scratch» (n ° 10-2) Valeur initiale du poids
Apprentissage profond à partir de zéro (propagation vers l'avant)
Apprentissage profond / Apprentissage profond à partir de zéro 2-Essayez de déplacer GRU
Alignement d'image: du SIFT au deep learning
[Windows 10] Construction de l'environnement "Deep Learning from scratch"
Enregistrement d'apprentissage de la lecture "Deep Learning from scratch"
[Deep Learning from scratch] À propos de l'optimisation des hyper paramètres
Apprentissage profond à partir des bases mathématiques (pendant la fréquentation)
Note d'étude LPIC201
Mémo d'apprentissage Django
Mémorandum d'apprentissage profond
Commencer l'apprentissage en profondeur
Python vs Ruby «Deep Learning from scratch» Chapitre 2 Circuit logique par Perceptron
Apprentissage en profondeur Python
Apprentissage profond × Python