[PYTHON] Apprenez Zundokokiyoshi en utilisant un simple RNN

Il y a déjà une personne qui implémente Zundokokiyoshi en utilisant LSTM, mais je pense que vous devriez pouvoir apprendre avec juste un simple RNN à ce niveau. , J'ai essayé de l'implémenter avec Chainer pour comprendre RNN.

Selon l'article 7.5 du livre "Deep Learning (Machine Learning Professional Series)" (ISBN-13: 978-4061529021), RNN se souvient Vous pouvez le faire pendant les 10 dernières heures. Vous n'avez qu'à vous souvenir du modèle dans lequel "Zun" apparaît quatre fois et "Doko" apparaît une fois, il doit donc être dans cette plage.

Façon de penser

J'ai pensé à une structure aussi simple que possible. Entrez deux ($ x_1, x_2 ). Il a une couche intermédiaire et le nombre d'unités est de 10. Il existe également deux sorties ( y_1, y_2 $). L'entrée est définie comme suit.

Bouse → x_0 = 0, x_1 = 1 \\
Doco → x_0 = 1, x_1 = 0

La sortie est définie comme suit: Disons que c'est un simple problème de classification.

Kiyoshi a échoué → y_0 = 1, y_1 = 0 \\
Kiyoshi établi → y_0 = 0, y_1 = 1

Définition du modèle

La définition du code est la suivante.

import numpy as np
import chainer
from chainer import cuda, Function, gradient_check, Variable, optimizers, serializers, utils
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L

class RNN(chainer.Chain):
    def __init__(self):
        super(RNN, self).__init__(
            w1 = L.Linear(2, 10),
            h1 = L.Linear(10, 10),
            o = L.Linear(10, 2)
            )
    def reset_state(self):
        self.last_z = chainer.Variable(np.zeros((1,10), dtype=np.float32))
    def __call__(self, x):
        z = F.relu(self.w1(x) + self.h1(self.last_z))
        self.last_z = z
        y = F.relu(self.o(z))
        return y

rnn = RNN()
rnn.reset_state()
model = L.Classifier(rnn)
optimizer = optimizers.Adam() #Utilisez Adam
optimizer.setup(model)
optimizer.add_hook(chainer.optimizer.GradientClipping(10.0)) #Définir la limite supérieure du dégradé

En supposant un mini-lot avec une taille fixe de 1, la valeur initiale (last_z) est zéro. Tout en maintenant la valeur de la couche cachée une fois calculée dans self.last_z, le résultat est donné à la couche de sortie.

Apprentissage

Générez des données de série modérément aléatoires et entraînez-les.

ans_zundoko = [0, 0, 0, 0, 1] #Bonne séquence de réponses
src_x_ary = [0, 0, 0, 1] #Rendre le taux d'apparition du tableau de génération de nombres aléatoires 0 supérieur à 1

def zd_gen(): #Générateur
    x_ary = [0, 0, 0, 0, 1]
    y_ary = [0, 0, 0, 0, 1]
    while True:
        x = x_ary.pop(0)
        y = y_ary.pop(0)
        x = [0, 1] if x == 0 else [1, 0]
        yield x, y
        new_x = src_x_ary[np.random.randint(0, 4)] #0 si 0 à 2,Si c'est 3, ce sera 1.
        x_ary.append(new_x)
        y_ary.append(1 if x_ary == ans_zundoko else 0) # x_ary est[0, 0, 0, 0, 1]Seulement quand

bprop_len = 40 #Heure limite BPPT
iter = 300 * 100 * 2 #Nombre d'apprentissage
loss = 0
i = 0
for xx, yy in zd_gen():
    x = chainer.Variable(np.asarray([xx], dtype=np.float32))
    t = chainer.Variable(np.asarray([yy], dtype=np.int32))
    loss += model(x, t)
    i += 1
    if i % bprop_len == 0:
        model.zerograds()
        loss.backward()
        loss.unchain_backward()
        optimizer.update()
        print("iter %d, loss %f, x %d, y %d" % (i, loss.data, xx[0], yy))
        loss = 0
    if i > iter:
        break

