ディープラーニング習得、次の一歩

ディープラーニング習得、次の一歩

word2vecリターンズ! 品詞分類による精度改善

2018年4月5日

Keras(+TensorFlow)を使って自然言語のベクトル化手法「word2vec」を実装。学習データに品詞分類を追加することによって、前回よりも予測精度が改善するかを検証する。

石垣哲郎 石垣 哲郎

word2vecの意義と応用分野

 word2vecとは文字どおり、単語(word)をn次元ベクトル(vector)に対応させること、つまり、各単語をn個の実数の組に対応させることであるが、重要なのはその表現に意味を持たせられることである。すなわち、word2vecの最大の特徴は、単語間の関係性をベクトル演算(足し引き)によって表現できるところである。属性の足し引きで別の単語を導出できる(例: king - man + woman = queen)ということは、よく考えてみると画期的なことである。

 またword2vecは、単語の類似度も表現できる。このような特徴は、機械翻訳などに応用されているといわれている。派手な特徴のわりに、表に現れている機会は少ないような気がするが、簡単に面白い結果を得られるので、今後も研究と応用が進んでいく分野であると筆者は考えている。

本稿のゴール

 本稿は前回に引き続いて、word2vecを取り上げる。今回は学習データに単語の品詞分類を追加することによって、ベクトル表現の精度を高める。前回同様、今回も江戸川乱歩の著作をコーパスに使用するが、「その登場人物の苗字と名前の組が、どれくらい当てられるようになるか」を評価する。

 本稿では前回同様、Kerasを使って実装する。ただし、今回は複数種類の入出力(単語それ自体と、単語の品詞種別)を使用するので、これまでのSequentialモデルではなく、Functional APIを用いる。

 なお、TensorFlowやKerasはインストール済みを前提に論を進める。

前回のおさらい

 前回は、江戸川乱歩の著作に京大黒橋・河原研究室のJUMAN++を適用して品詞分解し、これをコーパスとしてword2vecを実際に試してみた。JUMAN++については以下のURLを参照されたい。

 今回も全く同じデータを使用する。具体的には、青空文庫から江戸川乱歩の著作を入手して使用した。JUMAN++を使って、対象文章から、分かち書きされた出力をカンマ区切りで得て、これをCSVファイルとして保存しておく。

 word2vecには、

  • CBOWContinuous Bag-of-Words Model
  • Continuous Skip-gram Model(以下、skip-gramと表記)

の2つの手法があるが、これらのうちskip-gramを採用してニューラルネットワークを構築した。skip-gramは特定の単語が与えられたときに、その前後の複数の単語を当てようというものである。

 ニューラルネットワーク構築に当たっては、Kerasを使用した。EmbeddingとDenseのわずか2層で構築できる。学習後のEmbedding層の重み行列が、求めるベクトル表現である。

 予測単語すなわちラベルデータが複数あるので、各ラベルデータに対応するone-hotベクトル(1つの次元だけが1で他が0のベクトル)を事前に足しあわせて「n-hotベクトル」的なラベルに整形し、これを使って訓練を実施した。図1にイメージを示す。

図1 前回記事の実装イメージ

 図においてVは語彙集合、|V|はその要素数すなわち語彙数、Dは埋め込みベクトルの次元である。また、wは各単語のone-hotベクトル表現、vはその埋め込みベクトル表現、Eはwからvへの変換行列、E'はvを|V|次元空間に射影する行列である。

品詞分類データの入手

 JUMAN++の形態素解析結果には、品詞情報が含まれている。CSVファイルの4列目以降にそれが現れるが、このうち4列目と6列目を組み合わせて、品詞分類を実施する。

……省略……
蕗屋,蕗屋,蕗屋,名詞,6,普通名詞,1,*,0,*,0,自動獲得:Wikipedia Wikipediaページ内一覧:江戸川乱歩の美女シリーズ 読み不明
清一郎,せいいちろう,清一郎,名詞,6,人名,5,*,0,*,0,人名:日本:名:1686:0.00011
……省略……
CSVファイルの例

