利用贝叶斯公式通过python过滤垃圾邮件

2019-10-19  本文已影响0人  Michael孟良

朴素贝叶斯公式

朴素贝叶斯公式

也可以简写为:


转载请注明出处:Michael孟良

其中:

P(A)叫做A事件的先验概率,即一般情况下,认为A发生的概率。

P(B|A)叫做似然度,是A假设条件成立的情况下发生B的概率。

P(A|B)叫做后验概率,在B发生的情况下发生A的概率,也就是要求的概率。P(B)叫做标准化常量,即在一般情况下,认为B发生的概率。

理解朴素贝叶斯

假设现在有一堆邮件,正常邮件的比例是80%,垃圾邮件的比例是20%,这堆邮件中,5%的邮件中出现Viagra单词,如果有一封邮件,这封邮件中包含Viagra单词,求这封邮件是垃圾邮件的概率。



显然不能使用5%*20%=1%得到这封邮件是垃圾邮件的概率,因为垃圾邮件中有可能出现Viagra也有可能不会出现Viagra单词。那么根据贝叶斯公式可得包含Viagra单词的邮件是垃圾邮件的概率为:


P(spam)是已知20%,P(Viagra)也是已知 5%,那么如果求出P(Viagra|spam)的概率,结果就可以知道。我们可以根据邮件的数据集统计得到单词频率表:

其中P(spam|Viagra)表示在垃圾邮件中出现Viagra单词的概率,通过统计得出为4/20。可以得出如果一封邮件中有Viagra单词,这封邮件是垃圾邮件的概率为:



通过同样的计算可以得到,含有Viagra单词但不是垃圾邮件的概率为0.2。那么可以认为这封邮件是垃圾邮件的概率比较大。这里的Viagra可以理解为邮件中的一个特征。那么当一封邮件有额外更多的特征时,贝叶斯如何扩展?
假设所有历史邮件中只出现了4个单词,也就是4个特征,根据历史邮件统计的单词频率表如下:



假设现在给定一封邮件中有Viagra和Unsubscribe(取消订阅)两个单词,求这封邮件是垃圾邮件的概率、不是垃圾邮件的概率?
利用贝叶斯公式,我们可以得到:

是垃圾邮件的概率:



最后约等于98.07%
不是垃圾邮件的概率:

最后约等于17.4%

拉普拉斯估计

根据以上例子,假设有一封邮件这4个单词都出现了,求这封邮件是垃圾邮件的概率:



由于P(W3)的概率为0/20,会导致整个结果是垃圾邮件的概率为0,那么就否定了其他单词出现的权重。

拉普拉斯估计本质上是给频率表中的每个单词的计数加上一个较小的数,这样就保证每一类中每个特征发生的概率非零。通常,拉普拉斯估计中加上的数值为1,这样就保证了每一个特征至少在数据中出现一次。

以上例子如果四个单词都出现情况下计算是否是垃圾邮件,出现P(W3)的概率为0/20,可以增加4封垃圾邮件,使4封邮件中每个邮件中只有一个单词出现,这样就避免了垃圾邮件中有的单词出现概率为0的情况。同样在不是垃圾邮件中也增加4封,避免在正常邮件中出现有的单词出现概率为0的情况。

Python 贝叶斯案例

# coding:utf-8

import os
import sys
import codecs


# #####################################################
# Multinomial Naive Bayes Classifier
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer

print('*************************\nNaive Bayes\n*************************')

if __name__ == '__main__':
    # read the file
    corpus = []
    labels = []
    corpus_test = []
    labels_test = []
    f = codecs.open("./sms_spam.txt", "rb")
    count = 0
    while True:
        # readline() read every line,including "\n"
        line = f.readline().decode("utf-8")
        # read the first line and ignore it
        if count == 0:
            count = count + 1
            continue
        if line:
            count = count + 1
            line = line.split(",")
            label = line[0]
            sentence = line[1]
            corpus.append(sentence)
            if "ham" == label:
                labels.append(0)
            elif "spam" == label:
                labels.append(1)
            if count > 5550:
                corpus_test.append(sentence)
                if "ham" == label:
                    labels_test.append(0)
                elif "spam" == label:
                    labels_test.append(1)
        else:
            break
    # 文本特征提取:
    #     将文本数据转化成特征向量的过程
    #     比较常用的文本特征表示法为词袋法
    #
    # 词袋法:
    #     不考虑词语出现的顺序,每个出现过的词汇单独作为一列特征
    #     这些不重复的特征词汇集合为词表
    #     每一个文本都可以在很长的词表上统计出一个很多列的特征向量
    # CountVectorizer是将文本向量转换成稀疏表示数值向量(字符频率向量)  vectorizer 将文档词块化,只考虑词汇在文本中出现的频率
    # 词袋
    vectorizer = CountVectorizer()
    # 每行的词向量,fea_train是一个矩阵
    fea_train = vectorizer.fit_transform(corpus)

    print("vectorizer.get_feature_names is ", vectorizer.get_feature_names())
    print("fea_train is ", fea_train.toarray())

    # vocabulary=vectorizer.vocabulary_ 只计算上面vectorizer中单词的tf(term frequency 词频)
    vectorizer2 = CountVectorizer(vocabulary=vectorizer.vocabulary_)
    fea_test = vectorizer2.fit_transform(corpus_test)
    print("vectorizer2.get_feature_names()",vectorizer2.get_feature_names())
    print("fea_test.toarray()",fea_test.toarray())

    # create the Multinomial Naive Bayesian Classifier
    # alpha = 1 拉普拉斯估计给每个单词个数加1
    clf = MultinomialNB(alpha=1)
    clf.fit(fea_train, labels)

    pred = clf.predict(fea_test);
    for p in pred:
        if p == 0:
            print("正常邮件")
        else:
            print("垃圾邮件")

sms_spam.txt:

type,text
ham,you are having a good week. Just checking in 00 00 00 0089 0089
ham,K..give back my thanks.
ham,Am also doing in cbe only. But have to pay.
spam,"complimentary 4 STAR Ibiza Holiday or £10,000 cash needs your URGENT collection. 09066364349 NOW from Landline not to lose out! Box434SK38WP150PPM18+"
...

逻辑思路:

sms_spam.txt有5560条数据, 取5550条数据作为训练,后十条作为测试数据。每一行的第一个词是这份邮件的类型,就是之前已经人为判断过,ham表示它为正常邮件, spam为垃圾邮件。
这里labels就是收集人为判断的结果

    # 词袋
    vectorizer = CountVectorizer()
    # 每行的词向量,fea_train是一个矩阵
    fea_train = vectorizer.fit_transform(corpus)

vectorizer 拿到所有邮件的所有单词,fea_train 是所有邮件每个词在每份邮件出现的次数,相当一个矩阵。

# create the Multinomial Naive Bayesian Classifier
# alpha = 1 拉普拉斯估计给每个单词个数加1
clf = MultinomialNB(alpha=1)
clf.fit(fea_train, labels)

这里 alpha = 1 ,就是前面讲到的拉普拉斯估计,给每个单词个数加1,防止它出现0的情况。
fea_train是训练出来的矩阵,labels是人为判断的结果,把他们fit到clf里面。

pred = clf.predict(fea_test);
for p in pred:
    if p == 0:
        print("正常邮件")
    else:
        print("垃圾邮件")

fea_test是后面十条的数据,把它放到我们之前训练好的贝叶斯模型clf,然后预测出这十条数据。

打印出来的结果

代码:https://github.com/MichaelYipInGitHub/PythonTest/tree/master/com/test/bayes

上一篇下一篇

猜你喜欢

热点阅读