[PYTHON] Etude du réseau neuronal récurrent (RNN) par Chainer ~ Vérification de l'exactitude des nombres aléatoires dans Excel et R ~

Aperçu

J'aimerais commencer à étudier le réseau neuronal récurrent (RNN), que je ne pouvais pas me permettre. Fondamentalement, il s'agit d'une forme de prédiction utilisant un traitement récursif utilisant LSTM, mais il y a des choses qui sont encore proches de la copie, donc je ne le comprends pas complètement, alors veuillez signaler toute erreur, etc. Je te veux.

Destination référencée

Pour un exemple d'implémentation de RNN + LSTM dans Chainer, j'ai fait référence à [Sample] de Chainer (https://github.com/yusuketomoto/chainer-char-rnn). Cependant, tel quel, il ne rentre pas dans le cadre que j'ai créé jusqu'à présent, alors j'ai fait un peu de traitement. L'explication du code ci-dessus a été détaillée dans ici. J'ai également une compréhension approximative de RNN et LSTM dans le récent livre.

Problème de réglage

Ce n'est pas intéressant de déplacer l'échantillon tel quel, alors posons le problème.

Comme cela a été dit dans la pratique, la période des nombres aléatoires créée par la fonction Rand () d'Excel est de 2 $ ^ {24} $, et on sait que la période est plus courte que les autres nombres aléatoires. Par conséquent, si vous utilisez des nombres aléatoires Excel à Monte Carlo, les résultats seront biaisés. (Eh bien, cela peut être bon si le nombre d'échantillons est petit)

D'autre part, le nombre aléatoire de R est un nombre aléatoire avec une très longue période ($ 2 ^ {19937} $) créé par un algorithme appelé [Mersenne Twister](https://ja.wikipedia.org/wiki/Mersenne Twister). Je suis.

En d'autres termes, s'il y a périodicité, il peut être possible de prédire la valeur de nombre aléatoire suivante en effectuant un apprentissage séquentiel en utilisant RNN. Telle est la conscience du problème.

Je ne suis pas un expert des nombres aléatoires, donc je ne suis pas sûr que la méthode d'évaluation soit correcte.

Préparation des données

Tout d'abord, les nombres aléatoires Excel sont calculés à l'aide de la fonction Rand.

=ROUNDDOWN(RAND()*10,0)

Préparez 1001 pièces. Ensuite, vous obtiendrez 10 valeurs entières de 0 à 9.

Ensuite, le nombre aléatoire de R génère un nombre aléatoire uniforme comme suit.

x <- floor(runif(1001)*10);

(1001 est une valeur supplémentaire pour faire des données de réponse la valeur suivante.)

La variable explicative est la valeur aléatoire actuelle (0-9) et les données de l'enseignant sont la valeur aléatoire suivante (0-9).

Implémentation RNN

RNN a été implémenté en utilisant Chainer comme suit. Puisqu'il s'agit d'un processus séquentiel, nous définissons intentionnellement la taille du lot sur 1 et traitons chacun dans l'ordre depuis le début.

Tout d'abord, la classe de base est la suivante.

DL_chainer.py


# -*- coding: utf-8 -*-
from chainer import FunctionSet, Variable, optimizers,serializers
from chainer import functions as F
from chainer import links as L
from sklearn import base
from sklearn.cross_validation import train_test_split
from abc import ABCMeta, abstractmethod
import numpy as np
import six
import math
import cPickle as pickle

class BaseChainerEstimator(base.BaseEstimator):
    __metaclass__= ABCMeta  # python 2.x
    def __init__(self, optimizer=optimizers.MomentumSGD(lr=0.01), n_iter=10000, eps=1e-5, report=100,
                 **params):
        self.report = report
        self.n_iter = n_iter
        self.batch_size = params["batch_size"] if params.has_key("batch_size") else 100
        self.network = self._setup_network(**params)
        self.decay = 1.
        self.optimizer = optimizer
        self.optimizer.setup(self.network.collect_parameters())
        self.eps = eps
        np.random.seed(123)

    @abstractmethod
    def _setup_network(self, **params):
        return FunctionSet(l1=F.Linear(1, 1))

    @abstractmethod
    def forward(self, x,train=True,state=None):
        y = self.network.l1(x)
        return y

    @abstractmethod
    def loss_func(self, y, t):
        return F.mean_squared_error(y, t)

    @abstractmethod
    def output_func(self, h):
        return F.identity(h)
	
	@abstractmethod
    def fit(self, x_data, y_data):
        batchsize = self.batch_size
        N = len(y_data)
        for loop in range(self.n_iter):
            perm = np.random.permutation(N)
            sum_accuracy = 0
            sum_loss = 0
            for i in six.moves.range(0, N, batchsize):
                x_batch = x_data[perm[i:i + batchsize]]
                y_batch = y_data[perm[i:i + batchsize]]
                x = Variable(x_batch)
                y = Variable(y_batch)
                self.optimizer.zero_grads()
                yp = self.forward(x)
                loss = self.loss_func(yp,y)
                loss.backward()
                self.optimizer.update()
                sum_loss += loss.data * len(y_batch)
                sum_accuracy += F.accuracy(yp,y).data * len(y_batch)
            if self.report > 0 and (loop + 1) % self.report == 0:
                print('loop={:d}, train mean loss={:.6f} , train mean accuracy={:.6f}'.format(loop + 1, sum_loss / N,sum_accuracy / N))
            self.optimizer.lr *= self.decay

        return self

    def predict(self, x_data):
        x = Variable(x_data,volatile=True)
        y = self.forward(x,train=False)
        return self.output_func(y).data

    def predict_proba(self, x_data):
        x = Variable(x_data,volatile=True)
        y = self.forward(x,train=False)
        return self.output_func(y).data

    def save_model(self,name):
        with open(name,"wb") as o:
            pickle.dump(self,o)

