勉強のため、PHPでベイジアンフィルタを書いてみました。下記のサイトを参考にしています。スムージングとして、単語の出現回数に1を足しています。アルゴリズムはリンク先の解説が正確だと思うので、割愛しますが、手計算での計算と結果は合っているので、多分正しく動作しているかと。
●参考:
第3回 ベイジアンフィルタを実装してみよう http://gihyo.jp/dev/serial/01/machine-learning/0003?page=2
P(word|cate)を求める時の分母になるカテゴリの全単語出現回数(getAllWordNumCate($cate)
)を都度計算しているので、無駄がありますが、分かりやすさを優先してそのままにしています。
Classifer.php
<?php
error_reporting(E_ALL & ~E_NOTICE);
/**
* Classifier
*
* @package
* @author kure
*/
class Classifier {
private $cc = array();
private $fc = array();
private function getwords($doc) {
return array_unique(preg_split("/\\W/", $doc));
}
/**
*
* @param type $doc
* @param type $cate
*/
public function train($doc, $cate) {
$this->cc[$cate] ++;
$words = $this->getwords($doc);
foreach ($words as $v) {
$this->fc[$v][$cate] ++;
}
}
/**
*
* @param type $cate
* @return type
*/
private function cateProb($cate) {
$total = 0;
foreach ($this->cc as $v) {
$total = $total + $v;
}
return $this->cc[$cate] / $total;
}
/**
*
* @param type $word
* @param type $cate
* @return type
*/
private function getWordNumCate($word, $cate) {
foreach ($this->fc as $key => $value) {
if ($key === $word) {
return $value[$cate] + 1;
}
}
}
/**
*
* @param type $cateSelected
* @return type
*/
private function getAllWordNumCate($cateSelected) {
$total = 0;
foreach ($this->fc as $key => $value) {
foreach ($value as $cate => $num) {
if ($cate === $cateSelected) {
$total = $total + $num;
}
}
}
return $total;
}
/**
* P(word|cat)
* @param type $word
* @param type $cate
* @return type
*/
private function wordCateProb($word, $cate) {
return $this->getWordNumCate($word, $cate) / $this->getAllWordNumCate($cate);
}
/**
* P(doc|cat) = P(word1|cat)P(word2|cat)...
* @param type $doc
* @param type $cate
* @return type
*/
private function docCateProb($doc, $cate) {
$wordArr = $this->getwords($doc);
$wordProbArr = array();
$wordProbTotal = 1;
foreach ($wordArr as $word) {
$wordProbTotal = $wordProbTotal * $this->wordCateProb($word, $cate);
}
return $wordProbTotal;
}
/**
* P(cat|doc) = P(doc|cat)P(cat)
* @param type $doc
*/
public function classify($doc) {
foreach ($this->cc as $cate => $num) {
echo $cate . ": " . $this->cateProb($cate) * $this->docCateProb($doc, $cate) . "\n";
}
}
}
$c = new Classifier();
$c->train("I have", "bad");
$c->train("I donnot", "good");
$c->train("I know", "bad");
$c->classify("I");
実行結果
bad: 0.5
good: 0.33333333333333