この例なら「名詞_普通名詞」「名詞_人名」というように品詞分類を行う。

 各単語に対応する品詞分類のリストを作成し、そこから訓練データや、インデックス←→品詞分類の辞書を作成したりする。後述のコード(リスト4)参照。

正解率算出の見直し

 前回はcompileメソッドがデフォルトで出力する正解率をそのまま評価に使っていたが、よく考えてみると、ラベルデータがone-hotベクトルではないので、正解率の算出方法を別途考案する必要がある。

 そこで、ラベルベクトルのnon-zero要素数をn個とするとき、予測ベクトルの大きい方からn個の各データの次元のうち、ラベルベクトルのnon-zero要素の次元と一致したものの数が正解数、これをnで割った値が正解率である、と定義することにした。文章では分かりにくいので、以下の図を参照してイメージをつかんでいただきたい。

図2 正解率の考え方

 Kerasには、自作した評価関数を登録する機能があるが、上記の処理を評価関数として実装することは筆者の手に余ったので(爆)、訓練後に評価することにした。コードは後述する。訓練の進行に伴って、ここで算出した正解率が改善するかどうか、気になるところであるが、これは確かに改善することを確認した。

埋め込みベクトル次元の拡大

 いよいよ、品詞分類を適用したword2vecに取りかかるが、その前に、埋め込みベクトル次元数を大きくして、予測精度がどの程度改善するか見てみることにする。

 前回の記事では、埋め込みベクトル次元数を100としていたが、これを400にしてみる。評価尺度は、正解率の他に、作品中の登場人物の苗字と名前の組み合わせを当てられるかどうかで判断する。

 評価に使用する登場人物の氏名は以下のとおりである。

苗字名前登場作品
黒田清太郎一枚の切符
蕗屋清一郎心理試験
斎藤心理試験
松村二銭銅貨
人見廣介パノラマ島綺譚
菰田源三郎パノラマ島綺譚
郷田三郎屋根裏の散歩者
岩瀬早苗黒蜥蜴
雨宮潤一黒蜥蜴
桜山葉子黒蜥蜴
岩瀬庄兵衛黒蜥蜴
表1 登場人物氏名一覧

 これらに対し、埋め込みベクトル表現演算、

  「明智」 - 「小五郎」 + 予測対象の名前

の結果が「予測対象の名字」であれば正解であるとする。例えば「明智 - 小五郎 + 清太郎」の結果が「黒田」であれば正解である。

 前回記事のリスト5の「vec_dim」の値を、100から400に変えて実行する。以下がその結果である。

埋め込み次元100400
正解率0.3892710.393821
黒田清太郎××
蕗屋清一郎××
斎藤勇××
松村武××
人見廣介×
菰田源三郎×
郷田三郎×
岩瀬早苗××
雨宮潤一××
桜山葉子××
岩瀬庄兵衛×
表2 次元数を変化させた時の正解率の変化

 正解率はほとんど変わらないが、苗字当ては劇的に改善した。以降、埋め込み次元数=400で評価を進めることにする。

品詞分類を適用したword2vecの実装

 参考情報として、筆者が実行したソースコードを以下に示す。これらをJupyter Notebook上で、コードブロック単位に実行した。使用した各種ソフトのバージョンは、以下のとおりである。

  • Python: 3.6.4
  • Anaconda: 5.1.0
  • TensorFlow(with GPU): 1.3.0
  • Keras: 2.1.4
  • Jupyter Notebook: 4.4.0

 まず、各種import宣言である。

Python
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import csv
import pandas as pd
import random
import numpy.random as nr
import sys
import h5py
import keras
import math

from __future__ import print_function
from keras.layers.core import Activation
from keras.layers.core import Dense
from keras.layers.core import Flatten
from keras.layers.embeddings import Embedding
from keras.models import Sequential
from keras.layers import Input
from keras.models import Model
from keras.callbacks import EarlyStopping
from keras.initializers import glorot_uniform
from keras.initializers import uniform
from keras.optimizers import RMSprop
from keras.utils import np_utils
リスト1 import宣言

 次に、CSV読み込み処理であるが、これは前回記事と同じである。

