无尘阁日记

无尘阁日记

贝叶斯过滤:先猜一个,再看着办
silcano

你有没有过这样一个瞬间——

手机在桌上“叮”一声。你眼睛没离开手头的活儿,只是斜着瞥了一眼锁屏,上面写着:“【顺丰】您的包裹因地址不详无法派送,请点击链接确认。”

你还没伸手去拿,心里已经给它判了死刑:假的。

然后你把手机拿起来,看了一眼发件人是一串 106 开头的长号码,就更笃定了。

整个过程不到两秒。你没查号码归属地,没搜过这个措辞对不对,没打开链接去看域名,甚至没读完整句话。你就是“感觉不对”。

这篇文章想聊的,就是这个“感觉”背后的东西。它有个听着挺唬人的名字,叫贝叶斯过滤。

我先把结论放在这儿:它一点也不高深。拆开看,朴素得有点过分,甚至朴素到你会怀疑——就这?就这也能叫数学?

但就是这套“就这”,活了二百五十年,今天还在你手机里每天跑几十万次。

· · ·

一、先认识一个人:一个一辈子没红过的牧师

托马斯·贝叶斯,1702 年生,英国长老会牧师。

这人的正经工作跟数学关系不大,主要是写神学文章,生前没发表过多少东西,一辈子没什么名气。他死后,朋友在他遗物里翻出一篇没写完的论文,替他投给了英国皇家学会。论文的标题很长,直译过来大概是《关于求解机会理论中一个问题的尝试》。

这篇论文里,他提了一个问题,用今天的话说就是:

如果我不知道一件事到底有几分可能,但我看到了跟它相关的证据,那我该怎么调整我原本的判断?

他给出了答案。

这个答案后来被印进了每一台手机、每一个邮箱、每一个推荐系统、每一份体检报告。而他本人,一辈子没因为这件事得到过任何名声——甚至连“贝叶斯”这个姓氏,都是后人追认上去的。

一个牧师,解决了一个“看到证据之后该怎么改主意”的问题。这事本身有点意思:他干的活儿,是劝人相信;他留下的公式,讲的是如何怀疑。

· · ·

二、那个公式,翻译成人话只有一句

先上公式。别怕,我马上翻译,翻译完你会发现它其实是一句家常话。

P(A | B) = P(A) × P(B | A) ÷ P(B)

这四个符号,用大白话拆开:

符号大白话
P(A)我原来的判断。没看任何证据之前,我猜它是几分可能
P(B | A)假如我猜对了,看到这个证据的可能性有多大
P(B)不管我猜得对不对,看到这个证据的可能性有多大
P(A | B)看到证据之后,我该把判断改成几分

换个说法,就这么一句:

新判断 = 老判断 × 这个证据的含金量

那“含金量”到底是什么?是这句话:

这个证据,在“我猜对了”的世界里有多常见;在“我猜错了”的世界里又有多常见。

两者一比,就是它的含金量。

举个例子你就懂了。

假设你体检,医生说你某项指标异常。这个指标,在“真有病”的人里 90% 会异常;在“没病”的人里也有 30% 会异常。

那它算不算强证据?

90% ÷ 30% = 3 倍。也就是说:看到这个异常,你“有病”的可能性变成了原来的 3 倍。

不弱,但也不至于“天塌了”。

这就是贝叶斯的第一个直觉:证据不告诉你答案,它只告诉你“往哪边挪,挪多少”。

· · ·

三、第一个完整例子:门外那个人是谁

把这个公式忘掉。我们讲个更实在的场景。

晚上九点半,有人敲门。

你脑子里第一反应是什么?

不用细想。凭经验,晚上九点半敲门的,十个里大概七个是送外卖或者送快递的,两个是邻居,剩下一个是推销、物业、走错门的。

所以你的“先验判断”是:外卖/快递 70%,邻居 20%,其他 10%。

这个 70/20/10,就是公式里的 P(A)。没看任何证据之前,你心里那个大概的谱。

然后,证据一个一个来了。

证据一:敲门声很急,咚咚咚连着三下

外卖员的敲门声通常急,因为手上还挂着两袋餐,急着送下一单;邻居一般缓一些,还可能喊一声“是我”;推销员往往按门铃,不敲门。

假设这个证据在三种情况下的出现率是:外卖 60%,邻居 15%,推销 5%。

