Bayes 朴素贝叶斯实现垃圾邮件分类

Posted 野鹿的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bayes 朴素贝叶斯实现垃圾邮件分类相关的知识,希望对你有一定的参考价值。

本文选取了25封垃圾邮件和25封正常邮件,随机产生了10组测试集和40组训练集,使用朴素贝叶斯方法实现了垃圾邮件的分类。
 
Bayes公式
 
 
 
遍历每篇文档向量,扫描所有文档的单词,合并集合去重 ,并生成最终的词汇表
 
# 创建词汇表
# 输入:dataSet已经经过切分处理
# 输出:包含所有文档中出现的不重复词的列表
def createVocabList(dataSet):  # 文本去重,创建词汇表
    vocabSet = set([])  # 创建一个空的集合
    for document in dataSet:  # 遍历每篇文档向量,扫描所有文档的单词
        vocabSet = vocabSet | set(document)  # 合并集合,并生成最终的词汇表
    return list(vocabSet)
 
查找关键词语并标记
 
# ***词集模型:只考虑单词是否出现
# vocabList:词汇表
# inputSet :某个文档向量
def setOfWords2Vec(vocabList, inputSet):  # 查找词语并标记
    returnVec = [0] * len(vocabList)  # 初始化一个和单词等长集合,初始化为0
    # 依次取出文档中的单词与词汇表进行对照,若在词汇表中出现则为1
    for word in inputSet:
        if word in vocabList:
            # 单词在词汇表中出现,则记为1
            returnVec[vocabList.index(word)] = 1  # vocabList.index(word)下标
            # 若测试文档的单词,不在词汇表中,显示提示信息,该单词出现次数用0表示
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return returnVec
 
原始的朴素贝叶斯源码
 
# P(ci|w)=(P(w|ci) P(ci))/(P(w)) 词向量的贝叶斯准则
# P(w0,w1,w2,w3..|ci) ==p(w0|ci) P(w1|ci) ....
 
# 1.计算每个类别中的文档数目
# 2.针对每篇训练文档:
#       对每个类别:
#         如果词条出现在文档中--〉增加该词条的记数值
#         增加所有词条的记数值
#       对每个类别:
#          对每个词条:
#              将该词条的数目除以总词条书得到条件概率
#        返回每个类别的条件概率
 
# ====训练分类器,原始的朴素贝叶斯,没有优化=====
# 输入trainMatrix:词向量数据集
# 输入trainCategory:数据集对应的类别标签
# 输出p0Vect:词汇表中各个单词在正常言论中的类条件概率密度
# 输出p1Vect:词汇表中各个单词在侮辱性言论中的类条件概率密度
# 输出pAbusive:侮辱性言论在整个数据集中的比例
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)  # numTrainDocs训练集总条数
    numWords = len(trainMatrix[0])  # 训练集中所有不重复单词总数
    pAbusive = sum(trainCategory) / float(numTrainDocs)  # 侮辱类的概率(侮辱类占总训练数据的比例)
    # 初始化概率
    p0Num = ones(numWords)  # *正常言论的类条件概率密度 p(某单词|正常言论)=p0Num/p0Denom
    p1Num = ones(numWords)  # *侮辱性言论的类条件概率密度 p(某单词|侮辱性言论)=p1Num/p1Denom
    p0Denom = 0.0  # 初始化分母置为0
    p1Denom = 0.0
    for i in range(numTrainDocs):  # 遍历训练集数据
        if trainCategory[i] == 1:  # 若为侮辱类
            p1Num += trainMatrix[i]  # 统计侮辱类所有文档中的各个单词总数
            p1Denom += sum(trainMatrix[i])  # p1Denom侮辱类总单词数
        else:  # 若为正常类
            p0Num += trainMatrix[i]  # 统计正常类所有文档中的各个单词总数
            p0Denom += sum(trainMatrix[i])  # p0Denom正常类总单词数
    # 对每个元素作除法
    p1Vect = p1Num / p1Denom
    p0Vect = p0Num / p0Denom
    return p0Vect, p1Vect, pAbusive
 
文档分类,统计词频,切分词和大小写转换
 
# vec2Classify:待分类文档
# p0Vect:词汇表中每个单词在训练样本的正常言论中的类条件概率密度
# p1Vect:词汇表中每个单词在训练样本的侮辱性言论中的类条件概率密度
# pClass1:侮辱性言论在训练集中所占的比例
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 在对数空间中进行计算,属于哪一类的概率比较大就判为哪一类
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0
 
