pLSA(pLSI)用共起行列の作成

 既に2年前にやったことですが、今回はpLSA(pLSI)というものに焦点を。
 pLSA(なんと99年に発表された論文)は言語処理とかそういう分野に限定せず色々なところで応用の効く手法ですが、言語処理の場合、共起行列(単語文書行列)と呼ばれるものを前処理として作成しておく必要があります。なのでその部分のプログラムを。

 その前に : 共起行列(単語文書行列) なんじゃらほい?

 例えばある文書ファイルに、

   こんにちは
   今日の天気は
   明日の天気は

 という感じで書いてあったとしましょう。
 したら以下みたいな数字の羅列が共起行列になります。

   0:1
   1:1 2:1 3:1 4:1
   5:1 2:1 3:1 4:1

 大雑把すぎますが、各行がそのまま元のファイルの行に対応してます。注目すべきは2行目と三行目。「今日の天気は」という表現は「今日/の/天気/は」というように形態素解析されます。各単語にIDみたいな数字が割り振られ、その行に出現した回数が記録される・・・ので、「ID : 出現回数」という固まりがずらっと並びます。「明日/の/天気/は」のうち3単語は直前で出てるので、以前使った単語IDが使われてるわけですね。

 さてさて。

 形態素解析を行う必要があるのでまずはmecabをインストールしないといけません。(ちなみにやるならChasenでも可。その場合適宜プログラムを改変すれば動くと思います)
 とりあえず、家PCはMacになってるので、とりあえず家PCにもmecabさんを入れてみよーと思います。


 1. http://mecab.sourceforge.net/ 
    ここから、mecab-*.**.tar.gz、mecab-ipadic-*.**.tar.gzをダウンロード
 2. ターミナル起動
 3. DLしてきたやつを展開。まずはmecab本体のインストールから。

     $ tar zxvf mecab-0.98.tar.gz
     $ cd mecab-0.98
     $ ./configure --enable-utf8-only
       ※mecabの標準がEUC-JPなので文字コードを設定
     $ make
     $ sudo make install

 4. お次にmecab用の辞書をインストール

     $ tar zxvf mecab-ipadic-2.7.0-20070801.tar.gz
     $ cd mecab-ipadic-2.7.0-20070801
     $ ./configure --with-charset=utf8
       ※これまた文字コード指定
     $ make
     $ sudo make install

 以上の手順でmecabのインストールは終わり。あとは適当に、

     $ mecab

 で起動できます。適当な言葉を入れたら形態素解析結果が出たりと。細かいオプションとかは本家サイトでも参照してください。


 で、ここまでが長い前置きです。
 この形態素解析mecabを使って、pLSA用の共起行列(単語文書行列)を作成するのが本題です。
 てことで以下コードを。

#!/usr/bin/perl

use strict;
use warnings;

my $mem;
my %hash = ();
my %hashword = ();
my $wordnumber = 0;

open(IN, "test.txt");

# 各文をキーとしたハッシュを作成するので変数に保管
while(my $hashtitle = <IN>){
    chomp $hashtitle;

    # mecabにより形態素解析結果を得る
    open(MCB, "echo $hashtitle | mecab | ");
    while(<MCB>){
	# 単語のみ必要なので切り分ける
	my @data = split(/\t/, $_);

	# EOS及び記号以外の単語をハッシュへ保管
	if($data[0] !~ m/EOS/ &&
	   $data[1] !~ m/^記号/){

	    # 行->単語->出現回数 のようなハッシュを作る準備
	    my $subkey = $data[0];

	    # 単語の出現回数を格納
	    if( exists ($hash{$hashtitle}{$subkey}) ){
		$hash{$hashtitle}{$subkey}++;
	    }else{
		$hash{$hashtitle}{$subkey} = 1;
	    }

	    # 単語IDを放り込む
	    if(exists ($hashword{$subkey}) ){
		# 単語が存在していない場合はIDの付与を行わない
	    }else{
		$hashword{$subkey} = $wordnumber;
		$wordnumber++;
	    }
	}
    }
    close(MCB);

    # 共起行列を保管(処理が終わった行から保管)
    foreach my $key (keys %{$hash{$hashtitle}}){
	$mem = $mem."$hashword{$key}:$hash{$hashtitle}{$key} ";
    }
    $mem = $mem."\n";
}
close(IN);

# 共起行列を出力
print $mem;

 こんな感じ。
 Perlのハッシュを2つ利用して作っている感じ。1つは 行->単語->出現回数のハッシュ。もう一つは単語->IDのハッシュ。この2つを作って保存するようにしていけば基本的に出来上がりです。

 以下のファイル(test.txt)をこのスクリプトに与えてみましょ。

   こんにちは
   今日の天気はどうですか
   明日の天気は
   いらっしゃいませ
   さようなら
   この大学について教えてください
   広島市の名産は
   広島について教えてください

 で、実行結果は以下の通り。

   $ perl kyoki.pl
   > 0:1 
   > 6:1 4:1 2:1 3:1 1:1 7:1 5:1 
   > 4:1 2:1 8:1 3:1 
   > 10:1 9:1 
   > 11:1 
   > 15:1 13:1 17:1 16:1 14:1 12:1 
   > 4:1 2:1 20:1 18:1 19:1 
   > 15:1 17:1 18:1 16:1 14:1 

 あとはこの共起行列を使ってpLSAの計算を・・・という感じです。
 ちなみに、LSAというのもあって、それを確率的に与えたのがpLSAで。更に改良したのがLDAとかありますが・・・そのへんの詳細と利用法はまた後日にでも。