このシリーズでは、自然言語処理において主流であるTransformerを中心に、環境構築から学習の方法までまとめます。
今回の記事では、単語の分散表現の概要と、Word2Vecの基本的な実装方法を紹介します。
Google colabを使用して、簡単に最新の自然言語処理モデルを実装することができますので、ぜひ最後までご覧ください。
今回の内容
・単語の分散表現とは
・Word2Vecによる単語ベクトル
・単語・文章の類似度計算
- 1. 単語の分散表現とは
- 1.1. One-hotベクトル表現
- 1.2. 分散表現の登場
- 1.3. 分布仮説
- 2. Word2Vecとは
- 2.1. CBOW(continuous bag-of-words)
- 2.2. skip-gram
- 3. Word2Vecの導入
- 3.1. Google colabによる導入
- 3.2. 学習済モデルを取得
- 4. Word2Vecによる単語ベクトル
- 4.1. 単語ベクトル
- 4.2. 類似度の高い単語を表示
- 4.3. 複数単語の類似度が高い単語を表示する
- 4.4. 単語の意味を足し引きして類似度が高い単語を表示する
- 4.5. 2つの単語の類似度を算出する
- 5. Word2Vecによる文ベクトル
- 6. Word2Vecによる文ベクトルと文章類似度
- 7. まとめ
単語の分散表現とは
単語を固定長のベクトルで表現することを「単語の分散表現」と言います。
自然言語処理において機械学習を活用するためには、単語の持つ性質や意味を反映したベクトル表現を獲得することが重要となります。
単語をベクトルで表現することができれば、単語の意味を定量的に把握することができるため、様々な処理に応用することができます。
単語をベクトルで表現する方法として、one-hotベクトル、Word2Vec、fastTextといった手法が提案されてきました。
今回はその中の1つである、Word2Vecを紹介します。
One-hotベクトル表現
文字や単語をベクトル化する手法として、「One-hotベクトル」が提案されました。
これは、ベクトルのすべての要素のうちひとつだけが1であり、残りはすべて0であるベクトルを意味します。
例えば、以下のように単語ごとにベクトルを割り当てます。
私(1,0,0,0,0)
は(0,1,0,0,0)
今日(0,0,1,0,0)
勉強(0,0,0,1,0)
する(0,0,0,0,1)
この手法では、機械的にベクトルを割り当てることができます。
しかし、同一単語であるかどうかの判定はできますが、これでは単語の意味を含んだベクトルにはなっていません。
また、文章中の全ての単語を対象とすると、ベクトル数が膨大になるため計算時間が大幅に増加する課題がありました。
分散表現の登場
ベクトル数が膨大になるという課題を解決するため、提案された手法が「分散表現」です。
単語分散表現とは、「文字・単語をベクトル空間に埋め込み、その空間上のひとつの点として捉える」ことを指します。
単語分散表現は、単語埋め込み(Word Embedding)とも呼ばれます。
単語の意味を含めたベクトルとして表現できるようになったことで、意味の異なる単語同士でのベクトルの計算が可能となりました。
分布仮説
自然言語処理の世界では様々なベクトル化手法が研究されています。
人が経験的に把握している特徴を単語ベクトルに落とし込む手法に対して、これを機械的に行うアプローチを分布仮説に基づいて単語ベクトルを求める手法があります。
この手法は「単語の意味は周囲の単語によって形成される」というアイデアに基づいています。
本記事でご紹介するWord2Vecも分布仮説に基づいています。
Word2Vecとは
Word2vecは、Googleの研究者であるトマス・ミコロフ氏が率いる研究チームによって2013年に公開されました。
2層のニューラルネットワークのみで構成されているということが特徴です。
構造がシンプルであるため、大規模なデータによる分散表現学習を現実的な計算量によって行えるようになり、分散表現での自然言語処理を飛躍的に進めることができました。
なお、「Word2vec」はスキップグラム法(skip-gram法)、CBOW(continuous bag-of-words)という2つの技術の集合です。
CBOW(continuous bag-of-words)
CBOWは、教師あり学習が用いられます。
周辺語を入力として付与し、その中心語の予測を出力する学習を行います。
どんな単語が現れる可能性があるかを理解することで、単語の意味関係を把握できるようになります。
skip-gram
スキップグラム法では、教師あり学習が用いられます。
CBOWとは反対に、中心語を入力として付与し、その周辺語の予測を出力する学習を行います。
単語の周りにどんな単語が現れる可能性があるかを理解することで、単語の意味関係を把握できるようになります。
Word2Vecの導入
ここからはGoogle colabを使用して、Word2Vecを実装していきます。
今回紹介するコードは以下のボタンからコピーして使用していただくことも可能です。
Google colabによる導入
まずはGoogleドライブをマウントして、作業フォルダを作成します。
from google.colab import drive
drive.mount('/content/drive')
!mkdir -p '/content/drive/My Drive/huggingface_transformers_demo/'
%cd '/content/drive/My Drive/huggingface_transformers_demo/'
必要なライブラリをインストールします。
!pip install transformers[ja]
学習済モデルを取得
まずは学習済モデルを用意します。
今回はこちらのモデルを使用させていただくことにします。
(出典:https://github.com/singletongue/WikiEntVec/releases/download/20190520/jawiki.word_vectors.200d.txt.)
早速、モデルをダウンロードします。
!wget https://github.com/singletongue/WikiEntVec/releases/download/20190520/jawiki.word_vectors.200d.txt.bz2
!bzip2 -d jawiki.word_vectors.200d.txt.bz2
ダウンロードしたモデルを読み込んでおきます。
gensimというライブラリを使用することで、Word2Vecを簡単に扱えるようになります。
# モデルを読み込む
from gensim import models
w2v_model = models.KeyedVectors.load_word2vec_format('jawiki.word_vectors.200d.txt', binary=False)
以上で準備が完了しました。
Word2Vecによる単語ベクトル
単語ベクトル
まずは単語ベクトルを表示しています。
「男」という単語を例にします。
word = "男"
# Word2Vecで作成した単語ベクトルのshape
word_vec = w2v_model.wv.__getitem__(word)
print(word_vec.shape)
# Word2Vecで作成した単語ベクトル
print(word_vec)
実行すると、以下の通り出力されます。
(200,)
[ 0.015809 -0.3251379 0.05244135 0.06446267 0.05560444 -0.10130245
-0.10580788 -0.14394113 -0.34319535 -0.10822236 -0.19849339 -0.26917374
0.22541721 -0.07668435 0.48141098 -0.147957 0.43515927 -0.03782997
0.2389156 0.01180182 0.1836466 0.16968328 -0.3920643 0.12813346
-0.20881392 0.19826704 0.03449377 -0.04753834 0.2486179 -0.14926115
-0.02117037 -0.3065304 0.3936741 -0.10212087 -0.04573085 -0.1337663
-0.22365484 0.16874568 0.07610422 0.09013756 -0.29010847 0.18294546
-0.5175267 0.07319862 -0.18262151 -0.05056991 0.1262936 -0.02267754
-0.06510926 -0.01428416 -0.09494394 0.08261232 0.12032219 -0.01902988
0.04504069 -0.13478482 -0.17759144 -0.14752197 -0.02539819 0.10928936
-0.09864278 0.08571469 0.13544586 0.10696616 -0.03865746 -0.1618845
0.04960717 -0.15753698 0.3267987 0.08656162 -0.01817252 0.13675034
-0.3327987 0.2530842 0.12734842 0.06555606 0.160463 0.06241005
0.23274134 -0.11655759 0.16264422 0.53337 0.15273613 0.36126742
0.12710397 -0.0883073 0.16111715 -0.07801783 -0.05251171 -0.03803135
0.36643884 -0.05368368 -0.08609699 -0.19484329 0.12814972 0.00330684
0.07512157 0.15090348 -0.10693323 0.01813235 -0.2135235 -0.00780941
0.05442232 0.24011889 -0.51129556 -0.638149 0.02244863 -0.22941312
-0.23984718 -0.02993166 -0.05658468 -0.01124568 0.2406439 0.45328262
0.19353937 0.30052927 -0.11227108 -0.0850492 -0.16737182 -0.33904943
0.10570832 -0.09934106 0.03946824 -0.2834313 -0.04729119 0.2697491
0.12789859 -0.17199114 0.41526002 -0.23899308 -0.45907646 -0.12020862
-0.14896242 -0.04573169 -0.09786802 -0.1763842 0.12901594 0.02436519
0.00428179 -0.36937413 -0.4695615 -0.22230782 0.2830068 0.2566159
0.04325378 0.05985418 0.3014291 0.09938446 0.05811239 0.39835015
0.164487 -0.14685854 -0.17889863 -0.02350353 -0.1620599 -0.3441901
0.26197276 -0.294521 -0.05924391 -0.23041128 0.5574968 0.21000126
0.10179501 0.01910508 0.3779993 0.01058383 -0.28017193 0.15513653
-0.2171893 -0.27293232 0.1677527 -0.14999865 0.26054642 0.21831411
-0.07688119 0.25424448 0.08557764 -0.08890117 -0.0325109 -0.18752629
0.13194856 0.0568304 0.11748125 0.40530595 -0.3771344 -0.35576954
-0.22595452 -0.15970773 -0.22661117 0.03037297 0.09092331 -0.01097557
0.40739244 -0.01088747 0.28517485 0.10055861 -0.29882434 0.04821419
0.15081635 -0.32244784]
類似度の高い単語を表示
単語をベクトル化することで、内積を計算できるようなります。
これにより、単語間の類似度を計算することが可能となりました。
まずは、「勉強」という単語の類似度が高い単語を表示する例を紹介します。
results = w2v_model.most_similar(positive=['勉強'])
for result in results:
print(result)
実行すると、以下の通り出力されます。
('勉学', 0.8039138913154602)
('受験勉強', 0.7886025905609131)
('通い', 0.7718825340270996)
('学業', 0.7624354362487793)
('猛勉強', 0.7539629936218262)
('独学', 0.744247317314148)
('励ん', 0.7085117697715759)
('仕事', 0.7079863548278809)
('学び', 0.7079614400863647)
('教える', 0.7043171525001526)
コサイン類似度であるため、1に近いほど、より似ているということになります。
勉強に意味の近い単語が表示されていることがわかります。
複数単語の類似度が高い単語を表示する
複数単語の類似度が高い単語を表示する例を紹介します。
# 複数の単語で類似度の高いものを上から10個表示する
results = w2v_model.most_similar(['北海道', 'ランチ'])
for result in results:
print(result)
実行すると、以下の通り出力されます。
('グルメフェスタ', 0.6769476532936096)
('ELLCUBE', 0.6654763221740723)
('食材王国', 0.6653890609741211)
('新富良野プリンスホテル', 0.661788284778595)
('ステラプレイス', 0.6588473320007324)
('サッポロファクトリー', 0.6588059067726135)
('YAKINIKU', 0.657869815826416)
('FMわっぴ', 0.6575604677200317)
('空弁', 0.6536294221878052)
('函館', 0.6521193981170654)
単語の意味を足し引きして類似度が高い単語を表示する
単語の意味を足し引きして、類似度が高い単語を表示する例を紹介します。
# 単語の意味を足し引きして、類似度の高いものを上から10個表示する
results = w2v_model.most_similar(positive=['エンジニア'], negative=['プログラミング'])
for result in results:
print(result)
実行すると、以下の通り出力されます。
('チーフ', 0.418521523475647)
('技師', 0.3822118043899536)
('マネージングディレクター', 0.37540167570114136)
('責任者', 0.37215274572372437)
('録音技師', 0.3719862699508667)
('チーフエアロダイナミシスト', 0.3683624267578125)
('フィオリオ', 0.3636731207370758)
('重役', 0.3555722236633301)
('マネージング・ディレクター', 0.35550376772880554)
('働い', 0.3503777086734772)
2つの単語の類似度を算出する
2つの単語の類似度を表示する例を紹介します。
# 2つの単語の類似度を算出する
results = w2v_model.similarity('小学校', '中学校')
print(results)
実行すると、以下の通り出力されます。
0.90959257
「小学校」と「中学校」という単語をはかなり似ていることがわかりました。
Word2Vecによる文ベクトル
「私はラーメンが大好きです」という文章を例に、文章をベクトルに変換してみます。
単語に分割するために、まずは形態素解析を行います。
text = "私はラーメンが大好きです。"
import ipadic
from fugashi import GenericTagger
fugger = GenericTagger(ipadic.MECAB_ARGS)
# 形態素解析
sentence = [w.surface for w in fugger(text)]
print(sentence)
実行すると、以下の通り出力されます。
['私', 'は', 'ラーメン', 'が', '大好き', 'です', '。']
文章を単語に変換できたので、これを先ほどと同様にベクトル化していきます。
import numpy as np
# 単語ごとにベクトル化して平均値を算出
if len(sentence) >= 0:
for word in sentence:
if word == sentence[0]:
sentence_vec = w2v_model.wv.__getitem__(word)
else:
sentence_vec = sentence_vec + w2v_model.wv.__getitem__(word)
sentence_vec = sentence_vec / len(sentence)
# 文ベクトルのshape
print(sentence_vec.shape)
# 文ベクトル
print(sentence_vec)
実行すると、以下の通り出力されます。
(200,)
[-3.38039212e-02 -1.56029090e-01 1.54657945e-01 -3.83586586e-02
1.96839422e-02 1.10597476e-01 3.82851996e-02 1.84145197e-01
-4.18440282e-01 1.45817667e-01 -1.23335652e-01 -2.24432334e-01
8.53392407e-02 3.71390656e-02 1.95888042e-01 -1.45376818e-02
1.20641418e-01 -3.11215930e-02 -6.52358606e-02 -2.08409697e-01
6.65912181e-02 1.87993914e-01 -3.57952595e-01 2.86019053e-02
-5.84483929e-02 1.37063876e-01 -6.13732152e-02 8.75066891e-02
-1.01501249e-01 -1.76455691e-01 -7.32605383e-02 -1.24338292e-01
1.55234262e-01 -1.00432098e-01 3.13152671e-02 5.52263856e-02
-9.56741050e-02 1.85521558e-01 -2.16468945e-01 3.10559850e-02
-2.08864257e-01 2.00573094e-02 -7.27767795e-02 -1.60992041e-01
-2.11598709e-01 -2.22231701e-01 2.98906863e-01 8.53177160e-02
2.37533227e-01 4.83748969e-03 -1.10598141e-02 -6.67546019e-02
7.91712627e-02 -2.59290846e-05 -2.41242319e-01 -1.39898196e-01
-8.41563102e-03 1.61408693e-01 -1.08526506e-01 9.49750096e-02
-2.03567773e-01 -2.08939895e-01 4.13699806e-01 -3.82477567e-02
-9.20958668e-02 -2.97504459e-02 -4.46051881e-02 -3.13347012e-01
9.33347866e-02 2.46902749e-01 2.87009100e-03 2.59106122e-02
3.94271314e-02 5.81648573e-02 1.45566911e-01 -1.60978008e-02
2.60255992e-01 -2.43778098e-02 3.49167615e-01 -6.50708005e-02
2.76559561e-01 3.18176985e-01 1.52718559e-01 -2.11335614e-01
-1.76229820e-01 4.72730063e-02 6.62040487e-02 -6.29856959e-02
1.42167762e-01 -2.90722847e-02 1.51973248e-01 -2.87761725e-02
-1.73250049e-01 2.74232090e-01 -3.19385566e-02 2.75703110e-02
-3.22453715e-02 -3.10710728e-01 -1.81898847e-03 -1.56469956e-01
7.31044039e-02 -1.60759553e-01 1.82325006e-01 -1.33158475e-01
-2.38456815e-01 -1.57503098e-01 -2.45940953e-01 -2.42874309e-01
-5.23951463e-02 1.29076973e-01 2.53433734e-01 1.98730364e-01
5.82244918e-02 2.62177020e-01 -2.90772878e-02 1.76764011e-01
-1.27965659e-02 -7.29715303e-02 -5.68534508e-02 -1.99703909e-02
-3.38648469e-03 7.42036924e-02 -4.21568677e-02 -1.43283382e-01
1.52011514e-01 7.62781501e-02 2.30337352e-01 2.22422585e-01
1.56676978e-01 -1.68480262e-01 -1.13101348e-01 1.67655516e-02
-2.38538235e-01 6.55386969e-02 -1.82916537e-01 -1.75796062e-01
2.50540763e-01 1.45006448e-01 1.97284669e-01 -5.20360470e-02
-7.55599886e-02 -2.00327277e-01 -1.70799661e-02 2.11269885e-01
1.34679854e-01 1.73128933e-01 -3.80705595e-02 7.92996362e-02
5.70074981e-03 7.65080079e-02 -1.62488762e-02 -1.84523463e-02
-3.41796070e-01 5.28866649e-02 5.60105853e-02 -2.02509314e-01
3.42471972e-02 -8.21093563e-03 -1.30222753e-01 -8.96937177e-02
2.74617553e-01 -1.18117435e-02 8.87764469e-02 -2.31986549e-02
1.44019201e-01 7.46472925e-02 -2.05968723e-01 2.23734587e-01
-2.22577542e-01 -3.95089947e-02 1.49537176e-01 -2.31362343e-01
8.64526480e-02 -8.93972889e-02 5.82956672e-02 1.71734840e-01
5.83670437e-02 1.26397565e-01 -1.73176616e-01 -4.06415462e-02
1.48717672e-01 2.04361640e-02 -6.58475840e-03 2.02337071e-01
-3.48338544e-01 1.48159713e-01 -2.32268162e-02 1.62580177e-01
-1.19664386e-01 1.41115084e-01 8.63767862e-02 -9.67000127e-02
1.30110368e-01 -8.43105391e-02 -1.02745868e-01 2.25609407e-01
1.32621378e-01 3.22942078e-01 7.39117200e-03 -1.26509741e-01]
入力した文章のベクトルにすることができました。
Word2Vecによる文ベクトルと文章類似度
最後にまとめとして、2つの文章の類似度を測定してみます。
sentences = ["私はラーメンが好きです","チャーシューメンが好きです"]
import ipadic
from fugashi import GenericTagger
import numpy as np
from numpy import dot
from numpy.linalg import norm
fugger = GenericTagger(ipadic.MECAB_ARGS)
sentence_0 = [w.surface for w in fugger(sentences[0])]
sentence_1 = [w.surface for w in fugger(sentences[1])]
def create_sentence_vec(sentence):
if len(sentence) >= 0:
for word in sentence:
if word == sentence[0]:
sentence_vec = w2v_model.wv.__getitem__(word)
else:
sentence_vec = sentence_vec + w2v_model.wv.__getitem__(word)
sentence_vec = sentence_vec / len(sentence)
return sentence_vec
sentence_vec_0 = create_sentence_vec(sentence_0)
sentence_vec_1 = create_sentence_vec(sentence_1)
similarity_with_word2vec = dot(sentence_vec_0, sentence_vec_1)/(norm(sentence_vec_0)*norm(sentence_vec_1))
print(similarity_with_word2vec)
実行すると、以下の通り出力されます。
0.9116959
ベクトル化することで、2つの文章が似ていることを定量的に評価することができました。
まとめ
最後までご覧いただきありがとうございました。
今回の記事では単語の分散表現の概要と、Word2Vecの基本的な実装方法を紹介しました。
このシリーズでは、自然言語処理全般に関するより詳細な実装や学習の方法を紹介しておりますので、是非ご覧ください。