机器学习-----朴素贝叶斯

目录

  • 一 基本概念
    • 1 简介
    • 2 朴素贝叶斯的优缺点
    • 2 先验概率和后验概率
    • 3 条件概率与全概率公式
    • 4 贝叶斯推断
  • 二 贝叶斯分类器的简单应用
    • 1 数据说明
    • 2 进行分类
  • 三 朴素贝叶斯过滤垃圾邮件
    • 1 流程说明
    • 2 构建词向量
    • 3 词向量计算概率
    • 4 朴素贝叶斯分类函数
    • 5 使用朴素贝叶斯进行交叉验证
  • 四 总结

一 基本概念

1 简介

贝叶斯分类算法是统计学的一种概率分类方法,朴素贝叶斯(Naive Bayesian)分类是贝叶斯分类中最简单的一种。其分类原理就是利用贝叶斯公式根据某特征的先验概率计算出其后验概率,然后选择具有最大后验概率的类作为该特征所属的类。之所以称之为”朴素”,是因为贝叶斯分类只做最原始、最简单的假设:所有的特征之间是统计独立的。

2 朴素贝叶斯的优缺点

优点:

  • 对小规模的数据表现很好,能个处理多分类任务,适合增量式训练(即可以实时的对新增的样本进行训练)
  • 朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率
  • 朴素贝叶斯算法的健壮性比较好,对于不同类型的数据集不会呈现出太大的差异性

缺点

  • 由于我们是通过先验概率和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率
  • 对输入数据的表达形式较敏感
  • 属性独立性的条件同时也是朴素贝叶斯分类器的不足之处。数据集属性的独立性在很多情况下是很难满足的,因为数据集的属性之间往往都存在着相互关联,在属性个数比较多或者属性之间相关性较大时,分类效果不好

2 先验概率和后验概率

  • 先验概率是指根据以往经验和分析得到的概率,例如全概率公式 ,它往往作为“由因求果”问题中的“因”出现。
  • 后验概率是指在得到“结果”的信息后重新修正的概率,是“执果寻因”问题中的“因” 。后验概率是基于新的信息,来修正原来的先验概率后所获得的更接近实际情况的概率估计。

3 条件概率与全概率公式

条件概率指在已知事件B发生的情况下,事件A发生的概率,用 P ( A ∣ B ) 来表示。
机器学习-----朴素贝叶斯_第1张图片
根据上图可知:在已知事件B发生的情况下,事件A发生的概率就是 P ( A ∩ B ) 除以 P ( B ) 。

​​对于全概率公式,如果事件 { A 1 , A 2 , … , A n }构成一个完备事件且都有正概率,那么对于任意一个事件B则有:P(B)=P(BA 1 )+P(BA 2 )+…+P(BA n)=P(B∣A 1)P(A 1 )+P(B∣A 2)P(A 2 )+…+P(B∣A n )P(A n)
则有
机器学习-----朴素贝叶斯_第2张图片

4 贝叶斯推断

根据条件概率和全概率公式,可以得到贝叶斯公式如下
机器学习-----朴素贝叶斯_第3张图片
P(A) 称为先验概率(prior probability),即在B事件发生之前,我们对A事件概率的一个判断;
P ( A ∣ B ) 称为后验概率(posterior probability),即在B事件发生之后,我们对A事件概率的重新评估;
P ( B ∣ A ) /P(B) 称为可能性函数(Likely hood),这是一个调整因子,使得预估概率更接近真实概率。

所以条件概率可以理解为:后验概率 = 先验概率 × 调整因子
如果"可能性函数">1,意味着"先验概率"被增强,事件A的发生的可能性变大;
如果"可能性函数"=1,意味着B事件无助于判断事件A的可能性;
如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。

二 贝叶斯分类器的简单应用

1 数据说明

下表收集了15天内某网球爱好者每天是否打网球的数据,其中从左往右每列的属性分别为:天数、天气情况、温度、湿度、风力、当天是否打网球。
机器学习-----朴素贝叶斯_第4张图片

