[PYTHON] Mise en œuvre de l'apprentissage en série de Chainer à l'aide de mini-lots de longueur variable

introduction

logo.png

NStep LSTM a été implémenté dans la version de Chainer 1.16.0. Comme son nom l'indique, NStep LSTM est un modèle qui peut facilement réaliser un LSTM multicouche. En interne, RNN optimisé avec cuDNN est utilisé, et il fonctionne plus rapidement que le LSTM conventionnel. De plus, avec NStep LSTM **, il n'est plus nécessaire de faire correspondre les longueurs des données de mini-lots **, et vous pouvez désormais saisir chaque échantillon dans une liste tel quel. Vous n'avez plus besoin de remplir avec -1 et d'utiliser ignore_label = -1 et where, ou de transposer et d'entrer une liste triée par longueur de données.

Donc, cette fois, j'ai essayé d'apprendre l'étiquetage des séries en utilisant ce NStep LSTM.

Différence d'interface avec le LSTM conventionnel

Puisque NStep LSTM a des entrées / sorties différentes du LSTM conventionnel, il n'est pas possible de remplacer simplement le modèle implémenté jusqu'à présent par NStep LSTM.

L'entrée et la sortie de \ _ \ _ init \ _ \ _ () et \ _ \ _ call \ _ \ _ () de NStep LSTM sont les suivantes.

NStepLSTM.__init__(n_layers, in_size, out_size, dropout, use_cudnn=True)
"""
n_layers (int): Number of layers.
in_size (int): Dimensionality of input vectors.
out_size (int): Dimensionality of hidden states and output vectors.
dropout (float): Dropout ratio.
use_cudnn (bool): Use cuDNN.
"""

...

NStepLSTM.__call__(hx, cx, xs, train=True)
"""
hx (~chainer.Variable): Initial hidden states.
cx (~chainer.Variable): Initial cell states.
xs (list of ~chianer.Variable): List of input sequences.
        Each element ``xs[i]`` is a :class:`chainer.Variable` holding a sequence.
"""
    ...

    return hy, cy, ys

D'autre part, le LSTM conventionnel était le suivant.

LSTM.__init__(in_size, out_size, **kwargs)
"""
in_size (int) – Dimension of input vectors. If None, parameter initialization will be deferred until the first forward data pass at which time the size will be determined.
out_size (int) – Dimensionality of output vectors.
lateral_init – A callable that takes numpy.ndarray or cupy.ndarray and edits its value.
        It is used for initialization of the lateral connections.
        Maybe be None to use default initialization.
upward_init – A callable that takes numpy.ndarray or cupy.ndarray and edits its value.
        It is used for initialization of the upward connections.
        Maybe be None to use default initialization.
bias_init – A callable that takes numpy.ndarray or cupy.ndarray and edits its value.
        It is used for initialization of the biases of cell input, input gate and output gate, and gates of the upward connection.
        Maybe a scalar, in that case, the bias is initialized by this value.
        Maybe be None to use default initialization.
forget_bias_init – A callable that takes numpy.ndarray or cupy.ndarray and edits its value.
        It is used for initialization of the biases of the forget gate of the upward connection.
        Maybe a scalar, in that case, the bias is initialized by this value.
        Maybe be None to use default initialization.
"""

...

LSTM.__call__(x)
"""
x (~chainer.Variable): A new batch from the input sequence.
"""
    ...

    return y

Par conséquent, NStep LSTM est géré différemment de LSTM dans les points suivants.

-Spécifiez le nombre de couches et le taux d'abandon avec \ _ \ _ init () \ _ \ _

La grande différence est que l'appel à \ _ \ _call () \ _ \ _ reçoit les états masqués initiaux et les états de cellule, et les entrées et sorties sont répertoriées.

Facilitez la manipulation de NStep LSTM

Implémentez des sous-classes pour amener l'initialisation et les appels NStep LSTM aussi près que possible de LSTM.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from chainer import Variable
import chainer.links as L
import numpy as np