Python
# 元データ
df1 = csv.reader(open('rampo_separate.csv', 'r'))
df2 = csv.reader(open('rampo_separate2.csv', 'r'))
df3 = csv.reader(open('rampo_separate3.csv', 'r'))

data1 = [ v for v in df1]
data2 = [ v for v in df2]
data3 = [ v for v in df3]

mat1 = np.array(data1)
mat2 = np.array(data2)
mat3 = np.array(data3)

mat = np.r_[mat1[:,0],mat2[:,0],mat3[:,0]]
print(mat.shape)
リスト2 CSVファイル読み込み

品詞データ作成処理

 CSVファイルの4列目と6列目の情報を_でつないで、1つの品詞情報に整形している。これから、品詞情報用辞書や学習データを作成する。

Python
mat_part_of_speech1 = np.r_[mat1[:,3],mat2[:,3],mat3[:,3]]      # 品詞
mat_part_of_speech2 = np.r_[mat1[:,5],mat2[:,5],mat3[:,5]]      # 品詞詳細

mat_part_of_speech = []
for i in range (0,len(mat_part_of_speech1)) :
  mat_part_of_speech.append( mat_part_of_speech1[i] +'_'+ mat_part_of_speech2[i])

# サンプルとして1つ出力
print(mat[10])
print(mat_part_of_speech[10])
リスト3 品詞データ作成処理

辞書データの作成

 出現する単語に一意のインデックス番号を付与し、インデックス←→単語文字列の両引きが出来る辞書を作成する。これも、前回記事とほとんど同じである。

Python
words = sorted(list(set(mat)))
cnt = np.zeros(len(words))

print('total words:', len(words))
word_indices = dict((w, i) for i, w in enumerate(words))    # 単語をキーにインデックス検索
indices_word = dict((i, w) for i, w in enumerate(words))    # インデックスをキーに単語を検索

# 単語の出現数をカウント
for j in range (0, len(mat)):
  cnt[word_indices[mat[j]]] += 1

# 出現頻度の少ない単語を「UNK」で置き換え
words_unk = []                              # 未知語一覧

for k in range(0, len(words)):
  if cnt[k] <= 3 :
    words_unk.append(words[k])
    words[k] = 'UNK'

print('UNK words:', len(words_unk))          # words_unkはunkに変換された単語のリスト

words = sorted(list(set(words)))
print('total words:', len(words))
word_indices = dict((w, i) for i, w in enumerate(words))    # 単語をキーにインデックス検索
indices_word = dict((i, w) for i, w in enumerate(words))    # インデックスをキーに単語を検索
リスト4 辞書データ作成

品詞データ用辞書作成

 単語用の辞書と同じ要領で、品詞情報にインデックスを振り、辞書を作成する。

Python
parts = sorted(list(set(mat_part_of_speech)))

print('total parts:', len(parts))
parts_indices = dict((w, i) for i, w in enumerate(parts))    # 単語をキーにインデックス検索
indices_parts = dict((i, w) for i, w in enumerate(parts))    # インデックスをキーに単語を検索
リスト5 品詞データ用辞書作成

訓練データ作成

 単語用の訓練データに加えて、品詞用の訓練データも同時に作成する。ロジックはどちらも同じで、元データの文字列を、先に作った辞書を使って数字に置き換える。

Python
maxlen = 10                              # ウィンドウサイズ

mat_urtext = np.zeros((len(mat), 1), dtype='int16')
for i in range(0, len(mat)):
  # 出現頻度の低い単語のインデックスをunkのそれに置き換え
  if mat[i] in word_indices : 
    mat_urtext[i,0]=word_indices[mat[i]]
  else:
    mat_urtext[i,0]=word_indices['UNK']

mat_parts = np.zeros((len(mat_part_of_speech),1), dtype='int8')
for i in range(0,len(mat_part_of_speech)):
  mat_parts[i,0] = parts_indices[mat_part_of_speech[i]]

len_seq   = len(mat_urtext) - maxlen
x_train   = np.zeros((len_seq-maxlen,1), dtype='int16')
t_train   = np.zeros((len_seq-maxlen,maxlen*2), dtype='int16')
x_p_train = np.zeros((len_seq-maxlen,1), dtype='int8')
t_p_train = np.zeros((len_seq-maxlen,maxlen*2), dtype='int8')