2 进行分类

现在假设有一个样例 x = {Sunny, Hot, High, Weak},那该种情况下应该去打网球还是不去呢,如何对其进行分类呢?
首先分别统计对15天中去打排球和没去打排球的天数以及各自情况下不同属性分类的的个数,如下图:
机器学习-----朴素贝叶斯_第5张图片

上图中的P©表示15天中打网球的概率,|Dc,Xi|表示去打网球的情况下各个属性的不同取值的个数,P(Xi|c)表示表示去打网球的情况下各个属性的不同取值的个数占总属性取值个数的比例。例如,9天去打网球的情况下天气为下雨的概率为 1 3 \frac {1} {3} 31
根据上述方法,统计出是否打网球的情况下各个属性取值的占比,如下图:机器学习-----朴素贝叶斯_第6张图片
此时,根据贝叶斯公式,分别求解去打网球和不去打网球的概率,求解公式如下:
P ( y e s ∣ x ) = ​ P ( x ∣ y e s ) P ( y e s ) P ( x ) ​ P(yes∣x)=​\frac{P(x∣yes)P(yes)}{P(x)}​ P(yesx)=P(x)P(xyes)P(yes)
​ ​ P ( n o ∣ x ) = ​ P ( x ∣ n o ) P ( n o ) P ( x ) ​ ​​P(no∣x)=​\frac{P(x∣no)P(no)}{P(x)}​ P(nox)=P(x)P(xno)P(no)
其中,x={Sunny, Hot, High, Weak},由于分母相同,因此只需计算分子的结果来进行比较即可,计算结果如下图所示:
机器学习-----朴素贝叶斯_第7张图片因此对于样例 x = {Sunny, Hot, High, Weak}所述的情况下应该不去打网球,上述的分类过程即为贝叶斯分类器工作的大致过程。下面根据该过程使用贝叶斯分类器对垃圾邮件进行过滤。

三 朴素贝叶斯过滤垃圾邮件

1 流程说明

  • 朴素贝叶斯分类器模型的训练:首先提供两组已经识别好的邮件,一组是正常邮件,另一组是垃圾邮件,然后用这两组邮件对过滤器进行“训练”。首先解析所有邮件,提取每一个词,然后计算每个词语在正常邮件和垃圾邮件中出现的概率。例如,我们假定“cute”这个词在词数(不重复)为4000的垃圾邮件中出现200次,那么该词在垃圾邮件中出现的频率就是5%;而在词数(不重复)为4000的正常邮件中出现1000次,那么该词在正常邮件中出现的频率就是25%。此时就已经初步建立了关于“cute”这个词的过滤模型。
  • 使用朴素贝叶斯分类器模型进行过滤测试:读入一封未进行分类的新邮件,然后该邮件进行解析,发现其中包含了“cute”这个词,用P(W|S)和P(W|H)分别表示这个词在垃圾邮件和正常邮件中出现的概率。随后将邮件中解析出来的每个词(已建立对应的训练模型)属于正常邮件和垃圾邮件的概率分别进行累乘,最后比较该邮件属于正常邮件和垃圾邮件的概率大小,可将该邮件归类为概率较大的那一类。

2 构建词向量

我们把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。

代码实现

