《统计学习方法》李航_学习笔记_第4章_朴素贝叶斯

朴素贝叶斯

文章目录

    • 朴素贝叶斯
      • 1 朴素贝叶斯法的学习与分类
        • 1.1 基本方法
        • 1.2 后验概率最大化的含义
      • 2 朴素贝叶斯法的参数估计
        • 2.0 参数估计数目
        • 2.1 极大似然估计
        • 2.2 贝叶斯估计
        • 2.3 学习与分类算法
      • 思考
      • 代码实现
        • 1 连续数据(iris数据集)
        • 2 离散数据(文本分类)

《统计学习方法》李航 第4章 朴素贝叶斯
《机器学习》周志华 第7章 贝叶斯分类器
《机器学习实战》第4章 基于概率论的分类方法:朴素贝叶斯

1 朴素贝叶斯法的学习与分类

1.1 基本方法

  • 朴素贝叶斯中“朴素“的含义:只做了最原始、最简单的假设——特征条件独立

  • 朴素贝叶斯法:基于贝叶斯定理特征条件独立假设的分类方法
    实际上是学到生成数据的机制,属于生成模型

  • 特征条件独立假设:用于分类的特征在类确定的条件下都是条件独立的。

  • 朴素贝叶斯主要用于多分类(特征大多是类别型的,当然对于连续型的数据也有相应的处理方法)
    常见应用:文档分类

  • 先验概率vs后验概率
    先验概率:根据以往经验分析得到的概率(eg.所有人中患肝癌的概率P(B))
    后验概率:基于新的信息,利用贝叶斯公式,对先验概率进行修正得到的(eg.被血清…法测出患肝癌的人,的确患肝癌的概率P(B|A))

  • 判别式模型vs生成式模型
    判别式模型(discriminative probability):给定x,通过直接建模 P ( c ∣ x ) P(c|x) P(cx) 来预测c
    (eg.决策树、BP神经网络、支持向量机等)
    生成式模型(generative probability):先对联合概率分布 P ( x , c ) P(x,c) P(x,c) 建模,然后由此获得 P ( c ∣ x ) P(c|x) P(cx)

  • 贝叶斯公式(条件概率公式):
    P ( c ∣ x ) = p ( x ∣ c ) p ( c ) p ( x ) P(c|x)=\frac{p(x|c)p(c)}{p(x)} P(cx)=p(x)p(xc)p(c)
    其中:
    P ( c ) P(c) P(c)是类的先验概率(表达了样本空间中各类样本所占的比例),
    P ( x ∣ c ) P(x|c) P(xc)是样本 x x x对于类 c c c 的条件概率,或称“似然”,
    P ( x ) P(x) P(x)是“证据”因子
    估计 P ( c ∣ x ) P(c|x) P(cx) --> 基于训练集D,估计先验概率 P ( c ) P(c) P(c)和似然 P ( x ∣ c ) P(x|c) P(xc)
    实际背景:“执果溯因”
    已知某个实验结果,探讨每个原因导致这一结果的可能性大小。

1.2 后验概率最大化的含义

<----待补充证明---->
0-1损失函数时期望风险最小化 等价于 后验概率最大化

2 朴素贝叶斯法的参数估计

2.0 参数估计数目

假设样本类别数为 K K K,特征 x ( j ) x^{(j)} x(j)可取 S j S_j Sj个值
需要估计的参数有:先验概率 P ( Y = c k ) P(Y=c_k) P(Y=ck)和条件概率 P ( X ( j ) = x ( j ) ∣ Y = c k ) P(X^{(j)}=x^{(j)}|Y=c_k) P(X(j)=x(j)Y=ck)

  1. 先验概率: K K K 个 (当然,实际只需估计 K − 1 K-1 K1个即可)
  2. 条件概率:
    a) 原: K ∏ j = 1 n S j K\prod_{j=1}^{n}S_j Kj=1nSj 个(数据量为指数级)
    b) 特征条件独立假设下: K ∑ j = 1 n S j K\sum_{j=1}^{n}S_j Kj=1nSj 个(参数简化)

因此朴素贝叶斯法估计的参数量为: K ( ∑ j = 1 n S j + 1 ) K (\sum_{j=1}^{n}S_j+1) K(j=1nSj+1)

2.1 极大似然估计

