ベイジアンフィルタをPHPから利用する

ここ2ヶ月ほどずっと仕事から帰ってきて、ベイジアンフィルタの組み込みばかりやっていた。せっかく動くようになったので、技術面でのつまづき等についても残しておきたいと思う。

PHPでベイジアンフィルタ (棚からパルチャギ)」で紹介されて「PHP Naive Bayesian Filter(XHTML.net)」を参考に、というかライブラリもブログの記事もほとんどそのまま利用させていただいた。解説どおりにMySQLにデータベースを作成し、「Yahoo!日本語形態素解析」を噛ませる。上記サイトでは、トークンの切り出しメソッドをオーバーライドされていたが、それすらも面倒だったのでそのまま直接メソッドの内容を書き換えるが、ほぼそのまま利用させていただいたので、ソースも省略、詰まったところやさらに手を加えたところを中心に記すことにする。

ありがたいことに、思ったよりスムースに早い段階で動作が確認できたのだが、早速文書を大量に流し込んでフィルタを実行してみると、なぜかものの見事に、全ての記事が「スパム」に判定される。どんな内容でも「spamである確率1、hamである確率0」ってことはないだろうと、メソッド「categorize」の最深ブロックにデバッグ出力を組み込んでみる。

echo $category.’ ‘.$token.’ ‘.$word['count'].’ ‘.$proba.’ ‘.$scores[$category].”\n”;

これで、判定しようしている文書に出現した単語を元にスパム/ハムである確率が変化していく様子がみてとれる。追いかけていくと、どうもアルゴリズムの特性上、事前学習させる「スパム」「ハム」それぞれの文書量(単語量)の偏りが、フィルタリングの精度に大きく影響してしまうらしい。今回の場合、「ハムとして学習された文書量がスパムのそれと比べ多過ぎ」といったところのようだ。
学習した結果の単語量は、テーブル「nb_categories」のフィールド「word_count」にそれぞれ記録されるので、どのカテゴリ(今回はスパム・ハムの2カテゴリ)においても限りなく同等の単語量となるよう学習が必要のようだ。

もうひとつ、同様にメソッド「categorize」の最後にリスケール(rescale)というカテゴリごとの適合度を正規化する関数を通しているようなのだが、動かしてみるとどうもここで丸め誤差が大きい(ほぼ 0 or 1 の二値化に近い数値が返る)。なので、いっそのこと取っ払って出力された適合度に応じた動きはアプリケーションに任せる方針とする。

//return $this->_rescale($scores); //やめ
return $scores; //イキ

最終的にはアプリケーション側で以下のようにハム・スパムの適合度の比を求め、あとは平方根をかけまくって簡易リスケールしてる。結果が1未満なら概ねスパムと判断できそうだ。(ただし適合度の比で判定ができるのは、カテゴリが2通りの場合に限られる)

$scores['ham']/$scores['spam']
This entry was posted in 未分類. Bookmark the permalink.

Comments are closed.