class ChainerClassifier(BaseChainerEstimator, base.ClassifierMixin):
    def predict(self, x_data):
        return BaseChainerEstimator.predict(self, x_data).argmax(1) 

    def predict_proba(self,x_data):
        return BaseChainerEstimator.predict_proba(self, x_data)

L'implémentation RNN suivante ressemble à ceci:

DL_chainer.py


class RNNTS(ChainerClassifier):
    """
    Recurrent Neurarl Network with LSTM by 1 step
    """
    def _setup_network(self, **params):

        self.input_dim = params["input_dim"]
        self.hidden_dim = params["hidden_dim"]
        self.n_classes = params["n_classes"]
        self.optsize = params["optsize"] if params.has_key("optsize") else 30
        self.batch_size = 1 
        self.dropout_ratio = params["dropout_ratio"] if params.has_key("dropout_ratio") else 0.5

        network = FunctionSet(
            l0 = L.Linear(self.input_dim, self.hidden_dim),
            l1_x = L.Linear(self.hidden_dim, 4*self.hidden_dim),
            l1_h = L.Linear(self.hidden_dim, 4*self.hidden_dim),
            l2_h = L.Linear(self.hidden_dim, 4*self.hidden_dim),
            l2_x = L.Linear(self.hidden_dim, 4*self.hidden_dim),
            l3   = L.Linear(self.hidden_dim, self.n_classes),
        )
        return network

    def forward(self, x, train=True,state=None):
        if state is None:
            state = self.make_initial_state(train)
        h0 = self.network.l0(x)
        h1_in = self.network.l1_x(F.dropout(h0, ratio=self.dropout_ratio, train=train)) + self.network.l1_h(state['h1'])
        c1, h1 = F.lstm(state['c1'], h1_in)
        h2_in = self.network.l2_x(F.dropout(h1, ratio=self.dropout_ratio, train=train)) + self.network.l2_h(state['h2'])
        c2, h2 = F.lstm(state['c2'], h2_in)
        y = self.network.l3(F.dropout(h2, ratio=self.dropout_ratio, train=train))
        state = {'c1': c1, 'h1': h1, 'c2': c2, 'h2': h2}

        return y,state

    def make_initial_state(self,train=True):
        return {name: Variable(np.zeros((self.batch_size, self.hidden_dim), dtype=np.float32),
                volatile=not train)
                for name in ('c1', 'h1', 'c2', 'h2')}

    def fit(self, x_data, y_data):
        batchsize = self.batch_size
        N = len(y_data)
        for loop in range(self.n_iter):
            sum_accuracy = Variable(np.zeros((), dtype=np.float32))
            sum_loss = Variable(np.zeros((), dtype=np.float32))
            state = self.make_initial_state(train=True) #Génération de l'état initial
            for i in six.moves.range(0, N, batchsize):
                x_batch = x_data[i:i + batchsize]
                y_batch = y_data[i:i + batchsize]

                x = Variable(x_batch,volatile=False)
                y = Variable(y_batch,volatile=False)
                yp,state = self.forward(x,train=True,state=state)
                loss = self.loss_func(yp,y)
                accuracy = F.accuracy(yp,y)
                sum_loss += loss
                sum_accuracy += accuracy

                if (i + 1) % self.optsize == 0:
                    self.optimizer.zero_grads()
                    sum_loss.backward()
                    sum_loss.unchain_backward()
                    self.optimizer.clip_grads(5)
                    self.optimizer.update()

            if self.report > 0 and (loop + 1) % self.report == 0:
                print('loop={:d}, train mean loss={:.6f} , train mean accuracy={:.6f}'.format(loop + 1, sum_loss.data / N,sum_accuracy.data / N))
            self.optimizer.lr *= self.decay

        return self

    def output_func(self, h):
        return F.softmax(h)

    def loss_func(self, y, t):
        return F.softmax_cross_entropy(y, t)

    def predict_proba(self, x_data):
        N = len(x_data)
        state = self.make_initial_state(train=False)
        y_list = []
        for i in six.moves.range(0, N, self.batch_size):
            x = Variable(x_data[i:i+self.batch_size],volatile=True)
            y,state = self.forward(x,train=False,state=state)
            y_list.append(y.data[0]) #batch size =Supporte seulement 1
        y = Variable(np.array(y_list),volatile=False)
        return self.output_func(y).data

    def predict(self, x_data):
        return self.predict_proba(x_data).argmax(1)

