日本語単語によるトピック解析

 英単語によるトピック数調整を考えていて、各トピックごとにどれだけの筆者が分類されるのかを調べていたところ、トピック数6のときに、トピック0番により多くの筆者が分類されていることがわかりました。

 その後、トピック数6でトピック0番での出現確率が高い単語を確認してみたところ、だいたいそのトピックの内容を推測できるような単語が集まっているようでした。
 
 もう少しトピック解析の特徴を知りたいので、今度はポスターの日本語要旨を使ってトピック解析してみることにしました。まずはMeCabで名詞を取り出そうとしたのですが、Rubyのバージョンを変えたからか、スクリプトからMeCabをうまく呼び出せずにエラーで落ちてしまいました。
 またMeCabのインストールからやり直すのは手間がかかるので、Rubyから直接コマンドを呼び出して、標準出力に得られたMeCabの結果をパースすることを考えようと思います。

トピック数決定の基準

 LDAによるトピックモデル解析についてさらに調査と考察を行いました。今回のトピック解析の目的は、数千件のポスターをいくつかのクラスタに分け、その中で文章間関連度解析を行おうとしていました。その中で、各トピッククラスタがどれだけの文章数を内包するものになるのか、またトピックでみたときに各単語の出現率を推定することになります。
 とりあえずディリクレ分布のハイパーパラメータは0.1で固定、パラメータ推定の反復回数は1000回で決めうちにして、トピック数を変化させたときに、どのような単語出現確率になるのか、またどのような文章分類確率になるのかを確かめることにしました。
 トピック数を3〜10で変化させ、それぞれのときの各文章の文章分類確率の最大値を回収してみることにしました。各文章は各トピックに対し、そのトピックに分類される確率が計算されます。この確率は全トピックについて足し合わせるともちろん1になり、全トピックに分類される確率が当確率だと、それぞれ(1/トピック数)になります。
 私としては、各文章はある一つのトピックに分類されるようになってほしいので、あるトピックに属する確率だけが90%などと大きくなってほしいと考えています。なので、各文章ごとに、トピックへの分類確率の最大値を回収し、それらがトピック数を変えたときにどのような分布になるのかを調べました。

 今回は粗めに、10%刻みである確率の範囲に入る文章数を数えました。また、100%から考えて、n%〜100%までに分類されるポスター数が、全体の何%になるかを累積度数を調べることで計算しました。
 結果、分類率50%以上に全体の7割以上のポスターが属するのは、トピック数7以下のとき、という基準が見えてきました。今後は他の基準を探しつつ、トピック数7以下のときの単語の様子などを調べていきたいと思っています。

単語リストの修正とLDAパラメータ調整

 前回まで、ポスター筆者の単語リストを作成するとき、ポスターのアブストラクトとその筆者が発表した論文のアブストラクトをまとめて、単語リストとスコアを生成していました。しかしそれだと、そのポスター発表会における研究内容とはずれてきてしまうので、今回、ポスターのアブストラクトの単語を重視して単語リストを作成しなおしました。

 まずポスターのアブストラクトの名詞を回収して頻度を保持します。その後、その筆者の論文アブストラクトと照らし合わせて、そちらでも出現する単語について、出現数を加算していくようにしました。

 この方法ですべての筆者についての単語リストを作成しましたが、やはり「result」や「study」といった名詞がスコア上位に来てしまうようです。
 これらの単語を除くため、今回も出力抑制リストを作成することにしました。まずは全員の単語リストを統合し、スコア上位のものを見ていって、生物学用語としては意味を持たないものを、出力抑制リストに手動で追加していきました。
 今のところは私が目で見て判断していますが、今後、一般的な英文から出現数の高い単語のリストを作って、それを使用することにしようと考えています。

 そして、筆者ごとにまとめた単語リストを、前回同様LDAモデル解析を行いました。ここでも試すことがあって、まずギブスサンプリングの回数をどうするか、またハイパーパラメータのαをどのように設定するか、検討の余地があります。
 サンプリング数を増やしたり、αを変化させたりすることで、得られるトピックを確認しています。また、トピック数をいくつにするのかも問題です。
 トピック数を増やせば、その分あるトピックに分類されるポスター数が増えるので、どのトピックにも分類されなかったポスターを減らすことができます。このあたりも推定精度との兼ね合いになるので、今後様子を見ていこうと思います。

単語リストのトピックモデル解析

 ポスター著者ごとの単語リストを作成したのですが、これからどのように似た研究分野をクラスタリングすればいいのかを考えていました。
 すると、牧場名産品である「PCRH(プーアル茶)」で利用されている、LDA(Latent Dirichlet Allocation)という、トピックモデルがあるというお話をいただきました。

 LDAの詳しいアルゴリズムはわかりませんが、ある同じ文章中に出現する単語は、おなじトピックを表していると考えられるという前提により、単語リストを与えると各単語をT個のトピックに振り分けてくれる、という感じのものだそうです。
 詳しくは、かつての牧場民で大先輩の@y_benjo氏が作成されたスライド