那判断就动了。乘法先走一遍:

外卖:0.70 × 0.60 = 0.42

邻居:0.20 × 0.15 = 0.03

推销:0.10 × 0.05 = 0.005

三个数加起来是 0.455,不等于 1。所以要按比例缩回去(这叫归一化):

外卖 0.42 ÷ 0.455 ≈ 92%

邻居 0.03 ÷ 0.455 ≈ 6.6%

推销 0.005 ÷ 0.455 ≈ 1.1%

一个敲门声,就把“外卖”从 70% 顶到了 92%。

注意一件事:这个 92% 是从 70% 来的。如果你的先验是 30%,同样一个敲门声,顶到不了 92%。

证据的作用是“放大”,不是“创造”。放大倍数一样,起点不同,终点就不同。

证据二:从猫眼一看,对方没穿工服,穿的是一件 T 恤

你说,外卖员不都穿工服吗?不一定。众包骑手、跑腿、同城急送,很多人就穿自己的衣服。所以这个证据不是决定性的。

T 恤的出现率:外卖 30%,邻居 80%,推销 50%。

外卖:0.923 × 0.30 = 0.277

邻居:0.066 × 0.80 = 0.053

推销:0.011 × 0.50 = 0.0055

总共 0.335。归一化:外卖 82.6%,邻居 15.7%,推销 1.6%。

看见没有——外卖的判断掉下来了,从 92% 掉到 82.6%;邻居从 6.6% 涨到了 15.7%。

一个“没穿工服”,把答案往回拉了一截。

这就是贝叶斯最要紧的一点:它可以往下走。很多人的思维是“找到了支持我的证据 → 我更强了”,单向的。贝叶斯是双向的,新证据可以把你原来的判断直接按回去。

证据三:他手里拎着一个塑料袋,袋子上没有 logo

塑料袋出现率:外卖 85%,邻居 20%,推销 10%。

外卖:0.826 × 0.85 = 0.702

邻居:0.157 × 0.20 = 0.031

推销:0.016 × 0.10 = 0.0016

总共 0.735。归一化:外卖 95.5%,邻居 4.3%,推销 0.2%。

可以开门了。

这就是贝叶斯过滤的全部秘密:不是你一次算对,是你算了很多次,每一次都让新证据微调一下。

而且这里藏着一个特别容易理解错的地方。

塑料袋这个证据,不是“塑料袋证明他是外卖员”。而是“如果他是外卖员,他更容易拎塑料袋”。反过来——你邻居也可能拎塑料袋,只是概率低一些。

这个区别是贝叶斯的核心。很多人一知半解就栽在这儿:把证据当成“证词”,以为它在指认真凶。其实证据只是“似然”(likelihood)——它说的是在哪一边更容易出现,不是它站在哪一边。

· · ·

四、正题:一封邮件是怎么被“过滤”掉的

上面那个场景,只有三个证据,三行数。垃圾邮件过滤器干的是同一件事,只是证据有几千个,判断一天跑几十万次。

具体怎么做?四步。

第一步:把信拆成词

“恭喜您获得免费抽奖机会,点击领取” → 拆成:恭喜 / 您 / 获得 / 免费 / 抽奖 / 机会 / 点击 / 领取。

中文要分词,英文按空格切。有些系统还会把两三个字连在一起的片段也当成一个特征(“免费领取”算一个),这叫 n-gram。多粒度的特征一起上,判断更稳。

第二步:数历史上每个词的“垃圾率”

拿几十万封已经标好“垃圾 / 正常”的邮件,统计每个词在两个阵营里的出现频率。

比如下面这张表(数字是我编的,但量级差不多):

词垃圾邮件里出现率正常邮件里出现率倍数
免费38%1.2%32×
中奖12%0.05%240×
点击45%8%5.6×
您71%34%2.1×
会议2%22%0.09×
附件6%31%0.19×

重点看最后两行。“会议”的倍数是 0.09,小于 1。什么意思?

它是“反证据”。它出现在正常邮件里的可能性,是出现在垃圾邮件里的 11 倍。

所以贝叶斯过滤器不是只找坏词。它也认好词,好词会把判断一路往回拉。

你收到一封邮件,满屏都是“会议、附件、项目进度、下周一对齐”,就算里面夹了一句“限时优惠”,整体分数也会被压下去。

