微忘録

好奇心に記憶力がついていかない人のブログ

MALLETラッパーを用いた、gensimでの"Gibbs sampler"によるトピック推定を試す

LSIやLDAなどのトピック分析をPythonで実行するなら、gensimモジュールの利用が一般的です。しかしgensimでは変分ベイズ法による推定しか基本的に対応しておらず、その他のギブスサンプリングなどを用いるには少し工夫が必要です。

そこで今回はgensimにあるgensim.models.wrappers.LdaMallet()という、java製のオープンソースソフトであるMALLETのラッパー機能を用いて、Pythonかつgensimの環境でギブスサンプリングによるトピック推定をざっくりと流れだけ試します。

崩壊型ギブスサンプリングはldaモジュールで可能です。現行のscikit-learnモジュールのLDAは変分ベイズのみです。ご注意を。

なお以下の環境で準備します。詳細については適宜ググってください。

MALLETの準備

MALLETをダウンロードして、Apach antでディレクトリをビルドするだけ

  1. MALLETのダウンロード(こちらから Downloading MALLET
  2. MALLETのディレクトリにてantコマンドでビルド
  3. "BUILD SUCCESSFUL"が表示されたらビルド成功
  4. ディレクトリのパスを"Users/~.../mallet-2.0.8/bin/mallet"まで記憶

Pythonの実行

文書ごと単語リストの用意

トピック分析したい文書群(documents)リストを用意し、ストップワード除去を行う。
gensimチュートリアルを引用

documents = ["Human machine interface for lab abc computer applications",
              "A survey of user opinion of computer system response time",
              "The EPS user interface management system",
              "System and human system engineering testing of EPS",
              "Relation of user perceived response time to error measurement",
              "The generation of random binary unordered trees",
              "The intersection graph of paths in trees",
              "Graph minors IV Widths of trees and well quasi ordering",
              "Graph minors A survey"]

# 以下の7語をストップワードとして定義
stop_words = set('for a of the and to in'.split())

# 文を単語に分割し、ストップワードを除去した配列を作成
texts = [[word for word in document.lower().split() if word not in stop_words] for document in documents]

以下のような中身の単語リスト(texts)に格納されました。

    [['human', 'machine', 'interface', 'lab', 'abc', 'computer', 'applications'], ['survey', 'user', 'opinion', 'computer', 'system', 'response', 'time']]

辞書とコーパスの作成

gensimモジュールを呼び出し、文書ごと単語リストを元に辞書とコーパスを作成します。

from gensim import corpora, models
import gensim

#辞書とコーパスを作成
dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]

#コーパスをMALLET用の形式に変換します。
corpora.malletcorpus.MalletCorpus.serialize("./corpus.mallet", corpus)
mallet_corpus = corpora.malletcorpus.MalletCorpus("./corpus.mallet")

ギブスサンプリングによるトピックモデル推定

ビルドされたMALLETのパス(mallet_path)を指定し、用意した辞書とコーパスで3つのトピックを推定。

mallet_path = "Users/~.../mallet-2.0.8/bin/mallet"
lda = models.wrappers.LdaMallet(mallet_path, mallet_corpus, num_topics=3, id2word=dictionary)

推定されたトピックを確認。各トピック内容の解釈は割愛。

lda.print_topics()
 [(0,
  '0.182*"random" + 0.091*"user" + 0.091*"machine" + 0.091*"binary" + 0.091*"engineering" + 0.091*"paths" + 0.091*"human" + 0.091*"opinion" + 0.091*"minors" + 0.091*"lab"'),
 (1,
  '0.136*"perceived" + 0.136*"interface" + 0.136*"measurement" + 0.045*"survey" + 0.045*"graph" + 0.045*"eps" + 0.045*"time" + 0.045*"trees" + 0.045*"ordering" + 0.045*"testing"'),
 (2,
  '0.176*"quasi" + 0.118*"iv" + 0.118*"computer" + 0.059*"system" + 0.059*"relation" + 0.059*"generation" + 0.059*"management" + 0.059*"time" + 0.059*"response" + 0.059*"human"')]

最後に

今のところ「やってみた」系の記事では変分ベイズでの実装しか見当たらないので備忘録してみました。

gensimには他にも、Biei先生のフォーマットや、GibbsLDA++のフォーマットなどがあります(gensim: Corpora and Vector Spaces)。 そのため「gensimでデータ整形だけして他で実行するぜ!」な強者の方々は、その他の選択肢も是非ご検討ください。

※記事内容の改善点にお気付きの方は、コメントにてご一報いただけますと幸甚です。