机器学习之四:朴素贝叶斯

1、条件概率:

P(A|B)=P(AB)P(B)

     可以得出:
P(AB)=P(A)P(B|A)=P(B)P(A|B)

2、全概率公式:
    设 BiBj=i!=jB1+B2+B3+...+bB=Ω ,则这样的一组事件被称为一个完备事件组,则有:
P(A)=P(AΩ)=P(AB1+AB2+AB3+...+ABn)=P(AB1)+P(AB2)+P(AB3)+...+P(ABn)

P(A)=P(B1)P(A|B1)+P(B2)P(A|B2)+P(B3)P(A|B3)+...+P(Bn)P(A|Bn)

3、贝叶斯公式:
P(Bi|A)=P(ABi)P(A)=P(Bi)P(A|Bi)jP(Bj)P(A|Bj)

     全概率公式中, A 看成结果,Bi看成是导致结果发生的诸多原因之一,全概率公式是一个原因推导结果的过程。贝叶斯公式恰好相反,我们知道结果 A 已经发生了,所要做的就是反过来研究造成结果发生的原因,是xx原因造成的可能性有多大,即结果推原因。
4、分类算法:
     给定训练数据集:T={(x1,y1),(x2,y2),...(xn,yn)},由 P(X,Y) 独立同分布产生,朴素贝叶斯法通过训练集学习联合概率分布 P(X,Y) ,具体的学习以下先验概率及条件概率分布。
先验概率分布:
P(Y=ck)k=123K

条件概率分布:
P(X=x|Y=ck)=P(X(1)=x(1),...,X(n)=x(n)|Y=ck),k=1,2,3,...,k

     朴素贝叶斯对条件概率分布做了条件独立性假设,条件独立性假设等于是说用于分类的特征在类确定的条件下都是条件独立的,每个特征分量条件独立。由于这是一个较强的假设,朴素贝叶斯法也由此得名,这一假设使得贝叶斯法变得简单,但有时会牺牲一定的分类准确率。具体的,条件独立性假设是:
P(X=x|Y=ck)=P(X(1)=x(1),...,X(n)=x(n)|Y=ck)=j=1nP(X(j)=x(j)|Y=ck)

     朴素贝叶斯进行分类时,对于给定的输入x,通过学习到的模型计算后验概率分布 P(Y=ck|X=x) ,将后验概率最大的类作为x的类输出,后验概率的计算根据贝叶斯定理进行:
P(Y=ck|X=x)=P(Y=ck,X=x)P(X=x)=P(X=x|Y=ck)P(Y=ck)kP(X=x|Y=ck)P(Y=ck)=P(Y=ck)nj=1P(X(j)=x(j)|Y=ck)kP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)

     于是,朴素贝叶斯分类器可以表示为:
y=f(x)=argmaxckP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)kP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)

     注意到,上式中分母对于所有的 ck 都是相同的,所以分类器表示为:
y=f(x)=argmaxckP(Y=ck)j=1nP(X(j)=x(j)|Y=ck)

     注意:如果 P(X=x|Y=ck) 中的某一项为0,则其联合概率的乘积也可能为0,即公式的分子为0,为了避免这种现象出现,一般情况下会将这一项初始化为1,当然为了保证概率相等,分母应对应初始化为2(这里因为是2类,所以加2,如果是k类就需要加k,术语上叫做laplace光滑, 分母加k的原因是使之满足全概率公式)。

朴素贝叶斯的优点:
  对小规模的数据表现很好,适合多分类任务,适合增量式训练。
缺点:
  对输入数据的表达形式很敏感。
  
5、代码实现:

#encoding=utf-8
import numpy as np

#加载数据
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([])
    for document in dataSet:       
        vocabSet = vocabSet | set(document) #创建并集
    vacalist=list(vocabSet)   
    return vacalist 

#根据词汇表,将每一句话转化为向量。
# 词集模型:每个词的出现与否作为一个特征
# 词袋模型:每个词出现的次数作为一个特征
def bagOfWord2VecMN(vocabList,inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1  # 这里为词袋模型
    return returnVec  
#训练数据
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)  # 训练文本数量,即样本大小
    numWords = len(trainMatrix[0])   # 词向量大小
    pAbusive = sum(trainCategory)/float(numTrainDocs)  # 每个类的先验概率:侮辱性言论占的比例

    #1、利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率如果其中一个概率值为0 , 那么最后的乘积也为0。为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。
    p0Num = np.ones(numWords)  # 长度为词向量大小,元素都为1的列表,分子初始化为1
    p1Num = np.ones(numWords)  #计算频数分子初始化为1
    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]) # 正常性样本点中所有词出现的总次数

  # 2、另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算分母乘积时,由于大部分因子者3非常小, 所以程序会下溢出或者得到不正确的答案。一种解决办法是对乘积取自然对数。在代数中有ln(a*b) = ln(a)+ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。从函数f(x)和ln[f(x)]的曲线看,就会发现它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。通过修政return前的两行代码,将上述做法用到分类器中:
    # plVect = log(plNum/plDenom)
    # p0Vect = log(p0Num/p0Denom)
    p1Vect = np.log(p1Num/p1Denom) #注意,计算条件概率,然后取对数
    p0Vect = np.log(p0Num/p0Denom) #注意,计算条件概率,然后取对数
    return p0Vect,p1Vect,pAbusive#返回各类对应特征的条件概率向量,和各类的先验概率
# 进行分类
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)#注意,计算后验概率
    p0 = sum(vec2Classify * p0Vec) + np.log(1-pClass1)#注意,计算后验概率
    if p1 > p0:
        return 1
    else:
        return 0
# 测试
def testingNB():
    listOPosts,listClasses = loadDataSet()#加载原始数据和词向量
    myVocabList = createVocabList(listOPosts) #建立去重的所有词汇列表
    trainMat = [] # 保存每句话的词向量的列表
    # 对原始数据的每句话进行迭代,得出每句话的词向量
    for postinDoc in listOPosts:
        trainMat.append(bagOfWord2VecMN(myVocabList,postinDoc))
    # 每句话的词向量和该句话的类别 (0 | 1) 进行训练
    p0V,p1V,pAb = trainNB0(trainMat,listClasses)
    #测试
    testEntry = ['love','my','dalmation']
    thisDoc = bagOfWord2VecMN(myVocabList,testEntry)
    print testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)

if __name__ == '__main__':
    testingNB()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值