class LSTM(L.NStepLSTM):

    def __init__(self, in_size, out_size, dropout=0.5, use_cudnn=True):
        n_layers = 1
        super(LSTM, self).__init__(n_layers, in_size, out_size, dropout, use_cudnn)
        self.state_size = out_size
        self.reset_state()

    def to_cpu(self):
        super(LSTM, self).to_cpu()
        if self.cx is not None:
            self.cx.to_cpu()
        if self.hx is not None:
            self.hx.to_cpu()

    def to_gpu(self, device=None):
        super(LSTM, self).to_gpu(device)
        if self.cx is not None:
            self.cx.to_gpu(device)
        if self.hx is not None:
            self.hx.to_gpu(device)

    def set_state(self, cx, hx):
        assert isinstance(cx, Variable)
        assert isinstance(hx, Variable)
        cx_ = cx
        hx_ = hx
        if self.xp == np:
            cx_.to_cpu()
            hx_.to_cpu()
        else:
            cx_.to_gpu()
            hx_.to_gpu()
        self.cx = cx_
        self.hx = hx_

    def reset_state(self):
        self.cx = self.hx = None

    def __call__(self, xs, train=True):
        batch = len(xs)
        if self.hx is None:
            xp = self.xp
            self.hx = Variable(
                xp.zeros((self.n_layers, batch, self.state_size), dtype=xs[0].dtype),
                volatile='auto')
        if self.cx is None:
            xp = self.xp
            self.cx = Variable(
                xp.zeros((self.n_layers, batch, self.state_size), dtype=xs[0].dtype),
                volatile='auto')

        hy, cy, ys = super(LSTM, self).__call__(self.hx, self.cx, xs, train)
        self.hx, self.cx = hy, cy
        return ys

Dans la classe ci-dessus, \ _ \ _ init () \ _ \ _ est spécifié uniquement in_size et out_size comme auparavant (la valeur par défaut de dropout est 0,5, fixée à n_layers = 1 sans multicouche de LSTM). .. \ _ \ _ Call () \ _ \ _ initialise automatiquement cx et hx, et entre et sort uniquement la liste des chainer.Variable.

Implémentation du LSTM bidirectionnel avec NStep LSTM

Implémentez le LSTM bidirectionnel à l'aide de NStep LSTM. Faites une liste pour l'entrée backward-LSTM en inversant chaque échantillon du chainer. Après avoir calculé la sortie avec forward-LSTM et backward-LSTM, ** alignez l'orientation de chaque échantillon dans chaque liste de sortie et ** concaténez pour créer un vecteur. Dans la classe ci-dessous, une opération linéaire est ajoutée afin que out_size soit le nombre d'étiquettes pour l'étiquetage des séries.

class BLSTMBase(Chain):

    def __init__(self, embeddings, n_labels, dropout=0.5, train=True):
        vocab_size, embed_size = embeddings.shape
        feature_size = embed_size
        super(BLSTMBase, self).__init__(
            embed=L.EmbedID(
                in_size=vocab_size,
                out_size=embed_size,
                initialW=embeddings,
            ),
            f_lstm=LSTM(feature_size, feature_size, dropout),
            b_lstm=LSTM(feature_size, feature_size, dropout),
            linear=L.Linear(feature_size * 2, n_labels),
        )
        self._dropout = dropout
        self._n_labels = n_labels
        self.train = train

    def reset_state(self):
        self.f_lstm.reset_state()
        self.b_lstm.reset_state()

    def __call__(self, xs):
        self.reset_state()
        xs_f = []
        xs_b = []
        for x in xs:
            _x = self.embed(self.xp.array(x))
            xs_f.append(_x)
            xs_b.append(_x[::-1])
        hs_f = self.f_lstm(xs_f, self.train)
        hs_b = self.b_lstm(xs_b, self.train)
        ys = [self.linear(F.dropout(F.concat([h_f, h_b[::-1]]), ratio=self._dropout, train=self.train)) for h_f, h_b in zip(hs_f, hs_b)]
        return ys

Apprenez l'étiquetage des séries à l'aide du LSTM bidirectionnel

