计算垃圾邮件概率

3
我正在使用Python/Django构建一个网站,并希望预测用户提交的内容是有效还是垃圾邮件。
用户有一个类似于此网站的提交接受率。
用户可以审核其他用户的提交;这些审核稍后由管理员进行元审核。
鉴于此:
  • 注册用户A提交了一些东西,其提交接受率为60%。
  • 用户B将A的帖子审核为有效提交。但是,用户B70%的时间都是错误的。
  • 用户C将A的帖子审核为垃圾邮件。用户C通常是正确的。如果用户C说某个东西是垃圾邮件/不是垃圾邮件,则80%的时间是正确的。
我如何预测A的帖子成为垃圾邮件的几率?
编辑:我制作了一个Python脚本来模拟这种情况:
#!/usr/bin/env python

import random

def submit(p):
    """Return 'ham' with (p*100)% probability"""
    return 'ham' if random.random() < p else 'spam'

def moderate(p, ham_or_spam):
    """Moderate ham as ham and spam as spam with (p*100)% probability"""
    if ham_or_spam == 'spam':
        return 'spam' if random.random() < p else 'ham'
    if ham_or_spam == 'ham':
        return 'ham' if random.random() < p else 'spam'

NUMBER_OF_SUBMISSIONS = 100000 
USER_A_HAM_RATIO = 0.6 # Will submit 60% ham
USER_B_PRECISION = 0.3 # Will moderate a submission correctly 30% of the time
USER_C_PRECISION = 0.8 # Will moderate a submission correctly 80% of the time

user_a_submissions = [submit(USER_A_HAM_RATIO) \
                        for i in xrange(NUMBER_OF_SUBMISSIONS)]

print "User A has made %d submissions. %d of them are 'ham'." \
        % ( len(user_a_submissions), user_a_submissions.count('ham'))

user_b_moderations = [ moderate( USER_B_PRECISION, ham_or_spam) \
                        for ham_or_spam in user_a_submissions]

user_b_moderations_which_are_correct = \
    [i for i, j in zip(user_a_submissions, user_b_moderations) if i == j]

print "User B has correctly moderated %d submissions." % \
    len(user_b_moderations_which_are_correct)

user_c_moderations = [ moderate( USER_C_PRECISION, ham_or_spam) \
                        for ham_or_spam in user_a_submissions]

user_c_moderations_which_are_correct = \
    [i for i, j in zip(user_a_submissions, user_c_moderations) if i == j]

print "User C has correctly moderated %d submissions." % \
    len(user_c_moderations_which_are_correct)

i = 0
j = 0    
k = 0 
for a, b, c in zip(user_a_submissions, user_b_moderations, user_c_moderations):
    if b == 'spam' and c == 'ham':
        i += 1
        if a == 'spam':
            j += 1
        elif a == "ham":
            k += 1

print "'spam' was identified as 'spam' by user B and 'ham' by user C %d times." % j
print "'ham' was identified as 'spam' by user B and 'ham' by user C %d times." % k
print "If user B says it's spam and user C says it's ham, it will be spam \
        %.2f percent of the time, and ham %.2f percent of the time." % \
         ( float(j)/i*100, float(k)/i*100)

运行脚本后,我得到了以下输出:

  • 用户A提交了100000个内容。其中60194个是“正常”。
  • 用户B正确审核了29864个内容。
  • 用户C正确审核了79990个内容。
  • “垃圾邮件”被用户B识别为“垃圾邮件”,而被用户C识别为“正常”的次数为2346次。
  • “正常邮件”被用户B识别为“垃圾邮件”,而被用户C识别为“正常”的次数为33634次。
  • 如果用户B认为它是垃圾邮件,而用户C认为它是正常邮件,则有6.52%的概率为垃圾邮件,有93.48%的概率为正常邮件。

这里的概率合理吗?这是否是模拟情景的正确方式?

3个回答

5

贝叶斯定理 告诉我们:

<code>P(A|B)=P(B|A)P(A)/P(B)</code>

因为您使用了 A、B 和 C 来表示人,所以让我们将事件 A 和 B 的字母改为 X 和 Y,这样会更清晰易懂:

