php - naive - Combinación de probabilidades individuales en el filtrado de correo no deseado bayesiano
naive bayes (3)
Con la idea de compensar la longitud de los mensajes, puede estimar para cada conjunto las probabilidades de que una palabra del mensaje sea una palabra específica, y luego usar una distribución de poisson para estimar la probabilidad de que un mensaje de N palabras contenga esa palabra específica.
Actualmente estoy tratando de generar un filtro de spam mediante el análisis de un corpus que he acumulado.
Estoy usando la entrada de wikipedia http://en.wikipedia.org/wiki/Bayesian_spam_filtering para desarrollar mi código de clasificación.
He implementado un código para calcular la probabilidad de que un mensaje sea spam, ya que contiene una palabra específica al implementar la siguiente fórmula de la wiki:
Mi código 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;
}
De acuerdo con la sección Combinación de probabilidades individuales, he implementado un código para combinar las probabilidades de todas las palabras únicas en un mensaje de prueba para determinar el spam.
De la fórmula wiki:
Mi código 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);
}
En una cadena de prueba "Esto no es muy malo en absoluto", se produce el siguiente resultado:
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)
Aquí está mi pregunta: ¿Estoy implementando la combinación de probabilidades individuales correctamente? Suponiendo que estoy generando probabilidades válidas de palabras individuales, ¿es correcto el método de combinación?
Mi preocupación es la muy pequeña probabilidad resultante del cálculo. Lo probé en un mensaje de prueba más grande y terminé con una probabilidad resultante en notación científica con más de 10 lugares de ceros. Esperaba valores en los lugares 10s o 100ths.
Espero que el problema radique en mi implementación de PHP, pero cuando examino la función de combinación de wikipedia, el dividendo de la fórmula es un producto de fracciones. No veo cómo una combinación de probabilidades múltiples terminaría siendo incluso más del 0,1% de probabilidad.
Si es así, de manera que cuanto más largo sea el mensaje, menor será la puntuación de probabilidad, ¿cómo compenso la cuota de spam para predecir correctamente el spam / ham para casos de prueba pequeños y grandes?
Información adicional
Mi corpus es en realidad una colección de alrededor de 40k de comentarios reddit. En realidad estoy aplicando mi "filtro de spam" contra estos comentarios. Califico un comentario individual como spam / ham en función del número de votos a la baja a los votos a favor: si los votos son menos que los votos a la baja, se considera Ham, de lo contrario, es Spam.
Ahora, debido al tipo de cuerpo, resulta que en realidad hay pocas palabras que se usan más en el correo no deseado que en el jamón. Es decir, aquí hay una lista de las diez mejores palabras que aparecen en el spam con mayor frecuencia que ham.
+-----------+------------+-----------+
| 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 |
+-----------+------------+-----------+
Por el contrario, la mayoría de las palabras se usan en gran cantidad en jamón más que en jamón. Tomemos, por ejemplo, mi lista de las 10 palabras con el mayor número de spam.
+------+------------+-----------+
| 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 |
+------+------------+-----------+
Como puede ver, la frecuencia del uso de spam es significativamente menor que el uso de jamón. En mi corpus de 40k comentarios, 2100 comentarios son considerados spam.
Como se sugiere a continuación, una frase de prueba en una publicación considera las tasas de spam de la siguiente manera:
Frase
Cops are losers in general. That''s why they''re cops.
Análisis:
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)
De acuerdo con esto, existe una probabilidad extremadamente baja de que se trate de spam. Sin embargo, si tuviera que analizar ahora un comentario de jamón:
Frase
Bill and TED''s excellent venture?
Análisis
C:/projects/bayes>php test.php
bill: 0.19534050179211
and: 0.21093065570456
ted''s: 1
excellent: 0.16091954022989
venture: 0.30434782608696
float(1)
Está bien, esto es interesante. Estoy haciendo estos ejemplos mientras compongo esta actualización, así que esta es la primera vez que veo el resultado para este caso de prueba específico. Creo que mi predicción está invertida. En realidad, es elegir la probabilidad de Ham en lugar de Spam. Esto merece validación.
Nueva prueba en jamón conocido.
Frase
Complain about $174,000 salary being too little for self. Complain about $50,000 a year too much for teachers.
Scumbag congressman.
Análisis
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)
Lamentablemente no. Resulta que fue un resultado casual. Estoy empezando a preguntarme si tal vez los comentarios no pueden ser cuantificados tan fácilmente. Quizás la naturaleza de un mal comentario sea muy diferente a la naturaleza de un mensaje de spam.
¿Quizás sea el caso que el filtrado de spam solo funcione cuando tiene una clase específica de mensajes de spam?
Actualización final
Como se señaló en las respuestas, los resultados extraños se debieron a la naturaleza del corpus. El uso de un corpus de comentarios donde no existe una definición explícita de spam no se realiza en la clasificación bayesiana. Dado que es posible (y probable) que cualquier comentario pueda recibir clasificaciones tanto de spam como de jamón por parte de varios usuarios, no es posible generar una clasificación difícil para los comentarios de spam.
En última instancia, quería generar un clasificador de comentarios que pudiera determinar si una publicación de comentarios adornaría el karma basado en una clasificación bayesiana sintonizada para comentar el contenido. Todavía puedo investigar el ajuste del clasificador para enviar mensajes de correo no deseado y ver si dicho clasificador puede adivinar la respuesta del karma para los sistemas de comentarios. Pero por ahora, la pregunta está respondida. Gracias por su aportación a todos ustedes.
Si su filtro no está sesgado (Pr (S) = Pr (H) = 0.5), entonces: "También es recomendable que el conjunto de mensajes aprendidos se ajuste a la hipótesis del 50% sobre la partición entre el correo no deseado y el jamón, es decir, que los conjuntos de datos de El spam y el jamón son del mismo tamaño ".
Esto significa que debe enseñar a su filtro bayesiano la cantidad similar de mensajes de correo no deseado y de correo no deseado. Decir 1000 mensajes de spam y 1000 mensajes de jamón.
Asumiría (no marcado) que si su filtro está sesgado, el conjunto de aprendizaje debería ajustarse a la hipótesis de que cualquier mensaje es spam.
Variando solo con la calculadora, parece correcto para la frase no spam que publicaste. En ese caso, tiene $ pProductos un par de magnitudes menores que $ pSums.
Intente ejecutar cierto spam real desde su carpeta de correo no deseado, donde encontrará probabilidades como 0.8. Y adivina por qué los spammers intentan enviar un trozo de periódico en un marco oculto junto con el mensaje :)