根据特征条件独立假设,可得出:
P ( X = x ∣ Y = c k ) = P ( X ( 1 ) = x ( 1 ) , X ( 2 ) = x ( 2 ) , . . . , X ( n ) = x ( n ) ∣ Y = c k ) = ∏ i ∈ n P ( X ( i ) = x ( i ) ∣ Y = c k ) k = 1 , 2 , . . . , K \begin{aligned} P(X=x|Y=c_k)&=P(X^{(1)}=x^{(1)},X^{(2)}=x^{(2)},...,X^{(n)}=x^{(n)}|Y=c_k)\\ &=\prod_{i\in{n}}{P(X^{(i)}=x^{(i)}|Y=c_k)}\\ &k=1,2,...,K \end{aligned} P(X=xY=ck)=P(X(1)=x(1),X(2)=x(2),...,X(n)=x(n)Y=ck)=inP(X(i)=x(i)Y=ck)k=1,2,...,K
上式中连乘操作易造成下溢(过多较小的数相称造成的影响),常使用对数似然
L L ( c k ) = l o g P ( X = x ∣ Y = c k ) = ∑ i ∈ n l o g P ( X ( i ) = x ( i ) ∣ Y = c k ) LL(c_k)=logP(X=x|Y=c_k)=\sum_{i\in{n}}{logP(X^{(i)}=x^{(i)}|Y=c_k)} LL(ck)=logP(X=xY=ck)=inlogP(X(i)=x(i)Y=ck)
最大似然估计可能会出现概率值为0的情况

2.2 贝叶斯估计

贝叶斯估计:在极大似然估计的基础上进行了平滑处理,防止因训练集样本不充分而出现概率为0的情况

S j S_j Sj 表示第j个属性可能的取值数目,则条件概率的贝叶斯估计为:
P ( X ( j ) = a j l ∣ Y = c k ) = ∑ i = 1 N I ( x i ( j ) = a j l , y i = c k ) + λ ∑ i = 1 N I ( y i = c k ) + S j λ P(X^{(j)}=a_{jl}|Y=c_k)=\frac{\sum_{i=1}^NI(x_i^{(j)}=a_{jl},y_i=c_k)+\lambda}{\sum_{i=1}^NI(y_i=c_k)+S_j\lambda} P(X(j)=ajlY=ck)=i=1NI(yi=ck)+Sjλi=1NI(xi(j)=ajl,yi=ck)+λ
其中, λ \lambda λ为平滑因子,可以是整数或小数, λ = 1 \lambda=1 λ=1为拉普拉斯平滑; λ = 0 \lambda=0 λ=0时为最大似然估计

  • λ \lambda λ如何取值需要进行实验得到较优值
  • 先验概率一般不做平滑(划分数据集时需要进行数据打散,以防测试数据类别在训练集中未出现)

2.3 学习与分类算法

  1. 对于给定的训练集,基于特征条件独立假设计算先验概率及条件概率
    先验概率: P ( Y = c k ) , k = 1 , 2 , . . . , K P(Y=c_k),k=1,2,...,K P(Y=ck),k=1,2,...,K
    条件概率: P ( X = x ∣ Y = c k ) = ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) , k = 1 , 2 , . . . , K P(X=x|Y=c_k)=\prod _{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k),k=1,2,...,K P(X=xY=ck)=j=1nP(X(j)=x(j)Y=ck),k=1,2,...,K
  2. 对于给定样本,计算联合概率分布 P ( X , Y ) P(X,Y) P(X,Y)
  3. 求出后验概率最大的输出y,得到样本类别

思考

  1. 为什么叫“朴素“贝叶斯?
  2. 朴素贝叶斯的概率图模型是怎样的?
  3. 朴素贝叶斯是决策函数还是概率模型?
  4. 如果朴素贝叶斯是概率模型,则它是判别模型还是生成模型?
  5. 朴素贝叶斯不仅可以考虑特征出现与否,也可以考虑特征出现的次数、特征出现的位置?
  6. 特征之间是独立的 和 在给定类别的情况下特征之间两两独立 是不同的概念

代码实现

1 连续数据(iris数据集)

对于连续属性,以下代码通过计算类条件下的概率密度计算条件概率

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris

# 载入数据集
iris = load_iris()
X = iris['data']
Y = iris['target']
X = pd.DataFrame(X, columns= ['sepL','sepW','petL','petW'])
Y = pd.DataFrame(Y,columns = ['c'])

# 划分数据集
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 = 1)

'''
连续属性的处理:(此处采用第一种方法)
1.假设连续属性是正态分布的,通过样本计算出均值和方差,即得到正态分布的密度函数,通过密度函数
将值代入,算出某一点的密度函数值
2.将连续属性离散化
'''
# 计算均值和方差
def getMeanStd(X):
    X_mean = np.mean(X)
    X_std = np.std(X)
    return X_mean, X_std
    
# 计算连续属性的概率密度
def calGaussProb(x,x_mean,x_std):
    one = np.ones(x.shape)
    ex = np.exp(-np.power(x-x_mean,2)/(2*np.power(x_std,2)))
    gaussProb = one/(np.sqrt(2*np.pi)*x_std) * ex
    return gaussProb
    
# 计算得到先验概率、类条件下概率密度的均值和方差 
def trainNB(X_train, Y_train):
    # 类别数目
    K = len(set(Y_train['c']))
    
    # 统计训练集中不同类别的样本数
    X_y = []
    for i in range(K):
        X_y.append(X_train.loc[Y_train[Y_train['c']==i].index])
        
    # 计算先验概率
    N_train = Y_train.shape[0]
    prior = []
    for i in range(K):
        prior.append(X_y[i].shape[0]/N_train)
    
    X_y_mean = []
    X_y_std = []
    for i in range(K):
        mean, std = getMeanStd(X_y[i])
        X_y_mean.append(mean)
        X_y_std.append(std)
    return prior, X_y_mean, X_y_std

