Glean 拾遗
日刊 /2026-07-07 / 贝叶斯垃圾邮件过滤实战:五个关键改进

贝叶斯垃圾邮件过滤实战:五个关键改进

原文 www.paulgraham.com 收录 2026-07-07 14:44 阅读 25 min
AI 解读

本文是 Paul Graham 在 2003 年 Spam Conference 上的演讲,讲述他在《A Plan for Spam》基础上对贝叶斯垃圾邮件过滤算法的进一步优化。他对比了 Pantel & Lin 等早期工作的 92% 过滤率和 1.16% 假阳性率,通过五个关键改进将过滤率提高到 99.75%,假阳性率降至 0.06%。改进包括:利用邮件头部信息、保留分词大小写、不进行词干化、仅使用最显著的 15 个 token 并偏向假阳性(双倍计数非垃圾语料)。文章还讨论了 token 退化(degeneration)机制用于处理未见过 token,以及如何通过标记 token 出现位置(如 Subject*)来提升区分度。最后分析了四种漏网垃圾邮件类型和三个假阳性案例,指出假阳性本质上是 bug 而非统计误差。适合对机器学习分类器实现细节感兴趣的工程师。

原文 25 分钟
原文 www.paulgraham.com ↗
§ 1

Better Bayesian Filtering

更好的贝叶斯过滤

§ 2