Utilisons en fait le modèle implémenté ci-dessus et appliquons-le à la tâche d'étiquetage des séries. J'ai choisi la segmentation de mots chinois comme un problème d'étiquetage de série où le LSTM bidirectionnel est souvent utilisé. Contrairement à l'anglais, le chinois n'a pas de mots séparés par des espaces, vous devez donc identifier les limites des mots avant de traiter le texte.

Exemple)

L'hiver, peut porter, porter, porter; Summer, Noh (can) Wear (wear) Beaucoup (plus) Peu (peu) Wear (usure) Beaucoup (plus) Little (peu).

[Chen+, 2015]

L'exemple ci-dessus a des significations différentes selon qu'il est divisé en «certains» ou «plusieurs» et «petits». Comme la structure de la phrase est presque la même, le délimiteur est jugé dans le contexte des mots environnants.

B (Début, le début d'un mot avec deux lettres ou plus), M (Milieu, le milieu d'un mot avec deux lettres ou plus), E (Fin) pour une chaîne pour apprendre le fractionnement de mot chinois comme problème d'étiquetage de séquence. , Fin de deux mots ou plus), S (Mot simple, une lettre). En utilisant les données de texte avec cette étiquette, nous apprendrons l'étiquette attribuée à chaque caractère à partir des informations de contexte de la chaîne de mots.

Expérience

Ensemble de données cible

PKU (corpus de l'Université de Pékin, ensemble de données standard pour l'analyse comparative de la segmentation des mots chinois)

environnement

Paramètres du modèle et de l'expérience

Screen Shot 2016-12-03 at 05.36.56.png [Yao +, 2016] * Très similaire au modèle ci-dessus [^ 1]

Résultat expérimental

Processus d'apprentissage et résultats de ce modèle

Le processus d'apprentissage est décrit ci-dessous tel quel.