for i in range(maxlen, len_seq):
  # 単語
  x_train[i-maxlen,0] = mat_urtext[i]
  t_train[i-maxlen,0:maxlen] = np.array(mat_urtext[i-maxlen:i]).reshape(1, maxlen)
  t_train[i-maxlen,maxlen:maxlen*2] = np.array(mat_urtext[i+1:i+1+maxlen]).reshape(1, maxlen)

  # 品詞
  x_p_train[i-maxlen,0] = mat_parts[i]
  t_p_train[i-maxlen,0:maxlen] = np.array(mat_parts[i-maxlen:i]).reshape(1, maxlen)
  t_p_train[i-maxlen,maxlen:maxlen*2] = np.array(mat_parts[i+1:i+1+maxlen]).reshape(1, maxlen)

z = list(zip(x_train,t_train,x_p_train,t_p_train))
nr.seed(12345)
nr.shuffle(z)                               # シャッフル
x_train,t_train,x_p_train,t_p_train = zip(*z)

x_train   = np.array(x_train).reshape(len_seq-maxlen,1)
t_train   = np.array(t_train).reshape(len_seq-maxlen, maxlen*2)
x_p_train = np.array(x_p_train).reshape(len_seq-maxlen, 1)
t_p_train = np.array(t_p_train).reshape(len_seq-maxlen, maxlen*2)

print(x_train.shape, t_train.shape, x_p_train.shape, t_p_train.shape)
リスト6 訓練データ作成

ニューラルネットワーク構築

 今回は入出力データが単語と品詞の2種類あるので、KerasのFunctional APIを用いてニューラルネットワークを構築する。

 基本的な考え方は、それぞれのデータを同次元のベクトル空間にEmbeddingし、その結果を足し合わせたうえで、それぞれのデータごとにDense-Softmax演算を行って学習を進める。こうすることによって、品詞データの学習結果が単語のEmbedding重み行列(=求めるベクトル表現)にも反映されることを意図している。図3にそのイメージを示す。

図3 ニューラルネットワークのイメージ

 図においてVは語彙集合、|V|はその要素数すなわち語彙数、Dは埋め込みベクトルの次元である。また、wは各単語のone-hotベクトル表現、vはその埋め込みベクトル表現、Eはwからvへの変換行列、E'はvを|V|次元空間に射影する行列である。これらの意味は、前掲の図1と同じである。

 また、Pは品詞集合、|P|はその要素数すなわち品詞分類数である。また、pは各単語の品詞分類のone-hotベクトル表現、vpはその埋め込みベクトル表現、Fはpからvpへの変換行列、F'はvpを|P|次元空間に射影する行列である。

 図に現れる4つの行列のうち、Eが求めるベクトル表現の行列である。

 KerasのFunctional APIというのは文字どおり、ニューラルネットワークの各レイヤー処理を関数インターフェースで実現するもので、入力が1つ前のレイヤーの処理結果、出力がそのレイヤーの処理結果である。この仕組みにより、ニューラルネットワークを分岐させたり合流させたり、あるいは複数の入出力を持たせたりすることが、容易にできる。直感性ではSequentialモデルに一歩譲るものの、慣れてくればなかなか便利である。

 今回も学習の発散を防ぐため、EarlyStopping(=学習を適切なタイミングで早期に打ち切る機能)を使用する。今回は正解率を計算する仕組みを用意したので、普通に損失関数lossを監視対象とする。

