LoginSignup
18
16

More than 5 years have passed since last update.

PHPでベイジアンフィルタを書いてみた

Last updated at Posted at 2014-11-06

勉強のため、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
18
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16