Allocation(潜在的ディリクレ配分法、以下「LDA」と略)と呼ばれるトピックモデルについて取り上げます。いうトピックモデルを扱う上で押さえておくべき、トピックモデルやコーパスの概念に触れながら、前処理を含めた分析の流れやモデルの評価方法などについて、実装を通じて理解を深めていくことを目的とします。併せて、本記事では、結果の可視化の手法についてもいくつか紹介したいと思います。より結果の可視化を行っていきます。トピックモデルについて実装に入る前に、そもそもトピックモデルとは何かについて、説明したいと思います。「トピックモデル」とは、データが「潜在的意味のカテゴリ」から生成されると仮定したモデルのことです。この「潜在的意味のカテゴリ」の事を、「潜在トピック」または単に「トピック」と呼びます。ここでの「潜在的意味」とは、複数の単語の共起性によって創発される情報のことを指しています。因みに、なぜ「トピックモデル」の説明の中で「潜在的」という言葉が用いられているのかというと、文書ごとによって共起する単語が異なるため、トピックモデルでは、文書を「意味のカテゴリ」に分ける際に、文書に実際に現れる顕在的共起性だけでなく、文書上には現れない隠れた共起性である潜在的共起性も、同時に扱っているからです。Analysis(PLSA)とも呼ばれますが、以下では「PLSI」と略)というトピックモデルの拡張モデルでもあります。違いについても少し触れておきましょう。以下の数式のように表すことができます。消して、p(z)・p(w|z)・p(d|z)の3つのパラメータの式に変形します。学習データの尤度が最大となるような上記3つのパラメータを、EMアルゴリズムによって推定していきます。いう手法が開発されたことで、膨大な文書を意味のあるカテゴリに分類することができるようになりました。大きく2つの弱点が存在します。依存するため、新規のドキュメントに対しては自然に扱うことができません。数は、ドキュメント数×トピック数となるため、ドキュメント数が膨大になると、推定すべきパラメータ数も比例して増大することとなり、その結果、学習データに対しても簡単にオーバーフィットしてしまいます。自体を確率変数とみて生成することができれば、学習データに依存せず、ドキュメント数に応じてパラメータ数が増大することもなくなります。問題点に対処しています。違いが、各モデルで定義されているトピック分布に起因していることが理解できていれば大丈夫です。弱点であった新規のドキュメントも自然に扱えるようになっており、オーバーフィットもし難くなっています。違い、様々な拡張が可能なのも特徴の1つと言えるでしょう。優れているように見えるかもしれませんが、必ずしもそういうわけではありません。方が適していると言えます。分析をする際は、このように目的や状況に応じて、より適したモデルを選択することが肝要です。採用したといった経緯があります。以上で、トピックモデルについての説明を終わりたいと思います。本記事における実装では、KaggleのKernelを使用しています。参考までに、Kaggleの環境のスペック及び設定を、以下に挙げておきます。Offまた、ローカル環境を使用する場合は、以下のコマンドをコマンドプロンプトに打ち込んで、各モジュールをインストールしておきましょう。ClassificationというKaggleのコンペティションのデータセットを加工したものを使用します。テキストデータの加工については、また別の記事で取り上げる予定ですので、興味がある方はそちらをご参照下さい。モジュールの設定とデータのインポート上記の準備が終わったら,モジュールのインポートをします。今回使用するモジュールは以下の通りです。output.次に、分析で使用するデータをインポートしてきます。その際に、使用するテキストデータ以外に、別途用意したストップワードのリストもインポートしておきます。f.read()f.close()英語のストップワードのリストは、Kaggleのデータセットでもいくつか公開されています。詳細については、以下に公開されているデータセットのリンクを記載しておきますので、そちらをご参照下さい。Stopwords-En(https://www.kaggle.com/mauropelucchi/stopwordsen)前処理ここでは前処理として、シンボルとストップワードを除去してきます。最初に、シンボルの除去を行っていきます。今回使用するテキストデータでは、「?」や「!」などの記号や、「"」や「'」などの符号に加え、絵文字や顔文字なども多く使用されています。そこで、以下のコードでは、絵文字や顔文字などのリストをsymbolsとして定義した上で、除去する記号や符号のリストにsymbolsを加える形で対応しています。一部除去する記号や符号が重複していますが、その点はご容赦いただければと思います。train['comment_text'].apply(basic_preprocess)次に、ストップワードとして定義した単語を除外していきます。通常ストップワードとして設定される「and」や「of」に加えて、「good」や「bad」などの形容詞や、「they」や「you」などの代名詞も、ストップワードとして除外していきます。使用するため、それを考慮して、全文章において、頻出回数が閾値以下となっている単語を除外していきます。今回の分析では閾値を100回に設定しています。前処理が終わったら、LDAのモデリングに必要な辞書とコーパスを用意していきます。辞書の作成まず最初に辞書を作っていきます。Bag-of-Words(以下では「BoW」と略)というドキュメントの表現方法を使用しています。この表現方法では、1つのベクトルのそれぞれの要素を、以下のような質問と回答のペアとして表します。XX回この表現方法のメリットは、質問をそれに対応したIDのみで表せるところにあります。辞書とはこのIDと質問と回答の組み合わせのことを指しています。イメージとしては以下の表のようなものになります。dictionarydictionary.save_as_text("dictionary.txt")また今回の分析では、以下のコードの箇所で、no_belowとno_aboveの2つの引数を設定し、辞書に登録した単語をフィルタリングしています。精度の向上が期待できるからです。no_above=0.8)各引数の説明は以下の通りです。出現文書数/全文書数が閾値以下になるような単語のみを保持します因みに、上記2つの引数は、いずれも同一文書内での出現頻度については考慮しません。0.8を設定して、単語のフィルタリングをしています。して保存して、後で使用できるようにしておきます。いう関数を用いて、トークン化された文書をベクトルに変換します。単純にそれぞれの単語の出現回数を計算した後、単語はIDに変換し、その結果を疎ベクトルとして返します。pickle.dump(corpus,f)結果として以下のようなコーパスが出来上がります。13の単語が1回出現することを意味しています。より重み付けを適用したベクトル表現」に変換することもできます。詳細については割愛しますが、単語の出現頻度だけでなく、文章全体に占める割合も重みとして考慮するモデルであると理解しておけば大丈夫です。pickle.dump(corpus_tfidf,f)結果として以下のようなコーパスが出来上がります。コーパスに変換したものを載せています。コーパスと違って疎ベクトルのような形式ではなく、重み付けが考慮された値に変わっていることが分かります。コーパスの違いは、重み付けをしているかどうかだけなのですが、この重み付けによって、結果の精度も変わってくるので、分析の際の引き出しの1つとして覚えておくと良いでしょう。トピック数は自動で決まるわけではないので、何個のトピックに分けるのかを分析の際に決めてあげる必要があります。用いて、トピック数を検討していこうと思います。ついて、簡単に説明したいと思います。汎用能力を表す指標であり、高い精度で予測できるよい確率モデルとも見なされています。数式で書くと下記のようになります。最大になるようにパラメータを更新していくという流れになります。桁数が多すぎると値が飛んでしまうため、代わりに2を用いることもあります。特徴しては、負の対数尤度であり、また、一般的にトピック数を増やすと共に低くなる傾向にあり、低い程良いとされます。トピックの品質を測る指標であり、抽出されたトピックが人間にとって解釈しやすいかどうかを表します。高ければ、良いモデルと見做します。最適化アルゴリズムとの比較などに興味がある方は、そちらの論文も読んでみても良いかもしれません。説明はここまでにして、ここから実際に2つの評価指標を計算し、トピック数を増やしながら、各指標の変遷を確認していきたいと思います。coherence_vals.append(coherence_model_lda.get_coherence())今回はトピック数が2個からスタートして、1個ずつトピック数を増やしながら、トピック数が21個になるまで、モデルの学習と指標の計算を繰り返していきます。値を折れ線で表示し、それぞれの変遷を確認します。以下のようになりました。結果は以下のようになっています。値や推移が違っているのが良く分かります。は高ければ高い程、良いモデルであると考えられます。コーパスを使用した場合でも、トピック数としては10個に設定してあげると具合が良さそうです。比較の意味も込めて、2つのコーパスを使って、トピック数を10個に設定してモデルの学習をしていきます。用意されており、更に0.01など直接数値を設定することも可能です。上記のコードでは、random_state=0のところで乱数の設定もしています。今回の分析では、複数回実行したときの結果の再現性を高めるために、値を0に設定しています。学習が終わったら、後でモデルが使えるように、学習したモデルの保存もしておきます。コーパスを使用する場合は、上記のコードを次のように書き換えて下さい。random_state=0)これでモデルの学習が完了しました。モデルの評価ここで実際に学習したモデルについて、実際のテストデータを使って、モデルの評価をしてみたいと思います。選択肢となり得る単語の数がいくらになるかを見ていきます。実装したものが以下のコードとなっています。perplexity)結果は以下のようになりました。約520と非常に小さな値を取っているので、良いモデルであることが確認できます。いう2つのライブラリを使用した方法をご紹介します。可視化の手法としても良く使われます。よる単語頻度図は、それらの直感的な理解の際にとても役立ちます。その見た目から、データサイエンス感を出すのにも向いています(笑)今回は以下のコードで、トピックごとに単語頻度図を作っていきます。今回はBoWとTFIDFの2つのコーパスでのLDAの結果を、WordCloudで可視化しました。結果は以下の通りです。6のような数字のトピックがある一方で、TFIDFコーパスのモデルでは見られなかったりと、違いがあることが分かります。コーパスを使ったモデルよりも良さそうな気がします。本来の強みである特定のドキュメントにしか出てこない単語や、ドキュメント内で繰り返し用いられている単語に対する重み付けを上手く活かすことができず、結果的に解釈性が落ちてしまったとも考えられます。コーパスの良さが活かせるように前処理を修正した上で、トピック数の検討とモデリングを行った結果を記載しているので、そちらも参考にして下さい。pyLDAvispyLDAvisは、LDAの結果をインタラクティブに可視化するためのライブラリです。pyLDAvisにより生成されるビジュアルについて、簡単に見方を説明します。左側には各トピックを各多次元尺度法(MDS)で2次元に落として配置したものが表示されます。この配置された円の大きさは、各トピックに所属するドキュメントの合計を表しており、円と円の距離はトピック間の距離を表しています。右側には選択したトピックの頻出単語、同トピック内での出現頻度が大きい単語とその頻度が表示されます。変えると出現頻度、同トピック内での出現頻度の割合の比率を変えて順位を変えられます。右側の単語にマウスを重ねると、各トピック内における出現頻度が左側の円の大きさとして表示されます。以下の実装では、PCoA、MMDS、t-SNEの3つの多次元尺度法により、結果を可視化しています。Analysis)PCoAは別名、主座標分析とも呼ばれ、古典的MDSに該当する次元削減の手法です。主座標分析において、低次元に落とす際に使用する距離をユークリッド距離とした場合は、主成分分析と等価になります。主座標分析が主成分分析と異なっている点は、ユークリッド距離だけでなく、マハラノビス汎距離などの他の距離や、相関係数などの類似度も使えるという点です。'pyldavis_output_pcoa.html')結果のビジュアルは以下のようになります。温度や身長、長さといった比例尺度や間隔尺度等の、定量的数値データを対象に、個体の類似度を計算し、最適な配置を算出するものなので、より厳密な距離関係に向いていると言えます。'pyldavis_output_mds.html')結果は以下のようになります。め込みとも呼ばれる手法で、高次元のデータの可視化に適した次元削減アルゴリズムです。アルゴリズムの特徴としては、データ同士の「近さ」を、ユークリッド距離やコサイン類似度などではなく、確率分布によって表現することが挙げられます。以下のような弱点もあるので、使用する際は注意が必要です。'pyldavis_output_tsne.html')結果は以下の通りです。使用する次元削減アルゴリズムが変われば、当然ビジュアライズの結果も変わるので、その時々のデータに適した表現方法を選択できれば良いかと思います。因みに余談ですが、こうした高次元データの構造を解明する方法の一つとして、「トポロジカルデータアナリシス(TDA)」という分野を用いる方法があるそうです。コーパスの強みを活かすことができませんでした。モデリングを行い、結果がどのように変わるかを確認したいと思います。前処理の部分については、頻出回数が閾値以下の単語を除外する処理のコードを以下のように修正します。頻出回数の閾値を100から9に修正することで、10回以上頻出している単語を残すようにしています。texts]続いて、辞書の作成のところのフィルタリングのコードを、以下のように修正します。設定を削除することで、上記の分析では除外していた出現文書数が3未満の単語も保持しています。dictionary.filter_extremes(no_above=0.8)修正は以上となります。それでは、先程と同じくトピック数の検討をしていきたいと思います。以下のようになりました。上述のものと比べて、計算結果が全く異なっていることが分かります。値が比較的に低くなっていたのが、今回の場合では逆に比較的高い値を示しています。結果を見ると、トピック数を6個で設定してあげると良さそうです。結果は以下のようになりました。※上記の分析から、各イメージの幅を300から400に変更して、可視化しています。コーパスを使用した前述の分析結果と比べて、いずれのトピックにおいてもどのような内容なのかがはっきりしており、解釈性が飛躍的に向上してるように見えます。コーパスを2パターン作成し、それぞれを利用したモデリングの結果を比較することで、辞書やコーパスの前処理の度合いによって、結果の精度も大きく変わることが確認できました。書くに当り、参考になった書籍やサイトなどを紹介します。ついて基礎から学びたい方は、入門書として以下の3冊をオススメします。それから、実装の際に参考になったサイトも以下に挙げておきます。理論編:PLSIとLDAについて分かりやすく説明されています。gensimのDictionaryの中身を簡単にまとめてみた:gensimのDictionaryについて、各関数とその引数がまとめられています。WordCloudとpyLDAvisによるLDAの可視化について:日本語のテキストデータでLDAを実施する際に、どのような流れで分析すれば良いかの参考になります。tfidf、LSI、LDAの違いについて調べてみた:BoWやTFIDFといったコーパスの違いについて説明されています。実装する際の学習率の話や、トピック空間での類似度の比較などが説明されています。LDAとそれでニュース記事レコメンドを作った。:実装だけでなく、LDAについても数式を交えながら非常に丁寧に解説されています。エントロピーにも触れながら、丁寧に説明されています。計算手法について取り上げられています。ついて調べてみると、色々なことに話題が広がっていき、当初の想定を上回る超大作になってしまいました(笑)勉強会でこの記事を使って発表した際、「もっとこういったことも知りたい」といった意見もいただいたので、それらも可能な限り盛り込みました。勉強中であり、もしかしたら記事の中で間違っていることを書いているかもしれませんが、その時はそっと教えていただけますと幸いです。他にも記事にしたいネタはたくさんあるので、引き続き頑張って投稿していきたいと思います。Maskman@Spooky_Maskman駆け出しの凡骨データサイエンティスト。統計もプログラミングも知らないところから、現在歯を食い縛って勉強しているところです。SAS、SQL、Pythonで戯れるピチピチの17卒です。upLoginユーザーは見つかりませんでした。