hiroki-t:/private/work/blstm-cws$ python app/train.py --save -e 10 --gpu 0
2016-12-03 09:34:06.27 JST      13a653  [info]  LOG Start with ACCESSID=[13a653] UNIQUEID=[UNIQID] ACCESSTIME=[2016-12-03 09:34:06.026907 JST]
2016-12-03 09:34:06.27 JST      13a653  [info]  *** [START] ***
2016-12-03 09:34:06.27 JST      13a653  [info]  initialize preprocessor with /private/work/blstm-cws/app/../data/zhwiki-embeddings-100.txt
2016-12-03 09:34:06.526 JST     13a653  [info]  load train dataset from /private/work/blstm-cws/app/../data/icwb2-data/training/pku_training.utf8
2016-12-03 09:34:14.134 JST     13a653  [info]  load test dataset from /private/work/blstm-cws/app/../data/icwb2-data/gold/pku_test_gold.utf8
2016-12-03 09:34:14.589 JST     13a653  [trace]
2016-12-03 09:34:14.589 JST     13a653  [trace] initialize ...
2016-12-03 09:34:14.589 JST     13a653  [trace] --------------------------------
2016-12-03 09:34:14.589 JST     13a653  [info]  # Minibatch-size: 20
2016-12-03 09:34:14.589 JST     13a653  [info]  # epoch: 10
2016-12-03 09:34:14.589 JST     13a653  [info]  # gpu: 0
2016-12-03 09:34:14.589 JST     13a653  [info]  # hyper-parameters: {'adagrad_lr': 0.2, 'dropout_ratio': 0.2, 'weight_decay': 0.0001}
2016-12-03 09:34:14.590 JST     13a653  [trace] --------------------------------
2016-12-03 09:34:14.590 JST     13a653  [trace]
100% (19054 of 19054) |#######################################| Elapsed Time: 0:07:50 Time: 0:07:50
2016-12-03 09:42:05.642 JST     13a653  [info]  [training] epoch 1 - #samples: 19054, loss: 9.640346, accuracy: 0.834476
100% (1944 of 1944) |#########################################| Elapsed Time: 0:00:29 Time: 0:00:29
2016-12-03 09:42:34.865 JST     13a653  [info]  [evaluation] epoch 1 - #samples: 1944, loss: 6.919845, accuracy: 0.890557
2016-12-03 09:42:34.866 JST     13a653  [trace] -
100% (19054 of 19054) |#######################################| Elapsed Time: 0:07:40 Time: 0:07:40
2016-12-03 09:50:15.258 JST     13a653  [info]  [training] epoch 2 - #samples: 19054, loss: 5.526157, accuracy: 0.903373
100% (1944 of 1944) |#########################################| Elapsed Time: 0:00:24 Time: 0:00:24
2016-12-03 09:50:39.400 JST     13a653  [info]  [evaluation] epoch 2 - #samples: 1944, loss: 6.233129, accuracy: 0.900318
2016-12-03 09:50:39.401 JST     13a653  [trace] -
100% (19054 of 19054) |#######################################| Elapsed Time: 0:08:41 Time: 0:08:41
2016-12-03 09:59:21.301 JST     13a653  [info]  [training] epoch 3 - #samples: 19054, loss: 4.217260, accuracy: 0.921377
100% (1944 of 1944) |#########################################| Elapsed Time: 0:00:24 Time: 0:00:24
2016-12-03 09:59:45.587 JST     13a653  [info]  [evaluation] epoch 3 - #samples: 1944, loss: 5.650668, accuracy: 0.913843
2016-12-03 09:59:45.587 JST     13a653  [trace] -
100% (19054 of 19054) |#######################################| Elapsed Time: 0:07:25 Time: 0:07:25
2016-12-03 10:07:11.451 JST     13a653  [info]  [training] epoch 4 - #samples: 19054, loss: 3.488712, accuracy: 0.931668
100% (1944 of 1944) |#########################################| Elapsed Time: 0:00:26 Time: 0:00:26
2016-12-03 10:07:37.889 JST     13a653  [info]  [evaluation] epoch 4 - #samples: 1944, loss: 5.342249, accuracy: 0.917103
2016-12-03 10:07:37.890 JST     13a653  [trace] -
100% (19054 of 19054) |#######################################| Elapsed Time: 0:07:26 Time: 0:07:26
2016-12-03 10:15:03.919 JST     13a653  [info]  [training] epoch 5 - #samples: 19054, loss: 2.995683, accuracy: 0.938305
100% (1944 of 1944) |#########################################| Elapsed Time: 0:00:15 Time: 0:00:15
2016-12-03 10:15:19.749 JST     13a653  [info]  [evaluation] epoch 5 - #samples: 1944, loss: 5.320374, accuracy: 0.921863
2016-12-03 10:15:19.750 JST     13a653  [trace] -
100% (19054 of 19054) |########################################| Elapsed Time: 0:07:29 Time: 0:07:29
2016-12-03 10:22:49.393 JST     13a653  [info]  [training] epoch 6 - #samples: 19054, loss: 2.680496, accuracy: 0.943861
100% (1944 of 1944) |##########################################| Elapsed Time: 0:00:27 Time: 0:00:27
2016-12-03 10:23:16.985 JST     13a653  [info]  [evaluation] epoch 6 - #samples: 1944, loss: 5.326864, accuracy: 0.924161
2016-12-03 10:23:16.986 JST     13a653  [trace] -
100% (19054 of 19054) |########################################| Elapsed Time: 0:07:28 Time: 0:07:28
2016-12-03 10:30:45.772 JST     13a653  [info]  [training] epoch 7 - #samples: 19054, loss: 2.425466, accuracy: 0.947673
100% (1944 of 1944) |##########################################| Elapsed Time: 0:00:22 Time: 0:00:22
2016-12-03 10:31:08.448 JST     13a653  [info]  [evaluation] epoch 7 - #samples: 1944, loss: 5.270019, accuracy: 0.925341
2016-12-03 10:31:08.449 JST     13a653  [trace] -
100% (19054 of 19054) |########################################| Elapsed Time: 0:08:39 Time: 0:08:39
2016-12-03 10:39:47.461 JST     13a653  [info]  [training] epoch 8 - #samples: 19054, loss: 2.233068, accuracy: 0.950928
100% (1944 of 1944) |##########################################| Elapsed Time: 0:00:26 Time: 0:00:26
2016-12-03 10:40:14.2 JST       13a653  [info]  [evaluation] epoch 8 - #samples: 1944, loss: 5.792994, accuracy: 0.924707
2016-12-03 10:40:14.2 JST       13a653  [trace] -
100% (19054 of 19054) |########################################| Elapsed Time: 0:07:10 Time: 0:07:10
2016-12-03 10:47:24.806 JST     13a653  [info]  [training] epoch 9 - #samples: 19054, loss: 2.066807, accuracy: 0.953524
100% (1944 of 1944) |##########################################| Elapsed Time: 0:00:26 Time: 0:00:26
2016-12-03 10:47:51.745 JST     13a653  [info]  [evaluation] epoch 9 - #samples: 1944, loss: 5.864374, accuracy: 0.925294
2016-12-03 10:47:51.746 JST     13a653  [trace] -
100% (19054 of 19054) |########################################| Elapsed Time: 0:08:43 Time: 0:08:43
2016-12-03 10:56:34.758 JST     13a653  [info]  [training] epoch 10 - #samples: 19054, loss: 1.946193, accuracy: 0.955782
100% (1944 of 1944) |##########################################| Elapsed Time: 0:00:22 Time: 0:00:22
2016-12-03 10:56:57.641 JST     13a653  [info]  [evaluation] epoch 10 - #samples: 1944, loss: 5.284819, accuracy: 0.930201
2016-12-03 10:56:57.642 JST     13a653  [trace] -
2016-12-03 10:56:57.642 JST     13a653  [info]  saving the model to /private/work/blstm-cws/app/../output/cws.model ...
2016-12-03 10:56:58.520 JST     13a653  [info]  *** [DONE] ***
2016-12-03 10:56:58.521 JST     13a653  [info]  LOG End with ACCESSID=[13a653] UNIQUEID=[UNIQID] ACCESSTIME=[2016-12-03 09:34:06.026907 JST] PROCESSTIME=[4972.494370000]