P(X|Y) = P(Y|X) P(X) / P(Y)

编辑:以下内容略有错误,因为X应该是“这篇帖子_A发布的_是垃圾邮件”,而不仅仅是“这篇帖子是垃圾邮件”(因此Y应该只是“B接受A的帖子,C拒绝它”)。我不会在这里重新计算数学,因为数字已经改变 - 请参见下面的其他编辑以获取正确的数字和正确的算术。
您想让X表示“这篇帖子是垃圾邮件”,Y代表情况的组合A发布了它,B批准了它,C拒绝了它(假设条件独立)。
我们需要P(X),即任何帖子(无论是谁发帖还是谁批准帖子)都是垃圾邮件的先验概率; P(Y),先验概率,即A发布了一篇帖子,B批准了它,C拒绝了它(无论它是否是垃圾邮件);以及P(Y | X),与后者相同但帖子是垃圾邮件。
正如您可能已经注意到的那样,您并没有真正提供我们所需的所有要素进行计算。您的三个观点告诉我们:A发布的给定帖子是垃圾邮件的概率为0.4(似乎是第一个观点的含义);B的接受概率为0.3,但我们不知道它在垃圾邮件和非垃圾邮件方面的差异,除了应该有“很小”的差异(低准确性); C的接受率为0.8,再次我们不知道这是如何受到垃圾邮件和非垃圾邮件的影响的,除了应该有“大”的差异(高准确性)。
所以我们需要更多的数字!C具有高精度并接受80%的帖子告诉我们,总体上垃圾邮件必须非常少-如果整体垃圾邮件与A相同的40%,则C必须接受其中一半(即使他始终接受非垃圾邮件),才能获得总体80%的接受率,而这几乎不可能是“高准确性”。因此,假设总体垃圾邮件仅为20%,C只接受其中1/4(并拒绝1/16的非垃圾邮件),确实具有良好的准确性,并且与您提供的数字相匹配。
对于B,谁总共接受30%,现在“知道”总体垃圾邮件是20%,我们可以猜测B接受1/4的垃圾邮件和只有5/16的非垃圾邮件。
所以:P(X)=0.2P(Y)=0.3 * 0.2 = 0.06(B的总体接受率乘以C的拒绝概率); P(Y | X)=0.4 * 0.25 * 0.75 = 0.075(A的垃圾邮件概率乘以B接受垃圾邮件的概率乘以C拒绝垃圾邮件的概率)。
所以P(X|Y)=0.075*0.2/0.06=0.25——除非我算错了(很可能,重点是向您展示如何在这种情况下进行推理;-),这个特定帖子是垃圾邮件的概率为0.25——比任意随机帖子是垃圾邮件的概率略高,低于随机帖子A是垃圾邮件的概率。
但是,即使在所有地方都假设有条件独立的简化假设下,这种计算也极其敏感,因为我对B和C的误报率与漏报率以及整体垃圾邮件比率的猜测/假设。涉及到五个这样的数字(整体垃圾邮件概率,每个B和C的垃圾邮件和非垃圾邮件的条件概率),您只提供了两个相关(线性)约束(B和C的接受无条件概率)和两个模糊的“手挥”语句(关于低和高准确性),因此有很多自由度。
如果您可以更好地估计这五个关键数字,则可以使计算更加精确。
顺便说一句,Python(更不用说Django)与此案例完全无关——我建议您删除这些不相关的标签,以获得更广泛的响应!
编辑:用户在评论中澄清(应该真正编辑他的问题!):
当我说“B的调节接受率仅为30%”时,我的意思是对于B每次调节某些垃圾邮件/非垃圾邮件,他会做出错误决定7次。因此,他将标记某些垃圾邮件/非垃圾邮件的概率为70%,而实际上它们并不是。对于用户C,“他的调节接受率为80%”表示如果C说某些内容是垃圾邮件或非垃圾邮件,则他有80%的正确率。注册用户发送垃圾邮件的总体机会为20%。
...并要求我重新计算数学(我假设B和C的误报和漏报同等可能)。请注意,B是一个优秀的“反向指标”,因为他70%的时间都是错误的!-)。
无论如何:B接受A帖子的整体接受率必须为0.6*0.3(当他接受A的非垃圾邮件时)+0.4*0.7(当他接受A的垃圾邮件时)= 0.18 + 0.28 = 0.46;C必须为0.8*0.4 + 0.2*0.6 = 0.32 + 0.12 = 0.44。因此,我们有...:

