【Python】Doc2Vecで類似文書の表示
概要
自然言語処理分野において単語の分散表現を獲得する手法としてWord2Vecがありますが、その文章版、つまり文章を分散表現することができる手法としてDoc2Vecがあります。今回はPythonでDoc2Vecの使い方について勉強しました。
タスク設定
文章群をDoc2Vecでベクトル化し、そのなかの一つの文章を選び、それと類似度の高い文書を文書群の中から選んで表示する。
使用する諸々
- Mecab 0.996
- gensim
- livedoor News
MeCabは以下で構築したものを使います。
Step1:データ取得
今回は以下からlivedoor Newsコーパスをダウンロードして使用させて頂きました。ありがとうございます。
livedoor ニュースコーパス:https://www.rondhuit.com/download.html#ldcc
ldcc-20140209.tar.gzをサイトからダウンロードし7zipで展開しました。
ディレクトリ構造としてはこのような形になっていて、各フォルダの中に.txtの形式でコーパスが格納されていました。
text
|- dokujo-tsushin
|- it-life-hack
|- kaden-channel
|- livedoor-homme
|- movie-enter
|- peachy
|- smax
|- sports-watch
|- topic-news
|- CHANGES.txt
|- README.txt
今回はkaden-channelとsports-watchデータを使用することにします。
Step2:gensimのインストール
pip install gensim
Step3:モデル作成
データ読み込み
まずは展開したデータをglobで読み込みます。kaden-channelとsports-watchのみにしています。
import os
from glob import glob
os.chdir('[展開したディレクトリ]\\text')
text = []
#家電チャンネル読み込み
for file in glob('kaden-channel\\*.txt'):
with open(file,encoding="utf-8") as f:
text.append([f.read()])
l_kaden = len(text)
print('家電チャンネル記事数:',l_kaden)
#スポーツウォッチ読み込み
for file in glob('sports-watch\\*.txt'):
with open(file,encoding="utf-8") as f:
text.append([f.read()])
print('スポーツチャンネル記事数:',len(text)-l_kaden)
家電チャンネル記事数: 865
スポーツチャンネル記事数: 901
形態素解析
gensimのDoc2Vecに文章を渡すためには形態素解析をしておく必要があります。
以下のようにMeCabのparseメソッドを使用して分かち書きした上でリストに格納していきます。
import MeCab
m = MeCab.Tagger("-Ochasen")
text_wakati= []
for w in text:
text_wakati.append([d.split()[0] for d in m.parse(w[0]).splitlines()])
学習用データ準備
次にDoc2Vecメソッドに食わせるための学習用データセットを作っていきます。gensimのTaggedDocumentメソッドに分かち書きしたデータリストとインデックス番号を渡し、学習用データを作成します。
from gensim.models.doc2vec import TaggedDocument
cnt = 0
doc_train = []
for words in text_wakati:
doc_train.append(TaggedDocument(words,[cnt]))
cnt += 1
モデル作成
作ったdoc_trainリストをDoc2Vecに渡し、モデルを作成します。
from gensim.models.doc2vec import Doc2Vec
model = Doc2Vec(doc_train,dm=1, size=200, min_count=10, epochs=20)
パラメータ説明(やってみた感じエポック数は少なすぎるとあまり精度が上がらない印象でした。)
- dm=1でPV-DM
- size:ベクトル表現するときの次元サイズ
- min_count:無視する出現回数閾値
- epoch:エポック数
Step4:評価(定性)
記事の中から適当に一つ選んで、それと類似する上位5件のインデックスの記事を表示して、人間が見た時に似たような記事が選ばれているのかを評価してみます。
下記のコードでindexの数字を変えて任意の記事を選びます。上から865記事は家電チャンネルなので、以下のindex=1500の例はスポーツウォッチの記事から選んでいることになります。
index = 1500
sims = model.docvecs.most_similar(index)
sims
------
(1492, 0.7714306712150574),
(1398, 0.6727927923202515),
(1753, 0.6568506360054016),
(1501, 0.6562802791595459),
(1546, 0.6521874666213989),
(1560, 0.6450892686843872),
(1073, 0.6395328044891357),
(1106, 0.6377852559089661),
(1732, 0.6305040121078491),
(1585, 0.630274772644043)
print(text[index],'\n')
print(text[1492],'\n')
index 1500の記事はテニスの錦織圭選手関連の記事で、類似度トップで上がってきたindex 1492の記事も同じくテニスの錦織圭選手の話題が含まれる記事でした。大した前処理もせずにこんな簡単に類似文書がでてくるのはすごいですね。また、少なくとも家電チャンネルからは一つも選ばれていないため、それなりの学習はできている様子です。
ちなみに上記で選ばれた記事の中には女子サッカーの話題なども含まれていたので、もう少し前処理やパラメータチューニングを追い込んでみてもいいのかもと思いました。上でも書いていますが、epoch数を増やすとやはり精度が上がってくる感じがありました。端末のスペックがある場合はもっとepoch数を増やしてもいいのかもしれないです。
参考にさせて頂いたサイト
類似検索など:Doc2Vecについてまとめる
パラメータチューニングなど:Doc2Vecによる文書ベクトル推論の安定化について