# ***词袋模型:考虑单词出现的次数
# vocabList:词汇表
# inputSet :某个文档向量
def bagOfwrods2VecMN(vocabList, inputSet):
    # 创建所含元素全为0的向量
    returnVec = [0] * len(vocabList)
    # 依次取出文档中的单词与词汇表进行对照,统计单词在文档中出现的次数
    for word in inputSet:
        if word in vocabList:
            # 单词在文档中出现的次数
            returnVec[vocabList.index(word)] += 1
    return returnVec
 
# 准备数据,按空格切分出词
# 单词长度小于或等于2的全部丢弃
def textParse(bigString):
    import re
    listOfTokens = re.split(r"\\W*", bigString)
    # tok.lower() 将整个词转换为小写
    return [tok.lower() for tok in listOfTokens if len(tok) > 0]
 
spamTest 在50封邮件中选取10篇邮件随机选择为测试集交叉验证。
 
# 读出邮件,并进行训练和测试
def spamTest():
    docList = []  # 文章按篇存放
    classList = []  # 存放文章类别
    fullText = []  # 存放所有文章内容
    # 读文档
    for i in range(1, 26):
        # 读取垃圾邮件
        wordList = textParse(open("email\\spam\\%d.txt" % i).read())
        docList.append(wordList)  # docList按篇存放文章
        fullText.extend(wordList)  # fullText邮件内容存放到一起
        classList.append(1)  # 垃圾邮件类别标记为1
        # 读取正常邮件
        wordList = textParse(open("email\\ham\\%d.txt" % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    # 随机构建训练集
    vocabList = createVocabList(docList)  # 创建词典
    trainingSet = list(range(50))  # 训练集共50篇文章
    testSet = []  # 创建测试集
    # 随机选取10篇文章为测试集,测试集中文章从训练集中删除
    for i in range(20):
        # 0-50间产生一个随机数
        randIndex = int(random.uniform(0, len(trainingSet)))
        # 从训练集中找到对应文章,加入测试集中
        testSet.append(trainingSet[randIndex])
        # 删除对应文章
        del (trainingSet[randIndex])
    # 准备数据,用于训练分类器
    trainMat = []  # 训练数据
    trainClasses = []  # 类别标签
    for docIndex in trainingSet:  # 遍历训练集中文章数据
        # 每篇文章转为词袋向量模型,存入trainMat数据矩阵中
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
        # trainClasses存放每篇文章的类别
        trainClasses.append(classList[docIndex])
    # 训练分类器
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
    # errorCount记录测试数据出错次数
    errorCount = 0
    # 遍历测试数据集,每条数据相当于一条文本
    for docIndex in testSet:
        # 文本转换为词向量模型
        wordVector = setOfWords2Vec(vocabList, docList[docIndex])
        # 模型给出的分类结果与本身类别不一致时,说明模型出错,errorCount数加1
        if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
            # 输出出错的文章
            print("出错的文章", docList[docIndex])
            print("实际分类", classList[docIndex])
            print("预测分类", classifyNB(array(wordVector), p0V, p1V, pSpam))
    print(\'错误率 : \', float(errorCount) / len(testSet))
 
结果
 
 
出错的文章 [\'a\', \'home\', \'based\', \'business\', \'opportunity\', \'is\', \'knocking\', \'at\', \'your\', \'door\', \'don抰\', \'be\', \'rude\', \'and\', \'let\', \'this\', \'chance\', \'go\', \'by\', \'you\', \'can\', \'earn\', \'a\', \'great\', \'income\', \'and\', \'find\', \'your\', \'financial\', \'life\', \'transformed\', \'learn\', \'more\', \'here\', \'to\', \'your\', \'success\', \'work\', \'from\', \'home\', \'finder\', \'experts\']
实际分类 1
预测分类 0
错误率 :  0.05
 
由于取的是随机数,错误识别大部分情况下都很高,朴素贝叶斯的分母忽略和训练集过少是主要原因。

以上是关于Bayes 朴素贝叶斯实现垃圾邮件分类的主要内容,如果未能解决你的问题,请参考以下文章

朴素贝叶斯(Naive Bayes)及Python实现

深入理解朴素贝叶斯(Naive Bayes)算法

朴素贝叶斯(Naive Bayes)及python实现(sklearn)

深入理解朴素贝叶斯(Naive Bayes)

朴素贝叶斯-垃圾邮件分类实现

朴素贝叶斯(Naive Bayes)算法