P(X)=0.4 (之前我错了,写成了0.2,因为我忽略了A垃圾邮件的概率是0.4 -- 整体垃圾邮件的概率并不重要,因为我们知道这个帖子是A的!); P(Y)=0.46*0.56=0.2576(B对A的整体接受率乘以C对A的拒绝概率); P(Y|X)=0.7*0.8=0.56(B接受垃圾邮件的概率乘以C拒绝垃圾邮件的概率)。

所以P(X|Y)=0.56*0.4/0.2576=0.87(四舍五入)。换句话说:虽然先验概率是A的帖子是垃圾邮件的概率是0.4,但是B的接受和C的拒绝都增加了它的概率,因此这个特定的A的帖子大约有87%的概率是垃圾邮件。


我建议查看我的一个关于实现贝叶斯定理时精度的答案。https://dev59.com/HXE85IYBdhLWcg3wnU4d - Jacob
@Jacob,根据OP所需的操作数量,我认为在这种情况下不必担心(尽管始终使用对数远非坏主意)。如果需要更高精度的算术运算,GMP、MPIR等始终可用(如果OP想要使用Python,则可以在“gmpy”中包装,因为他最初表明了这一点——尽管我说服他删除了该标签,因为在这个阶段,他的问题是找到正确的公式,因此担心在_实现_该公式时出现数字和编程问题有点过早——建议进行“关注分离”;-)。 - Alex Martelli
@alex - 如果你能重新做一下算术,我会非常非常感激! :-) 我发现通过实际数字的例子学习比阅读算法更容易。 - Hobhouse
@alex 我已经尝试模拟提交和审核(请参见编辑)。垃圾邮件的概率最终为6.52% - 如果有任何错误,你能发现我做错了什么吗? - Hobhouse
1
@Hobhouse,在您最初的问题中,您说“用户B将A的帖子作为有效提交进行了审核...用户C将A的帖子作为垃圾邮件进行了审核。”但在您编写的模拟中,却是“如果用户B说这是垃圾邮件,用户C说这是合法邮件”,这完全是相反的!;)只需模拟您所提出的同样问题,您会看到我计算出的87%更好;-)。 - Alex Martelli
显示剩余2条评论

2

可以使用贝叶斯分类来检测垃圾邮件,并根据修改结果选择垃圾邮件和非垃圾邮件的训练集。结果也可以按用户接受帖子的比率进行加权。

高概率为垃圾邮件的结果可以被推送到审核工作流程中(如果您有专门的审核员)。类似地,以前审核的结果样本可提交到元审查工作流程中,以了解分类的质量(即我们是否存在不可接受的误报和漏报)。

最后,‘上诉’功能使用户能够抱怨被不公平分类的帖子,也可以将帖子推送到元审查工作流程中。如果用户曾多次被驳回或过多地提出上诉(也许是DOS攻击的尝试),他们的帖子可能会逐渐降低在上诉工作流程中的优先级。


-1

我们更加经验主义地处理它。

我们发现垃圾邮件的最佳指标之一是帖子/评论中外部链接的数量,因为垃圾邮件的整个目的是让您去某个地方购买东西和/或让友好的Googlebot认为所链接的页面更有趣。

我们对未注册用户的一般规则是:1个链接可能没问题,2个链接80%以上可能是垃圾邮件,3个或更多就被拒绝。我们会列出在拒绝帖子中出现的主要域名,并且即使只有1个或2个链接,它们也会成为触发器。您也可以使用RBL,但要小心,因为它们可能非常严厉。

这样简单的方法可能不适用于您,但它大大减轻了我们的Moderator的工作负担,并且我们没有收到真人投诉。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接