January 2003(This article was given as a talk at the 2003 Spam Conference. It describes the work I've done to improve the performance of the algorithm described in A Plan for Spam, and what I plan to do in the future.)

The first discovery I'd like to present here is an algorithm for lazy evaluation of research papers. Just write whatever you want and don't cite any previous work, and indignant readers will send you references to all the papers you should have cited. I discovered this algorithm after ``A Plan for Spam'' [1] was on Slashdot.

Spam filtering is a subset of text classification, which is a well established field, but the first papers about Bayesian spam filtering per se seem to have been two given at the same conference in 1998, one by Pantel and Lin [2], and another by a group from Microsoft Research [3].

When I heard about this work I was a bit surprised. If people had been onto Bayesian filtering four years ago, why wasn't everyone using it? When I read the papers I found out why. Pantel and Lin's filter was the more effective of the two, but it only caught 92% of spam, with 1.16% false positives.

When I tried writing a Bayesian spam filter, it caught 99.5% of spam with less than .03% false positives [4]. It's always alarming when two people trying the same experiment get widely divergent results. It's especially alarming here because those two sets of numbers might yield opposite conclusions. Different users have different requirements, but I think for many people a filtering rate of 92% with 1.16% false positives means that filtering is not an acceptable solution, whereas 99.5% with less than .03% false positives means that it is.

So why did we get such different numbers? I haven't tried to reproduce Pantel and Lin's results, but from reading the paper I see five things that probably account for the difference.

2003年1月(本文是作者在2003年垃圾邮件会议上的演讲,讲述了为改进《垃圾邮件过滤计划》中算法性能所做的工作,以及未来的计划。)

我要介绍的第一个发现是一种“懒汉式”论文评估算法:你只管写,不用引用任何前人工作,愤怒的读者自会寄来所有你本该引用的论文。这个算法是在《垃圾邮件过滤计划》[1]登上Slashdot之后发现的。

垃圾邮件过滤是文本分类的一个子问题,而文本分类早已是一门成熟的学科。但真正关于贝叶斯垃圾邮件过滤的首批论文,似乎出现在1998年同一个会议上——一篇来自Pantel和Lin [2],另一篇则出自微软研究院的一个团队[3]。

当我听说这些工作时有些惊讶。如果四年前就已经有人研究贝叶斯过滤,为什么还没有普及呢?读了论文后我才明白原因。Pantel和Lin的过滤器是两者中表现更好的,但也不过能拦截92%的垃圾邮件,同时有1.16%的误判率。

而我尝试编写的贝叶斯垃圾邮件过滤器,能够拦截99.5%的垃圾邮件,误判率低于0.03%[4]。两个人用同样的实验得到截然不同的结果,这总是令人警惕。尤其令人警惕的是,这两组数字可能导向完全相反的结论。不同的用户有不同的需求,但我觉得对多数人来说,92%的拦截率和1.16%的误判率意味着过滤不是一个可接受的方案,而99.5%拦截率和低于0.03%误判率则意味着它是。

那么为什么我们的数字相差这么大?我没有尝试复现Pantel和Lin的结果,但从论文中我看到了五个可能造成差异的问题。

§ 3

One is simply that they trained their filter on very little data: 160 spam and 466 nonspam mails. Filter performance should still be climbing with data sets that small. So their numbers may not even be an accurate measure of the performance of their algorithm, let alone of Bayesian spam filtering in general.

其一,他们用来训练过滤器的数据量实在太少:160封垃圾邮件和466封正常邮件。用这么小的数据集,过滤性能本应还有很大的上升空间。因此,他们的数字可能根本不能准确衡量他们算法的性能,更不用说代表贝叶斯垃圾邮件过滤的普遍表现了。

§ 4

But I think the most important difference is probably that they ignored message headers. To anyone who has worked on spam filters, this will seem a perverse decision. And yet in the very first filters I tried writing, I ignored the headers too. Why? Because I wanted to keep the problem neat. I didn't know much about mail headers then, and they seemed to me full of random stuff. There is a lesson here for filter writers: don't ignore data. You'd think this lesson would be too obvious to mention, but I've had to learn it several times.

但我认为最重要的差别,可能在于他们忽略了邮件头。对任何做过垃圾邮件过滤的人来说,这似乎都是一个反常的决定。然而我最初尝试编写的过滤器,同样也忽略了邮件头。为什么?因为我希望保持问题的简洁。当时我对邮件头了解不多,觉得里面满是随机内容。这里有一个教训给过滤器的开发者:不要忽略数据。你可能觉得这条教训过于明显,不值一提,但我也曾多次重蹈覆辙。

§ 5

Third, Pantel and Lin stemmed the tokens, meaning they reduced e.g. both mailing'' and mailed'' to the root ``mail''. They may have felt they were forced to do this by the small size of their corpus, but if so this is a kind of premature optimization.

第三,Pantel和Lin对词元做了词干提取,例如将“mailing”和“mailed”都还原为词根“mail”。他们可能觉得受限于语料库太小而不得不这样做,但如果是这样,那这就是一种过早优化。

§ 6

Fourth, they calculated probabilities differently. They used all the tokens, whereas I only use the 15 most significant. If you use all the tokens you'll tend to miss longer spams, the type where someone tells you their life story up to the point where they got rich from some multilevel marketing scheme. And such an algorithm would be easy for spammers to spoof: just add a big chunk of random text to counterbalance the spam terms.

第四,他们计算概率的方式不同。他们使用了全部词元,而我仅用最显著的15个。如果使用全部词元,你很可能会漏掉那些长篇垃圾邮件——比如某人讲述生平故事最后引出某个传销致富骗局的那种。而且这种算法很容易被垃圾邮件发送者欺骗:只需加入一大段随机文字来抵消垃圾词项即可。

§ 7

Finally, they didn't bias against false positives. I think any spam filtering algorithm ought to have a convenient knob you can twist to decrease the false positive rate at the expense of the filtering rate. I do this by counting the occurrences of tokens in the nonspam corpus double.

最后,他们没有对假阳性做偏置处理。我认为任何垃圾邮件过滤算法都应提供一个方便的“旋钮”,用来牺牲过滤率以降低假阳性率。我通过将正常语料库中词元出现次数计为两倍来实现这一目标。

§ 8

Another project I heard about after the Slashdot article was Bill Yerazunis' CRM114 [5]. This is the counterexample to the design principle I just mentioned. It's a straight text classifier, but such a stunningly effective one that it manages to filter spam almost perfectly without even knowing that's what it's doing.

Once I understood how CRM114 worked, it seemed inevitable that I would eventually have to move from filtering based on single words to an approach like this. But first, I thought, I'll see how far I can get with single words. And the answer is, surprisingly far.

Mostly I've been working on smarter tokenization. On current spam, I've been able to achieve filtering rates that approach CRM114's. These techniques are mostly orthogonal to Bill's; an optimal solution might incorporate both.

``A Plan for Spam'' uses a very simple definition of a token. Letters, digits, dashes, apostrophes, and dollar signs are constituent characters, and everything else is a token separator. I also ignored case.

Now I have a more complicated definition of a token:

Case is preserved.

Exclamation points are constituent characters.

Periods and commas are constituents if they occur between two digits. This lets me get ip addresses and prices intact.

A price range like $20-25 yields two tokens, $20 and $25.

Tokens that occur within the To, From, Subject, and Return-Path lines, or within urls, get marked accordingly. E.g. foo'' in the Subject line becomes Subject*foo''. (The asterisk could be any character you don't allow as a constituent.)

Such measures increase the filter's vocabulary, which makes it more discriminating. For example, in the current filter, ``free'' in the Subject line has a spam probability of 98%, whereas the same token in the body has a spam probability of only 65%.

在Slashdot那篇文章之后,我还听说了另一个项目:Bill Yerazunis的CRM114[5]。这恰恰是我刚才提到的设计原则的反例。它是一个纯粹的文本分类器,但效果惊人,几乎完美地过滤垃圾邮件,甚至根本不知道自己在干这行。

在我理解CRM114的工作原理后,我意识到自己最终很可能会从基于单个单词的过滤转向类似的方法。但我想先试试看单靠单词能做到什么程度。结果是,意外地好。

我主要是在做更智能的词元化。对于当前的垃圾邮件,我已经能达到接近CRM114的过滤率。这些技术大多与Bill的技术正交;最优解决方案可能融合两者。

《垃圾邮件过滤计划》中使用的词元定义非常简单:字母、数字、连字符、撇号和美元符号是构成字符,其余都是分隔符。我还忽略了大小写。

现在我采用更复杂的词元定义:

  • 保留大小写。
  • 感叹号算作构成字符。
  • 句点和逗号若出现在两个数字之间,也算作构成字符。这样我就能完整获取IP地址和价格。
  • 像$20-25这样的价格范围会生成两个词元:$20和$25。
  • 出现在To、From、Subject、Return-Path行内或URL内的词元会做相应标记。例如,Subject行中的“foo”将变为“Subject*foo”。(星号可以是任何不允许作为构成字符的字符。)

这些措施增加了过滤器的词汇量,让它更具辨别力。例如,在当前过滤器中,Subject行里的“free”垃圾邮件概率为98%,而同样的词元在正文中仅为65%。

§ 9

Here are some of the current probabilities [6]:

SubjectFREE 0.9999 free!! 0.9999 Tofree 0.9998 Subjectfree 0.9782 free! 0.9199 Free 0.9198 Urlfree 0.9091 FREE 0.8747 From*free 0.7636 free 0.6546

In the Plan for Spam filter, all these tokens would have had the same probability, .7602. That filter recognized about 23,000 tokens. The current one recognizes about 187,000.

The disadvantage of having a larger universe of tokens is that there is more chance of misses. Spreading your corpus out over more tokens has the same effect as making it smaller. If you consider exclamation points as constituents, for example, then you could end up not having a spam probability for free with seven exclamation points, even though you know that free with just two exclamation points has a probability of 99.99%.

One solution to this is what I call degeneration. If you can't find an exact match for a token, treat it as if it were a less specific version. I consider terminal exclamation points, uppercase letters, and occurring in one of the five marked contexts as making a token more specific. For example, if I don't find a probability for Subject*free!'', I look for probabilities for Subject*free'', free!'', and free'', and take whichever one is farthest from .5.

Here are the alternatives [7] considered if the filter sees ``FREE!!!'' in the Subject line and doesn't have a probability for it.

SubjectFree!!! Subjectfree!!! SubjectFREE! SubjectFree! Subjectfree! SubjectFREE SubjectFree Subjectfree FREE!!! Free!!! free!!! FREE! Free! free! FREE Free free

If you do this, be sure to consider versions with initial caps as well as all uppercase and all lowercase. Spams tend to have more sentences in imperative mood, and in those the first word is a verb. So verbs with initial caps have higher spam probabilities than they would in all lowercase. In my filter, the spam probability of Act'' is 98% and for act'' only 62%.

以下是当前的一些概率值[6]:

SubjectFREE 0.9999 free!! 0.9999 Tofree 0.9998 Subjectfree 0.9782 free! 0.9199 Free 0.9198 Urlfree 0.9091 FREE 0.8747 From*free 0.7636 free 0.6546

在《垃圾邮件过滤计划》的过滤器中,所有这些词元具有相同的概率0.7602。那个过滤器识别约23,000个词元,而当前过滤器识别约187,000个。

词元空间过大的缺点是更容易错失匹配。将语料库分散到更多词元上,效果等同于缩小语料库。例如,如果你将感叹号视为构成字符,那么“free”后面跟七个感叹号的情况可能没有垃圾邮件概率,尽管你知道“free”跟两个感叹号时概率是99.99%。

我采用的一种解决方案是“退化”。如果找不到词元的精确匹配,就将它视为更不具体的版本。我认为尾部感叹号、大写字母以及出现在五种标记上下文中的词元具有更高的特异性。例如,如果找不到“Subjectfree!”的概率,我会查找“Subjectfree”、“free!”和“free”的概率,并选取最远离0.5的那个。

当过滤器在Subject行中看到“FREE!!!”却没有对应概率时,会考虑以下备选词元[7]:

SubjectFree!!! Subjectfree!!! SubjectFREE! SubjectFree! Subjectfree! SubjectFREE SubjectFree Subjectfree FREE!!! Free!!! free!!! FREE! Free! free! FREE Free free

如果你这样做,请务必同时考虑首字母大写、全大写和全小写的版本。垃圾邮件中常常包含更多祈使句,其中第一个词是动词。因此首字母大写的动词比全小写时有更高的垃圾邮件概率。在我的过滤器中,“Act”的垃圾邮件概率是98%,而“act”仅为62%。

§ 10

If you increase your filter's vocabulary, you can end up counting the same word multiple times, according to your old definition of ``same''. Logically, they're not the same token anymore. But if this still bothers you, let me add from experience that the words you seem to be counting multiple times tend to be exactly the ones you'd want to.

Another effect of a larger vocabulary is that when you look at an incoming mail you find more interesting tokens, meaning those with probabilities far from .5. I use the 15 most interesting to decide if mail is spam. But you can run into a problem when you use a fixed number like this. If you find a lot of maximally interesting tokens, the result can end up being decided by whatever random factor determines the ordering of equally interesting tokens. One way to deal with this is to treat some as more interesting than others.

For example, the token dalco'' occurs 3 times in my spam corpus and never in my legitimate corpus. The token Url*optmails'' (meaning ``optmails'' within a url) occurs 1223 times. And yet, as I used to calculate probabilities for tokens, both would have the same spam probability, the threshold of .99.

That doesn't feel right. There are theoretical arguments for giving these two tokens substantially different probabilities (Pantel and Lin do), but I haven't tried that yet. It does seem at least that if we find more than 15 tokens that only occur in one corpus or the other, we ought to give priority to the ones that occur a lot. So now there are two threshold values. For tokens that occur only in the spam corpus, the probability is .9999 if they occur more than 10 times and .9998 otherwise. Ditto at the other end of the scale for tokens found only in the legitimate corpus.

I may later scale token probabilities substantially, but this tiny amount of scaling at least ensures that tokens get sorted the right way.

Another possibility would be to consider not just 15 tokens, but all the tokens over a certain threshold of interestingness. Steven Hauser does this in his statistical spam filter [8]. If you use a threshold, make it very high, or spammers could spoof you by packing messages with more innocent words.

增加过滤器的词汇量后,根据旧的“相同”定义,你可能会多次计数同一个单词。逻辑上它们已不再是同一个词元。但如果你仍感困扰,我可以从经验告诉你,那些看起来被重复计数的词,恰好是你希望这样做的词。

更大词汇量的另一个效果是,检查一封新邮件时会发现更多“有趣”的词元——即概率远离0.5的那些。我使用最有趣的15个词元来判断邮件是否为垃圾邮件。但固定数量会带来一个问题:如果你发现大量极度有趣的词元,最终结果可能由决定同等有趣词元排序的某个随机因素决定。解决办法之一是将某些词元视为比其他词元更有趣。

例如,词元“dalco”在我的垃圾邮件语料库中出现3次,正常邮件中从未出现。词元“Url*optmails”(即在URL中的optmails)出现1223次。然而按照我过去的计算方法,两者具有相同的垃圾邮件概率,即0.99阈值。这感觉不对。理论上应该给它们显著不同的概率(Pantel和Lin就这样做),但我还没尝试。至少,如果我们发现超过15个只出现在一个语料库中的词元,应该优先考虑出现次数多的。因此我现在设定两个阈值:垃圾邮件特有词元中,出现超过10次的概率为0.9999,否则为0.9998。正常邮件特有词元同理。未来我可能会大幅调整词元概率,但当前这种微调至少能确保词元排序正确。

另一种可能性是不只考虑15个词元,而是考虑所有超过某个兴趣度阈值的词元。Steven Hauser在他的统计垃圾邮件过滤器中就这样做[8]。如果使用阈值,请设得非常高,否则垃圾邮件发送者可能通过填充大量无害词来欺骗你。

§ 11

Finally, what should one do about html? I've tried the whole spectrum of options, from ignoring it to parsing it all. Ignoring html is a bad idea, because it's full of useful spam signs. But if you parse it all, your filter might degenerate into a mere html recognizer. The most effective approach seems to be the middle course, to notice some tokens but not others. I look at a, img, and font tags, and ignore the rest. Links and images you should certainly look at, because they contain urls.

I could probably be smarter about dealing with html, but I don't think it's worth putting a lot of time into this. Spams full of html are easy to filter. The smarter spammers already avoid it. So performance in the future should not depend much on how you deal with html.

最后,如何处理HTML?我尝试过从完全忽略到完全解析的各种方案。忽略HTML是糟糕的主意,因为它富含垃圾邮件的特征。但如果完全解析,过滤器可能退化为一个纯粹的HTML识别器。最有效的方法似乎是折中路线:识别某些标签而忽略其他。我查看a、img和font标签,忽略其余部分。链接和图片肯定需要关注,因为它们包含URL。

我或许能在HTML处理上做得更聪明,但我觉得不值得投入太多时间。充斥HTML的垃圾邮件很容易过滤。聪明的垃圾邮件发送者已经避开HTML。因此,未来的过滤性能不会过多依赖你如何处理HTML。

§ 12

Between December 10 2002 and January 10 2003 I got about 1750 spams. Of these, 4 got through. That's a filtering rate of about 99.75%.

Two of the four spams I missed got through because they happened to use words that occur often in my legitimate email.

The third was one of those that exploit an insecure cgi script to send mail to third parties. They're hard to filter based just on the content because the headers are innocent and they're careful about the words they use. Even so I can usually catch them. This one squeaked by with a probability of .88, just under the threshold of .9.

Of course, looking at multiple token sequences would catch it easily. ``Below is the result of your feedback form'' is an instant giveaway.

The fourth spam was what I call a spam-of-the-future, because this is what I expect spam to evolve into: some completely neutral text followed by a url. In this case it was was from someone saying they had finally finished their homepage and would I go look at it. (The page was of course an ad for a porn site.)

If the spammers are careful about the headers and use a fresh url, there is nothing in spam-of-the-future for filters to notice. We can of course counter by sending a crawler to look at the page. But that might not be necessary. The response rate for spam-of-the-future must be low, or everyone would be doing it. If it's low enough, it won't pay for spammers to send it, and we won't have to work too hard on filtering it.

Now for the really shocking news: during that same one-month period I got three false positives.

In a way it's a relief to get some false positives. When I wrote ``A Plan for Spam'' I hadn't had any, and I didn't know what they'd be like. Now that I've had a few, I'm relieved to find they're not as bad as I feared. False positives yielded by statistical filters turn out to be mails that sound a lot like spam, and these tend to be the ones you would least mind missing [9].

Two of the false positives were newsletters from companies I've bought things from. I never asked to receive them, so arguably they were spams, but I count them as false positives because I hadn't been deleting them as spams before. The reason the filters caught them was that both companies in January switched to commercial email senders instead of sending the mails from their own servers, and both the headers and the bodies became much spammier.

The third false positive was a bad one, though. It was from someone in Egypt and written in all uppercase. This was a direct result of making tokens case sensitive; the Plan for Spam filter wouldn't have caught it.

It's hard to say what the overall false positive rate is, because we're up in the noise, statistically. Anyone who has worked on filters (at least, effective filters) will be aware of this problem. With some emails it's hard to say whether they're spam or not, and these are the ones you end up looking at when you get filters really tight. For example, so far the filter has caught two emails that were sent to my address because of a typo, and one sent to me in the belief that I was someone else. Arguably, these are neither my spam nor my nonspam mail.

Another false positive was from a vice president at Virtumundo. I wrote to them pretending to be a customer, and since the reply came back through Virtumundo's mail servers it had the most incriminating headers imaginable. Arguably this isn't a real false positive either, but a sort of Heisenberg uncertainty effect: I only got it because I was writing about spam filtering.

Not counting these, I've had a total of five false positives so far, out of about 7740 legitimate emails, a rate of .06%. The other two were a notice that something I bought was back-ordered, and a party reminder from Evite.

I don't think this number can be trusted, partly because the sample is so small, and partly because I think I can fix the filter not to catch some of these.

False positives seem to me a different kind of error from false negatives. Filtering rate is a measure of performance. False positives I consider more like bugs. I approach improving the filtering rate as optimization, and decreasing false positives as debugging.

So these five false positives are my bug list. For example, the mail from Egypt got nailed because the uppercase text made it look to the filter like a Nigerian spam. This really is kind of a bug. As with html, the email being all uppercase is really conceptually one feature, not one for each word. I need to handle case in a more sophisticated way.

So what to make of this .06%? Not much, I think. You could treat it as an upper bound, bearing in mind the small sample size. But at this stage it is more a measure of the bugs in my implementation than some intrinsic false positive rate of Bayesian filtering.

从2002年12月10日到2003年1月10日,我收到了大约1750封垃圾邮件,其中4封漏网。过滤率约为99.75%。

漏掉的4封中有两封是因为它们碰巧用了我正常邮件中常见的词。

第三封是利用不安全CGI脚本向第三方发送邮件的那种。仅基于内容很难过滤,因为邮件头无害,用词也很谨慎。但即便如此,我通常也能抓住它们。这封以0.88的概率侥幸通过,刚好低于0.9的阈值。当然,如果观察多词序列会很容易抓住它。“以下是您的反馈表单结果”是一个明显信号。

第四封我称为“未来型垃圾邮件”,因为我认为垃圾邮件会演化成这种形式:完全中性的文本后面跟一个URL。这次是某人说终于完成了主页,请我去看看(当然页面是色情网站的广告)。

如果垃圾邮件发送者小心处理邮件头并使用新的URL,这类垃圾邮件没有任何可供过滤器察觉的特征。我们当然可以派爬虫去查看页面,但可能没必要。这种垃圾邮件的回应率必定很低,否则所有人都会这么做。如果回应率低到一定程度,垃圾邮件发送者不会费力去发,我们也不用在过滤上太费劲。

现在来说真正令人震惊的消息:就在那一个月里,我遇到了3个误判。

从某种程度上说,出现误判反而让我松了口气。写《垃圾邮件过滤计划》时我还没遇到过误判,不知道它们会是什么样。现在遇到了一些,我松了口气,发现它们并没有我担心的那么糟糕。统计过滤器产生的误判通常是听起来很像垃圾邮件的邮件,这些恰好是你最不介意漏掉的那类[9]。

其中两个误判是来自我买过东西的公司的新邮件。我从未订阅过,所以严格来说它们也是垃圾邮件,但我还是把它们算作误判,因为我之前并没有把它们当作垃圾邮件删除。过滤器之所以抓住它们,是因为这两家公司在一月份切换到了商业邮件发送商,而不是从自己的服务器发送,导致邮件头和正文都变得很像垃圾邮件。

第三个误判是个糟糕的情况。它来自埃及,全部使用大写字母。这是词元大小写敏感的直接后果;《垃圾邮件过滤计划》的过滤器不会抓住它。

很难说出总体误判率,因为从统计上说我们已经处于噪声中。任何做过过滤器(至少有效过滤器)的人都会意识到这个问题。有些邮件很难判断是否是垃圾邮件,当过滤器调得非常严格时,你看到的正是这些邮件。例如,到目前为止,过滤器抓住了两封因为拼写错误而发到我的地址的邮件,以及一封因为别人把我当成另一个人而发来的邮件。可以说,这些既不是我的垃圾邮件也不是我的正常邮件。

另一个误判是来自Virtumundo的副总裁。我假装成客户给他们写信,回复通过Virtumundo的邮件服务器发回,因此邮件头显得极其可疑。可以说这也不是真正的误判,而是一种海森堡不确定效应:我收到它只是因为我正在写关于垃圾邮件过滤的文章。

排除这些,到目前为止我总共收到5个误判,大约7740封正常邮件,误判率为0.06%。另外两个是我买的商品缺货通知和Evite的聚会提醒。

我不认为这个数字可信,部分因为样本太小,部分因为我能修复过滤器以避免其中一些误判。

误判在我看来是一种与漏网不同的错误。过滤率是性能指标,而误判更像是bug。我把提高过滤率视为优化,把减少误判视为调试。

所以这五个误判就是我的bug列表。例如,埃及邮件之所以被抓住,是因为大写文本让过滤器觉得它像尼日利亚垃圾邮件。这确实是一种bug。和HTML一样,全大写的邮件本质上是一个特征,而不是每个单词一个特征。我需要以更精致的方式处理大小写。

那么如何看待这个0.06%?我认为意义不大。你可以把它视为一个上限,同时考虑到样本量小。但在现阶段,它更多反映的是我实现中的bug,而不是贝叶斯过滤内在的误判率。

§ 13

What next? Filtering is an optimization problem, and the key to optimization is profiling. Don't try to guess where your code is slow, because you'll guess wrong. Look at where your code is slow, and fix that. In filtering, this translates to: look at the spams you miss, and figure out what you could have done to catch them.

For example, spammers are now working aggressively to evade filters, and one of the things they're doing is breaking up and misspelling words to prevent filters from recognizing them. But working on this is not my first priority, because I still have no trouble catching these spams [10].

There are two kinds of spams I currently do have trouble with. One is the type that pretends to be an email from a woman inviting you to go chat with her or see her profile on a dating site. These get through because they're the one type of sales pitch you can make without using sales talk. They use the same vocabulary as ordinary email.

The other kind of spams I have trouble filtering are those from companies in e.g. Bulgaria offering contract programming services. These get through because I'm a programmer too, and the spams are full of the same words as my real mail.

I'll probably focus on the personal ad type first. I think if I look closer I'll be able to find statistical differences between these and my real mail. The style of writing is certainly different, though it may take multiword filtering to catch that. Also, I notice they tend to repeat the url, and someone including a url in a legitimate mail wouldn't do that [11].

The outsourcing type are going to be hard to catch. Even if you sent a crawler to the site, you wouldn't find a smoking statistical gun. Maybe the only answer is a central list of domains advertised in spams [12]. But there can't be that many of this type of mail. If the only spams left were unsolicited offers of contract programming services from Bulgaria, we could all probably move on to working on something else.

Will statistical filtering actually get us to that point? I don't know. Right now, for me personally, spam is not a problem. But spammers haven't yet made a serious effort to spoof statistical filters. What will happen when they do?

I'm not optimistic about filters that work at the network level [13]. When there is a static obstacle worth getting past, spammers are pretty efficient at getting past it. There is already a company called Assurance Systems that will run your mail through Spamassassin and tell you whether it will get filtered out.

Network-level filters won't be completely useless. They may be enough to kill all the "opt-in" spam, meaning spam from companies like Virtumundo and Equalamail who claim that they're really running opt-in lists. You can filter those based just on the headers, no matter what they say in the body. But anyone willing to falsify headers or use open relays, presumably including most porn spammers, should be able to get some message past network-level filters if they want to. (By no means the message they'd like to send though, which is something.)

The kind of filters I'm optimistic about are ones that calculate probabilities based on each individual user's mail. These can be much more effective, not only in avoiding false positives, but in filtering too: for example, finding the recipient's email address base-64 encoded anywhere in a message is a very good spam indicator.

But the real advantage of individual filters is that they'll all be different. If everyone's filters have different probabilities, it will make the spammers' optimization loop, what programmers would call their edit-compile-test cycle, appallingly slow. Instead of just tweaking a spam till it gets through a copy of some filter they have on their desktop, they'll have to do a test mailing for each tweak. It would be like programming in a language without an interactive toplevel, and I wouldn't wish that on anyone.

接下来做什么?过滤是一个优化问题,优化的关键是剖析。不要猜测代码哪里慢,因为你总是猜错。去看代码哪里慢,然后修复它。在过滤问题上,这转化为:检查你漏掉的垃圾邮件,找出本可以抓住它们的方法。

例如,垃圾邮件发送者正积极逃避过滤器,手段之一是拆分单词和拼写错误以防止过滤器识别。但解决这一问题并非我的首要任务,因为我目前仍然毫不费力就能抓住这些垃圾邮件[10]。

目前让我棘手的垃圾邮件有两种:

一种是伪装成女性邀请你去聊天或看她在约会网站资料的那种。它们能通过是因为这是唯一一种不需要销售话术的推销方式,使用的词汇与普通邮件相同。

另一种是来自保加利亚等地的公司提供外包编程服务的垃圾邮件。它们能通过是因为我也是程序员,垃圾邮件中充满了与我的真实邮件相同的词汇。

我可能会先专注于个人广告类。我认为仔细分析能发现它们与真实邮件的统计差异。写作风格肯定不同,尽管可能需要多词过滤才能抓住。此外,我注意到它们倾向于重复同一个URL,而正当邮件中不会这样[11]。

外包类很难抓住。即使派爬虫去访问网站,也找不到确凿的统计证据。也许唯一的答案是维护一个垃圾邮件中出现的域名黑名单[12]。但这类邮件数量不会太多。如果剩下的只有来自保加利亚的未经请求的外包编程服务邀请,我们大可以转而研究其他事情了。

统计过滤真的能带我们走到那一步吗?我不知道。目前,对我来说垃圾邮件不是问题。但垃圾邮件发送者还没有认真尝试欺骗统计过滤器。当他们开始这样做时会发生什么?

我对网络层面的过滤器并不乐观[13]。当存在静态障碍值得跨越时,垃圾邮件发送者会高效地跨越它。已经有一家名为Assurance Systems的公司,会把你的邮件通过Spamassassin运行一遍,然后告诉你它是否会被过滤掉。

网络层面的过滤器并非完全无用。它们也许足以消灭所有“opt-in”垃圾邮件,即来自Virtumundo和Equalamail等声称运营着真实的opt-in列表的公司的垃圾邮件。只需基于邮件头就可以过滤它们,无论正文说什么。但任何愿意伪造邮件头或使用开放中继的人——大概包括大多数色情垃圾邮件发送者——如果愿意,应该能够绕过网络层面的过滤器发送一些消息(尽管绝不可能是他们想发送的那种消息)。

我乐观的是基于每个用户个人邮件计算概率的过滤器。它们能更有效,不仅在于避免误判,也在于过滤本身:例如,在消息中发现收件人电子邮件地址的base-64编码,就是一个非常好的垃圾邮件指标。

但个性化过滤器的真正优势是它们各不相同。如果每个人的过滤器都有不同的概率,就会让垃圾邮件发送者的优化循环——程序员称之为编辑-编译-测试周期——变得极其缓慢。他们不能只在桌面上调整垃圾邮件直到通过某个过滤器的副本,而必须为每次调整做一次测试发送。这就像用没有交互式顶层环境的语言编程,我不想让任何人遭受这种折磨。

打开原文 ↗