В настоящее время я пытаюсь создать спам-фильтр, анализируя собранный мной корпус.
Я использую запись в Википедии http://en.wikipedia.org/wiki/Bayesian_spam_filtering для разработки своего классификационного кода.
Я реализовал код для расчета вероятности того, что сообщение является спамом, учитывая, что оно содержит определенное слово, используя следующую формулу из вики:
Мой PHP-код:
public function pSpaminess($word)
{
$ps = $this->pContentIsSpam();
$ph = $this->pContentIsHam();
$pws = $this->pWordInSpam($word);
$pwh = $this->pWordInHam($word);
$psw = ($pws * $ps) / ($pws * $ps + $pwh * $ph);
return $psw;
}
В соответствии с разделом "Объединение индивидуальных вероятностей", я реализовал код для объединения вероятностей всех уникальных слов в тестовом сообщении для определения спамности.
Из вики-формулы:
Мой PHP-код:
public function predict($content)
{
$words = $this->tokenize($content);
$pProducts = 1;
$pSums = 1;
foreach($words as $word)
{
$p = $this->pSpaminess($word);
echo "$word: $p\n";
$pProducts *= $p;
$pSums *= (1 - $p);
}
return $pProducts / ($pProducts + $pSums);
}
На тестовой строке "Это совсем не плохо." получен следующий результат:
C:\projects\bayes>php test.php
this: 0.19907407407407
isn't: 0.23
very: 0.2
bad: 0.2906976744186
at: 0.17427385892116
all: 0.16098484848485
probability message is spam: float(0.00030795502523944)
Вот мой вопрос: Правильно ли я реализую объединение индивидуальных вероятностей? Если предположить, что я генерирую достоверные вероятности отдельных слов, то правилен ли метод объединения?
Меня беспокоит очень маленькая результирующая вероятность вычисления. Я протестировал его на большом тестовом сообщении и в итоге получил результирующую вероятность в научной нотации с более чем 10 нулями. Я ожидал получить значения в 10-х или 100-х долях.
Я надеюсь, что проблема кроется в моей реализации PHP, но когда я изучаю функцию комбинации из Википедии, дивиденд формулы представляет собой произведение дробей. Я не понимаю, как комбинация нескольких вероятностей может в итоге оказаться даже больше, чем 0,1% вероятности.
Если это так, что чем длиннее сообщение, тем ниже будет оценка вероятности, то как мне компенсировать квоту спамности, чтобы правильно предсказать спам/хам для маленьких и больших тестовых случаев?
Дополнительная информация
Мой корпус - это коллекция из примерно 40 тысяч комментариев на reddit. Я фактически применяю свой "спам-фильтр" к этим комментариям. Я оцениваю отдельный комментарий как спам/хам, основываясь на количестве голосов "за" и "против": Если количество голосов "за" меньше, чем "против", он считается "хамом", в противном случае - "спамом".
Теперь, из-за типа корпуса, оказалось, что есть несколько слов, которые используются в спаме чаще, чем в хаме. То есть, вот десятка слов, которые встречаются в спаме чаще, чем в хаме.
+-----------+------------+-----------+
| word | spam_count | ham_count |
+-----------+------------+-----------+
| krugman | 30 | 27 |
| fetus | 12.5 | 7.5 |
| boehner | 12 | 10 |
| hatred | 11.5 | 5.5 |
| scum | 11 | 10 |
| reserve | 11 | 10 |
| incapable | 8.5 | 6.5 |
| socalled | 8.5 | 5.5 |
| jones | 8.5 | 7.5 |
| orgasms | 8.5 | 7.5 |
+-----------+------------+-----------+
Напротив, большинство слов в большом количестве используются в спаме чаще, чем в хаме. Возьмем, к примеру, мой топ-10 список слов с наибольшим количеством спама.
+------+------------+-----------+
| word | spam_count | ham_count |
+------+------------+-----------+
| the | 4884 | 17982 |
| to | 4006.5 | 14658.5 |
| a | 3770.5 | 14057.5 |
| of | 3250.5 | 12102.5 |
| and | 3130 | 11709 |
| is | 3102.5 | 11032.5 |
| i | 2987.5 | 10565.5 |
| that | 2953.5 | 10725.5 |
| it | 2633 | 9639 |
| in | 2593.5 | 9780.5 |
+------+------------+-----------+
Как вы можете видеть, частота использования спама значительно меньше, чем хама. В моем корпусе из 40 тысяч комментариев 2100 комментариев считаются спамом.
Как показано ниже, тестовая фраза в сообщении, которое считается спамом, оценивается следующим образом:
Фраза
Cops are losers in general. That's why they're cops.
Анализ:
C:\projects\bayes>php test.php
cops: 0.15833333333333
are: 0.2218958611482
losers: 0.44444444444444
in: 0.20959269435914
general: 0.19565217391304
that's: 0.22080730418068
why: 0.24539170506912
they're: 0.19264544456641
float(6.0865969793861E-5)
Согласно этому, вероятность того, что это спам, крайне мала. Однако, если я сейчас проанализирую комментарий хама:
Фраза
Bill and TED's excellent venture?
Анализ
C:\projects\bayes>php test.php
bill: 0.19534050179211
and: 0.21093065570456
ted's: 1
excellent: 0.16091954022989
venture: 0.30434782608696
float(1)
Хорошо, это интересно. Я делаю эти примеры, пока составляю это обновление, так что это первый раз, когда я вижу результат для этого конкретного тестового случая. Я думаю, что мое предсказание перевернуто. На самом деле он выбирает вероятность ветчины вместо спама. Это заслуживает подтверждения.
Новый тест на известном ветчине.
Phrase
Complain about $174,000 salary being too little for self. Complain about $50,000 a year too much for teachers.
Scumbag congressman.
Analysis
C:\projects\bayes>php test.php
complain: 0.19736842105263
about: 0.21896031561847
174: 0.044117647058824
000: 0.19665809768638
salary: 0.20786516853933
being: 0.22011494252874
too: 0.21003236245955
little: 0.21134020618557
for: 0.20980452359022
self: 0.21052631578947
50: 0.19245283018868
a: 0.21149315683195
year: 0.21035386631717
much: 0.20139771283355
teachers: 0.21969696969697
scumbag: 0.22727272727273
congressman: 0.27678571428571
float(3.9604152477223E-11)
Unfortunately no. Оказывается, это был случайный результат. Я начинаю задумываться о том, что, возможно, комментарии нельзя так легко оценить количественно. Возможно, природа плохого комментария слишком сильно отличается от природы спама.
Возможно, дело в том, что фильтрация спама работает только тогда, когда у вас есть определенный класс слов в спам-сообщениях?
Последнее обновление
Как было указано в ответах, странные результаты были связаны с природой корпуса. При использовании корпуса комментариев, где нет явного определения спама, байесовская классификация не работает. Поскольку возможно (и даже вероятно), что один и тот же комментарий может получить как спам, так и оценку "ham" от разных пользователей, невозможно создать жесткую классификацию для спам-комментариев.
В конечном итоге я хотел создать классификатор комментариев, который мог бы определять, будет ли комментарий набирать карму, основываясь на байесовской классификации, настроенной на содержание комментария. Я могу еще исследовать настройку классификатора на спам по электронной почте и посмотреть, может ли такой классификатор угадать кармический ответ для систем комментариев. Но пока что вопрос исчерпан. Спасибо всем за ваше участие.