def testNB(prior, X_y_mean, X_y_std, X):
    K = 3
    Y_pred = []
    for i in range(X.shape[0]):
        post = []
        for k in range(K):
            gaussProb = calGaussProb(X.iloc[i], X_y_mean[k], X_y_std[k])
            res = sum(np.log(gaussProb)) + np.log(prior[k])
            post.append(res)
        Y_pred.append(post.index(max(post)))
    return Y_pred


prior, X_y_mean, X_y_std = trainNB(X_train, Y_train)
Y_pred1 = testNB(prior, X_y_mean, X_y_std, X_test)
print(Y_pred1)

# 将预测结果与sklearn中的GaussianNB比较,基本一致
from sklearn.naive_bayes import GaussianNB
clf = GaussianNB()
clf.fit(X_train,Y_train)
Y_pred = clf.predict(X_test)
print(Y_pred)

print(Y_pred1 == Y_pred)

2 离散数据(文本分类)

'''
《机器学习实战》
'''

from numpy import *

'''
postingList: 进行词条切分后的文档集合
classVec:类别标签    
'''
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]    #1代表侮辱性文字,0代表正常言论
    return postingList,classVec

'''建立词汇表'''
def createVocabList(dataSet):
    #创建一个空集,用于存放词条
    vocabSet = set([])#使用set创建不重复词表库
    #包含所有文档的单词
    for document in dataSet:
        vocabSet = vocabSet | set(document) #创建两个集合的并集
    #将单词集合转化成列表并返回
    return list(vocabSet)

'''根据词汇表中的单词是否在文档中出现(出现1,未出现0),将文档转化为单词向量'''
def setOfWords2Vec(vocabList, inputSet):
    #创建一个所包含元素都为0的向量
    returnVec = [0]*len(vocabList)
    #遍历文档中的所有单词,如果出现了词汇表中的单词,则对应值设为1
    for word in inputSet:
        if word in vocabList:
            #通过index获得当前单词的下标
            returnVec[vocabList.index(word)] = 1
        else: print("the word: %s is not in my Vocabulary!" % word)
    return returnVec
'''
我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-words model)。
如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,
这种方法被称为词袋模型(bag-of-words model)。
在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。
为适应词袋模型,需要对函数setOfWords2Vec稍加修改,修改后的函数称为bagOfWords2VecMN
'''
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

'''
朴素贝叶斯训练函数
trainMat:由每篇文档的单词向量构成的文档矩阵
trainCate:每篇文档的类别标签构成的向量
'''
def trainNB0(trainMat,trainCate):
    #计算文档数目
    numTrainDocs=len(trainMat)
    #获得每篇文档的单词个数
    numWords=len(trainMat[0])
    #计算侮辱性文档出现的概率p(c=1)
    pAbusive=sum(trainCate)/float(numTrainDocs)
    #采用极大似然估计
    #p0Num=zeros(numWords);p1Num=zeros(numWords)
    #为了防止出现概率值为0的情况,采用贝叶斯估计  lambda=1,拉普拉斯平滑
    #初始化两个矩阵,值为1
    p0Num=ones(numWords);p1Num=ones(numWords)
    p0Denom=2.0;p1Denom=2.0
    #遍历每一篇文档的单词向量
    for i in range(numTrainDocs):
        #如果文档属于侮辱性文档
        if trainCate[i]==1:
            #统计所有类别为1的单词向量中各个单词出现的次数
            p1Num+=trainMat[i]
            #统计类别为1的所有文档中出现的单词数目
            p1Denom+=sum(trainMat[i])
            #print(p1Num,p1Denom)
        else:
            p0Num+=trainMat[i]
            p0Denom+=sum(trainMat[i])
    #对结果取对数,避免下溢出(过多较小的数相称造成的影响)
    #计算条件概率p(wi|c=1)
    p1Vect=log(p1Num/p1Denom)
    #计算条件概率p(wi|c=0)
    p0Vect=log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive           

#朴素贝叶斯分类函数
#vec2Classify:待测试分类的单词向量
#p0Vec:类别0所有文档中各个单词出现的频数p(wi|c=0)
#p0Vec:类别1所有文档中各个单词出现的频数p(wi|c=1)
#pClass1:类别为1的文档占文档总数比例
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


if __name__=='__main__':
    #产生文档矩阵和对应的标签
    listOPosts,listClasses = loadDataSet() 
    #创建词汇表
    myVocabList = createVocabList(listOPosts) 
    #创建一个空的列表
    trainMat = []   
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList,postinDoc))  #使用词向量来填充trainMat列表
    p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))  #训练函数
    testEntry = ['love','my','dalmation']   #测试文档列表
    thisDoc = array(setOfWords2Vec(myVocabList,testEntry)) #声明矩阵
    print(thisDoc)
    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))

你可能感兴趣的:(统计学习,机器学习,python算法,机器学习)