L'apprentissage prend du temps et peut ou non fonctionner selon les valeurs initiales. Cela ne fonctionnera pas correctement à moins que la perte ne tombe finalement en dessous de 0,1.

Essayez de changer l'optimiseur en SGD ou de changer bprop_len et les résultats changeront. La valeur définie ici utilise le cas qui s'est en quelque sorte bien déroulé.

Évaluation

Évaluez le modèle entraîné. Vous pouvez générer les colonnes d'entrée de manière aléatoire, mais par souci de simplicité, j'ai préparé des données d'évaluation statiques.

#Dung Dung Dung Dung Doko Doko Dung
x_data = [[0,1], [0,1], [0,1], [0,1], [1,0], [1,0], [1,0], [0,1]]

rnn.reset_state()
for xx in x_data:
    print('Bouse' if xx[1] == 1 else 'Doco')
    x = chainer.Variable(np.asarray([xx], dtype=np.float32))
    y = model.predictor(x)
    z = F.softmax(y, use_cudnn=False)
    if z.data[0].argmax() == 1: #Kiyoshi est établi lorsque l'indice du tableau avec la valeur la plus élevée est 1.
        print('Kiyoshi')

Sortie de référence

La sortie quand elle va bien est affichée pour référence.

iter 59520, loss 0.037670, x 1, y 0
iter 59560, loss 0.051628, x 0, y 0
iter 59600, loss 0.037519, x 0, y 0
iter 59640, loss 0.041894, x 0, y 0
iter 59680, loss 0.059143, x 0, y 0
iter 59720, loss 0.062305, x 0, y 0
iter 59760, loss 0.055293, x 0, y 0
iter 59800, loss 0.060964, x 1, y 1
iter 59840, loss 0.057446, x 1, y 0
iter 59880, loss 0.034730, x 1, y 0
iter 59920, loss 0.054435, x 0, y 0
iter 59960, loss 0.039648, x 0, y 0
iter 60000, loss 0.036578, x 0, y 0
Bouse
Bouse
Bouse
Bouse
Doco
Kiyoshi
Doco
Doco
Bouse

Impressions

Je sens que je peux enfin comprendre le RNN que je n'ai pas complètement digéré. Au début, le nombre d'unités dans la couche intermédiaire était petit et le temps de coupure BPTT était trop court, donc cela n'a pas fonctionné comme prévu, mais après avoir fait divers ajustements, cela a finalement commencé à fonctionner. Il existe de nombreux exemples d'utilisation de LSTM et d'expressions d'insertion de mots dans le monde, mais j'ai décidé de l'essayer avec un problème plus minimisé, et je suis heureux de l'avoir finalement réalisé.

Cependant, c'est un problème qu'il y ait encore une partie qui dépend de la chance.

Recommended Posts

Apprenez Zundokokiyoshi en utilisant un simple RNN
Créer un tableau simple à l'aide de prettytable
Une histoire sur l'apprentissage automatique simple avec TensorFlow
Exemple pour dessiner une horloge simple en utilisant ebiten
Apprenez Zundokokiyoshi en utilisant LSTM
Créez une application CRUD simple en utilisant la vue de classe générique de Django
J'ai créé un éditeur de texte simple en utilisant PyQt
Apprenez librosa avec un tutoriel 1
Un simple exemple de pivot_table.
Mesure du temps à l'aide d'une horloge
Créer un serveur REST (simple)
# 1 [python3] Calcul simple à l'aide de variables
Tutoriel Pepper (5): Utilisation d'une tablette
Utiliser une imprimante avec Debian 10
Créer un serveur textlint simple
Un mémorandum d'utilisation de eigen3
Fabriquez un incubateur à CO2 simple à l'aide de Raspberry PI et d'un capteur de CO2 (MH-Z14A)
Créez un lot planifié simple à l'aide de l'image Python de Docker et de parse-crontab
J'ai fait apprendre à RNN l'onde de péché et j'ai essayé de prédire: l'ajustement des paramètres hyper
Évaluer les performances d'un modèle de régression simple à l'aide de la validation d'intersection LeaveOneOut