Javaでトピック分析(LDA)


このページについて

JavaFX&自己完結型パッケージの練習をしようと思ったが、
どうせなら使えるツールを作りたいと思い、LDAにするかと。
ここではLDA on Javaについてまとめています。
JavaFX&自己完結型パッケージについては本末に記載のリンクから参照ください。

LDAって何?

自然言語処理における、指定した文書群からトピックを推定する(汎用的な)機械学習の手法の一つです。
2019年現在はディープラーニングが盛んですが、それ以前のNLP界隈では数々の精度向上に出張っていた。
論理について読みたい方は、以下ページなどが読み易くおススメです。

Latent Dirichlet Allocation (LDA) ゆるふわ入門 より抜粋

LDA は1つの文書が複数のトピックから成ることを仮定した言語モデルの一種です。
日本語だと「潜在的ディリクレ配分法」と呼ばれます。
単語などを表層的と表現するならば、トピックは単語と違って表面には現れないので潜在的です。
その潜在的な要素の分布の事前分布にディリクレ分布を仮定してごにょごにょするから「潜在的ディリクレ配分法」と呼ぶのかなぁと思ってます。
(中略)
ディリクレ分布はざっくりと説明すると確率分布の確率分布です。
例えば、3つのトピック「スポーツ」、「経済」、「政治」があったとして、
各トピックの生成確率 (スポーツ、経済、政治) = (0.3, 0.2, 0.5) になる確率は
0>.1、(スポーツ、経済、政治) = (0.1, 0.2, 0.7) になる確率は 0.2 のように確率分布の確率を定めます。

実装方針

LDAの実装方針としては、gensimというPythonライブラリが有名な様です。
参考:gensim入門

後続のJavaFXアプリを簡単にする観点から、Python&Javaコラボはせず、
GitHubでJavaのLDAの実装例が公開されていましたので、こちらを拝借することにしました。感謝。
※注:ロジックの妥当性は検証していません。悪しからず。

LDA4jという名でふたつHITしましたが、今回はhankcs氏のモジュールを採用しました。
ほぼ好みです。
(インプットとなる文書集合の持ち方として、breakbee/LDA4J では1ファイル(1行1文書)、
hankcs/LDA4j では複数ファイル(1ファイル1文書)の違いがあったが、
個人的にファイル別の方が好みだった)

実行してみよう

環境 サービス/バージョン
実行環境 Windows10
開発環境 eclipse 4.1.0
開発言語 Java 8

適当にモジュールをeclipseに引っ張ってくる。
Githubからフォーク&クローン or Zipをダウンロードしてプロジェクトをインポートなど。
その後、自前の実行用モジュールを作成しました。(MainRunner.java)

ReadMeの言うとおりに…。

MainRunner.java
package com.ketman.app;

import java.io.IOException;
import java.util.Map;

import com.hankcs.lda.Corpus;
import com.hankcs.lda.LdaGibbsSampler;
import com.hankcs.lda.LdaUtil;

public class MainRunner {
    public static void main(String[] args)
    {
        // 1. Load corpus from disk
        Corpus corpus;
        try {
            corpus = Corpus.load("data/mini");
            // 2. Create a LDA sampler
            LdaGibbsSampler ldaGibbsSampler = new LdaGibbsSampler(corpus.getDocument(), corpus.getVocabularySize());
            // 3. Train it
            ldaGibbsSampler.gibbs(10);
            // 4. The phi matrix is a LDA model, you can use LdaUtil to explain it.
            double[][] phi = ldaGibbsSampler.getPhi();
            Map<String, Double>[] topicMap = LdaUtil.translate(phi, corpus.getVocabulary(), 10);
            LdaUtil.explain(topicMap);
        } catch (IOException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
    }
}

実行⇒実行構成⇒JavaアプリケーションよりMainRunnerを実行してみます。
コンソールに以下の様な出力が出てくるはず。
data/mini に格納されている文書集合に対して、指定した数(10)のトピックを推定しています。

Sampling 1000 iterations with burn-in of 100 (B/S=20).
BBBBB|S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||S||
topic 0 :
中国=0.0097164123524064
市场=0.007268178259268298
企业=0.006646897977003122
公司=0.006420165848545306
发展=0.005931172520179485
旅游=0.005517115761050293
目前=0.004144655174798414
记者=0.003896963247878764
产品=0.0038405773231741857
服务=0.0036131627315211285

topic 1 :
美国=0.007753386939328633
日本=0.004271883755069139
训练=0.0039382838929572965
系统=0.0038821627109404673
飞机=0.0037908977218186262
部队=0.003713327985408122
军事=0.003662570207063461
进行=0.003548971364140448
作战=0.003465095755923189
装备=0.0033491792847693187

~中略~

topic 9 :
比赛=0.00887335526016362
队员=0.003820752808354389
联赛=0.0034088636107220934
球队=0.0030593385176732896
俱乐部=0.002519739439727434
冠军=0.0025101075962186965
中国=0.002314435002019442
球员=0.0023066510579788685
决赛=0.002282312176369107
记者=0.0022029528425211455

実際にツールとして活用してみた話