第三步:把证据乘起来

来了一封新邮件:“恭喜您获得免费抽奖机会,点击领取”。

假设先验是 50%(一封邮件事先是垃圾的概率,看你的邮箱历史)。

把那几个词的倍数乘起来:

32 × 240 × 5.6 × 2.1 ≈ 90,300

光这四个词,就把概率往上翻了九万倍。再算上“恭喜”“获得”“机会”“领取”这些,整体能翻到几十万倍。

这封信是垃圾的概率,四舍五入就是 99.99…%。

你看,这里其实有个挺漂亮的性质:单个证据可以很弱(2.1 倍,跟掷硬币差不了太多),但证据一多,就会滚雪球。

一个“您”,什么也说明不了。十个“您”加上“免费”“中奖”“点击”,就是铁证。

第四步:跟一个阈值比

实际系统不会真的把概率乘到 99.9999%——浮点数乘太多次会直接下溢成 0。所以工程上一般干两件事:

一是取对数。把“乘法”变成“加法”。几十个证据的 log 值加起来,数值稳稳当当,不会溢出。数学上等价,工程上保命。

二是只取最有信息量的 N 个词。按“倍数离 1 有多远”排序,只留前 15 个左右。剩下的信息量本来就小,扔掉不影响结论,还快。

然后跟阈值比:超过 0.9 直接进垃圾箱,0.5 到 0.9 打上“疑似”标记,低于 0.5 放行。

问题来了:阈值为什么不是 0.5?

因为两种错误的代价不一样。把正常邮件扔进垃圾箱,用户会打电话来骂人,可能还丢了一笔生意;把垃圾邮件放进来,用户只是划掉,多花半秒。

所以阈值往上调。宁可放过,不可错杀。

这一点也特别贝叶斯:决策不是概率问题,是代价问题。概率告诉你“有多可能”,代价告诉你“该不该动”。这两件事经常被混为一谈。

· · ·

五、那个“朴素”是什么意思,是夸还是骂

你可能听过“朴素贝叶斯”这个词。“朴素”(naive)不是夸它,是骂它——准确说是自嘲。

因为它做了一个明显不成立的假设:假设所有词之间互相独立。

可现实里,“中奖”和“奖金”明显是一伙的,出现一个,另一个的概率就暴涨;“免费”和“领取”也是一对。它们根本不独立,它们是一起出场的兄弟。

贝叶斯过滤器假装不知道这件事。它把每个词当成一个互不相干的路人,各自投一票,互不串供。

那问题就来了:假设错了,为什么结果还这么准?

因为它的目标不是“算准概率”,是“排对顺序”。

垃圾邮件得分 0.997,正常邮件 0.003。就算真实概率其实是 0.85 和 0.02,两者的相对次序也没变。分类器只需要一条分界线,不需要一把精确的尺子。

这是个特别工程化的智慧:当你只需要排序时,别浪费力气去校准绝对值。

但这句话有个前提,得说清楚——

如果你只是要把邮件分成两堆,排序就够了。可如果你要拿这个概率去做决策(比如“这个病人到底该不该开刀”),排序就不够了,必须校准。这是两件事,别混。

· · ·

六、生活里到处都是它,只是没跟你打招呼

贝叶斯过滤不是只活在邮箱里。换个场景,它就在你手边。

例子一:输入法

你打 “wo xiang chi”,候选第一个是“我想吃”。

为什么不是“我像池”“我向迟”?因为“我想吃”在语料里出现的次数,比另外两个高出几万倍。

输入法干的事就是:给定这串拼音,算一算哪个句子最可能。这不就是 P(句子 | 拼音) 吗。

而且你打了前三个字母 “wo x”,它就已经在猜了——这就是先验在起作用。它不等你打完,它一直在赌。

例子二:天气预报的“降水概率 30%”

很多人以为这是“30% 的地方会下雨”,或者“30% 的时间在下雨”。都不是。

它的意思是:在跟今天类似的气象条件下(气压、湿度、风向、云量),历史上 100 次里有 30 次下了雨。

纯贝叶斯:先验是“这个季节这个月份下雨的基础概率”,然后按今天的观测一点点修正。

例子三:这个最震撼——乳腺癌筛查

假设某种病的发病率是 1%。也就是 1000 个人里有 10 个病人。

