老生常谈:如何通过贝叶斯分类器进行邮件过滤
我相信,今天几乎每个人都拥有智能手机,很多人都保留一两封电子邮箱,您过去可能已经收到大量的垃圾消息,这些消息提出了很多钱,惊人的彩票中奖,精美的礼物和生活中的秘密。除非您使用训练有素的过滤器,否则我们每天都会收到数十封垃圾邮件。它们可能是有害的,只是令人讨厌或占用空间,但它们还可能包含病毒或钓鱼尝试,无论如何,这不是我们要处理的内容。因此,对优质垃圾邮件过滤器始终是有需求的。
让我让您熟悉(或提醒您是否已经熟悉)一种有效的垃圾邮件过滤算法:朴素贝叶斯分类,尽管scikit-learn包中已经存在实现, 我想从头开始重新创建算法。首先,我想揭示实现背后隐藏的逻辑。其次,我想结合数据集准备展示算法
1. 理论基础;
朴素贝叶斯分类是一种基于以下事实的简单概率算法:模型的所有特征都是独立的,我们假设在垃圾邮件过滤器的上下文中,邮件中的每个单词都独立于所有其他单词,并且我们根据忽略上下文含义来对其进行计数;我们的分类算法根据当前字词集的条件得出邮件为垃圾邮件或非垃圾邮件的可能性,概率的计算基于贝叶斯公式,并且公式的组成部分基于整个消息集中单词的出现频率进行计算。
2. 数学基础:
首先,我们采用条件概率的贝叶斯公式并将其应用于我们的任务:
包含单词(w1,w2,w3,...)成为垃圾邮件的邮件的概率与将垃圾邮件乘以邮件中每个单词属于垃圾邮件的概率的乘积相乘的概率成比例,这是什么意思?对于邮件中的每个单词,我们计算在垃圾邮件中找到它的概率。
首先定义:
P_spam — 在数据中垃圾邮件的比例
Pwispam — wi在垃圾邮件中出现的比例.
类比定义:
Pnotspam — 在数据非垃圾邮件的比例
Pwinon_spam — wi在非垃圾邮件的比例
但是我们仍然不知道如何计算每个单词的概率。虽然,我们有另一个公式:
其中的定义如下:
N_vocabulary — 在整个数据中,每个单词的去重数量
N_spam — 垃圾邮件中的单词数量.
Nwispam — 在所有垃圾邮件中重复的单词数
Alpha — 数据集中缺少消息中单词的情况下的系数
简而言之:一个单词属于垃圾邮件的概率是该单词在我们数据集“垃圾邮件部分”中的出现频率 同样,对于一个单词属于非垃圾邮件的概率,相同的公式(但具有其他值)是正确的. 数学结束了,但是请放心,我将在数据集示例中显示所有值和公式。
3. 数据集
数据集结构很简单。它包含两列,一列用于标签“ spam / ham”,另一列用于邮件的文本。
sms_data.groupby('Label').count()
Out[6]:
ham 4825
spam 747
它包含5572条不同邮件的记录以及747条垃圾邮件.
4. 数据准备
在应用算法之前,我们需要准备数据。首先,我们将删除标点符号。然后,我们将所有文本转换为小写并将其拆分为单独的单词;
sms_data_clean['SMS'] = sms_data_clean['SMS'].str.replace('\W+', ' ').str.replace('\s+', ' ').str.strip()
sms_data_clean['SMS'] = sms_data_clean['SMS'].str.lower()
sms_data_clean['SMS'] = sms_data_clean['SMS'].str.split()
接下来是将数据集拆分为训练和测试数据。但是我们还需要保持垃圾邮件和非垃圾邮件的分布。
train_data = sms_data_clean.sample(frac=0.8,random_state=1).reset_index(drop=True)
test_data = sms_data_clean.drop(train_data.index).reset_index(drop=True)
train_data = train_data.reset_index(drop=True)
sms_data_clean['Label'].value_counts() / sms_data.shape[0] * 100
'''
ham 86.593683
spam 13.406317
Name: Label, dtype: float64
'''
train_data['Label'].value_counts() / train_data.shape[0] * 100
'''
ham 86.54105
spam 13.45895
Name: Label, dtype: float64
'''
test_data['Label'].value_counts() / test_data.shape[0] * 100
'''
ham 86.983842
spam 13.016158
Name: Label, dtype: float64
'''
功能应用后,我们在测试数据上看到了令人印象深刻的结果:99.1%的数据已成功分类
出于兴趣,我们来看看错误识别的消息: