LoginSignup
11
11

More than 5 years have passed since last update.

単語埋め込み表現を活用したトピック数Nの決定

Last updated at Posted at 2017-12-20

本記事は自然言語処理アドベントカレンダー第20日です。

自然言語処理 Advent Calendar 2017

概要

本記事ではWord2Vecによる単語埋め込みモデルを用いてトピックモデルにおけるトピック数Nの決定を行います。

トピックモデルを用いたソフトクラスタリングを用いていて、どのようなトピック数Nが妥当かを把握したいことがあります。

妥当なトピック数Nが把握できれば、パラメータチューニングを自動化したり、ある程度の数値的な裏付けをトピック数に与えられます。

単語埋め込みモデルやトピックモデルについて云々するのはスキップします。これらの詳細について気になる方がいらっしゃいましたら下記の2つの書籍をご覧ください。

トピックモデルをどう評価するか

トピックモデルの評価指標としては次の2つがあります

  • Perplexity: モデルの予測性能を測るための指標
  • Coherence: 抽出されたトピックの品質を評価する指標

Coherence評価指標についてはほくそ笑むさんの記事が参考になります。そちらを御覧ください

上記の論文まとめの紹介二つ目(Automatic Evaluation of Topic Coherence)にもあるように、Coherence評価指標としてはトピック内トップ単語の単語間類似度で測る場合があり、その類似度として単語埋め込みモデルを用いたものが提案されています。

下記の論文はトピックモデルの評価指標を単語埋め込みモデルを用いたCoherenceを用いて計測し、クラウドソーシングを用いた人間の判断と比較しています。

具体的には

  • トピックごとのCoherenceの順序と、人間の判断による順序と比較
  • トピックA, Bを2つ並べて下記の三通りの選択肢についてCoherenceを用いた場合と人間の判断とが合致しているかどうかを判定
    • トピックAの方がより望ましい
    • トピックBの方がより望ましい
    • どちらでもない

の2つを評価しています。

Coherenceは様々なメトリクス(PMI, LSA, Word2Vec)を用いて計算したものが用いられていますが、結果的に従来のCoherence計算方法(PMI, LSA)を用いたのよりもWord2Vecを用いたもののほうが安定して人間の判断と合致する割合が高いという結果が得られています。

最近になってPythonでトピックモデルを扱うためのライブラリ、gensimにもWord2VecでCoherenceを計算する機能が組み込まれたようで、導入もしやすくなっています。

環境

CoherenceをWord2Vecを用いて計算し、トピックの良さを判定するサンプルを作ってみましょう。以下環境情報です

  • Anaconda3 3.4.1
  • mecab-python3 == 0.7
  • gensim == 3.2.0

データセット

今回用いるデータセットは以下になります。XMLの前処理が面倒ですが、適度な大きさのサンプルサイズで、かつサンプルごとの文章が短すぎないため、トピックモデルのような共起関係を捉えるアルゴリズムにはよくマッチしています

コード概要

実際にトピックモデルのCoherenceを計算するコードを記載したノートブックはこちらになります。

TopicCoherenceW2V

  • データセットに特有の前処理
  • mecab-ipadic-neologdによる分かち書き
  • 特定の形態素の抽出
  • ライフログドキュメントごとのTFIDF表現の抽出

等はありふれた内容なのでコードを読んでみてください(リクエストがあれば解説追記します)

今回はトピックモデルのアルゴリズムとしてNMFを用いており、NMFに対するCoherenceを計算しているメソッドについて説明してみます。

import numpy as np
import pandas as pd

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import NMF

from gensim.corpora.dictionary import Dictionary
from gensim.models.coherencemodel import CoherenceModel
from gensim.models import Word2Vec
from gensim.matutils import Sparse2Corpus

