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



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

[自然言語処理 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/nlp)

## 概要

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

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

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

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

- [深層学習による自然言語処理 (機械学習プロフェッショナルシリーズ)](https://www.amazon.co.jp/dp/4061529242)
- [トピックモデル (機械学習プロフェッショナルシリーズ)](https://www.amazon.co.jp/dp/B018K6C986)

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

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

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

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

- [トピックモデルの評価指標 Coherence に関する論文まとめ - ほくそ笑む](http://d.hatena.ne.jp/hoxo_m/20160122/p1)

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

- [An Analysis of the Coherence of Descriptors in Topic Modeling](http://irserver.ucd.ie/bitstream/handle/10197/6482/insight_publication.pdf?sequence=1)

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

- [Using Word Embedding to Evaluate the Coherence of Topics from Twitter Data](http://www.dcs.gla.ac.uk/~anjie/papers/fang_sigir_2016_we.pdf)

具体的には

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

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

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

[最近になってPythonでトピックモデルを扱うためのライブラリ、gensimにもWord2VecでCoherenceを計算する機能が組み込まれた](https://github.com/RaRe-Technologies/gensim/pull/1416)ようで、導入もしやすくなっています。

## 環境

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

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

## データセット

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

- [健康アドバイスデータセット - 京都大学 黒橋・河原研究室](http://nlp.ist.i.kyoto-u.ac.jp/index.php?%E5%81%A5%E5%BA%B7%E3%82%A2%E3%83%89%E3%83%90%E3%82%A4%E3%82%B9%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88)

## コード概要

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

[TopicCoherenceW2V](https://github.com/UsrNameu1/TopicCoherenceW2V/blob/master/TopicCoherenceW2V.ipynb)

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

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

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


```python
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のコーパス、辞書などの概念については下記エントリが詳しいです。

- [gensim入門](https://qiita.com/u6k/items/5170b8d8e3f41531f08a)

## 結果

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

![coherence.png](https://qiita-image-store.s3.amazonaws.com/0/32405/5ec2d544-cfda-2c8c-19e9-37b41c318ec9.png)

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

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

```
topic
0      [ご飯, 善, 体重, カロリー, コーヒー]
1    [風邪, 体, マスク, インフルエンザ, 電車]
2      [おにぎり, 熱, シャワー, 体調, 食欲]
3      [風呂, 帰り, シャワー, コンビニ, 体]
4        [サラダ, 食事, 大根, 白菜, 朝食]
5        [ストレッチ, 腰, 首, 肩, ポーズ]
```

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

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

## 参考

- [トピックモデルの評価指標 Coherence に関する論文まとめ - ほくそ笑む](http://d.hatena.ne.jp/hoxo_m/20160122/p1)
- [An Analysis of the Coherence of Descriptors in Topic Modeling](http://irserver.ucd.ie/bitstream/handle/10197/6482/insight_publication.pdf?sequence=1)
- [Using Word Embedding to Evaluate the Coherence of Topics from Twitter Data](http://www.dcs.gla.ac.uk/~anjie/papers/fang_sigir_2016_we.pdf)
- [健康アドバイスデータセット - 京都大学 黒橋・河原研究室](http://nlp.ist.i.kyoto-u.ac.jp/index.php?%E5%81%A5%E5%BA%B7%E3%82%A2%E3%83%89%E3%83%90%E3%82%A4%E3%82%B9%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88)
- Tetsuaki Nakamura, Takashi Awamura, Yiqi Zhang, Eiji Aramaki, Daisuke Kawahara and Sadao Kurohashi, Toward an Advice Agent for Diet and Exercise Based on Diary Texts, In proceedings of 2015 AAAI Spring Symposium Series - Ambient Intelligence for Health and Cognitive Enhancement, pp.43-48, Mar.2015.
- [word2vecの学習済み日本語モデルを公開します - カメリオ開発者ブログ](http://aial.shiroyagi.co.jp/2017/02/japanese-word2vec-model-builder/)