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.
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.
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é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
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.
PKU (corpus de l'Université de Pékin, ensemble de données standard pour l'analyse comparative de la segmentation des mots chinois)
[Yao +, 2016] * Très similaire au modèle ci-dessus [^ 1]
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.
[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.
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.
--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