Ce n'est pas la valeur Precision, Recall, F mais la valeur Précision, mais elle est de 93,0 à la 10e époque. Le temps de traitement était de 10 époques, soit un peu plus de 80 minutes.

Comparer avec les résultats des études précédentes

Screen Shot 2016-12-03 at 10.27.55.png [Yao+, 2016] [^2]

All the models are trained on NVIDIA GTX Geforce 970, it took about 16 to 17 hours to train a model on GPU while more than 4 days to train on CPU, in contrast.

[Yao+, 2016]

Il existe quelques différences par rapport aux recherches précédentes telles que l'initialisation des Embeddings, mais il semble que la précision et le temps de traitement du BLSTM à 1 couche soient raisonnables.

Decoding

hiroki-t:/private/work/blstm-cws$ python app/parse.py
2016-12-03 11:01:13.343 JST     549e15  [info]  LOG Start with ACCESSID=[549e15] UNIQUEID=[UNIQID] ACCESSTIME=[2016-12-03 11:01:13.343412 JST]
2016-12-03 11:01:13.343 JST     549e15  [info]  *** [START] ***
2016-12-03 11:01:13.344 JST     549e15  [info]  initialize preprocessor with /private/work/blstm-cws/app/../data/zhwiki-embeddings-100.txt
2016-12-03 11:01:13.834 JST     549e15  [trace]
2016-12-03 11:01:13.834 JST     549e15  [trace] initialize ...
2016-12-03 11:01:13.834 JST     549e15  [trace]
2016-12-03 11:01:13.914 JST     549e15  [info]  loading a model from /private/work/blstm-cws/app/../output/cws.model ...
Input a Chinese sentence! (use 'q' to exit)
La troisième étape de la modernisation du peuple chinois, une nouvelle expédition.
B E B E B E S S B M E B E B E S B E B E B E S S B E S
Progression du peuple chinois Achèvement de la modernisation de la construction Stratégie des troisièmes étapes Nouvelle conquête.
-
q
2016-12-03 11:02:08.961 JST     549e15  [info]  *** [DONE] ***
2016-12-03 11:02:08.962 JST     549e15  [info]  LOG End with ACCESSID=[549e15] UNIQUEID=[UNIQID] ACCESSTIME=[2016-12-03 11:01:13.343412 JST] PROCESSTIME=[55.618552000]

