前置知识
词集模式与词袋模式
以一篇文档举例:
词集模式: 只关注单词是否在这篇文档里出现, 至于出现的次数以及相互之间的顺序, 词集模式并不关心;
词袋模式: 关注单词的出现次数, 并不关心单词的相互顺序;
这里我们用的是词集模式;
思路
训练阶段
一封邮件有两种分类, 这里我们以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)
举例, 我们用贝叶斯推一下:
其中分母是一个全概率公式;
因为朴素贝叶斯假设样本的各特征之间相互独立, 也就是 W 下所有随机分量相互独立, 所以
同理可推 P(W∣h) ;
然后比较 P(W∣s)和P(W∣h) 的大小, 谁大则该封邮件就是哪个分类;
这里有个小技巧, 公式1的分母 P(W) 是固定的, 所以我们只需要比较二者的分子谁大谁小即可;
这里还有一个要注意的事:
预测阶段, 如果一个单词是新出现的, 即训练出的vocabulary里没有收录这个单词, 那就假定其
P(w∣s)=0.4
, 因为”垃圾邮件用的往往是固定的词语, 如果一个单词从没出现过, 那它多半是一个正常的词”
代码
项目放在码云上, 地址:FirstByesSpam
改造
改造的方法在参考文献一里有介绍, 是保罗大佬提出来的一种方案, 这个在他的个人网站中有介绍: http://www.paulgraham.com/
(英文的)
思路在参考文献一里写的已经很清楚了, 这里我只提一下保罗这么改造的原因:
像上面那种传统的朴素贝叶斯分类, 应用在垃圾邮件过滤的时候, 很容易被规避掉, 因为朴素贝叶斯需要计算一个样本的全部特征, 只要spam的发送者在其中插入一大段随机文本, 就可以大大降低
P(s∣w1)P(s∣w2)⋯P(s∣wn)
的数值, 从而降低
P(s∣W)
, 藉此伪装成健康邮件通过;
基于这种情况, 保罗从spam中永远不会变的内容入手, 提取前15个 P(s∣w) 最大的特征; 计算这15个单词全部存在的情况下, 该邮件是垃圾邮件的联合概率;
思路很棒, 但是我不是很能理解那个联合概率的形式, 可能是我概率论基本功还不到家吧;
另外, 保罗大佬这么做的话, 需要知道一个阈值, 联合概率超过这个阈值才能判定它是垃圾邮件, 这个阈值也需要摸索;
参考文献
参考文献一
作者:CodingFish
链接:http://www.jianshu.com/p/c59851b1c0f3
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
数据集出处