检测手段很好:真有病的人,99% 能查出来。没病的人,5% 会被误报成阳性。

现在你去做检查,拿到一张阳性报告。

你的第一反应大概是:完了,99% 的概率。

真实概率是 16.8%。

算给你看,还是 1000 个人:

10 个真病人,99% 被检出 → 9.9 个阳性

990 个健康人,5% 被误报 → 49.5 个阳性

所有阳性加起来:9.9 + 49.5 = 59.4 人

这 59.4 个人里,真病人只有 9.9 个

9.9 ÷ 59.4 ≈ 16.7%

也就是说,拿到阳性报告的人里,六分之五是虚惊一场。

为什么会这样?因为病人太少。基数太小,就算检出率再高,误报的绝对数量也能把真病人淹掉。

这就是贝叶斯最反直觉、也最值钱的地方:它逼你看分母。

所以那些“某某因素致癌风险提高 3 倍”的新闻,如果没有告诉你基础发病率,这个“3 倍”其实毫无意义。基础概率是万分之一,提高 3 倍还是万分之三。而如果基础概率是 10%,提高 3 倍就值得连夜改生活习惯。

同一个倍数,配不同的先验,是两个完全不同的世界。这也是为什么媒体天天用“提高 X 倍”吓人——因为不给你先验,你就只能被那个 X 吓住。

例子四:修 bug

线上报了个 500。你的第一反应不是从头读代码,是心里先排个序:

“昨天刚上线的支付模块,最可疑;缓存层,次之;跑了三年的老代码,基本不可能。”

这就是先验。

然后你看日志:错误发生在 14:03,而 14:02 有一次发布。这个证据把“支付模块”的嫌疑顶到了 90%。

再往下查:14:02 那次发布其实只改了一个配置文件,而那个配置是缓存层的。判断瞬间反转。

你不是“猜对了”,你是“改得及时”。

这也是为什么老手排查问题快——不是他算得快,是他的先验更准。他知道哪个模块常年不稳,哪个模块三年没出过事。这个“谱”,是拿无数次事故换来的。

例子五:相亲

见第一面,印象分 60(先验,来自介绍人的描述和照片)。

吃饭时他对服务员说了声“谢谢”——加分。这个信号在“教养”这个维度上,倍数明显大于 1。

聊到工作,他说“我们那个傻逼领导”——减分。这个信号在“情绪稳定性”上,倍数小于 1。

手机响了,他看了一眼,直接挂掉,说了句“不重要”——小幅加分。

两个小时下来,你的判断从 60 挪到了 78,或者从 60 掉到了 42。

事后朋友问你为什么“感觉对了”或者“感觉不对”,你说不清。因为你的大脑做了几十次贝叶斯更新,但只把最后那个结论输出给了你。中间过程没留下日志。

· · ·

七、它便宜得离谱,所以活了两百五十年

现在有个合理的疑问:都 2026 年了,大模型满天飞,这么个老掉牙的公式还有什么用?

拿它跟深度学习比一比。

朴素贝叶斯深度学习
训练数据量几百条就能跑几万条起步,上不封顶
训练时间秒级几小时到几天
算力一个 CPU 核GPU 集群
单条推理微秒到毫秒几十毫秒起
可解释性能告诉你“因为这几个词”黑箱
垃圾邮件准确率98% 上下99% 上下

看最后两行。准确率差 1 个百分点,可解释性差了一整个维度。

在垃圾邮件这个场景里,可解释性比那 1% 重要。用户投诉“为什么我的邮件进垃圾箱了”,你得能答上来。答不上来的系统,用户会直接关掉过滤器。

还有成本。一个日均百万封的邮件网关,全上深度学习,电费和显卡钱是实打实的。而贝叶斯过滤器跑在一台普通服务器上,内存几百兆,稳如老狗。

所以现实中的垃圾邮件系统,通常是混合的:贝叶斯打底做粗筛,再叠上黑名单、发信域名信誉、SPF/DKIM 校验,个别疑难杂症才交给模型。

不是最强的赢,是最合适的活下来。

· · ·

八、它在哪儿翻车

说点实话。贝叶斯过滤不是万能的,它有明确的死角。

翻车一:没见过的词

词库里没有“数字人民币升级认证”这个词,那它的倍数就是“未知”。

而朴素贝叶斯的算法里,没见过的特征算出来是 0。0 乘任何数都是 0——一封邮件里只要有一个生词,整个概率直接归零。

这显然荒唐。没见过的词,不等于不可能出现。

解法叫拉普拉斯平滑:给每个词的计数都加 1(分子分母同时加),把它从 0 拉到一个很小的正数。这是贝叶斯过滤器里最重要的一行代码,没有之一。

但平滑只解决“不至于归零”,解决不了“真的不认识”。新出现的诈骗话术,仍然要等它出现在样本里,模型才学得会。这是所有统计方法的通病:它只能对见过的东西有把握。

翻车二:对抗攻击

垃圾邮件发送者也不傻。他们知道过滤器在看词,于是往邮件里塞一堆“正常词”:

“关于本次项目会议纪要的附件,请查收。顺带一提,恭喜您中奖,免费点击领取。”

前面那些词是“好词”,倍数远小于 1,一路把概率往下拉。这叫“贝叶斯投毒”。

解法:取对数后只保留最强的 15 个证据,而不是全部;或者限制好词的抵消能力——“好词最多只能把分数拉回中性,不能拉到正数”。也有系统直接把好词权重砍半。

这事挺有意思的:只要模型是公开的,就一定有人研究怎么骗它。机器学习和反垃圾,本质上是一场没有终点的军备竞赛。

翻车三:先验是错的

如果你的训练集里 90% 是英文邮件,那模型对中文垃圾邮件的判断会系统性偏弱——不是算法不行,是它一开始看到的“世界”就是歪的。

贝叶斯有个冷酷的特点:它不会告诉你先验是错的,它只会忠实地按错误的先验往下推,而且推得特别有逻辑。

歪掉的先验 + 严密的推理 = 严密的错误。这比随机犯错危险得多。

所以你会发现,很多系统出问题的根源不在算法,在数据。这是所有做模型的人迟早要撞上的墙。

翻车四:长文本的概率塌缩

一篇 5000 字的文章,有 3000 个词特征。就算每个词的倍数只有 1.1,乘 3000 次也是 10 的 124 次方。概率直接饱和到 0 或 1,中间的区分度全没了。

解法还是那两个:取对数、限制特征数量。短文本(邮件、短信、评论)是贝叶斯的主场,长文本它就不灵了。

· · ·

九、最后,聊点不那么数学的

贝叶斯这套东西,最迷人的地方其实不在公式,在它对待“判断”的态度。

它从不认为自己的输出是“真理”。它给出的永远是一个当前的、可修改的、带条件的估计。

P(A | B) 这个记号本身就在说明问题:竖线后面的 B,意思是“在已知 B 的前提下”。换个 B,答案就变了。它不存在无条件的答案。

这跟我们习惯的思维方式挺不一样的。我们习惯“是/不是”、“对/错”、“好人/坏人”。贝叶斯说的是:在你现在掌握的证据下,它更可能是这样。等你掌握更多证据,再说。

我特别喜欢它的一点是:它允许你一开始就错。

先验可以很离谱,没关系。只要证据足够多、足够强,判断会自动收敛到正确的地方。跑一百个证据之后,你从 10% 起步和从 90% 起步,最后的结论几乎一样。

这一点在工程上特别有用。你不需要一开始就设计一个完美的模型,你只需要一个能持续吸收证据的结构。错着错着,就对了。

但它也有冷酷的一面:证据不够的时候,再聪明的推理也救不了你。你只能等,或者去多找点证据。

所以那些“我一眼就看出来了”的判断——有时候是经验丰富(先验准),有时候是运气好,有时候是……你根本没在更新,只是把第一印象重复了一百遍,还越重复越觉得自己有道理。

这三种情况从外面看一模一样,从里面看天差地别。

区别在哪儿?

在于你有没有在问自己那个问题:

“如果我是错的,我现在看到的这些东西,出现的机会有多大?”

问出这句话的人,就已经在做贝叶斯了。

而这个问题,比那个公式有用得多。

——

参考:Thomas Bayes, “An Essay towards solving a Problem in the Doctrine of Chances”(1763,身后发表);朴素贝叶斯在文本分类中的工程实践,主要参考 SpamAssassin 与 Paul Graham 的 “A Plan for Spam”(2002)。