# ^Remarque[gold]Progression du peuple chinois Achèvement de la modernisation de la construction Stratégie des troisièmes étapes Nouvelle conquête.

Lorsque le décodage était effectué sur la base du résultat d'apprentissage, la séquence d'étiquettes et le résultat de division de mot corrects étaient renvoyés à partir de la chaîne de caractères non divisée.

en conclusion

J'ai appris l'étiquetage des séries avec le LSTM bidirectionnel en utilisant le NStep LSTM de Chainer. Avec la prise en charge des mini-lots de longueur variable + cuDNN, le traitement des données d'entrée est devenu plus facile et le calcul est devenu plus rapide qu'auparavant. Le modèle mis en œuvre cette fois peut être utilisé non seulement pour la division de mots en chinois mais aussi pour l'apprentissage en série, il peut donc être intéressant de l'appliquer à d'autres tâches telles que le balisage de mots partiels.

Le code source est disponible sur GitHub. https://github.com/chantera/blstm-cws

En plus du BLSTM présenté ci-dessus, le référentiel contient le code que j'utilise réellement en combinaison avec Chainer pour l'implémentation de BLSTM + CRF et la recherche sur la PNL, donc j'espère que ce sera utile.

référence

--Faites un mini-lot de données de longueur variable avec le chainer où --studylog / Northern Cloud http://studylog.hateblo.jp/entry/2016/02/04/020547 --Chainer's cuDNN-RNN (NStepLSTM) start --studylog / Northern Cloud http://studylog.hateblo.jp/entry/2016/10/03/095406


written by chantera at NAIST cllab

[^ 1]: [Yao +, 2016] renvoie le vecteur v ∈ R ^ 2d de la sortie de BLSTM à la dimension d avec une matrice de W ∈ R ^ d * 2d. [^ 2]: dans [Yao +, 2016], la dimension de Word Embeddings est définie sur 200 dimensions et un dictionnaire est créé à partir des caractères de l'ensemble d'apprentissage sans pré-apprentissage.

Recommended Posts

Mise en œuvre de l'apprentissage en série de Chainer à l'aide de mini-lots de longueur variable
Apprentissage des classements à l'aide d'un réseau neuronal (implémentation RankNet par Chainer)
À propos de la variable du chainer
Implémentation de TF-IDF à l'aide de gensim
Apprentissage par renforcement profond 2 Mise en œuvre de l'apprentissage par renforcement
Implémentation simple d'un réseau neuronal à l'aide de Chainer
Importance de l'apprentissage automatique et de l'apprentissage par mini-lots
Renforcer l'apprentissage 8 Essayez d'utiliser l'interface utilisateur de Chainer
Implémentation des notifications de bureau à l'aide de Python
Othello-De la troisième ligne de "Implementation Deep Learning" (3)
Qiskit: mise en œuvre de l'apprentissage des circuits quantiques (QCL)
Implémentation d'un réseau neuronal à 3 couches (pas d'apprentissage)
Algorithme d'apprentissage automatique (implémentation de la classification multi-classes)
Othello-De la troisième ligne de "Implementation Deep Learning" (2)
[Mémo d'apprentissage] Apprentissage profond à partir de zéro ~ Mise en œuvre de l'abandon ~
Implémentation du modèle Deep Learning pour la reconnaissance d'images
Apprentissage profond appris par mise en œuvre (segmentation) ~ Mise en œuvre de SegNet ~
Essayez d'utiliser le bloc-notes Jupyter à partir d'Azure Machine Learning
Raisonnement causal utilisant l'apprentissage automatique (organisation des méthodes de raisonnement causal)
[Pour les débutants en apprentissage profond] Implémentation d'une classification binaire simple par couplage complet à l'aide de Keras
Implémentation de la méthode de clustering k-shape pour les données de séries chronologiques [Apprentissage non supervisé avec python Chapitre 13]