机器学习:基于概率的朴素贝叶斯分类器详解--Python实现以及项目实战

简介: 机器学习:基于概率的朴素贝叶斯分类器详解--Python实现以及项目实战

前言


前篇基础理论知识:机器学习:贝叶斯分类器详解(一)-贝叶斯决策理论与朴素贝叶斯

这篇主要使用代码实现贝叶斯分类。


一、准备数据


创建一个bayes.py程序,从文本中构建词向量,实现词表向向量转换函数。

from numpy import *
def loadDataSet():
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],  # 分词可用wordcloud
                ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],# 此文档为斑点犬爱好者留言板
                ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0,1,0,1,0,1]#1代表侮辱性文字,0代表正常言论
    return postingList,classVec #返回的第二个变量为人工标注用于区别侮辱性和非侮辱性的标签。
#创建一个空集
def createVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document) #创建两个集合的并集 划掉重复出现的单词
    return list(vocabSet)
#处理样本输出为向量形式
def setOfWords2Vec(vocaList , inputSet):
    returnVec = [0]*len(vocaList)#创建一个其中所含元素全为0的向量代替文本
    for word in inputSet:
        if word in vocaList:
            returnVec[vocaList.index(word)] = 1
        else:
            print("the word:%s is not in my Vocabulary!"" % word")
    return returnVec


第一个函数创建了一些实验样本,第二个函数创建一个包含在所有文档中出现的不重复的列表,第三个函数输入参数为词汇表及某个文档,输出的是文档向量,向量的每一个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现。

可检验函数是否正常工作:

listOPosts,listClasses=loadDataSet()
myVocabList=createVocabList(listOPosts)
print(myVocabList)
print(setOfWords2Vec(myVocabList,listOPosts[0]))
print(setOfWords2Vec(myVocabList,listOPosts[3]))

20201202110142395.png

二、训练算法:从词向量计算概率


该函数伪代码如下:

20201202110627883.png


根据前篇基础理论先求得P(w|ci),再计算P(ci)。

朴素贝叶斯分类器训练函数:


def trainNB0(trainMatrix,trainCategory):#朴素贝叶斯分类器训练函数。参数:1:向量化文档2:词条向量
    numTrainDocs = len(trainMatrix)#文本矩阵
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numWords)
    p0Num = zeros(numWords);p1Num = zeros(numWords)#创建两个长度为词条向量等长的列表,平滑处理:初始值设为1
    p0Denom = 0.000001;p1Denom = 0.000001
    for i in range (numTrainDocs):
        if trainCategory[i] ==1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
        p1Vect = p1Num/p1Denom  # 利用Numpy数组计算p(wi/c1),即类1条件下各词条出现的概率
        p0Vect = p0Num/p0Denom  # 利用Numpy数组计算p(wi/c0),为避免下溢,后面会改为log()
    return p0Vect, p1Vect, pAbusive  # 返回


由于当p0Num时会报RuntimeWarning: invalid value encountered in true_divide,这是由0/0导致,因此在设置p0Denom时不能设置为0.


首先,计算文档属于侮辱性文档(class=1)的概率,即P(1)。P(0)可由1-P(1)得到。


检验:

print(p1v)
print(p0v)
print(pAb)

结果:

20201202114427130.png

利用贝叶斯分类器对文档进行分类时,要进行多个概率的乘积可获得文档属于某个类别额的概率,即计算p(w0|1)p(w1|1)p(w2|1)。其中一个概率值为0,那么最后的乘积也为0.我们可以将所有出现的词初始值初始化为1,并将分母初始化为2.

修改:

    p0Num = ones(numWords); p1Num = ones(numWords)#创建两个长度为词条向量等长的列表,平滑处理:初始值设为1
    p0Denom = 2.0;p1Denom = 2.0#平滑处理,初始值设为2


另一个问题为下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0|ci) * p(w1|ci) * p(w2|ci)... p(wn|ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0)。一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。


修改:

    p1Vect = log(p1Num/p1Denom)#利用Numpy数组计算p(wi/c1),即类1条件下各词条出现的概率
    p0Vect = log(p0Num/p0Denom)#利用Numpy数组计算p(wi/c0),为避免下溢,后面会改为log()

202012021947486.png


三、分类函数

朴素贝叶斯分类函数:

#朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#注意参数2,3均已log化
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)# P(w|c1) * P(c1) ,即贝叶斯准则的分子
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) # P(w|c0) * P(c0) ,即贝叶斯准则的分子
    if p1 > p0:
        return 1
    else:
        return 0
    """
    使用算法:
        # 将乘法转换为加法
        乘法:P(C|F1F2...Fn) = P(F1F2...Fn|C)P(C)/P(F1F2...Fn)
        加法:P(F1|C)*P(F2|C)....P(Fn|C)P(C) -> log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
    :param vec2Classify: 待测数据[0,1,1,1,1...],即要分类的向量
    :param p0Vec: 类别0,即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表
    :param p1Vec: 类别1,即侮辱性文档的[log(P(F1|C1)),log(P(F2|C1)),log(P(F3|C1)),log(P(F4|C1)),log(P(F5|C1))....]列表
    :param pClass1: 类别1,侮辱性文件的出现概率
    :return: 类别1 or 0
    """
    # 计算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))


测试:

def testingNB():
    """
    测试朴素贝叶斯算法
    """
    # 1. 加载数据集
    listPosts,listClasses =loadDataSet()
    #2. 创建单词集合
    myVocabList = createVocabList(listPosts)
    #3.计算单词是否出现并创建数据矩阵
    trainMat = []
    for postinDoc in listPosts:
        trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
    #4.训练数据
    p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
    #5.测试数据
    testEntry = ['love','my','dalmation']
    thisDoc = array(setOfWords2Vec(myVocabList,testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    testEntry = ['stupid', 'garbage']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))


20201202201254624.png


四、文档词袋模型


由于我们将每个词的出现作为一个特征,这可以被描述为词集模型。但单词往往有多义性,意味着一个单词在文档出现可能代表有不同的含义。这种方法被称为词袋模型。


在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数setOfWords2Vec稍加修改:

def bagOfWords2VecMN(vocaList , inputSet):
    returnVec = [0]*len(vocaList)#创建一个其中所含元素全为0的向量代替文本
    for word in inputSet:
        if word in vocaList:
            returnVec[vocaList.index(word)] += 1 #每遇到一个单词,相应加一
        else:
            print("the word:%s is not in my Vocabulary!"" % word")
    return returnVec


五、使用朴素贝叶斯过滤垃圾邮件

20201202202911921.png


1.收集数据


使用朴素贝叶斯过滤垃圾邮件数据集

数据集说明: 数据集下包含两个文件夹,其中spam文件夹下为垃圾邮件,ham文件夹下为非垃圾邮件。

数据集格式: txt文件


2.准备数据(处理数据)


英文由于单词之间有空格,方便切分。中文有jieba库,有兴趣的可以了解一下。


myStr = 'This book is the best book on Python.'
myStr.split()
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python.']


但是最后一个词有标点符号,这个我们通过正则表达式解决,正则表达式在文本分类中是有很大作用的。

import re
regEx = re.compile('\\W*')
listOfTokens = regEx.split(myStr)
listOfTokens
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', '']

20201202210338582.jpg


这里会有空字符串产生。我们可以计算字符串的长度,只返回字符串长度大于0的字符串。  

[tok for tok in listOfTokens if len(tok)>0]
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python']


另外我们考虑构建词库,并不用考虑单词的大小写,全部改为小写

[tok.lower() for tok in listOfTokens if len(tok)>0]
['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python']


这么一来我们就完成了简单文本的切分。当然一些文本也有非常复杂的处理方法,具体看文本的内容和性质。


3.测试算法:使用朴素贝叶斯进行交叉验证


直接贴上代码


