old school magic

機械学習に関する備忘録です。

LDAで日本語PDF分析

概要

最近、LDAを(pythonで)実装する機会がありました。
サンプリングを用いる実装だったので、Python等のスクリプト言語だとどうしても計算時間が問題になってしまいます(特に大規模なデータに対して)。
せっかくなのでコンパイル系の言語であるJavaで実装し直し、ついでに日本語PDFファイル(というか日本語論文)をLDAで分析してみました。

全体的な手順としては、

  1. PDFからテキスト抽出
  2. 正規表現で日本語を抽出
  3. Mecab形態素解析
  4. 特徴語(今回は名詞)の選択
  5. ストップワードの除去
  6. LDAで分析

となっています。

分析に使ったLDAの実装やスクリプトGithubにあります。
LDAのJava実装
https://github.com/breakbee/LDA4J
PDF分析のスクリプト
https://github.com/breakbee/PDFAnalysis

を使用しました。

LDA

LDA(Latent Dirichlet Allocation)は、簡単に言うと自然言語処理における確率的なクラスタリング手法です。クラスタのことを「トピック」と読んでいます。
ある文書 {d}{i} 番目の単語がトピック {z} に属する、という形でクラスタリングします。
例えば、トピック 0 には「ホームラン ヒット スクイズ」など野球に関する単語が、トピック 1 には「ワールドカップ 得点王 オーバーヘッド」などサッカーに関する単語が頻出する、という結果が得られます。

どのトピックが選ばれるかについての確率(トピック分布の確率)と、そのトピック内での単語の選ばれやすさ(単語分布の確率)があり、それを見ることによって分析の概略を掴むことができます。

参考
Latent Dirichlet Allocation ゆるふわ入門 - あらびき日記
Latent Dirichlet Allocations の Python 実装 - Mi manca qualche giovedi`?
Latent Dirichlet Allocation(LDA)を用いたニュース記事の分類 | SmartNews開発者ブログ

PDFからテキスト抽出

PDFファイルからテキストを抽出するのに、Apache PDFBox というライブラリを使用しました。
Apache PDFBox
https://pdfbox.apache.org/

使用方法はこんな感じです(公式より)。

java -jar pdfbox-app-x.y.z.jar ExtractText [OPTIONS] <inputfile> [Text file]

参考
https://pdfbox.apache.org/commandline/#extractText

楽にテキストを抽出できますが結構エラー吐きます。
また、出力されたテキストはPDFでの改行位置で文章が途切れているので、そこらへんをうまく処理する必要があるかと思います。

分析

テキスト抽出後、一度整形し、日本語のみを抽出し、形態素解析し、名詞だけをとりだして特徴量とし、LDAで分析します。
自分ではこんな感じで実験してみました。

  • 主に機械学習関係の論文とスライドを462ファイル用意
  • 特徴抽出前のPDFファイルは合計約628MB
  • 特徴抽出後のテキストファイルは約3.7MB
  • LDAのトピック数は100、サンプリング回数は10000回
  • 学習はおよそ26時間

実験結果はこんな感じになりました。
トピック分布の確率(のパラメータ)が0.1以上のトピックについて、頻出単語上位10個を表示しました。

Summarization:
	Topic num = 100
	print Topic if theta > 0.1 and 10 frequent words in Topic
Topic : 11 (theta = 0.5105679678472282)
	確率 : 0.025439009730502445
	モデル : 0.022912977793661367
	分布 : 0.02147939375233516
	データ : 0.01885995548985526
	学習 : 0.01504654071703569
	推定 : 0.014226189509251287
	関数 : 0.012995662697574684
	計算 : 0.009730502444809046
	問題 : 0.009628973829984244
	アルゴリズム : 0.007346610568722689
	状態 : 0.007196348218781981
Topic : 51 (theta = 0.12858576604531036)
	利用 : 0.025817941788200714
	評価 : 0.020462555889056332
	推薦 : 0.018718941875381416
	情報 : 0.017971678726663595
	アイテム : 0.015729889280510133
	システム : 0.01372473316478398
	データ : 0.012255115638972264
	ユーザ : 0.010598682325981094
	嗜好 : 0.007958352533844793
	検索 : 0.007385450786494464
	予測 : 0.005903378874870786
Topic : 77 (theta = 0.1504352870224158)
	言語 : 0.0222420226095673
	表現 : 0.016327894414434482
	意味 : 0.01551484100907724
	単語 : 0.015069332293812998
	パターン : 0.01056969426964415
	処理 : 0.010001670657682241
	翻訳 : 0.008453527872138999
	構造 : 0.007807540235005847
	解析 : 0.007729576209834605
	概念 : 0.007328618366096787
	参照 : 0.0066492175753188176
LDA completed.

今回用意した論文は半分くらいは統計的機械学習について、あとは自然言語処理データマイニングニューラルネットワーク等となっています。
Topic : 11 は統計的機械学習に、Topic : 51 はデータマイニング(特に推薦)、Topic : 77 は自然言語処理に対応していると考えられます。
表示されてはいませんが、ニューラルネットワークに対応するトピックもあったので、用意したPDFファイルの傾向を掴めていると言っていいのではないかと思います。

実際には、自分の持っているPDFファイルを適当に突っ込んだので、分析結果が出たあとに何となく眺めてみて「確かにこんな感じだなー」と思いました。

感想

  • 分析結果をどう活用するか

今回は分析して結果を見ただけですが、実際の分析結果を用いて、自分の興味のありそうな未読論文を推薦してくれるシステムとかが作れるかもしれません(Google Scholar のマイライブラリ等が使えそう?)。

今回の試みとして、テキストの整形等はPythonシェルスクリプト、計算の重い部分はJava、と役割分担をしてみました。

Javaでの機械学習の実装は面倒でしたが、Pythonでの実装より大幅に高速化されているのを確認しました(少なくとも倍近く)。
今回は26時間とかなりかかっていますが、サンプリング回数とトピック数が多いためであり、特にトピック数を減らせば計算時間が大幅に減らせます。トピック数が一桁だと数秒から数分でした(まあトピック数なんて事前にわからないんですけれども...)。
サンプリング回数はできるだけ多い方が良いと思います(本当はburn-inとかやらなきゃいけないんですがやってないです...)。
また、Javaに比べて手軽な処理やテキスト整形等はスクリプト言語であるPythonが便利でした。

結論として、前処理や特徴抽出に関しては、スクリプト言語で手軽に行うのが楽で良いと思います。
また、機械学習の手法に関しては、PythonMatlab、Rで実装してみて、精度や計算時間を考慮してからC++Javaで実装し直すのが良いのかな、と思いました。

  • データが大事

今回一番実感したのは、データの収集方法と、前処理、特徴抽出の大変さです。
PDFファイルからのテキスト抽出は結構ノイズが入るので、前処理でしくじると結果も悲惨なことになりました。正規表現ユニコードなど色々と勉強し直しました。

特徴抽出も、最初は全単語を用いていたのですが、収拾がつかなくなってしまったので、用いる品詞を限定することにしました。

今まで私は分析手法ばかりに目が行っていたのですが、データの収集や前処理、実験結果の分析なども同じ位大切ということだと思います。
そして分析手法とは違いあまり定式化されていないので、自分なりのアプローチを確立し、問題ごとに良く考える必要があると感じました。