http://www.slideshare.net/ybenjo/patent-analysislda-and-spotfire
をご覧ください。

 そして今回、y_benjo様の遺産、GitHubのLDA実装(https://github.com/ybenjo/LDA)をそのまま使わせていただくことにしました!!
 さっそく上記ページよりダウンロードしてきて、牧場のマシンにg++ 4.8をインストールしてもらってコンパイルしました。

 その後、「Author-Topic Model」で使えるように、私の単語リストを改変するRubyスクリプトを作成しました。
 各筆者ごとにスコアつき単語リストがあるので、スコアが2以上の単語を筆者のポスター名の後ろに、"\t"をはさみながら追記していくだけのプログラムです。

 これで作成した単語リストを「author_topic」に渡し、αを0.1、トピック数を5、イテレーションを100、出力制限を100語にして実行してみました。
 渡した単語リストは、約4000人のポスター筆者について、各30単語ぐらいのものでしたが、計算は2分程度で終わり、結果を見ることができました。
 5つのトピックに分かれるように指定しましたが、単語を見たところ、だいたい「発現解析」「がん治療」「シグナル伝達」「DNA配列解析」「メディカル研究」といったような分野の単語が集まっているようでした。なかなかいい感じに分かれています。

 次回は、使用している単語リストについて、ポスターの英語アブストラクトの単語に重きを置くように修正して、トピック解析を続けてみようと思っています。

単語スコア計算の続き

 前回、ポスター筆者の論文ごとに単語の出現頻度を計算し、さらに論文全体の単語スコアを計算する部分まで実装し、それを全筆者について実行した結果を確認しました。
 まず気になったのは、筆者名で検索した論文が少なかった場合に、研究の特徴を表すような単語のスコアが高くなりにくかったことです。今回の実装では、同じ単語が論文間に出現するほど点数が高くなるので、論文数が少ないと点数があまり高くならなかったようです。このため、単語スコアを計算するのに、論文だけでなく筆者のポスターの英語アブストラクトも使用することにして、文献数を稼ぐことにしました。
 また予想はしていましたが、英文一般に出現する単語がどうしても高スコアになってしまいがちでした。なのでGENIA taggerの、生命科学用語に注釈を付けてくれる機能を使って、GENIA taggerにより生命科学用語だと判定された単語には2倍のスコアを与えることにしました。

 このように実装を変更し、もう一度牧場のマシンで走らせています。結果は次回確認することにして、次に単語スコアリストから筆者をクラスタリングする方法について考えています。とりあえずk個のクラスタに非階層的に分けてみようと思い、kmeans法で分類する方向で行こうと考えています。実装も次回入るつもりです。

論文間単語スコアの計算

 GENIA taggerを使った単語数計算ができるようになったので、今回は、論文アブストラクトごとに計算した単語数から、全体の出現単語のスコアを算出する部分を実装しました。
 あるポスターの筆者が過去に出した論文n本に対して、1つずつアブストラクトから英単語の出現数を求め、それを初期スコアとします。次に、n本の論文について各々計算した出現数を足し合わせて値xとし、さらに何本の論文に共通して出現したかも計算して値yとします。最後にその筆者の研究に関係する英単語のスコアとして、x*yとしました。
 すなわち、その筆者の研究に関連する英単語を、(各論文に出現した回数)×(1回でも出現した論文数)のスコアで順位付けしてみたことになります。これでいくつかの論文について確認してみましたが、その人が何をしているのかそれなりに分かりそうな単語が高順位に来ていたので、とりあえずこの計算方法で全筆者の結果を見てみようと考えています。

 前回作成したGENIA taggerのサーバプログラムを牧場のマシンに移動し、スコア計算スクリプトを走らせてみました。次回はその結果を見て、もう少し抑制単語リストなどを用意して調整するか、このままクラスタリングへ移行するかを考えようと思います。

GENIA taggerを使ってみる

 今までは英語の品詞解析にEngTaggerを用いていましたが、名詞を抽出するだけでも複数形と単数形が別々に数えられてしまうので、各単語の原形を解析して返してくれるソフトを探しなおしました。
 この条件を満たす形態素解析プログラムとして、「GENIA tagger」がよさそうだったので、早速使ってみることにしました。
 本家のホームページからではなかなかダウンローダが進まなかったので、GitHubからダウンロードしました。

 makeしてすぐに実行ファイルは作成できますが、これをRubyから使用するには外部コマンドとして実行しなければならないので、「GENIA Taggerで英語の形態素解析」のサイトさんを参考に、GENIAサーバを作成して実行できるようにしました。

 このサーバはソケットに送られてきた文字列を、1行ずつGENIA taggerに送って、解析結果を1行にまとめて送り返してくれます。これを利用して、頻度解析クラスの方で解析したい文章を1行にまとめて、結果をハッシュにまとめるというようにコードしました。
 GENIA taggerの返却結果は、2列目に原形、3列目に品詞名が入っているので、これをたよりに単語の出現数を数えました。

@socket.puts document.gsub("\n", " ")
result = @socket.gets.gsub(" ", "\n").chomp
word_list = Hash.new #単語出現数を数えるハッシュ

result.each_line do |line|
elements = line.split(/\t/) #タブ区切りにする

if elements[2] =~ /^NN/ #名詞を回収
word = elements[1].downcase #小文字に直す

if word_list.include?(word) #既に追加済なら
word_list[word] +=1
else #新規登録
word_list[word] = 1
end
end
end
#スコアの大きい順に並び替え
word_list = word_list.sort{|a,b| b[1] <=> a[1]}

 結果を確認してみたところ、「mice」は「mouse」として数えられていましたし、他の「-s」付きの名詞も原形で回収できていました。次回は論文間の出力の比較を行いたいと思います。