Le code qui est presque référencé est adopté.

Si vous écrivez jusqu'à présent, vous pouvez simplement écrire le processus principal,

main.py


# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from DL_chainer import *
import warnings
from sklearn.metrics import classification_report
warnings.filterwarnings("ignore")

I_FILE_EXCEL="excelrand.csv"
I_FILE_R="RRandom.csv"

def main(file=""):
    """
Apprenez et prédisez des nombres aléatoires Excel avec RNN
    :return:
    """
    df0 = pd.read_csv(file)
    N = len(df0)
    x_all = df0.iloc[:,0]
    y_all = []
    y_all.extend(x_all)
    y_all.extend([np.nan])
    y_all = y_all[1:(N+1)]

    x_all_array = np.reshape(np.array(x_all[0:(N-1)],dtype=np.float32),(len(x_all)-1,1))/10
    y_all_array = np.reshape(np.array(y_all[0:(N-1)],dtype=np.int32),(len(x_all)-1))

    train_n = 2 * N/3

    x_train = x_all_array[0:train_n]
    y_train = y_all_array[0:train_n]

    x_test = x_all_array[train_n:]
    y_test = y_all_array[train_n:]

    params = {"input_dim":1,"hidden_dim":100,"n_classes":10,"dropout_ratio":0.5,"optsize":30}
    print params
    print len(x_train),len(x_test)
    rnn = RNNTS(n_iter=200,report=1,**params)
    rnn.fit(x_train,y_train)
    pred = rnn.predict(x_train)
    print classification_report(y_train,pred)

    pred = rnn.predict(x_test)
    print classification_report(y_test,pred)

if __name__ == '__main__':
    main(I_FILE_R)
    main(I_FILE_EXCEL)

Fondamentalement, c'est un problème de classification en 10 classes.

Évaluation des résultats

L'évaluation des résultats est difficile. On considère qu'une évaluation correcte ne peut pas être effectuée avec des données d'essai avec un petit nombre d'échantillons.

L'apprentissage RNN se déroule essentiellement quelle que soit la séquence de nombres aléatoires utilisée. Par conséquent, j'ai décidé de voir en quoi la vitesse d'apprentissage diffère avec le même nombre d'époques.

En d'autres termes, à la même époque, ceux qui sont plus précis interprètent qu'il y a un certain modèle dans les données et qu'il a été "appris".

En tant que nombre aléatoire, il vaut mieux qu'il n'y ait pas de modèle, il est donc naturellement préférable que cette vitesse d'apprentissage soit lente.

Le résultat est ci-dessous

精度.png

Vous pouvez voir que la vitesse d'apprentissage est plus rapide côté Excel que sur R. En d'autres termes, je me demande si R est meilleur en termes de qualité des nombres aléatoires. En fait, c'est mieux si l'apprentissage ne progresse pas.

Résumé

Cette fois, pour la pratique de RNN, j'ai essayé l'implémentation et appris les nombres aléatoires d'Excel et de R. Indépendamment du fait que la méthode d'évaluation des résultats soit correcte ou non, il a été constaté que l'apprentissage du côté Excel progressait plus rapidement avec cette méthode d'évaluation et que R était meilleur en tant que nombre aléatoire.

Probablement, d'innombrables séquences de nombres aléatoires sont nécessaires pour vérifier plus correctement, mais en raison de la puissance de l'ordinateur, je l'ai gardé à ce niveau.

Mais comment accélérer le calcul avec RNN?

Recommended Posts

Etude du réseau neuronal récurrent (RNN) par Chainer ~ Vérification de l'exactitude des nombres aléatoires dans Excel et R ~
Reconnaissance des nombres manuscrits par un réseau neuronal multicouche
Apprentissage des classements à l'aide d'un réseau neuronal (implémentation RankNet par Chainer)
Génération de séries temporelles par réseau neuronal récurrent (traduction magenta ① où l'intelligence artificielle fait de l'art et de la musique)
Implémentation de réseaux neuronaux "flous" avec Chainer
[Chainer] Classification des documents par réseau de neurones convolutifs