Python
class Prediction :
  def __init__(self, input_dim, output_dim, part_dim):
    self.input_dim = input_dim
    self.output_dim = output_dim
    self.part_dim = part_dim

  def create_model(self):
    word_input = Input(shape=(1,), dtype='int32', name='word_input')
    x_w = Embedding(output_dim=self.output_dim, input_dim=self.input_dim, input_length=1,
                    embeddings_initializer=uniform(seed=20170719))(word_input)
    part_input = Input(shape=(1,), dtype='int32', name='part_input')
    x_p = Embedding(output_dim=self.output_dim, input_dim=self.part_dim, input_length=1,
                    embeddings_initializer=uniform(seed=20170719))(part_input)
    x = keras.layers.add([x_w , x_p])
    x = Flatten()(x)
    word_output = Dense(self.input_dim, use_bias=False,activation='softmax', name='word_output',
                        kernel_initializer=glorot_uniform(seed=20170719))(x)
    part_output = Dense(self.part_dim, use_bias=False,activation='softmax', name='part_output',
                        kernel_initializer=glorot_uniform(seed=20170719))(x)
    model = Model(inputs=[word_input, part_input], outputs=[word_output, part_output])
    model.compile(optimizer='RMSprop',
        loss = {'word_output': 'categorical_crossentropy', 'part_output': 'categorical_crossentropy'},
        loss_weights = {'word_output': 1., 'part_output': 0.2})

    return model

  # 学習
  def train(self, x_train, t_train,x_p_train,t_p_train,batch_size,epochs, emb_param) :
    early_stopping = EarlyStopping(monitor='loss', patience=1, verbose=1)
    model = self.create_model()
    #model.load_weights(emb_param)               # 埋め込みパラメーターセット
    model.fit({'word_input': x_train, 'part_input': x_p_train},
              {'word_output': t_train, 'part_output': t_p_train},
                batch_size=batch_size, epochs=epochs,verbose=1,
                shuffle=True, callbacks=[early_stopping], validation_split=0.0)

    return model
リスト7 ニューラルネットワーク本体

 単語データの入力をword_input、品詞データの入力をpart_inputとして定義し、それらのEmbedding結果をそれぞれx_wx_pとする。

 これらを足し合わせたxに対し、それぞれ別次元のDenseを施行して出力word_outputpart_outputを得る、という流れである。

 compileメソッドのパラメーターloss_weightsは、今回のように複数の入出力がある場合に、それらの損失関数にどのように重みを付けて、全体の損失関数を計算するかを指定するものである。

メイン処理

 上記で定義したニューラルネットを動かして、学習を進める。

Python
vec_dim = 400 
epochs = 1
batch_size = 200
input_dim = len(words)
output_dim = vec_dim
part_dim = len(parts)

prediction = Prediction(input_dim, output_dim, part_dim)

emb_param = 'param_skip_gram_' + str(maxlen) + '_' + str(vec_dim) + '.hdf5'    # パラメーターファイル名
print(emb_param)

row=t_train.shape[0]

t_one_hot = np.zeros((row, input_dim), dtype='int8')     # ラベルデータをn-hot化
for i in range(0, row) :
  for j in range(0, maxlen*2):
    t_one_hot[i, t_train[i,j]] += 1
  if i % 50000 == 0 :
    print(i)
print(t_one_hot.shape)

t_p_one_hot = np.zeros((row, part_dim), dtype='int8')    # ラベルデータをn-hot化
for i in range(0, row) :
  for j in range(0, maxlen*2):
    t_p_one_hot[i, t_p_train[i,j]] += 1 
  if i % 50000 == 0 :
    print(i)
print(t_p_one_hot.shape)

model = prediction.train(x_train, t_one_hot, x_p_train, t_p_one_hot, batch_size, epochs, emb_param)

model.save_weights(emb_param)                    # 学習済みパラメーターを保存
リスト8 メイン処理

 ラベルデータを「n-hot」化する処理は、Pythonなのにループを二重に回しているため、非常に重い。これの改善については、読者諸氏に任せることとしたい(爆)。

正解率の算出

 学習終了後に、あらためて正解率を算出する。考え方は先に記述したとおりである。

Python
x_eval = x_train[:40000,:]
x_p_eval = x_p_train[:40000,:]
t_eval = t_one_hot[:40000,:]

y_pred = model.predict([x_eval, x_p_eval], batch_size=batch_size, verbose=1)   # 評価結果

n_true = 0
n_pred = 0
for i in range(0, x_eval.shape[0]) :
  label = set(t_one_hot[i,:].nonzero()[0])
  n_true += len(label)
  argmax_pred = y_pred[0][i,:].argsort()[::-1][0:len(label)]

  hit = set(label) & set(argmax_pred)

  n_pred += len(hit)
  del label,argmax_pred
  del hit