def textParse(bigString):
     import re
     listOfTokens = re.split(r'\W*', bigString)
     return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
    docList = []  # 文档(邮件)矩阵
    classList = []  # 类标签列表
    for i in range(1, 26):
        wordlist = textParse(open('trashclass/spam/{}.txt'.format(str(i))).read())
        docList.append(wordlist)
        classList.append(1)
        wordlist = textParse(open('trashclass/ham/{}.txt'.format(str(i))).read())
        docList.append(wordlist)
        classList.append(0)
    vocabList = bayes.createVocabList(docList)  # 所有邮件内容的词汇表
    import pickle
    file=open('trashclass/vocabList.txt',mode='wb')  #存储词汇表 二进制方式写入
    pickle.dump(vocabList,file)
    file.close()
    # 对需要测试的邮件,根据其词表fileWordList构造向量
    # 随机构建40训练集与10测试集
    trainingSet = list(range(50))
    testSet = []
    for i in range(10):
        randIndex = int(np.random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del (trainingSet[randIndex])
    trainMat = []  # 训练集
    trainClasses = []  # 训练集中向量的类标签列表
    for docIndex in trainingSet:
        # 使用词袋模式构造的向量组成训练集
        trainMat.append(bayes.setOfWords2Vec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0v,p1v,pAb=bayes.trainNB0(trainMat,trainClasses)
    file=open('trashclass/threeRate.txt',mode='wb') #用以存储分类器的三个概率 二进制方式写入
    pickle.dump([p0v,p1v,pAb],file)
    file.close()
    errorCount=0
    for docIndex in testSet:
        wordVector=bayes.setOfWords2Vec(vocabList,docList[docIndex])
        if bayes.classifyNB(wordVector,p0v,p1v,pAb)!=classList[docIndex]:
            errorCount+=1
    return float(errorCount)/len(testSet)

  加入序列化永久性保存对象,保存对象的字节序列到本地文件中。本例中共有50封电子邮件,其中10封电子邮件被随机选择为测试集合。选择出的数字所对应的文档被添加到测试集,同时也将其从训练集中剔除。这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证。现在我们只作出一次迭代,为了更精确的估计分类器的错误率,我们应该多次迭代后求出平均错误率。


当然你也可以用:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    test_size=0.2,
                                                    random_state=0)

 方法很多很简单,这里不重复叙述。

(对了上面的代码setOfWords2Vec其实是bagOfWords2VecMN,我只是没有换名字而已,内容是bagOfWords2VecMN)

开始构造分类器:

import bayes
import numpy as np
import tkinter as tk
from tkinter import filedialog
def fileClassify(filepath):
    import pickle
    fileWordList=textParse(open(filepath,mode='r').read())
    file=open('trashclass/vocabList.txt',mode='rb')
    vocabList=pickle.load(file)
    vocabList=vocabList
    fileWordVec=bayes.setOfWords2Vec(vocabList,fileWordList) #被判断文档的向量
    file=open('trashclass/threeRate.txt',mode='rb')
    rate=pickle.load(file)
    p0v=rate[0];p1v=rate[1];pAb=rate[2]
    return bayes.classifyNB(fileWordVec,p0v,p1v,pAb)
if __name__=='__main__':
    print('朴素贝叶斯分类的错误率为:{}'.format(spamTest()))  #测试算法的错误率
    # filepath=input('输入需判断的邮件路径')
    root = tk.Tk()
    root.withdraw()
    Filepath = filedialog.askopenfilename()  # 获得选择好的文件
    print(Filepath)
    #判断某一路径下的邮件是否为垃圾邮件
    if fileClassify(Filepath)==1:
        print('垃圾邮件')
    else:
        print('非垃圾邮件')

这里我直接用Tk直接选路径懒得打了QWQ

2020120902221074.png

20201209022229423.png


完成!~~~~感觉不错就点个赞吧~


总结


实践才是硬道理。

目录
相关文章
|
6天前
|
机器学习/深度学习 人工智能 算法
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
手写数字识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Flask框架,开发网页端操作平台,实现用户上传一张图片识别其名称。
22 0
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
|
9天前
|
机器学习/深度学习 数据采集 人工智能
探索机器学习:从理论到Python代码实践
【10月更文挑战第36天】本文将深入浅出地介绍机器学习的基本概念、主要算法及其在Python中的实现。我们将通过实际案例,展示如何使用scikit-learn库进行数据预处理、模型选择和参数调优。无论你是初学者还是有一定基础的开发者,都能从中获得启发和实践指导。
20 2
|
10天前
|
机器学习/深度学习 数据采集 搜索推荐
利用Python和机器学习构建电影推荐系统
利用Python和机器学习构建电影推荐系统
26 1
|
10天前
|
机器学习/深度学习 算法 PyTorch
用Python实现简单机器学习模型:以鸢尾花数据集为例
用Python实现简单机器学习模型:以鸢尾花数据集为例
31 1
|
17天前
|
机器学习/深度学习 数据采集 算法
Python机器学习:Scikit-learn库的高效使用技巧
【10月更文挑战第28天】Scikit-learn 是 Python 中最受欢迎的机器学习库之一,以其简洁的 API、丰富的算法和良好的文档支持而受到开发者喜爱。本文介绍了 Scikit-learn 的高效使用技巧,包括数据预处理(如使用 Pipeline 和 ColumnTransformer)、模型选择与评估(如交叉验证和 GridSearchCV)以及模型持久化(如使用 joblib)。通过这些技巧,你可以在机器学习项目中事半功倍。
21 3
|
Python
简单的贝叶斯分类器的python实现
  1 # -*- coding: utf-8 -*- 2 ''' 3 >>> c = Classy() 4 >>> c.train(['cpu', 'RAM', 'ALU', 'io', 'bridge', 'disk'], 'architecture') 5 True 6 >>> c.
752 0
|
6天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
6天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
6天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
8天前
|
设计模式 算法 搜索推荐
Python编程中的设计模式:优雅解决复杂问题的钥匙####
本文将探讨Python编程中几种核心设计模式的应用实例与优势,不涉及具体代码示例,而是聚焦于每种模式背后的设计理念、适用场景及其如何促进代码的可维护性和扩展性。通过理解这些设计模式,开发者可以更加高效地构建软件系统,实现代码复用,提升项目质量。 ####