日本語の潜在意味解析(Latent Semantic Analysis, LSA あるいはLatent Semantic Indexing, LSI)を試すのに、どのような方法が一番良いのか調べています。
現在、一番手軽に始められそうなのはタイトルにあるとおり、
- gensim
- MeCab
を使った方法ではないでしょうか。
以下に参考にしたサイトを挙げます。
SELECT * FROM life;
LSIやLDAを手軽に試せるGensimを使った自然言語処理入門(2011/06/23)
takuti.me
gensimでWikipedia日本語版からコーパスを作ってトピックモデリング(2017/07/22)
どちらもWikipedia日本語版の記事を読み込んでgensimに処理させようというもので、試しにWikipediaの記事を処理させてみる、というのは自然言語処理を試してみる際の定番のようですね。
他のサイトなどもいっぱい出てきます。
SELECT * FROM life;の記事が古く、それを参考に一部最新化したのがtakuti.meの記事です。
しかし現在ではこれも古く、takuti.meで公開されているソースでもエラーになって処理できませんでした。
潜在意味解析もpythonもそれほど詳しくない中、手探りで実行した軌跡です。
準備
当面の目標はpython3でgensimを使ってWikipedia日本語版のコーパスを作成し、LSIを実行してみることです。
私の環境はCentOS7(Docker)ですので、以下のパッケージをyumでインストールしています。
gcc
gcc-c++
python35u
python35u-devel
python35u-pip
mecab
mecab-devel
mecab-ipadic
インストール方法などは検索すれば出てくると思います。
pipでインストールするものは、以下のとおりです。
gensim
mecab-python3
環境によっては他にも必要なものがあるかもしれませんが、きっちりエラーを出してくれると思いますので、その都度追加してください。
コーパスの作成
takuti.meの記事では、Wikipedia英語版からコーパスを作るgensim公式サンプルを、MeCabで使えるように、以下のような変更を加えたソースを公開してくれています。
我々は gensim.corpora.WikiCorpus が内部的に使っている分かち書き用の関数 gensim.corpora.wikicorpus.tokenize を日本語向けに置き換えればよろしい:
感謝しつつ何も考えずにそのソースをコピーしてきて試すと以下のようなエラーが出て、プロセスは3つ動いたままになりますが、処理はされていません。
PicklingError: Can't pickle
何か、マルチプロセス関連のエラーのようですが、これはPython2でないと動かないというものなのか、まったく別の問題なのか、原因がよく分かりません。
そもそも、公開いただいているソースは、gensim公式のソースとはひどく乖離してしまっており、gensim公式のソースがどんどんメンテナンスされた結果、gensimのライブラリをは整合性が取れていないのかもしれません。
「分かりません」「しれません」の連打で先に進んで良いものか「分かりません」が、gensim公式のソースに改めてMeCabによる分かち書きの処理を挿入してみます。
takuti.meで公開されているソースをありがたく参考にカスタマイズします。
ソースを修正
takuti.meのソースではtokenize
という関数を定義して、WikiCorpus
クラスのインスタンスのtokenize
メソッドをオーバーライドしています。
wikicorpus.tokenize = tokenize
しかし現在では、WikiCorpus
クラスのコンストラクタにtokenizer_func
パラメータが追加されており、分かち書きを実行する関数を指定できます。
https://radimrehurek.com/gensim/corpora/wikicorpus.html
ですので、この仕組みを利用することにします。
ここで指定する関数では4つのパラメータが必要です。
https://radimrehurek.com/gensim/corpora/wikicorpus.html#gensim.corpora.wikicorpus.tokenize
- content (str) – String without markup
- token_min_len (int) – Minimal token length.
- token_max_len (int) – Maximal token length.
- lower (bool) – If True – convert content to lower case.
パラメータtoken_min_len
とtoken_max_len
は、参考ソースのマジックナンバー、2と15にそのまま置き換えました。
そして出来上がったのがこちらでgistに挙げています。
ストップワードの除去などの前処理を何もしていませんし、各種オプションの調整もしていませんのでこのままでは使えませんが、とにかく動きます。
lsi.print_topic(4)
などでトピックを表示したりはできました。
活用してみる
しかしこの後、takuti.meにあるように、特定の日本語ワードを含むトピックを見つけようとすると、python特有のUnicodeEncodeError
がどうたら、SyntaxError: (unicode error)
などでうんざりしてしまい、Javaでやろうと心に誓ってしまったので続きはありません。
お願い
なにせイマイチな理解のまま動かしていますので、もしかしたらこの記事を参考にされる方のために何か誤りなどあればご指摘いただけると幸いです。