def coherence_for_topic_n(topic_n: int, nmf: NMF, X: pd.Series, wv: KeyedVectors = w2v_vectors.wv) -> pd.DataFrame:
    tfidf_vectorizer = TfidfVectorizer(token_pattern=r'\S+', norm='l1')
    tfidf = tfidf_vectorizer.fit_transform(X)
    word_df = pd.DataFrame({'word': tfidf_vectorizer.get_feature_names()})
    nmf.set_params(n_components = topic_n) # -- (1)
    _ = nmf.fit_transform(tfidf) # -- (2)
    H = nmf.components_ # -- (3)
    topY = 5
    topY_indices = H.argsort(axis=1)[:, :-(topY + 1):-1]
    topic_rank_index_df = pd.DataFrame(topY_indices).stack().reset_index()\
        .rename(columns={'level_0': 'topic', 'level_1': 'rank', 0: 'word_idx'})
    topic_rank_word_df = pd.merge(
        topic_rank_index_df,
        word_df,
        left_on='word_idx', right_index=True
    ).drop('word_idx', axis=1).sort_values(['topic', 'rank']) # -- (4)
    topics = topic_rank_word_df.groupby('topic') ['word'].apply(lambda words: words.tolist()).tolist() # -- (5)
    corpus = Sparse2Corpus(tfidf) # -- (6)
    corpus_dict = Dictionary.from_corpus(corpus, word_df.to_dict()['word']) # -- (7)
    coherence_model = CoherenceModel(
        topics = topics,
        corpus = corpus,
        dictionary = corpus_dict,
        keyed_vectors = wv,
        coherence='c_w2v',
        topn=topY
    ) # -- (8)
    result = {'n_components': topic_n, 'coherence': coherence_model.get_coherence()} # -- (9)
    return result
  1. NMFのハイパーパラメータとしてトピック数Nをセットします。
  2. NMFにTFIDF特徴量を与えて行列分解を実施します。
  3. NMFの行列分解の結果のうち右側の行列をHとして抽出します。
  4. NMFで得られたトピックごとにトップ5単語をデータフレームとして展開します。
  5. 後でgensimのCoherenceModelの引数に渡すためにデータフレームをList[List[str]]の形式に変換します。
  6. gensimのコーパスをTFIDF特徴量から算出します。Sparse2Corpusは引数としてTfidfVectorizerの出力である疎行列をとれます。
  7. gensimのコーパスと単語リストから辞書を生成します。
  8. Word2vecによる類似度計算の方法を指定してCoherenceを計算するモデルを生成します。
  9. Coherenceを計算します。

尚、gensimのコーパス、辞書などの概念については下記エントリが詳しいです。

結果

上記コードによって計算されたトピック数ごとのCoherenceのグラフは下記のようになります

coherence.png

このグラフの範囲で考えるとトピック数6の時のモデルが最も品質がいいということになります。

Coherenceの高いトピック数6と低めのトピック数8の結果を比較してみましょう

topic
0      [ご飯, 善, 体重, カロリー, コーヒー]
1    [風邪, 体, マスク, インフルエンザ, 電車]
2      [おにぎり, 熱, シャワー, 体調, 食欲]
3      [風呂, 帰り, シャワー, コンビニ, 体]
4        [サラダ, 食事, 大根, 白菜, 朝食]
5        [ストレッチ, 腰, 首, 肩, ポーズ]
topic
0       [ご飯, 善, 体重, カロリー, コーヒー]
1         [ポテト, 口, サイズ, ご飯, 幼少]
2       [おにぎり, 熱, シャワー, 体調, 食欲]
3    [子ども, 自転車, 保育園, お好み焼き, 運動]
4       [風呂, 帰り, シャワー, コンビニ, 体]
5         [ストレッチ, 腰, 首, 肩, ポーズ]
6           [風邪, 体, マスク, 電車, 別]
7         [サラダ, 食事, 大根, 白菜, 朝食]

感覚的なものでしかないですが、トピック数8の方はご飯系の話題がばらついてしまっている感があり、トピック数6の時と比較してモデルとして優れていないようにも思えます(本当に感覚的ですが)

参考

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11