潜在ディリクレ配分法
内山 雄司 (@y__uti)
2020-01-24 社内勉強会
自己紹介
内山 雄司 (@y__uti)
◦ http://y-uti.hatenablog.jp/ (phpusers-ja)
仕事
◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています
興味
◦ プログラミング言語処理系
◦ 機械学習
2020-01-24 社内勉強会 2
はじめに
潜在ディリクレ配分法 (Latent Dirichlet Allocation; LDA) とは?
◦ "collections of discrete data" の確率的生成モデル
2020-01-24 社内勉強会 3
典型的な例
Bag of Words 表現された文書集合
LDA を使うと何ができるのか?
◦ 直接的には
◦ 単語や文書を低次元のベクトルで表現できる
◦ 応用例
◦ 類似文書の検索
◦ 文書や単語の分類
社会
現象
認識
組織
学問
プログラム
言語
コンピュータ
人工
計算
下線部は Latent Dirichlet Allocation (Blei, et. al. JMLR 2003) の abstract の表現を引用
トピックモデル
トピック = 文書集合に内在する話題
◦ トピックによって「出現しやすい単語」が異なる
文書 = いくつかのトピックから出現した単語の集まり
2020-01-24 社内勉強会 4
https://ja.wikipedia.org/wiki/社会学
トピックモデルの推定
2020-01-24 社内勉強会 5
トピック = 文書集合に内在する話題
◦ トピックによって「出現しやすい単語」が異なる
文書 = いくつかのトピックから出現した単語の集まり
として、文書集合から次のような情報を抽出する
1. 各トピックからどんな単語が出やすいか?
◦ ある単語がどのトピックから出てきたか?と裏表の関係
2. 文書中に各トピックがどのくらいの割合で混ざっているか?
https://ja.wikipedia.org/wiki/社会学
単語や文書のベクトル表現
ある単語がどのトピックから出てきたか?の確率
◦ 単語のベクトル表現 (ベクトルの次元数 = トピック数)
ある文書に各トピックがどのくらいの割合で混ざっているか
◦ 文書のベクトル表現 (ベクトルの次元数 = トピック数)
ベクトルで表現することで「距離」を計算できる
2020-01-24 社内勉強会 6
ここからの話の流れ
LDA を使ってみよう
◦ gensim というライブラリで LDA を試してみよう
LDA を理解しよう
◦ ブラックボックスのままにせず中身を理解しよう
gensim 再訪
◦ 中身を理解したうえで gensim の LDA をもう一度見てみよう
2020-01-24 社内勉強会 7
LDA を使ってみよう
2020-01-24 社内勉強会 8
LDA の実装 (代表的な?もの)
◦ lda-c (https://github.com/blei-lab/lda-c)
Blei ら (LDA を提案した論文の著者) による C での実装。変分ベイズ
◦ GibbsLDA++ (http://gibbslda.sourceforge.net)
C++ での LDA の実装。崩壊型ギブスサンプリング
◦ PLDA (http://ai.deepq.com/plda)
C++ での LDA の実装。崩壊型ギブスサンプリング
◦ gensim (https://radimrehurek.com/gensim)
Python でのトピックモデルライブラリ (LDA を含む)。変分ベイズ
◦ MALLET (http://mallet.cs.umass.edu)
Java での自然言語処理ライブラリ (LDA を含む)。崩壊型ギブスサンプリング
2020-01-24 社内勉強会 9
文書集合の準備
Wikipedia 日本語版を利用
1. データベースダンプファイルを取得
◦ 取得元:https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-abstract.xml.gz
◦ 取得日:2019-12-07
◦ abstract 要素を持つ記事数:1,178,741
2. abstract が 100 語以上の記事を対象として名詞を抽出
◦ 形態素解析には MeCab (ipadic) を利用した
◦ 対象記事数:20,488
2020-01-24 社内勉強会 10
文書の例
先頭の 5 文書のタイトル
2020-01-24 社内勉強会 11
$ head -n 5 wikipedia_words.txt | cut -f1-10 -d' ' | sed 's/ / /g'
社会 学 ゃかいがく 社会 現象 実態 現象 原因 メカニズム 因果
形式 言語 けい しき げん 文法 構文 統語 論 場合
プログラミング 言語 1960 年代 JIS プログラム 言語 訳語 JIS C
}} 自然 科学 一 分野 自然 界 現象 人間 恣意
著作 権 ちょ さ コピーライト 知的 財産 権 知的 所有
$ head -n 5 wikipedia_titles.txt
社会学
形式言語
プログラミング言語
物理学
著作権
これらの abstract に含まれる語 (先頭の 10 語)
モデルの学習 [1/5]
文書集合を読み込む
2020-01-24 社内勉強会 12
with open('wikipedia_words.txt', 'r') as f:
docs = [l.strip().split(' ') for l in f]
docs の確認
len(docs)
# 20488
docs[0][:10]
# ['社会', '学', 'ゃかいがく', '社会', '現象', '実態', '現象', '原因',
# 'メカニズム', '因果']
モデルの学習 [2/5]
辞書を作成する
2020-01-24 社内勉強会 13
from gensim.corpora import Dictionary
dic = Dictionary(docs)
dic.filter_extremes(no_below=5, no_above=0.1)
dic.compactify()
◦ 出現する文書数が 5 未満または全体の 10% 超の語は捨てる
辞書の確認
len(dic.items())
# 19544
[x for x in dic.items()][:500:100]
# [(0, '08'), (100, 'プログラミング'), (200, 'New'), (300, '愛知'),
# (400, '賞')]
モデルの学習 [3/5]
辞書を用いて docs を BoW 表現に変換する
2020-01-24 社内勉強会 14
corpus = [dic.doc2bow(d) for d in docs]
corpus の確認
sorted(corpus[0], key=lambda x:x[1], reverse=True)[:10]
# [(45, 5), (4, 3), (13, 3), (22, 2), (27, 2), (29, 2), (31, 2), (33, 2),
# (42, 2), (0, 1)]
dic[45]
# '社会'
モデルの学習 [4/5]
LDA のモデルを学習する
2020-01-24 社内勉強会 15
import logging
from gensim.models import LdaModel
logging.basicConfig(level=logging.INFO)
lda = LdaModel(corpus=corpus, num_topics=50, id2word=dic, passes=30,
chunksize=len(corpus), update_every=0, decay=0,
alpha='auto')
◦ パラメータ設定の意図
◦ トピック数 50
◦ 反復回数 30
◦ バッチ学習 (chunksize, update_every, decay)
◦ 超パラメータ α を非対称として、データから推定する
モデルの学習 [5/5]
学習済みのモデルを保存する
2020-01-24 社内勉強会 16
lda.save('lda_model')
作成されるファイル群
$ ls -1 lda_model*
lda_model
lda_model.expElogbeta.npy
lda_model.id2word
lda_model.state
モデルの利用 [1/4]
保存されたモデルを読み込む
2020-01-24 社内勉強会 17
lda = LdaModel.load('lda_model')
辞書を用いて文書 (0: 社会学) の BoW 表現を得る
bow = lda.id2word.doc2bow(docs[0])
sorted(bow, key=lambda x: x[1], reverse=True)[:10]
# [(45, 5), (4, 3), (13, 3), (22, 2), (27, 2), (29, 2), (31, 2), (33, 2),
# (42, 2), (0, 1)]
lda.id2word[45]
# '社会'
モデルの利用 [2/4]
文書 (0: 社会学) のベクトル表現を得る
2020-01-24 社内勉強会 18
doc_topics = lda[bow]
sorted(doc_topics, key=lambda x: x[1], reverse=True)
# [(40, 0.5127592), (17, 0.16900457), (13, 0.07605075), (21, 0.055048402),
# (18, 0.052849013), (11, 0.048535615), (28, 0.038251393),
# (19, 0.0284172)]
◦ 値が 0.01 未満の次元は無視される (minimum_probability で指定可)
トピック (40) から出やすい単語を確認する
lda.show_topic(40)
# [('学', 0.037192468), ('社会', 0.019697921), ('性', 0.016326433),
# ('研究', 0.014481985), ('論', 0.013506126), ('経済', 0.012653638),
# ('分野', 0.010562398), ('科学', 0.01027073), ('人間', 0.010165557),
# ('哲学', 0.009113403)]
モデルの利用 [3/4]
単語 (45: 社会) のベクトル表現を得る
2020-01-24 社内勉強会 19
from gensim.matutils import unitvec
word_topics = unitvec(lda.get_term_topics(45), 'l1')
sorted(word_topics, key=lambda x: x[1], reverse=True)
# [(40, 0.4573952962266497), (21, 0.2643119328144538),
# (27, 0.1328625669290675), (48, 0.07961762773507063),
# (38, 0.06300880483462193), (29, 0.002803771460136456)]
各トピックから出やすい単語を確認する
for t in [40, 21, 27]:
words = ' '.join([w for w, p in lda.show_topic(t)])
print(f'{t}: {words}')
# 40: 学 社会 性 研究 論 経済 分野 科学 人間 哲学
# 21: 主義 世紀 政治 運動 派 社会 革命 宗教 歴史 自由
# 27: 研究 大学 教授 科学 学 医療 学会 科 専門 教育
モデルの利用 [4/4]
応用例:文書 (0: 社会学) に近い文書を検索する
2020-01-24 社内勉強会 20
from gensim.matutils import hellinger
corpus = [lda.id2word.doc2bow(d) for d in docs]
doc_topics = lda[corpus]
similarities = [hellinger(doc_topics[0], vec) for vec in doc_topics]
with open('wikipedia_titles.txt', 'r') as f:
titles = [l.strip() for l in f]
sorted(zip(titles, similarities), key=lambda x: x[1])[:5]
# [('社会学', 0.000358726519171783),
# ('消極的事実の証明', 0.39963072308952974),
# ('計量学', 0.4017121026471338),
# ('心身問題', 0.41389200570973506),
# ('外向性と内向性', 0.41777963953887276)]
ここまでのまとめ
LDA の使い方
1. 文書の集合を用意する
2. モデルを学習する
◦ トピック数を指定する
3. モデルから文書や単語のベクトルを獲得する
◦ ベクトルの次元数 = トピック数
これらのベクトルは何を表しているのか?
2020-01-24 社内勉強会 21
LDA を理解しよう
2020-01-24 社内勉強会 22
グラフィカルモデル
2020-01-24 社内勉強会 23
α
θd zd,i wd,i
βk
Nd
D
K
η
グラフィカルモデル
2020-01-24 社内勉強会 24
α
θd zd,i wd,i
βk
Nd
D
K
η
確率変数
観測された確率変数
決定的なパラメータ
プレート:
枠内の要素が複数個あることを示す確率変数の依存関係
d 番目の文書の単語数
グラフィカルモデル
2020-01-24 社内勉強会 25
α
θd zd,i wd,i
βk
Nd
D
K
η
文書数
d 番目の文書の i 番目の単語
d 番目の文書の単語数
グラフィカルモデル
2020-01-24 社内勉強会 26
α
θd zd,i wd,i
βk
Nd
D
K
η
文書数
d 番目の文書の i 番目の単語
トピック数
d 番目の文書のトピック分布
d 番目の文書の i 番目の単語のトピック
k 番目のトピックの単語分布
文書集合の生成過程
サイコロ
◦ 単語サイコロ (β) 振ると「単語」が出る
◦ トピックサイコロ (θ) 振ると「トピック番号」が出る
サイコロ工場
◦ 単語サイコロ工場 (η) 「単語サイコロ」を製作する
◦ トピックサイコロ工場 (α) 「トピックサイコロ」を製作する
ただし (これがポイント)
◦ どの目が出やすいかはサイコロごとに異なる
2020-01-24 社内勉強会 27
文書集合の生成過程
トピック k = 1, 2, ..., K に対して:
◦ 単語サイコロ工場から単語サイコロ βk を得る
文書 d = 1, 2, ..., D に対して:
◦ トピックサイコロ工場からトピックサイコロ θd を得る
◦ 文書 d の語数を Nd として i = 1, 2, ..., Nd に対して:
◦ トピックサイコロ θd を振る。出目を zd,i とする
◦ トピック zd,i の単語サイコロ βzd,i
を振って単語 wd,i を得る
2020-01-24 社内勉強会 28
文書集合の生成過程
トピック k = 1, 2, ..., K に対して:
◦ βk ~ Dirichlet(η) 単語サイコロ工場から単語サイコロ βk を得る
文書 d = 1, 2, ..., D に対して:
◦ θd ~ Dirichlet(α) トピックサイコロ工場からトピックサイコロ θd を得る
◦ 文書 d の語数を Nd として i = 1, 2, ..., Nd に対して:
◦ zd,i ~ Categorical(θd) トピックサイコロ θd を振る。出目を zd,i とする
◦ wd,i ~ Categorical(βzd,i
) トピック zd,i の単語サイコロ βzd,i
を振って単語 wd,i を得る
2020-01-24 社内勉強会 29
カテゴリカル分布
確率変数 X がパラメータ θ のカテゴリカル分布に従うとは
2020-01-24 社内勉強会 30
例:3 の目が出やすいサイコロ
◦ θ = (0.1, 0.1, 0.5, 0.1, 0.1, 0.1)
◦ 3 の目が出る確率は 0.5
◦ その他の目が出る確率はそれぞれ 0.1
◦ このサイコロを振って 3 が出る確率は p(X=3|θ) = θ3 = 0.5
ただし
単語の生成確率
文書 d の i 番目の単語が wd,i になる確率
◦ = θd を振って 1 が出て β1 を振って wd,i が出る確率
◦ + θd を振って 2 が出て β2 を振って wd,i が出る確率
◦ + ...
◦ + θd を振って K が出て βK を振って wd,i が出る確率
2020-01-24 社内勉強会 31
文書の生成確率
文書 d の単語列が wd,1 wd,2 wd,3 ... wd,Nd
になる確率
◦ = 文書 d の 1 番目の単語が wd,1 になる確率
◦ × 文書 d の 2 番目の単語が wd,1 になる確率
◦ × ...
◦ × 文書 d の Nd 番目の単語が wd,Nd
になる確率
2020-01-24 社内勉強会 32
文書集合全体の生成確率
文書集合 D の全体の単語列が W になる確率
◦ = 文書 d1 の単語列が wd1,1 wd1,2 wd1,3 ... wd1,Nd1
になる確率
◦ × 文書 d2 の単語列が wd2,1 wd2,2 wd2,3 ... wd2,Nd2
になる確率
◦ × ...
◦ × 文書 dN の単語列が wdN,1 wdN,2 wdN,3 ... wdN,NdN
になる確率
2020-01-24 社内勉強会 33
トピックモデルの 最尤推定
文書集合全体の生成確率
2020-01-24 社内勉強会 34
◦ この確率を最大にするパラメータ (= Θ, Β) を求める
用語
◦ 尤度 (Likelihood)
◦ 確率をパラメータの関数として見たもの
◦ 最尤推定 (Maximum Likelihood Estimation)
◦ 最も「尤もらしい」パラメータを推定する
Expectation-Maximization アルゴリズム
(今回は説明を割愛)
トピックモデルの ベイズ推定
各サイコロが製造される確率を含めて考える
◦ トピックサイコロ θd が製造される確率
◦ 文書サイコロ βk が製造される確率
文書集合全体の生成確率
2020-01-24 社内勉強会 35
トピックモデルの ベイズ推定
文書集合全体の生成確率
2020-01-24 社内勉強会 36
これを利用してパラメータ (= Θ, Β) の事後分布を求める
推定手法 (今回は説明を割愛)
• 変分ベイズ推定
• 崩壊型ギブスサンプリング
ディリクレ分布
確率変数 X がパラメータ α のディリクレ分布に従うとは
2020-01-24 社内勉強会 37
https://ja.wikipedia.org/wiki/ディリクレ分布 より引用
ディリクレ分布を眺める
いろいろ試してみよう
2020-01-24 社内勉強会 38
ディリクレ分布の例 [1/2]
αi が 1 より大きい場合
2020-01-24 社内勉強会 39
LDA ではこういう設定はしない
ディリクレ分布の例 [2/2]
αi が 1 より小さい場合
2020-01-24 社内勉強会 40
LDA ではこういう設定をする
解釈:
◦ 各文書は、すべてのトピック (x1,
x2, x3) のうちごく少数を主題と
する
◦ 各トピックからは、すべての語
彙 (x1, x2, x3) のうちごく少数が
頻出する
ここまでのまとめ
LDA とは・・・
◦ 文書の生成過程をトピックモデルで表現する
◦ トピックモデルのパラメータ θ, β をベイズ推定する
◦ パラメータ θ, β の事前分布としてディリクレ分布を用いる
◦ ディリクレ分布のパラメータを α, η とする
という理解のもとで改めて gensim を見てみましょう
2020-01-24 社内勉強会 41
gensim 再訪
2020-01-24 社内勉強会 42
モデルの学習
LDA のモデル学習時に実行したコマンド
2020-01-24 社内勉強会 43
lda = LdaModel(corpus=corpus, num_topics=50, id2word=dic, passes=30,
chunksize=len(corpus), update_every=0, decay=0,
alpha='auto')
LdaModel のパラメータ
2020-01-24 社内勉強会 44
num_topics
corpus
eta
alpha
α
θd zd,i wd,i
βk
Nd
D
K
η
alpha と eta の設定
以下の指定方法がある
2020-01-24 社内勉強会 45
指定方法 alpha, eta の設定値 備考
"symmetric" 全要素に (1 / num_topics) を設定 既定値
"asymmetric" for i = 0, 1, ..., num_topics-1
ci = 1 / (i + sqrt(num_topics))
αi = ci / sum(ci)
eta では
指定不可
"auto" 初期値 (1 / num_topics) を全要素に設定
学習中に更新する
数値指定 指定された値を全要素に設定
リスト指定 リストで指定された値を各要素に設定
行列指定 トピックごとに eta を指定 alpha では
指定不可
モデルの利用
BoW 表現された文書のトピック分布を得る
2020-01-24 社内勉強会 46
doc_topics = lda[bow]
sorted(doc_topics, key=lambda x: x[1], reverse=True)
# [(40, 0.5127592), (17, 0.16900457), (13, 0.07605075), (21, 0.055048402),
# (18, 0.052849013), (11, 0.048535615), (28, 0.038251393),
# (19, 0.0284172)]
◦ get_document_topics メソッドでも概ね同じ
トピックの単語分布を得る
lda.show_topic(40)
# [('学', 0.037192468), ('社会', 0.019697921), ('性', 0.016326433),
# ('研究', 0.014481985), ('論', 0.013506126), ('経済', 0.012653638),
# ('分野', 0.010562398), ('科学', 0.01027073), ('人間', 0.010165557),
# ('哲学', 0.009113403)]
◦ get_topic_terms メソッドを使えば単語は word_id で戻る
LdaModel から取れる情報
2020-01-24 社内勉強会 47
get_document_topics
get_topic_words
α
θd zd,i wd,i
βk
Nd
D
K
η
get_document_topics (per_word_topics=True)
per_word_topics=True
BoW 表現された文書のトピック分布を得る
2020-01-24 社内勉強会 48
doc_topics, word_topics, word_topic_probs = ¥
lda.get_document_topics(bow, per_word_topics=True)
word_topic_probs[0]
# (0, [(17, 0.5443574), (28, 0.45384607)])
◦ word_id=0 は topic 17 から 0.544 回、topic 28 から 0.454 回出現
lda.id2word[45]
# '社会'
dict(word_topic_probs)[45]
# [(21, 0.26105392), (40, 4.7388897)]
◦ word_id=45 は topic 21 から 0.261 回、topic 40 から 4.739 回出現
◦ "社会" はこの文書に 5 回出現しているので合計が 5 になる
全体のまとめ
LDA とは
◦ 文書の生成モデル (トピックモデル)
◦ パラメータ θ, β をベイズ推定する
LDA の実装
◦ gensim など多数ある
スライドで触れていない主な話題
◦ パラメータ推定方法
◦ モデル評価
◦ オンライン学習
◦ 超パラメータの推定
◦ トピックモデルの拡張
2020-01-24 社内勉強会 49
参考:日本語の教科書
(私が知る限り) 2 冊出版されている
2020-01-24 社内勉強会 50
想定質問集
2020-01-24 社内勉強会 51
トピック数の目安は?
何通りか試してみて選ぶ
◦ 経験的には 30, 50, 100 とか
◦ トピック数を増やすと計算負荷は大きくなる
◦ 各トピックの解釈を考えるなら増やし過ぎても扱いに困る
トピック数の推定
◦ 階層ディリクレ過程 (Hierarchical Dirichlet Process)
2020-01-24 社内勉強会 52
α や η の目安は?
私にはよくわからない
◦ Blei らの論文[1]
◦ α は非対称 (asymmetric), η は対称 (symmetric) として話が進む
◦ これらの超パラメータも推定する (論文 Appendix に記載)
◦ (著者らによる) lda-c の実装は symmetric α のみ対応。η は存在しない
◦ Blei らの論文では η は "Smoothing" として途中から導入される
◦ Griffiths らの論文[2]
◦ α = 50/K (K = 50, 100, 200, 300, 400, 500, 600, 1000 で実験している)
◦ η = 0.1
◦ (CGS に基づく) GibbsLDA++ と PLDA は α, η とも symmetric のみ対応。推定もなし
◦ Wallach らの論文[3]
◦ Asymmetric α の方が良いモデル (低 perplexity) が得られたという報告
2020-01-24 社内勉強会 53
[1] D. Blei, A. Ng, M. Jordan. Latent Dirichlet Allocation. JMLR 2003.
[2] T. Griffiths, M. Steyvers. Finding Scientific Topics. PNAS 2004.
[3] H. Wallach, D. Mimno, A. McCallum. Rethinking LDA: Why Priors Matter. NIPS 2009.
未知語の取り扱いはどうなる?
(おそらく) LDA では未知語を無視する
◦ 少なくとも gensim の実装では
未知語を捨てた BoW に対して θ を推定している
2020-01-24 社内勉強会 54
語数 Nd は生成過程に含まないのか?
Blei らの論文で以下のように説明されている
◦ 文書の生成過程としては Nd ~ Poisson(ξ) とする
◦ しかし Nd は他の変数と独立であり Nd のランダム性は無視できる
補足
◦ Nd と θd が独立だというモデルになっていることは要注意
◦ ある文書が長文か短文かは、その文書の内容 (トピック) とは無関係だと言っている
◦ 現実は (おそらく) そんなはずはない
◦ モデルはあくまでも事象をある側面で抽象化したもの
2020-01-24 社内勉強会 55
他の文献を見ると
トピック単語分布を φ で表しているが?
Griffiths らの論文で Blei らの論文と異なる記法が使われた (下記)
2020-01-24 社内勉強会 56
ライブラリによってはこちらの記法を使っているので注意
◦ gensim は Blei らの記法に沿っている
◦ gensim は変分ベイズを実装しているので、これは自然
◦ PLDA, GibbsLDA++, Mallet は Griffiths らの記法に沿っている
◦ これらは崩壊型ギブスサンプリングを実装しているので、これもまた自然
他の文献を見ると
多項分布として説明されているが?
多項分布で n = 1 とすればカテゴリカル分布である
2020-01-24 社内勉強会 57
gensim のドキュメントには
update_every=0 でバッチ学習とあるが?
実装を確認したところ以下も設定すべきに思える
◦ chunksize を文書数にする (または None にする)
◦ decay=0 とする (0.5 から 1 と書かれているが 0 でも通る)
実装の詳細
◦ update_every は M-step の実行間隔を制御するだけ
◦ update_every=0 でも E-step や超パラメータ更新は chunksize ごとに実行される
◦ decay の設定は常に (update_every=0 でも) 有効
補足:gensim の LDA が参照する論文[4] にある記述
◦ "Note that we recover batch VB when S=D and κ=0."
◦ ここでの S が chunk に、κ が decay に対応する
2020-01-24 社内勉強会 58
[4] M. Hoffman, D. Blei, F. Bach. Online Learning for Latent Dirichlet Allocation. NIPS 2010.
さまざまな word/document embedding
手法との比較は?
不勉強でよくわかりません (教えてください)
2020-01-24 社内勉強会 59

潜在ディリクレ配分法

  • 1.
  • 2.
    自己紹介 内山 雄司 (@y__uti) ◦http://y-uti.hatenablog.jp/ (phpusers-ja) 仕事 ◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています 興味 ◦ プログラミング言語処理系 ◦ 機械学習 2020-01-24 社内勉強会 2
  • 3.
    はじめに 潜在ディリクレ配分法 (Latent DirichletAllocation; LDA) とは? ◦ "collections of discrete data" の確率的生成モデル 2020-01-24 社内勉強会 3 典型的な例 Bag of Words 表現された文書集合 LDA を使うと何ができるのか? ◦ 直接的には ◦ 単語や文書を低次元のベクトルで表現できる ◦ 応用例 ◦ 類似文書の検索 ◦ 文書や単語の分類 社会 現象 認識 組織 学問 プログラム 言語 コンピュータ 人工 計算 下線部は Latent Dirichlet Allocation (Blei, et. al. JMLR 2003) の abstract の表現を引用
  • 4.
    トピックモデル トピック = 文書集合に内在する話題 ◦トピックによって「出現しやすい単語」が異なる 文書 = いくつかのトピックから出現した単語の集まり 2020-01-24 社内勉強会 4 https://ja.wikipedia.org/wiki/社会学
  • 5.
    トピックモデルの推定 2020-01-24 社内勉強会 5 トピック= 文書集合に内在する話題 ◦ トピックによって「出現しやすい単語」が異なる 文書 = いくつかのトピックから出現した単語の集まり として、文書集合から次のような情報を抽出する 1. 各トピックからどんな単語が出やすいか? ◦ ある単語がどのトピックから出てきたか?と裏表の関係 2. 文書中に各トピックがどのくらいの割合で混ざっているか? https://ja.wikipedia.org/wiki/社会学
  • 6.
    単語や文書のベクトル表現 ある単語がどのトピックから出てきたか?の確率 ◦ 単語のベクトル表現 (ベクトルの次元数= トピック数) ある文書に各トピックがどのくらいの割合で混ざっているか ◦ 文書のベクトル表現 (ベクトルの次元数 = トピック数) ベクトルで表現することで「距離」を計算できる 2020-01-24 社内勉強会 6
  • 7.
    ここからの話の流れ LDA を使ってみよう ◦ gensimというライブラリで LDA を試してみよう LDA を理解しよう ◦ ブラックボックスのままにせず中身を理解しよう gensim 再訪 ◦ 中身を理解したうえで gensim の LDA をもう一度見てみよう 2020-01-24 社内勉強会 7
  • 8.
  • 9.
    LDA の実装 (代表的な?もの) ◦lda-c (https://github.com/blei-lab/lda-c) Blei ら (LDA を提案した論文の著者) による C での実装。変分ベイズ ◦ GibbsLDA++ (http://gibbslda.sourceforge.net) C++ での LDA の実装。崩壊型ギブスサンプリング ◦ PLDA (http://ai.deepq.com/plda) C++ での LDA の実装。崩壊型ギブスサンプリング ◦ gensim (https://radimrehurek.com/gensim) Python でのトピックモデルライブラリ (LDA を含む)。変分ベイズ ◦ MALLET (http://mallet.cs.umass.edu) Java での自然言語処理ライブラリ (LDA を含む)。崩壊型ギブスサンプリング 2020-01-24 社内勉強会 9
  • 10.
    文書集合の準備 Wikipedia 日本語版を利用 1. データベースダンプファイルを取得 ◦取得元:https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-abstract.xml.gz ◦ 取得日:2019-12-07 ◦ abstract 要素を持つ記事数:1,178,741 2. abstract が 100 語以上の記事を対象として名詞を抽出 ◦ 形態素解析には MeCab (ipadic) を利用した ◦ 対象記事数:20,488 2020-01-24 社内勉強会 10
  • 11.
    文書の例 先頭の 5 文書のタイトル 2020-01-24社内勉強会 11 $ head -n 5 wikipedia_words.txt | cut -f1-10 -d' ' | sed 's/ / /g' 社会 学 ゃかいがく 社会 現象 実態 現象 原因 メカニズム 因果 形式 言語 けい しき げん 文法 構文 統語 論 場合 プログラミング 言語 1960 年代 JIS プログラム 言語 訳語 JIS C }} 自然 科学 一 分野 自然 界 現象 人間 恣意 著作 権 ちょ さ コピーライト 知的 財産 権 知的 所有 $ head -n 5 wikipedia_titles.txt 社会学 形式言語 プログラミング言語 物理学 著作権 これらの abstract に含まれる語 (先頭の 10 語)
  • 12.
    モデルの学習 [1/5] 文書集合を読み込む 2020-01-24 社内勉強会12 with open('wikipedia_words.txt', 'r') as f: docs = [l.strip().split(' ') for l in f] docs の確認 len(docs) # 20488 docs[0][:10] # ['社会', '学', 'ゃかいがく', '社会', '現象', '実態', '現象', '原因', # 'メカニズム', '因果']
  • 13.
    モデルの学習 [2/5] 辞書を作成する 2020-01-24 社内勉強会13 from gensim.corpora import Dictionary dic = Dictionary(docs) dic.filter_extremes(no_below=5, no_above=0.1) dic.compactify() ◦ 出現する文書数が 5 未満または全体の 10% 超の語は捨てる 辞書の確認 len(dic.items()) # 19544 [x for x in dic.items()][:500:100] # [(0, '08'), (100, 'プログラミング'), (200, 'New'), (300, '愛知'), # (400, '賞')]
  • 14.
    モデルの学習 [3/5] 辞書を用いて docsを BoW 表現に変換する 2020-01-24 社内勉強会 14 corpus = [dic.doc2bow(d) for d in docs] corpus の確認 sorted(corpus[0], key=lambda x:x[1], reverse=True)[:10] # [(45, 5), (4, 3), (13, 3), (22, 2), (27, 2), (29, 2), (31, 2), (33, 2), # (42, 2), (0, 1)] dic[45] # '社会'
  • 15.
    モデルの学習 [4/5] LDA のモデルを学習する 2020-01-24社内勉強会 15 import logging from gensim.models import LdaModel logging.basicConfig(level=logging.INFO) lda = LdaModel(corpus=corpus, num_topics=50, id2word=dic, passes=30, chunksize=len(corpus), update_every=0, decay=0, alpha='auto') ◦ パラメータ設定の意図 ◦ トピック数 50 ◦ 反復回数 30 ◦ バッチ学習 (chunksize, update_every, decay) ◦ 超パラメータ α を非対称として、データから推定する
  • 16.
    モデルの学習 [5/5] 学習済みのモデルを保存する 2020-01-24 社内勉強会16 lda.save('lda_model') 作成されるファイル群 $ ls -1 lda_model* lda_model lda_model.expElogbeta.npy lda_model.id2word lda_model.state
  • 17.
    モデルの利用 [1/4] 保存されたモデルを読み込む 2020-01-24 社内勉強会17 lda = LdaModel.load('lda_model') 辞書を用いて文書 (0: 社会学) の BoW 表現を得る bow = lda.id2word.doc2bow(docs[0]) sorted(bow, key=lambda x: x[1], reverse=True)[:10] # [(45, 5), (4, 3), (13, 3), (22, 2), (27, 2), (29, 2), (31, 2), (33, 2), # (42, 2), (0, 1)] lda.id2word[45] # '社会'
  • 18.
    モデルの利用 [2/4] 文書 (0:社会学) のベクトル表現を得る 2020-01-24 社内勉強会 18 doc_topics = lda[bow] sorted(doc_topics, key=lambda x: x[1], reverse=True) # [(40, 0.5127592), (17, 0.16900457), (13, 0.07605075), (21, 0.055048402), # (18, 0.052849013), (11, 0.048535615), (28, 0.038251393), # (19, 0.0284172)] ◦ 値が 0.01 未満の次元は無視される (minimum_probability で指定可) トピック (40) から出やすい単語を確認する lda.show_topic(40) # [('学', 0.037192468), ('社会', 0.019697921), ('性', 0.016326433), # ('研究', 0.014481985), ('論', 0.013506126), ('経済', 0.012653638), # ('分野', 0.010562398), ('科学', 0.01027073), ('人間', 0.010165557), # ('哲学', 0.009113403)]
  • 19.
    モデルの利用 [3/4] 単語 (45:社会) のベクトル表現を得る 2020-01-24 社内勉強会 19 from gensim.matutils import unitvec word_topics = unitvec(lda.get_term_topics(45), 'l1') sorted(word_topics, key=lambda x: x[1], reverse=True) # [(40, 0.4573952962266497), (21, 0.2643119328144538), # (27, 0.1328625669290675), (48, 0.07961762773507063), # (38, 0.06300880483462193), (29, 0.002803771460136456)] 各トピックから出やすい単語を確認する for t in [40, 21, 27]: words = ' '.join([w for w, p in lda.show_topic(t)]) print(f'{t}: {words}') # 40: 学 社会 性 研究 論 経済 分野 科学 人間 哲学 # 21: 主義 世紀 政治 運動 派 社会 革命 宗教 歴史 自由 # 27: 研究 大学 教授 科学 学 医療 学会 科 専門 教育
  • 20.
    モデルの利用 [4/4] 応用例:文書 (0:社会学) に近い文書を検索する 2020-01-24 社内勉強会 20 from gensim.matutils import hellinger corpus = [lda.id2word.doc2bow(d) for d in docs] doc_topics = lda[corpus] similarities = [hellinger(doc_topics[0], vec) for vec in doc_topics] with open('wikipedia_titles.txt', 'r') as f: titles = [l.strip() for l in f] sorted(zip(titles, similarities), key=lambda x: x[1])[:5] # [('社会学', 0.000358726519171783), # ('消極的事実の証明', 0.39963072308952974), # ('計量学', 0.4017121026471338), # ('心身問題', 0.41389200570973506), # ('外向性と内向性', 0.41777963953887276)]
  • 21.
    ここまでのまとめ LDA の使い方 1. 文書の集合を用意する 2.モデルを学習する ◦ トピック数を指定する 3. モデルから文書や単語のベクトルを獲得する ◦ ベクトルの次元数 = トピック数 これらのベクトルは何を表しているのか? 2020-01-24 社内勉強会 21
  • 22.
  • 23.
  • 24.
    グラフィカルモデル 2020-01-24 社内勉強会 24 α θdzd,i wd,i βk Nd D K η 確率変数 観測された確率変数 決定的なパラメータ プレート: 枠内の要素が複数個あることを示す確率変数の依存関係
  • 25.
    d 番目の文書の単語数 グラフィカルモデル 2020-01-24 社内勉強会25 α θd zd,i wd,i βk Nd D K η 文書数 d 番目の文書の i 番目の単語
  • 26.
    d 番目の文書の単語数 グラフィカルモデル 2020-01-24 社内勉強会26 α θd zd,i wd,i βk Nd D K η 文書数 d 番目の文書の i 番目の単語 トピック数 d 番目の文書のトピック分布 d 番目の文書の i 番目の単語のトピック k 番目のトピックの単語分布
  • 27.
    文書集合の生成過程 サイコロ ◦ 単語サイコロ (β)振ると「単語」が出る ◦ トピックサイコロ (θ) 振ると「トピック番号」が出る サイコロ工場 ◦ 単語サイコロ工場 (η) 「単語サイコロ」を製作する ◦ トピックサイコロ工場 (α) 「トピックサイコロ」を製作する ただし (これがポイント) ◦ どの目が出やすいかはサイコロごとに異なる 2020-01-24 社内勉強会 27
  • 28.
    文書集合の生成過程 トピック k =1, 2, ..., K に対して: ◦ 単語サイコロ工場から単語サイコロ βk を得る 文書 d = 1, 2, ..., D に対して: ◦ トピックサイコロ工場からトピックサイコロ θd を得る ◦ 文書 d の語数を Nd として i = 1, 2, ..., Nd に対して: ◦ トピックサイコロ θd を振る。出目を zd,i とする ◦ トピック zd,i の単語サイコロ βzd,i を振って単語 wd,i を得る 2020-01-24 社内勉強会 28
  • 29.
    文書集合の生成過程 トピック k =1, 2, ..., K に対して: ◦ βk ~ Dirichlet(η) 単語サイコロ工場から単語サイコロ βk を得る 文書 d = 1, 2, ..., D に対して: ◦ θd ~ Dirichlet(α) トピックサイコロ工場からトピックサイコロ θd を得る ◦ 文書 d の語数を Nd として i = 1, 2, ..., Nd に対して: ◦ zd,i ~ Categorical(θd) トピックサイコロ θd を振る。出目を zd,i とする ◦ wd,i ~ Categorical(βzd,i ) トピック zd,i の単語サイコロ βzd,i を振って単語 wd,i を得る 2020-01-24 社内勉強会 29
  • 30.
    カテゴリカル分布 確率変数 X がパラメータθ のカテゴリカル分布に従うとは 2020-01-24 社内勉強会 30 例:3 の目が出やすいサイコロ ◦ θ = (0.1, 0.1, 0.5, 0.1, 0.1, 0.1) ◦ 3 の目が出る確率は 0.5 ◦ その他の目が出る確率はそれぞれ 0.1 ◦ このサイコロを振って 3 が出る確率は p(X=3|θ) = θ3 = 0.5 ただし
  • 31.
    単語の生成確率 文書 d のi 番目の単語が wd,i になる確率 ◦ = θd を振って 1 が出て β1 を振って wd,i が出る確率 ◦ + θd を振って 2 が出て β2 を振って wd,i が出る確率 ◦ + ... ◦ + θd を振って K が出て βK を振って wd,i が出る確率 2020-01-24 社内勉強会 31
  • 32.
    文書の生成確率 文書 d の単語列がwd,1 wd,2 wd,3 ... wd,Nd になる確率 ◦ = 文書 d の 1 番目の単語が wd,1 になる確率 ◦ × 文書 d の 2 番目の単語が wd,1 になる確率 ◦ × ... ◦ × 文書 d の Nd 番目の単語が wd,Nd になる確率 2020-01-24 社内勉強会 32
  • 33.
    文書集合全体の生成確率 文書集合 D の全体の単語列がW になる確率 ◦ = 文書 d1 の単語列が wd1,1 wd1,2 wd1,3 ... wd1,Nd1 になる確率 ◦ × 文書 d2 の単語列が wd2,1 wd2,2 wd2,3 ... wd2,Nd2 になる確率 ◦ × ... ◦ × 文書 dN の単語列が wdN,1 wdN,2 wdN,3 ... wdN,NdN になる確率 2020-01-24 社内勉強会 33
  • 34.
    トピックモデルの 最尤推定 文書集合全体の生成確率 2020-01-24 社内勉強会34 ◦ この確率を最大にするパラメータ (= Θ, Β) を求める 用語 ◦ 尤度 (Likelihood) ◦ 確率をパラメータの関数として見たもの ◦ 最尤推定 (Maximum Likelihood Estimation) ◦ 最も「尤もらしい」パラメータを推定する Expectation-Maximization アルゴリズム (今回は説明を割愛)
  • 35.
    トピックモデルの ベイズ推定 各サイコロが製造される確率を含めて考える ◦ トピックサイコロθd が製造される確率 ◦ 文書サイコロ βk が製造される確率 文書集合全体の生成確率 2020-01-24 社内勉強会 35
  • 36.
    トピックモデルの ベイズ推定 文書集合全体の生成確率 2020-01-24 社内勉強会36 これを利用してパラメータ (= Θ, Β) の事後分布を求める 推定手法 (今回は説明を割愛) • 変分ベイズ推定 • 崩壊型ギブスサンプリング
  • 37.
    ディリクレ分布 確率変数 X がパラメータα のディリクレ分布に従うとは 2020-01-24 社内勉強会 37 https://ja.wikipedia.org/wiki/ディリクレ分布 より引用
  • 38.
  • 39.
    ディリクレ分布の例 [1/2] αi が1 より大きい場合 2020-01-24 社内勉強会 39 LDA ではこういう設定はしない
  • 40.
    ディリクレ分布の例 [2/2] αi が1 より小さい場合 2020-01-24 社内勉強会 40 LDA ではこういう設定をする 解釈: ◦ 各文書は、すべてのトピック (x1, x2, x3) のうちごく少数を主題と する ◦ 各トピックからは、すべての語 彙 (x1, x2, x3) のうちごく少数が 頻出する
  • 41.
    ここまでのまとめ LDA とは・・・ ◦ 文書の生成過程をトピックモデルで表現する ◦トピックモデルのパラメータ θ, β をベイズ推定する ◦ パラメータ θ, β の事前分布としてディリクレ分布を用いる ◦ ディリクレ分布のパラメータを α, η とする という理解のもとで改めて gensim を見てみましょう 2020-01-24 社内勉強会 41
  • 42.
  • 43.
    モデルの学習 LDA のモデル学習時に実行したコマンド 2020-01-24 社内勉強会43 lda = LdaModel(corpus=corpus, num_topics=50, id2word=dic, passes=30, chunksize=len(corpus), update_every=0, decay=0, alpha='auto')
  • 44.
    LdaModel のパラメータ 2020-01-24 社内勉強会44 num_topics corpus eta alpha α θd zd,i wd,i βk Nd D K η
  • 45.
    alpha と etaの設定 以下の指定方法がある 2020-01-24 社内勉強会 45 指定方法 alpha, eta の設定値 備考 "symmetric" 全要素に (1 / num_topics) を設定 既定値 "asymmetric" for i = 0, 1, ..., num_topics-1 ci = 1 / (i + sqrt(num_topics)) αi = ci / sum(ci) eta では 指定不可 "auto" 初期値 (1 / num_topics) を全要素に設定 学習中に更新する 数値指定 指定された値を全要素に設定 リスト指定 リストで指定された値を各要素に設定 行列指定 トピックごとに eta を指定 alpha では 指定不可
  • 46.
    モデルの利用 BoW 表現された文書のトピック分布を得る 2020-01-24 社内勉強会46 doc_topics = lda[bow] sorted(doc_topics, key=lambda x: x[1], reverse=True) # [(40, 0.5127592), (17, 0.16900457), (13, 0.07605075), (21, 0.055048402), # (18, 0.052849013), (11, 0.048535615), (28, 0.038251393), # (19, 0.0284172)] ◦ get_document_topics メソッドでも概ね同じ トピックの単語分布を得る lda.show_topic(40) # [('学', 0.037192468), ('社会', 0.019697921), ('性', 0.016326433), # ('研究', 0.014481985), ('論', 0.013506126), ('経済', 0.012653638), # ('分野', 0.010562398), ('科学', 0.01027073), ('人間', 0.010165557), # ('哲学', 0.009113403)] ◦ get_topic_terms メソッドを使えば単語は word_id で戻る
  • 47.
    LdaModel から取れる情報 2020-01-24 社内勉強会47 get_document_topics get_topic_words α θd zd,i wd,i βk Nd D K η get_document_topics (per_word_topics=True)
  • 48.
    per_word_topics=True BoW 表現された文書のトピック分布を得る 2020-01-24 社内勉強会48 doc_topics, word_topics, word_topic_probs = ¥ lda.get_document_topics(bow, per_word_topics=True) word_topic_probs[0] # (0, [(17, 0.5443574), (28, 0.45384607)]) ◦ word_id=0 は topic 17 から 0.544 回、topic 28 から 0.454 回出現 lda.id2word[45] # '社会' dict(word_topic_probs)[45] # [(21, 0.26105392), (40, 4.7388897)] ◦ word_id=45 は topic 21 から 0.261 回、topic 40 から 4.739 回出現 ◦ "社会" はこの文書に 5 回出現しているので合計が 5 になる
  • 49.
    全体のまとめ LDA とは ◦ 文書の生成モデル(トピックモデル) ◦ パラメータ θ, β をベイズ推定する LDA の実装 ◦ gensim など多数ある スライドで触れていない主な話題 ◦ パラメータ推定方法 ◦ モデル評価 ◦ オンライン学習 ◦ 超パラメータの推定 ◦ トピックモデルの拡張 2020-01-24 社内勉強会 49
  • 50.
  • 51.
  • 52.
    トピック数の目安は? 何通りか試してみて選ぶ ◦ 経験的には 30,50, 100 とか ◦ トピック数を増やすと計算負荷は大きくなる ◦ 各トピックの解釈を考えるなら増やし過ぎても扱いに困る トピック数の推定 ◦ 階層ディリクレ過程 (Hierarchical Dirichlet Process) 2020-01-24 社内勉強会 52
  • 53.
    α や ηの目安は? 私にはよくわからない ◦ Blei らの論文[1] ◦ α は非対称 (asymmetric), η は対称 (symmetric) として話が進む ◦ これらの超パラメータも推定する (論文 Appendix に記載) ◦ (著者らによる) lda-c の実装は symmetric α のみ対応。η は存在しない ◦ Blei らの論文では η は "Smoothing" として途中から導入される ◦ Griffiths らの論文[2] ◦ α = 50/K (K = 50, 100, 200, 300, 400, 500, 600, 1000 で実験している) ◦ η = 0.1 ◦ (CGS に基づく) GibbsLDA++ と PLDA は α, η とも symmetric のみ対応。推定もなし ◦ Wallach らの論文[3] ◦ Asymmetric α の方が良いモデル (低 perplexity) が得られたという報告 2020-01-24 社内勉強会 53 [1] D. Blei, A. Ng, M. Jordan. Latent Dirichlet Allocation. JMLR 2003. [2] T. Griffiths, M. Steyvers. Finding Scientific Topics. PNAS 2004. [3] H. Wallach, D. Mimno, A. McCallum. Rethinking LDA: Why Priors Matter. NIPS 2009.
  • 54.
    未知語の取り扱いはどうなる? (おそらく) LDA では未知語を無視する ◦少なくとも gensim の実装では 未知語を捨てた BoW に対して θ を推定している 2020-01-24 社内勉強会 54
  • 55.
    語数 Nd は生成過程に含まないのか? Bleiらの論文で以下のように説明されている ◦ 文書の生成過程としては Nd ~ Poisson(ξ) とする ◦ しかし Nd は他の変数と独立であり Nd のランダム性は無視できる 補足 ◦ Nd と θd が独立だというモデルになっていることは要注意 ◦ ある文書が長文か短文かは、その文書の内容 (トピック) とは無関係だと言っている ◦ 現実は (おそらく) そんなはずはない ◦ モデルはあくまでも事象をある側面で抽象化したもの 2020-01-24 社内勉強会 55
  • 56.
    他の文献を見ると トピック単語分布を φ で表しているが? Griffithsらの論文で Blei らの論文と異なる記法が使われた (下記) 2020-01-24 社内勉強会 56 ライブラリによってはこちらの記法を使っているので注意 ◦ gensim は Blei らの記法に沿っている ◦ gensim は変分ベイズを実装しているので、これは自然 ◦ PLDA, GibbsLDA++, Mallet は Griffiths らの記法に沿っている ◦ これらは崩壊型ギブスサンプリングを実装しているので、これもまた自然
  • 57.
    他の文献を見ると 多項分布として説明されているが? 多項分布で n =1 とすればカテゴリカル分布である 2020-01-24 社内勉強会 57
  • 58.
    gensim のドキュメントには update_every=0 でバッチ学習とあるが? 実装を確認したところ以下も設定すべきに思える ◦chunksize を文書数にする (または None にする) ◦ decay=0 とする (0.5 から 1 と書かれているが 0 でも通る) 実装の詳細 ◦ update_every は M-step の実行間隔を制御するだけ ◦ update_every=0 でも E-step や超パラメータ更新は chunksize ごとに実行される ◦ decay の設定は常に (update_every=0 でも) 有効 補足:gensim の LDA が参照する論文[4] にある記述 ◦ "Note that we recover batch VB when S=D and κ=0." ◦ ここでの S が chunk に、κ が decay に対応する 2020-01-24 社内勉強会 58 [4] M. Hoffman, D. Blei, F. Bach. Online Learning for Latent Dirichlet Allocation. NIPS 2010.
  • 59.