前置知识
词集模式与词袋模式
以一篇文档举例:
词集模式: 只关注单词是否在这篇文档里出现, 至于出现的次数以及相互之间的顺序, 词集模式并不关心;
词袋模式: 关注单词的出现次数, 并不关心单词的相互顺序;
这里我们用的是词集模式;
思路
训练阶段
一封邮件有两种分类, 这里我们以h代表健康邮件(healthy email)状态, 以s代表垃圾邮件(spam)状态;
w表示一个单词变量;
W表示一个单词向量, 或者说是单词集合;
维护一个词汇表vocabulary, 存储训练过程中出现的所有单词, 以及它们在垃圾邮件和健康邮件中的出现频率; vocabulary[i][x,y]表示第i个单词, 在健康邮件中出现x次(或者说是出现有该单词的健康邮件的数目), 在垃圾邮件中出现y次(或者说是出现有该单词的垃圾邮件的数目);毕竟我们使用的是词集模式;
首先读取训练数据集中的垃圾邮件数据集, 对每封邮件, jieba分词 -> 去掉停用词 -> 去掉重复值(词集模式), 对剩下的所有单词做如下处理:
如果当前单词w在vocabulary中已存在, 相应的y进行y++;如果当前单词w在vocabulary未出现, 将其新增进词汇表, 然后y++;
然后读取训练数据集中的健康邮件数据集, 对每封邮件也进行上述处理, 只不过把y改成x;
执行到这里, vocabulary[i]里放的是健康邮件集和垃圾邮件集分别包含第i号单词的邮件数, 我们需要分别除以健康邮件集和垃圾邮件集的大小, 将其转换成频率;
如果一个词在健康邮件中从没出现过, 为了避免概率为0影响连乘计算, 我们假定它在健康邮件的出现频率为0.01;
训练阶段结束:
输入: 垃圾邮件数据集 + 健康邮件数据集;
输出:
vocabulary, 二维列表; word_to_id_map: 保存单词和id的映射关系, key是单词, value是对应的id
预测阶段
经过训练阶段, 我们最终得到的vovabulary中,
词w在健康邮件中的出现频率实际上就是条件概率P(w∣h)=vocabulary[ word_to_id_map[w] ][0];
同理, 词w在垃圾邮件中的出现频率就是条件概率P(w∣s)=vocabulary[ word_to_id_map[w] ][1];
而我们最终想要的是:
对于一封邮件W={w1,w2,w3,⋯,wn}, 已知W, 分别求它的状态分类是
以P(s∣W)举例, 我们用贝叶斯推一下:
P(s∣W)=P(s,W)P(W)=P(W∣s)P(s)P(W∣s)P(s)+P(W∣h)P(h)(公式1)
其中分母是一个全概率公式;
因为朴素贝叶斯假设样本的各特征之间相互独立, 也就是W下所有随机分量相互独立, 所以
同理可推P(W∣h);
然后比较P(W∣s)和P(W∣h)的大小, 谁大则该封邮件就是哪个分类;
这里有个小技巧, 公式1的分母P(W)是固定的, 所以我们只需要比较二者的分子谁大谁小即可;
这里还有一个要注意的事:
预测阶段, 如果一个单词是新出现的, 即训练出的vocabulary里没有收录这个单词, 那就假定其P(w∣s)=0.4, 因为”垃圾邮件用的往往是固定的词语, 如果一个单词从没出现过, 那它多半是一个正常的词”
代码
项目放在码云上, 地址:FirstByesSpam
改造
改造的方法在参考文献一里有介绍, 是保罗大佬提出来的一种方案, 这个在他的个人网站中有介绍: /
(英文的)
思路在参考文献一里写的已经很清楚了, 这里我只提一下保罗这么改造的原因:
像上面那种传统的朴素贝叶斯分类, 应用在垃圾邮件过滤的时候, 很容易被规避掉, 因为朴素贝叶斯需要计算一个样本的全部特征, 只要spam的发送者在其中插入一大段随机文本, 就可以大大降低P(s∣w1)P(s∣w2)⋯P(s∣wn)的数值, 从而降低P(s∣W), 藉此伪装成健康邮件通过;
基于这种情况, 保罗从spam中永远不会变的内容入手, 提取前15个P(s∣w)最大的特征; 计算这15个单词全部存在的情况下, 该邮件是垃圾邮件的联合概率;
思路很棒, 但是我不是很能理解那个联合概率的形式, 可能是我概率论基本功还不到家吧;
另外, 保罗大佬这么做的话, 需要知道一个阈值, 联合概率超过这个阈值才能判定它是垃圾邮件, 这个阈值也需要摸索;
参考文献
参考文献一
作者:CodingFish
链接:/p/c59851b1c0f3
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
数据集出处
/shijing888/BayesSpam