J'ai appliqué Sentence Piece, un tokenizer pour le traitement du langage neuronal, à la classification de documents.
L'autre jour, j'ai découvert Phrase Piece comme option de fractionnement de mots dans le traitement du langage naturel. Il semble que la traduction automatique ait atteint un score qui dépassait la méthode conventionnelle de division des mots, et cela m'intéressait simplement, et je me demandais ce qui se passerait avec la classification de documents sur laquelle je travaille actuellement, alors je l'ai essayé.
SentencePiece(GitHub) Article de l'auteur taku910 (Qiita)
J'ai utilisé KNB Analyzed Blog Corpus. Il s'agit d'un corpus de blog analysé avec un total de 4 186 phrases réparties en quatre catégories: «Kyoto sightseeing», «mobile phone», «sports» et «gourmet», et comprend la morphologie et le cas. Cette fois, nous n'utiliserons que des catégories et des phrases pour résoudre le problème de classification de la catégorie à laquelle appartient chaque phrase. 10% de toutes les données ont été divisées en données de test, et les 90% restants ont été utilisés pour la formation de SentencePiece et de réseau neuronal.
Je l'ai exécuté sur Python 3.6.1 sur Bash sous Windows. Voir required.txt pour la version détaillée du module Python.
Le code est résumé dans GitHub. Je suis encore immature, alors j'apprécierais si vous pouviez signaler des erreurs ou me donner des conseils.
En gros, SentencePiece semble être utilisé depuis la ligne de commande, mais je voulais l'utiliser depuis Python et je voulais l'utiliser facilement avec mecab, donc ce n'est pas très intelligent, mais je l'ai appelé à partir d'un sous-processus.
def train_sentencepiece(self, vocab_size):
cmd = "spm_train --input=" + self.native_text + \
" --model_prefix=" + self.model_path + \
" --vocab_size=" + str(vocab_size)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_data, stderr_data = p.communicate()
Lorsque vous écrivez séparément à l'aide du modèle appris, le traitement de chaque ligne prend trop de temps, donc je l'écris une fois dans un fichier texte et je le fais en même temps. Je ne l'ai pas utilisé cette fois, mais j'ai également ajouté une fonction de fractionnement caractère par caractère pour l'apprentissage au niveau du personnage.
Cette fois, nous comparerons Sentence Piece et mecab + neologd
Je voulais utiliser LSTM pour le réseau, mais cela prend beaucoup de temps à apprendre, j'ai donc choisi CNN cette fois. Nous avons implémenté un réseau avec trois types de filtres: 3, 4 et 5 mots.
class CNN(Chain):
def __init__(self, n_vocab, n_units, n_out, filter_size=(3, 4, 5), stride=1, use_dropout=0.5, ignore_label=-1):
super(CNN, self).__init__()
initializer = initializers.HeNormal()
with self.init_scope():
self.word_embed=L.EmbedID(n_vocab, n_units, ignore_label=-1)
self.conv1 = L.Convolution2D(None, n_units, (filter_size[0], n_units), stride, pad=(filter_size[0], 0), initialW=initializer)
self.conv2 = L.Convolution2D(None, n_units, (filter_size[1], n_units), stride, pad=(filter_size[1], 0), initialW=initializer)
self.conv3 = L.Convolution2D(None, n_units, (filter_size[2], n_units), stride, pad=(filter_size[2], 0), initialW=initializer)
self.norm1 = L.BatchNormalization(n_units)
self.norm2 = L.BatchNormalization(n_units)
self.norm3 = L.BatchNormalization(n_units)
self.l1 = L.Linear(None, n_units)
self.l2 = L.Linear(None, n_out)
self.use_dropout = use_dropout
self.filter_size = filter_size
def forward(self, x, train):
with using_config('train', train):
x = Variable(x)
x = self.word_embed(x)
x = F.dropout(x, ratio=self.use_dropout)
x = F.expand_dims(x, axis=1)
x1 = F.relu(self.norm1(self.conv1(x)))
x1 = F.max_pooling_2d(x1, self.filter_size[0])
x2 = F.relu(self.norm2(self.conv2(x)))
x2 = F.max_pooling_2d(x2, self.filter_size[1])
x3 = F.relu(self.norm3(self.conv3(x)))
x3 = F.max_pooling_2d(x3, self.filter_size[2])
x = F.concat((x1, x2, x3), axis=2)
x = F.dropout(F.relu(self.l1(x)), ratio=self.use_dropout)
x = self.l2(x)
return x
D'autres paramètres ont été définis comme suit.
Nombre d'unités | Mini taille de lot | max epoch | WeightDecay | GradientClipping | Optimizer |
---|---|---|---|---|---|
256 | 32 | 30 | 0.001 | 5.0 | Adam |
Tokenizer | mecab+neologd | SentencePiece |
---|---|---|
Meilleure précision | 0.68496418 | 0.668257773 |
Hmmm ... pas très bien Le nombre de phrases était-il trop petit parce que seules les données textuelles du train étaient utilisées pour l'apprentissage de SentencePiece? J'ai essayé d'apprendre le modèle de Sentence Piece avec jawiki (2017/05/01 dernière version).
Tokenizer | mecab+neologd | SentencePiece | SentencePiece(Apprenez avec jawiki) |
---|---|---|---|
Meilleure précision | 0.68496418 | 0.668257773 | 0.758949876 |
Cette fois, ça a l'air bien.
La précision pour chaque époque est la suivante.
J'ai essayé d'échantillonner certaines des phrases qui étaient en fait divisées
【SentencePiece】
C'est trop petit / te / press / press / spicy /.
Combien / combien / lance / prend / ga / continuation / ku / kana / a / ♪
Un autre / un / têtu / tension / ri /, / je pense / je pense /.
[Phrase Piece (apprise sur jawiki)]
Small / Sa / Too / Te / Button / Press / Spicy / Spicy / of /.
Do / no / ku / rai / ya / ri / take / continue / no / kana / a / ♪
A / et / Déjà / Un / Têtu / Zhang / Ri /, / Shi / Yo / Ka / To / Think / U /.
【mecab + neologd】
Petit / trop / te / bouton / appuyez / épicé / de / est / est /.
Combien / combien / échange / est / continue / / ou / hé / ♪
Ah / et un autre / un / fais de mon mieux /, / allons / ou / pense / pense /.
Même avec la même phrase, je pense que l'apprentissage avec jawiki est plus détaillé. Mecab + neologd est sensuellement proche des humains, mais il est intéressant de noter que cela ne signifie pas que l'apprentissage des réseaux de neurones donnera de bons résultats.
Cette fois, tous les ajustements de paramètres tels que le nombre d'unités ont été fixés, donc je pense qu'il est nécessaire de faire les ajustements appropriés et de comparer avec chaque meilleur. Aussi, comme je l'ai mentionné un peu dans la section sur les séparateurs, j'aimerais essayer une comparaison avec l'apprentissage au niveau des caractères. Après cela, j'ai utilisé une phrase différente des données du train pour apprendre le fragment de phrase, cette fois jawiki, mais j'aimerais vérifier comment d'autres phrases affectent la précision.
Au début, je l'ai essayé sur CentOS (docker), mais je n'ai pas pu installer Sentence Piece avec succès. J'ai abandonné, mais il semble qu'il puisse être installé sur CentOS si vous incluez ce qui suit en plus de la procédure de GitHub.
$ yum install protobuf-devel boost-devel gflags-devel lmdb-devel
Recommended Posts