6

私のニュース ページ プロジェクトには、次の構造を持つデータベース テーブルnewsがあります。

 - id: [integer] unique number identifying the news entry, e.g.: *1983*
 - title: [string] title of the text, e.g.: *New Life in America No Longer Means a New Name*
 - topic: [string] category which should be chosen by the classificator, e.g: *Sports*

さらに、単語の頻度に関する情報を含むテーブルベイがあります。

 - word: [string] a word which the frequencies are given for, e.g.: *real estate*
 - topic: [string] same content as "topic" field above, e.h. *Economics*
 - count: [integer] number of occurrences of "word" in "topic" (incremented when new documents go to "topic"), e.g: *100*

ここで、PHP スクリプトですべてのニュース エントリを分類し、いくつかの可能なカテゴリ (トピック) の 1 つをそれらに割り当てたいと考えています。

これは正しい実装ですか?改善できますか?

<?php
include 'mysqlLogin.php';
$get1 = "SELECT id, title FROM ".$prefix."news WHERE topic = '' LIMIT 0, 150";
$get2 = mysql_abfrage($get1);
// pTOPICS BEGIN
$pTopics1 = "SELECT topic, SUM(count) AS count FROM ".$prefix."bayes WHERE topic != '' GROUP BY topic";
$pTopics2 = mysql_abfrage($pTopics1);
$pTopics = array();
while ($pTopics3 = mysql_fetch_assoc($pTopics2)) {
    $pTopics[$pTopics3['topic']] = $pTopics3['count'];
}
// pTOPICS END
// pWORDS BEGIN
$pWords1 = "SELECT word, topic, count FROM ".$prefix."bayes";
$pWords2 = mysql_abfrage($pWords1);
$pWords = array();
while ($pWords3 = mysql_fetch_assoc($pWords2)) {
    if (!isset($pWords[$pWords3['topic']])) {
        $pWords[$pWords3['topic']] = array();
    }
    $pWords[$pWords3['topic']][$pWords3['word']] = $pWords3['count'];
}
// pWORDS END
while ($get3 = mysql_fetch_assoc($get2)) {
    $pTextInTopics = array();
    $tokens = tokenizer($get3['title']);
    foreach ($pTopics as $topic=>$documentsInTopic) {
        if (!isset($pTextInTopics[$topic])) { $pTextInTopics[$topic] = 1; }
        foreach ($tokens as $token) {
            echo '....'.$token;
            if (isset($pWords[$topic][$token])) {
                $pTextInTopics[$topic] *= $pWords[$topic][$token]/array_sum($pWords[$topic]);
            }
        }
        $pTextInTopics[$topic] *= $pTopics[$topic]/array_sum($pTopics); // #documentsInTopic / #allDocuments
    }
    asort($pTextInTopics); // pick topic with lowest value
    if ($chosenTopic = each($pTextInTopics)) {
        echo '<p>The text belongs to topic '.$chosenTopic['key'].' with a likelihood of '.$chosenTopic['value'].'</p>';
    }
}
?>

トレーニングは手動で行われ、このコードには含まれていません。「不動産を売ればお金を稼ぐことができる」というテキストがカテゴリ/トピック「経済」に割り当てられている場合、すべての単語 (you,can,make,...) が「経済」のテーブルベイに挿入されます。トピックと 1が標準カウントです。単語が同じトピックとの組み合わせで既に存在する場合、カウントがインクリメントされます。

サンプル学習データ:

単語のトピック数

カチンスキー 政治 1

ソニーテクノロジー1

銀行経済学1

電話技術 1

ソニー経済学3

エリクソンテクノロジー2

サンプル出力/結果:

テキストのタイトル: 電話テスト Sony Ericsson Aspen - 敏感な Winberry

政治

....電話 ....テスト ....ソニー ....エリクソン ....アスペン ....センシティブ ....ウィンベリー

テクノロジー

....phone FOUND ....test ....sony FOUND ....ericsson FOUND ....aspen ....sensitive ....winberry

経済

....電話 ....test ....sony FOUND ....ericsson ....aspen ....sensitive ....winberry

結果: テキストはトピック Technology に属し、可能性は 0.013888888888889 です。

事前にどうもありがとうございました!

4

1 に答える 1

7

コードは正しいように見えますが、コードを最適化する簡単な方法がいくつかあります。たとえば、すべての単語についてその場でp(word | topic)を計算しますが、これらの値は事前に簡単に計算できます。(ここで複数のドキュメントを分類することを想定しています。単一のドキュメントのみを実行している場合は、ドキュメントにない単語については計算しないため、これで問題ないと思います)

同様に、p(topic)の計算はループの外に移動できます。

最後に、最大値を見つけるために配列全体をソートする必要はありません。

すべての小さなポイント!しかし、それはあなたが求めたものです:)

これをどのように実装するかを示す、テストされていないPHPコードを以下に記述しました。

<?php

// Get word counts from database
$nWordPerTopic = mystery_sql();

// Calculate p(word|topic) = nWord / sum(nWord for every word)
$nTopics = array();
$pWordPerTopic = array();
foreach($nWordPerTopic as $topic => $wordCounts)
{
    // Get total word count in topic
    $nTopic = array_sum($wordCounts);

    // Calculate p(word|topic)
    $pWordPerTopic[$topic] = array();
    foreach($wordCounts as $word => $count)
        $pWordPerTopic[$topic][$word] = $count / $nTopic;

    // Save $nTopic for next step
    $nTopics[$topic] = $nTopic;
}

// Calculate p(topic)
$nTotal = array_sum($nTopics);
$pTopics = array();
foreach($nTopics as $topic => $nTopic)
    $pTopics[$topic] = $nTopic / $nTotal;

// Classify
foreach($documents as $document)
{
    $title = $document['title'];
    $tokens = tokenizer($title);
    $pMax = -1;
    $selectedTopic = null;
    foreach($pTopics as $topic => $pTopic)
    {
        $p = $pTopic;
        foreach($tokens as $word)
        {
            if (!array_key_exists($word, $pWordPerTopic[$topic]))
                continue;
            $p *= $pWordPerTopic[$topic][$word];
        }

        if ($p > $pMax)
        {
            $selectedTopic = $topic;
            $pMax = $p;
        }
    }
} 
?>

数学は...

p(topic | words)を最大化しようとしているので、

arg max p(topic|words)

(つまり、p(topic | words)が最も高い引数トピック)

ベイズの定理は言う

                  p(topic)*p(words|topic)
p(topic|words) = -------------------------
                        p(words)

だからあなたは探しています

         p(topic)*p(words|topic)
arg max -------------------------
               p(words)

ドキュメントのp(words)はどのトピックでも同じであるため、これは検索と同じです。

arg max p(topic)*p(words|topic)

単純ベイズの仮定(これは単純ベイズ分類器になります)は次のとおりです

p(words|topic) = p(word1|topic) * p(word2|topic) * ...

だからこれを使って、あなたは見つける必要があります

arg max p(topic) * p(word1|topic) * p(word2|topic) * ...

どこ

p(topic) = number of words in topic / number of words in total

                   p(word, topic)                         1
p(word | topic) = ---------------- = p(word, topic) * ----------
                      p(topic)                         p(topic)

      number of times word occurs in topic     number of words in total
   = -------------------------------------- * --------------------------
            number of words in total           number of words in topic

      number of times word occurs in topic 
   = --------------------------------------
            number of words in topic
于 2010-09-02T12:18:01.537 に答える