print()
print('正解率 =', n_pred * 1.0 / n_true )
リスト9 正解率算出

 コーパス全量を使って正解率を計算するのは時間がかかるので、ここでは使用データ量を40000個に絞っている。

 行単位に正解率を算出し、それらを足しあわせて最終的な正解率を求める。各行において、labelは「n-hot」化したラベルデータの中で0でないものの集合、argmax_predは予測結果のうち、大きい方からlen(label)を抽出した集合である。

 この2つの集合の共通要素数が正解数であり、これを使って正解率を算出している。

評価

 まず、学習した重み行列の構成を確認する。get_weights()load_weights()save_weights()で扱っているパラメーターファイルは、重み行列のリストになっているので、それらのshapeを表示してみる。

Python
param_list = model.get_weights()
print(len(param_list))
for i in range(0, len(param_list)):
  print(param_list[i].shape)
リスト10 重みパラメーターファイルの構成確認

 今回の場合、リスト10を実行すると、重み行列が4つあることが分かるが、その0番目の行列が求める埋め込み行列である。この行列のi行目の行ベクトルがインデックスiの単語のベクトル表現になっている。

 演算の結果がどの単語に近いのかは、「コサイン類似度」で評価する。コサイン類似度とはひと言でいうと、2つのベクトルが見込む角度の余弦である。当然、値の範囲は-11で、1に近いほど(要は大きいほど)類似度が高いと評価する。

 苗字当ては以下のコードで実施する。コサイン類似度の計算ロジックは、前回記事と同じである。

Python
param = param_list[0]

word_m0 = '明智'
word_m1 = '小五郎'

word0 = '清太郎'
word1 = '清一郎'
word2 = '勇'
word3 = '武'
word4 = '廣介'
word5 = '源三郎'
word6 = '三郎'
word7 = '早苗'
word8 = '潤一'
word9 = '葉子'
word10 = '庄兵衛'

vec0 = param[word_indices[word_m0],:]
vec1 = param[word_indices[word_m1],:]
vec2 = param[word_indices[word2],:]

for i in range(0,11) :
  vec2 = param[word_indices[eval('word'+str(i))],:]
  vec = vec0 - vec1 + vec2
  vec_norm = math.sqrt(np.dot(vec, vec))

  w_list = [word_indices[word_m0], word_indices[word_m1], word_indices[eval('word'+str(i))]]
  dist = -1.0
  m = 0
  for i in range(0, len(words)) :
    if i not in w_list :
      dist0 = np.dot(vec, param[i,:])
      dist0 = dist0 / vec_norm / math.sqrt(np.dot(param[i,:], param[i,:]))
      if dist < dist0 :
        dist = dist0
        m = i
  print('コサイン類似度=', dist, ' ', m, ' ', indices_word[m])
リスト11 苗字当て評価

 結果は以下のとおりである。正解率はほとんど変らないが、精度はさらに向上していることが分かる。

品詞分類無し有り
正解率0.3938210.394787
黒田清太郎×
蕗屋清一郎××
斎藤勇×
松村武×
人見廣介
菰田源三郎
郷田三郎
岩瀬早苗××
雨宮潤一×
桜山葉子×
岩瀬庄兵衛
表3 苗字当ての結果

 同じ要領で、肩書が当てられるかどうかも試してみた。「明智」-「探偵」の対に対し、以下の肩書から苗字が当てられるかどうかを評価した。

苗字肩書登場作品
宗像博士悪魔の紋章
笠森判事心理試験
小林刑事D坂の殺人事件
緑川夫人黒蜥蜴
中村警部悪魔の紋章
表4 登場人物の肩書一覧

 コードは以下のとおり。苗字当てのときとほぼ同じである。

Python
param = param_list[0]

word_m0 = '明智'
word_m1 = '探偵'

word0 = '博士'
word1 = '判事'
word2 = '刑事'
word3 = '夫人'
word4 = '警部'

vec0 = param[word_indices[word_m0],:]
vec1 = param[word_indices[word_m1],:]
vec2 = param[word_indices[word2],:]

