自然言語処理編 - 単語のベクトル化
自然言語処理の単語のベクトル化について試してゆく。 「単語のベクトル化」と、「単語の分散表現」は、ほぼ同じ意味として用いられるようだが、 「単語の分散表現」は、「単語のベクトル化」の一種であるらしい。 ソースコードは、~/jupyter-work ディレクト上で実行する前提とする。
gensim パッケージを利用する。 gensim とは、文章の意味をベクトルとして扱うためのパッケージとなる。 教師なし機械学習アルゴリズムを採用しているため、 構造化していないそのままの文章を処理出来る。 文章の集まり(コーパス)を意味を表すベクトル(セマンティックベクトル)に変換し、 ある意味のベクトルを、別の意味のベクトルに変換する方法(モデル)を定義することができる。
この記事では、単語ベクトルを扱う Word2Vec モデルと、KeyedVectors を扱う。 KeyedVectors は、Word2Vec、Doc2Vec や、fastText の単語ベクトルを格納・操作する部分を共通化したものとなる。 モデルは、学習用の情報と、単語ベクトルの両方を持っているため、追加学習が可能であるが、 KeyedVectors は、単語ベクトルのみ保持しており、追加学習ができない代わりに、モデルよりデータサイズが小さい。 KeyedVectors のみでも、単語間の類似度判定などの機能は利用できる。
ここでは、学習済みの日本語単語ベクトルセット(KeyedVectors)である (chiVe: Sudachi による日本語単語ベクトル) を利用して、単語ベクトルの使い方を見て行くことにする。
はじめに
gensim のログ出力設定を行う。 この記事中の処理では、あまり有用ではないが、 今後は、内部処理の把握や、エラー時の対応などで、必須と言っても過言ではない。
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
単語ベクトル
まず単語「犬」のベクトルを表示してみる。
import gensim
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
vec = model['犬']
print(vec.length)
print(vec)
この chiVe のベクトルは、300 次元となっている。 ベクトル次元が多いことは、単語をより細かく表現できるということらしい。
size: 300
[-4.34520751e-01 9.06087607e-02 -7.00369626e-02 -1.12125240e-01
-8.08637887e-02 -1.06122769e-01 -3.61174434e-01 2.76316881e-01
..... 中略 .....
-1.53031647e-01 1.85751691e-01 -2.53787078e-02 -2.46930525e-01
-4.60487492e-02 -3.08201551e-01 -1.48632944e-01 9.46458429e-02]
単語間の類似度
単語「犬」と「人」の類似度を算出してみる。
import gensim
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
print(model.similarity('犬', '人'))
結果は次のようになる。
0.29249397
この値は、二つのベクトルのコサインを計算しているようだ。
\[ \cos \theta = \frac{v1 \cdot v2}{|v1||v2|} \]
Python で実装してみると
import gensim
import numpy as np
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
v1 = model['犬']
v2 = model['人']
n1 = np.linalg.norm(v1, ord=2)
n2 = np.linalg.norm(v2, ord=2)
print(np.dot(v1, v2) / (n1 * n2))
前述の値と同じになる。
0.29249397
失業
に対する感情を判定してみる。
import gensim
emotions = ['嬉しい', '腹立たしい', '悲しい', '楽しい']
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
for emotion in emotions:
print(str(model.similarity('失業', emotion)) + ' :' + emotion)
結果を見ると、悲しい
が高く、嬉しい
が低いので、想像通りだと思う。
0.05855734 :嬉しい
0.19939116 :腹立たしい
0.21386954 :悲しい
0.06767238 :楽しい
昇進
に対する感情を判定してみる。
import gensim
emotions = ['嬉しい', '腹立たしい', '悲しい', '楽しい']
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
for emotion in emotions:
print(str(model.similarity('昇進', emotion)) + ' :' + emotion)
結果を見ると、楽しい
が最も低く、腹立たしい
最も高い。
なんとなく理解できる・・・・・複雑な感じ。
0.1669954 :嬉しい
0.17801343 :腹立たしい
0.15692115 :悲しい
0.11359178 :楽しい
類似の単語を取得
単語「徳島」の類似単語を取得してみる。
import gensim
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
print(model.most_similar('徳島', topn=5))
類似度の高いものが、候補に上がっている。
[('徳島県', 0.7955368757247925), ('愛媛', 0.7929633855819702), ('高知', 0.7896395325660706), ('徳島市', 0.7692087888717651), ('香川', 0.7571912407875061)]
アナロジー推論
Gemini 先生によれば、 「アナロジー推論とは、既知の事柄(ベース)と未知の事柄(ターゲット)の間に類似性を見出し、 その類似性に基づいて未知の事柄について推論すること」らしい。 自然言語処理の単語ベクトルでは、「単語間の関係をベクトル演算で表現し、類推問題を解く」ことらしい。
式で表すと以下のようになる。 日本と東京の関係が、アメリカとニューヨークの関係と同様であることを示しているらしい。
\[ v_{日本} - v_{東京} + v_{ニューヨーク} \simeq v_{アメリカ} \]
では、実際に動作させてみる。
import gensim
model = gensim.models.KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
print(model.most_similar(
positive=['日本', 'ニューヨーク'],
negative=['東京'],
topn=3))
次のようにアメリカ
が最も近い結果となった。
[('アメリカ', 0.7020306587219238), ('米国', 0.6050792932510376), ('欧米', 0.5738331079483032)]
Word Mover Distance による文章間距離
Word Mover Distance は、2 つの文章間の距離(類似度では無い)を計算する。 距離が、0 であれば、同一の文章ということ。
ソースの全文を載せる。
import logging
import spacy
from gensim.models.keyedvectors import KeyedVectors
# ログ出力設定
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
def tokenize(doc):
"""spaCy の戻り値を分かち書き配列に変換する"""
return [token.orth_ for token in doc]
# 分かち書きに利用するモデルを読み込む。
nlp = spacy.load('ja_ginza_bert_large')
# 単語ベクトルセットを読み込む。
model = KeyedVectors.load("../models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
tokens1 = tokenize(nlp('私は、猫である。'))
tokens2 = tokenize(nlp('私は、犬である。'))
print(model.wmdistance(tokens1, tokens1))
print(model.wmdistance(tokens1, tokens2))
以下のような結果となる。 一つ目は、同じ文章なので、距離は 0 となる。
0.0
0.10580116662522744
- 前の記事:環境構築編 - 自然言語処理環境
- 次の記事:自然言語処理編 - 単語のベクトル化 その弐