Python+Doc2Vecで似た意味を持つ文章を調べる
以前、単語をベクトル化できる技術「Word2Vec」を用いて似た意味を持つ単語を調べてみました。
今度は、文章をベクトル化できる技術「Doc2Vec」を用いて、似た意味を持つ文章を調べてみます。
Doc2Vecは2014年に発表された方法で、語順・意味なども含めて文章を捉えられる手法として非常に有名になりました。
それまでは単語の出現回数を見たり、人間が登録した辞書データを用いて意味を判断するくらいしかできませんでした。
まさにセンセーショナルで、映画で見るような「AI」が現実になりつつあるのを感じたものです。
(しかし、そこまで正確に意味を判断できる訳ではないことを付け加えておきます。)
そんなDoc2Vecも、Pythonを使えば誰でも簡単に動かすことができます。
早速やってみましょう。
PythonによるDoc2Vecの実行方法
学習モデルの読み込み
自分で用意したデータで学習させることも出来ますが、既に大量の文章で学習させた学習モデルが幾つか公開されていますので、今回はそれを拝借します。
日本語の文章を扱うので、「日本語WIKIPEDIAで学習したDOC2VECモデル」で公開されている「dbow300d」を使用させて頂きました。
もう1つの「dmpv300d」とは学習の方法が違うのですが、一概にどちらが良いとは言えません。
今回は単純に容量の軽い方にしました。
(軽い方と言っても5GB以上ありますので、パソコンの容量にはご注意を・・・)
ダウンロードしたら、以下のようにするだけで読み込みます。
1 2 |
from gensim.models.doc2vec import Doc2Vec model = Doc2Vec.load("jawiki.doc2vec.dbow300d.model") |
パソコンのスペックにもよりますが、この読み込みにも少々時間がかかります。
ベクトル化したい文章の読み込み
さて、モデルを読み込んだら、続いてはベクトル化したい文章を読み込みます。
今回は「livedoorニュースコーパス」を読み込み、最終的に指定したニュースと近い内容のニュースを推定して提示してくれるAIを作ってみましょう。
ということで、livedoorニュースコーパスをダウンロードし、以下のように内容を全て読み込みます。
1 2 3 4 5 6 7 8 9 10 11 12 |
#ニュースコーパスの取り込み import glob documents = [] for x in glob.glob("text/*/*"): text = "" with open(x) as f: next(f) next(f) for line in f: text = text + line f.close() documents.append(text) |
「text」というディレクトリを作成し、その配下にデータを置きました。
globパッケージを使ってそのフォルダ内のデータを全て読み込んでいます。
また、livedoorニュースコーパスでは、全て上の2行がURLと日時情報になっているので、next(f)を2回でそれを排除しています。
文章のベクトル化
お次は読み込んだ文章をベクトル化します。
日本語の場合は、まず形態素解析をしなければいけません。
形態素解析器はお好みでOKですが、今回はjanomeを使ってみます。
以下のように、テキストを入れるとjanomeにより単語を分割し、スペースを挟んで連結してくれるメソッドを作成しました。
1 2 3 4 5 6 7 8 |
from janome.tokenizer import Tokenizer def sep_by_janome(text): t = Tokenizer() tokens = t.tokenize(text) docs=[] for token in tokens: docs.append(token.surface) return docs |
このメソッドを使って、先程の文章を以下のように分割した後に、infer_vectorメソッドを使えば簡単にベクトル化できます。
1 2 3 4 |
#ベクトル化 document_vecs=[] for d in documents: document_vecs.append(model.infer_vector(sep_by_janome(d))) |
この「input_vec」こそが、検索対象の文章をすべてベクトル化したデータベースのようになっています。
類似文章の検索
ここまで来たらいよいよ文章の検索が出来ます。流れとしては、
「調べたいテキストの読み込み」⇒「そのテキストのベクトル化」⇒「最も近いベクトルの取得」
という感じです。
まずは調べたい文章を入力します。
試しに「it-life-hack」の一番テキストサイズが小さい「it-life-hack-6842412.txt」に近いニュースを検索してみましょう。
1 2 3 4 5 6 |
with open("text/it-life-hack/it-life-hack-6842412.txt",encoding="utf-8") as f: next(f) next(f) for line in f: text = text + line f.close() |
ちなみに、以下のようなニュース内容になっています。
プレイ可能なGoogleロゴ第四弾! Googleロゴがスポーツ関連画像に変化第十一弾
実際にプレイ可能なGoogleロゴ第四弾。昨日、8月9日に紹介したGoogle検索のトップページのロゴは、カヌースラロームだった。さて、今回のGoogleロゴのトップページはサッカー(PK戦)である。ここまで来ると最終日までプレイ可能なロゴになりそうな雰囲気だ。
キーボードの左右矢印キーを交互に押すことで左右の移動、スペースキーでジャンプの動作を行う。マウスの左右移動で同様に左右に移動し、マウスのクリックでジャンプする。相手のキックは低めのゴロで来る時もあれば、高めのボレーで飛んでくることもある。それに合わせてボールを弾けばOKだ。
このテキストを、先程と同様にベクトル化します。
1 2 |
tokens = sep_by_janome(text) input_vec = model.infer_vector(tokens) |
では、ベクトルの近いデータを取得してみましょう。ベクトルの近さは「コサイン類似度」を用いて計測します。
コサイン類似度については下記の記事で詳しく述べています。
以下のようにすると、「入力文のベクトル」vs「計算しておいた全てのベクトル」で、類似度が高い順に5件表示することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#コサイン類似度の計算+ランキング化 import numpy as np rank_size = 5 v1 = np.linalg.norm(input_vec) cos_sim = [] for v2 in document_vecs: cos_sim.append( np.dot(input_vec,v2)/(v1*np.linalg.norm(v2)) ) doc_sort = np.argsort(np.array(cos_sim))[::-1] cos_sort = sorted(cos_sim,reverse=True) for i in range(rank_size): print(cos_sort[i]) print(documents[doc_sort[i]]) |
doc_sortには似ている順に並び替えられたテキストデータの番号が、cos_sortには降順に並び替えられた類似度の値が格納されています。
結果を見てみます。(全文掲載すると長いので、上の方だけ掲載します。)
No.1 : 類似度 0.96
プレイ可能なGoogleロゴ第四弾! Googleロゴがスポーツ関連画像に変化第十一弾
実際にプレイ可能なGoogleロゴ第四弾。昨日、8月9日に紹介したGoogle検索のトップページのロゴは、カヌースラロームだった。さて、今回のGoogleロゴのトップページはサッカー(PK戦)である。ここまで来ると最終日までプレイ可能なロゴになりそうな雰囲気だ。
No.2 : 類似度 0.70
プレイ可能なGoogleロゴ第三弾! Googleロゴがスポーツ関連画像に変化第十弾
実際にプレイ可能なGoogleロゴ第三弾。昨日、8月8日に紹介したGoogle検索のトップページのロゴは、バスケット(フリースロー)だった。さて、今回のGoogleロゴのトップページはカヌースラロームである。
No.3 : 類似度 0.69
本日はプレイ不可なロゴ! Googleロゴがスポーツ関連画像に変化第十二弾
実際にプレイ可能なGoogleロゴが四連続で続いていたのでこのまま最終日まで行くのかと思っていたがどうやら打ち止めみたいで、本日はプレイできません。昨日、8月10日に紹介したGoogle検索のトップページのロゴは、サッカー(PK戦)だった。さて、今回のGoogleロゴのトップページは新体操である。
No.4 : 類似度 0.66
本日のGoogleロゴはプレイ可能なハードル Googleロゴがスポーツ関連画像に変化第八弾
昨日、8月6日に紹介したGoogle検索のトップページのロゴはやり投だった。女子サッカーのメダル確定に沸いていると思うが本日はハードルだ。女子卓球団体もメダル確定と、今回のオリンピックは女性陣の活躍が目立つ大会になっている(と筆者は思っている)。
No.5 : 類似度 0.64
オリンピック最終日ロゴは閉会式! Googleロゴがスポーツ関連画像に変化第十三弾
7月27日から本日8月12日まで、各種競技で熱い戦いを繰り広げたロンドンオリンピックも、本日が最終日だ。男子サッカーは残念だったが、今オリンピックは女子の活躍が非常に目立つ大会だったように思う。
1番上は入力したニュースの内容そのままなので当然です。
しかし2番目以降も全て、同じGoogleロゴの話題が取れているようです。
今回はニュースの文章を用いて検索しましたが、もちろんニュース以外の適当なテキストでも検索することができます。
最後に
以上がPythonを用いて文章の類似度を計算する方法でした。
注意としては、Doc2Vecでは、同じ文章でも計算する度にベクトルが変化します。
なので、同じデータなのに結果が違うこともあり得ます。
それが困る場合は、別の手法を用いるか、Word2Vecで文章中の単語を全てベクトル化して、最後に平均するといった方法もあります。
あれから更に高度な手法も多数発表されていますが、シンプルに文章をベクトル化する技術としてはまだまだDoc2Vecも有力手法のひとつです。
自然言語処理を扱うのなら、是非一度は試しておきたい手法です。