for i in range(0,5) :
  vec2 = param[word_indices[eval('word'+str(i))],:]
  vec = vec0 - vec1 + vec2
  vec_norm = math.sqrt(np.dot(vec,vec))

  w_list = [word_indices[word_m0], word_indices[word_m1], word_indices[eval('word'+str(i))]]
  dist = -1.0
  m = 0
  for i in range(0,len(words)) :
    if i not in w_list :
      dist0 = np.dot(vec,param[i,:])
      dist0 = dist0 / vec_norm / math.sqrt(np.dot(param[i,:], param[i,:]))
      if dist < dist0 :
        dist = dist0
        m = i
  print('コサイン類似度=', dist,' ', m, ' ', indices_word[m])
リスト11 苗字当て評価

 結果は以下のとおり。なかなかの正答率である。

苗字肩書結果
宗像博士
笠森判事
小林刑事×
緑川夫人
中村警部
表5 登場人物の肩書一覧

前後の単語数を増加させた時の結果

 原論文では、skip-gramの評価に使う、前後の単語数を増加させると、精度が改善するとあった。そこで、これを試してみる。リスト6のmaxlenで単語数を指定しているので、これに203050を設定して、結果を比べた。以下にそれを示す。

評価尺度人物名/肩書maxlen=10maxlen=20maxlen=30maxlen=50
正解率0.3947870.4039260.4036290.399828
苗字当て黒田清太郎
蕗屋清一郎××
斎藤勇×
松村武
人見廣介×
菰田源三郎
郷田三郎×××
岩瀬早苗××××
雨宮潤一×
桜山葉子
岩瀬庄兵衛×
平均cos類似度0.3659220.3868490.4156110.410962
肩書当て宗像博士
笠森判事
小林刑事××
緑川夫人
中村警部
平均cos類似度0.4658000.5331700.5054460.544478
表3 苗字当ての結果

 さらなる劇的な効果を期待していたのだが、正解率や予測精度は横ばいである。しかし、予測的中時の平均コサイン類似度は、改善傾向にある。

終わりに

 本稿では、word2vecの学習データに品詞分類を追加することによって、予測精度が改善するかどうかを検証してみたが、これは確かに効果があった。他の自然言語処理でも効果が期待できるので、そのような機会があった際には、ぜひ試していただきたい。

  • このエントリーをはてなブックマークに追加
ディープラーニング習得、次の一歩
1. Kerasを用いたディープラーニング(LSTM)による株価予測

ディープラーニングのチュートリアルが一通り終わったら、次に何をやる? 今回は、誰にでも簡単にできる「株価予測」をテーマに、LSTMのニューラルネットワークを、Kerasを使って実装する方法を説明する。

2017年12月11日(月)
ディープラーニング習得、次の一歩
2. 挑戦! word2vecで自然言語処理(Keras+TensorFlow使用)

自然言語のベクトル化手法の一つである「word2vec」を使って、単語間の関連性を表現してみよう。Keras(+TensorFlow)を使って実装する。

2018年1月30日(火)
ディープラーニング習得、次の一歩
3. 【現在、表示中】≫ word2vecリターンズ! 品詞分類による精度改善

Keras(+TensorFlow)を使って自然言語のベクトル化手法「word2vec」を実装。学習データに品詞分類を追加することによって、前回よりも予測精度が改善するかを検証する。

2018年4月5日(木)
ディープラーニング習得、次の一歩
4. ディープラーニングで自動筆記 - Kerasを用いた文書生成(前編)

ディープラーニングによる自然言語処理の一つ「文書生成」にチャレンジしてみよう。ネットワークにLSTM、ライブラリにKeras+TensorFlowを採用し、徐々に精度を改善していくステップを説明する。

2018年7月5日(木)
ディープラーニング習得、次の一歩
5. ディープラーニングで自動筆記 - Kerasを用いた文書生成(後編)

「文書生成」チャレンジの後編。ネットワークにLSTM、ライブラリにKeras+TensorFlowを採用し、さらに精度を改善していく。最後に、全然関係ない入力文章から、江戸川乱歩風文書が生成されるかを試す。

2018年8月8日(水)
Deep Insider の SNS :