# 创建实验样本
def loadDataSet():
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                   ['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]
    return postingList,classVec
"""
函数说明:将实验样本中的词条进行合并(剔除重复项)
Parameters:
    dataset:样本数据集列表
Rerurns:
    vocabSet:包含所有文档中出现的不重复词的列表
"""
def createVocabList(dataset):
    vocabSet = set([])
    for document in dataset:
        # 取并集
        vocabSet = vocabSet | set(document)
    return list(vocabSet)
"""
函数说明:将样本列表(inputSet)向量化(0或1)
Parameters:
    vocabList:词集列表(createVocabList()函数返回后的列表)
    inputSet:待向量化的词条列表
Rerurns:
    returnVec:inputSet列表向量化后映射到vocabList中的列表
"""
# 将数据集转为词向量(创建一个包含在所有文档中出现的不重复的词集)
def setOfWords2Vec(vocabList, inputSet):
    # 创建一个其中所含元素都为0的列表
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print('the word: %s is not in my Vocabulary!' % word)
    return returnVec
if __name__ == '__main__':
    postingList, classVec = loadDataSet()
    myVocabList = createVocabList(postingList)
    print('myVocabList:\n', myVocabList)
    print(setOfWords2Vec(myVocabList, postingList[0]))

运行结果:在这里插入图片描述

3 词向量计算概率

记w表示一个向量(可理解为经过向量化后的一封邮件),它由多个数值组成。在该例中数值个数与词汇表中的词条个数相同,则贝叶斯公式可表示为:

P ( c i ∣ w ) = ​ P ( w ∣ c ​ i ) p ( c ​ i ) P ( w ) ​ P(ci∣w)=​\frac{P(w∣c​i)p(c​i)}{P(w)}​ P(ciw)=P(w)P(wci)p(ci)
其中ci​表示第i个属性的取值,由于分母不变,只用比较分子即可。
​​
代码实现

"""
函数说明:朴素贝叶斯分类器训练函数
Parameter:
    trainMatrix — 训练文档矩阵,即setOfWord2Vec函数返回的returnVec构成的矩阵
    trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
    p0Vect - 非侮辱类的条件概率数组
    p1Vect - 侮辱类的条件概率数组
    pAbusive - 文档属于侮辱类的概率
"""
def trainNBO(trainMatrix,trainCategory):
    #文档数目(6)
    numTrainDocs = len(trainMatrix) 
    #词数
    numWords = len(trainMatrix[0])
    ##初始化概率     
    pAbusive = sum(trainCategory) / float(numTrainDocs)
    #使用zeros()初始化后的数组如果其中一个概率值为0则最后的结果也为0,因此使用ones() 
    # numpy中的ndarray对象用于存放同类型元素的多维数组。
    p0Num = np.ones(numWords)
    p1Num = np.ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    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 = np.log(p1Num/p1Denom)
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive
if __name__ == '__main__':
    postingList, classVec = loadDataSet()
    myVocabList = createVocabList(postingList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0Vect, p1Vect, pAbusive = trainNBO(trainMat, classVec)
    print('p0Vect:\n', p0Vect)
    print('p1Vect:\n', p1Vect)
    print('文档属于侮辱性文档的概率:', pAbusive)

运行结果:

机器学习-----朴素贝叶斯_第8张图片

4 朴素贝叶斯分类函数

代码实现:

"""
函数说明:朴素贝叶斯分类函数
Parameter:
    vec2Classify - 待分类的词条数组
    p0Vec - 侮辱类的条件概率数组
    p1Vec - 非侮辱类的条件概率数组
    pClass1 - 文档属于侮辱类的概率
Returns:
    1 - 属于侮辱类
    0 - 属于非侮辱类
"""
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0
if __name__ == '__main__':
    postingList, classVec = loadDataSet()
    myVocabList = createVocabList(postingList)
    trainMat = []
    for postinDoc in postingList:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V,p1V,pAb = trainNBO(np.array(trainMat),np.array(classVec))
    testEntry = ['love', 'my', 'dalmation']                                    
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    if classifyNB(thisDoc,p0V,p1V,pAb):
         print(testEntry,'属于侮辱类')
    else:
         print(testEntry,'属于非侮辱类')                                          
    testEntry = ['stupid', 'love', 'cute']                                    
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))                
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')
    else:
        print(testEntry,'属于非侮辱类')

运行结果
在这里插入图片描述

5 使用朴素贝叶斯进行交叉验证

代码实现:

"""
函数说明:根据vocabList词汇表,构建词袋模型
Parameter:
    vocabList - creatVocabList
    inputSet - 切分的词条列表
Returns:
    returnVec - 文档向量
"""
def bagOfWords2Vec(vocabList,inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec
"""
函数说明:对文本进行切分
Parameter:
    bigString:长字符串(一封邮件)
Returns:
    返回形式统一的、被切分后的词表
"""
def textParse(bigString):
    #将非数字、单词外的任意字符串作为切分标志
    listOfTokens = re.split('\\W*',bigString)       
    #为了使所有词的形式统一,除了单个字母,例如大写的I,其余的单词变成小写
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]
"""
函数说明: 垃圾邮件测试函数
"""
def spamTest():
    docList = [];classList = [];fullTest = []
    #导入并解析50个文件,并记录对应的标签值,垃圾邮件标记为1
    for i in range(1,26):
        wordList = textParse(open('C:/Users/cool/Desktop/mach/email/spam/%d.txt'% i,'r').read())
        docList.append(wordList)
        fullTest.append(wordList)
        classList.append(1)
        wordList = textParse(open('C:/Users/cool/Desktop/mach/email/ham/%d.txt'% i,'r').read())
        docList.append(wordList)
        fullTest.append(wordList)
        classList.append(0)
    #获得50个文件构成的词列表
    vocabList = createVocabList(docList)
    trainingSet = list(range(50))
    testSet = []
    #随机选取测试集合,并从训练集中去除,留存交叉验证
    for i in range(10):
        randIndex = int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = [];trainClass = []
    #构建训练集词向量列表,训练集标签
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
        trainClass.append(classList[docIndex])
    #训练算法,计算分类所需的概率
    p0V,p1V,pSpam = trainNBO(np.array(trainMat),np.array(trainClass))
    #分类错误个数初始化为0
    errorCount = 0
    #,遍历测试集,验证算法错误率
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList,docList[docIndex]) 
        if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
    print('错误率:%.2f%%'%(float(errorCount)/len(testSet)*100))
if __name__ == '__main__':
    spamTest()

运行结果

"""
函数说明: 垃圾邮件测试函数
"""
def spamTest():
    docList = [];classList = [];fullTest = []
    #导入并解析50个文件,并记录对应的标签值,垃圾邮件标记为1
    for i in range(1,26):
        wordList = textParse(open('C:/Users/lenovo/Desktop/machinelearninginaction/Ch04/email/spam/%d.txt'% i,'r').read())
        docList.append(wordList)
        fullTest.append(wordList)
        classList.append(1)
        wordList = textParse(open('C:/Users/lenovo/Desktop/machinelearninginaction/Ch04/email/ham/%d.txt'% i,'r').read())
        docList.append(wordList)
        fullTest.append(wordList)
        classList.append(0)
    #获得50个文件构成的词列表
    vocabList = createVocabList(docList)
    trainingSet = list(range(50))
    testSet = []
    #随机选取测试集合,并从训练集中去除,留存交叉验证
    for i in range(10):
        randIndex = int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = [];trainClass = []
    #构建训练集词向量列表,训练集标签
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))
        trainClass.append(classList[docIndex])
    #训练算法,计算分类所需的概率
    p0V,p1V,pSpam = trainNBO(np.array(trainMat),np.array(trainClass))
    #分类错误个数初始化为0
    errorCount = 0
    #,遍历测试集,验证算法错误率
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList,docList[docIndex]) 
        if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
            errorCount += 1
    print('错误率:%.2f%%'%(float(errorCount)/len(testSet)*100))
if __name__ == '__main__':
    spamTest()

运行结果:
机器学习-----朴素贝叶斯_第9张图片

四 总结

通过朴素贝叶斯我们可以对属性相对独立的数据进行比较好的分类,对于不同类型的数据集不会呈现出太大的差异性。但是对于数据集的属性之间存在着较强关联时,分类效果不好。

你可能感兴趣的:(python,机器学习,朴素贝叶斯算法)