机器学习03-贝叶斯算法

Posted Qunar_尤雪萍

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习03-贝叶斯算法相关的知识,希望对你有一定的参考价值。

朴素贝叶斯算法

如下图的点分布图:

给定单(x,y), 它属于红色区域c1概率p1, 蓝色区域c2概率p2
* 如果p1(x,y) > p2(x,y) , 那么类别为1
* 如果p1(x,y) < p2(x,y) , 那么类别为2
我们在判断(x,y)时会选择高概率所对应的类别,贝叶斯的理论就是选择最高概率的决策。
这里我们观察的两个公式
p(x|c) = p(x and c) / p(c)
p(c|x) = p(x|c)p(c) / p(x)
将这个公式应用到我们的分类算中,求上面的p1,p2 转换为 p(c1|x, y),p(c2|x, y)通过上面的转换公式可以转换为 p(c1|x, y) = p(x,y |c1)p(c1) / p(x,y) , p(c2|x, y) = p(x, y| c2) p(c2) / p(x, y)
* 当p(c1|x,y) > p(c2|x,y) , 则(x,y)属于类别1
* 当p(c1|x,y) < p(c2|x,y) , 则(x,y)属于类别2
对于一个实例我们需要关心的可能是多个维度的特征值以及特征值在实例分类中的概率分布,因此我们将上面的p(c1|x, y)替换为 p(c1|w),其中w为一个多维的特征向量。上面的公式就转换为 p(c1|w) = p(w|c1)p(c1)/p(w) 和 p(c2|w) = p(w|c2)p(c2)/p(w)。
单独看p(w)在朴素贝叶斯理论中我们做的一个假设是,p(w1,w2,w3….wn)的每个特征向量不存在相关性,p(w1,w2,w3….wn)退化为计算p(w1)p(w2)p(3)…p(wn)。
下面通过一个垃圾邮件分类算法具体介绍如何使用朴素贝叶斯概率分布实现机器学习和垃圾邮件分类:
* 首先我们需要从训练文本中提取全量特征值,具体做法是将文本进行切分成单个词条
* 其次我们需要将每条训练数据的特征值转换为一个特征向量
* 通过计算训练数据的特征向量以及对应的分类数据获取整个分类在全量特征值中的概率分布
* 通过需要计算分类的条目的特征向量,计算出条目对应的每个分类的概率,选取概率大的分类作为目标分类

垃圾邮件过滤算法

训练集合:
[
[‘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’]
]
训练集合对应的分类(0表示正常评论,1表示侮辱评论):
[0, 1, 0, 1, 0, 1]

获取所有特征属性

代码实现:

#-*- coding=utf-8 -*-
form numpy import *
## 这里的dataSet每个item对应测试数据中的词条(单词)
def getVecList(dataSet):
    vecList = set([])
    for line in dataSet:
        vacList = vacList | set(line)

    return list(vecList)

##最终获得的所有特征属性
## set(['cute', 'love', 'help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea', 'dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has', 'worthless', 'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid', 'so', 'take', 'mr', 'steak', 'my'])

计算数据的特征向量

特征向量表示的每个训练item的一个特征分布
代码实现:

#-*- coding=utf-8 -*-
from numpy import *

def getVec(dataItem, vecList):
    #初始化特征向量,0 表示特征值不存在 1表示特征值存在
    vecResult = [0] * len(vecList)
    for feature in dataItem:
        if feature in vecList:
            ##特征值存在获取向量id
            vecResult[vecList.index(feature)] = 1

    return vecResult

#result:
#[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1]
#[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0]
#[1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1]
#[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
#[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1]
#[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0]

分类训练算法

输入为上面方法得到的特征向量和每个特征向量对应的分类
代码实现:

#trainMatrix 特征向量举证
#trainCategory 特征分类
def trainNB0(trainMatrix, trainCategory):
        numTrainDocs = len(trainMatrix)
        numWords = len(trainMatrix[0])
        pAbusive = sum(trainCategory)/float(numTrainDocs)
        #p0Num = zeros(numWords)
        #p1Num = zeros(numWords)
        #p0Denom = 0.0
        #p1Denom = 0.0
        #如果按照上面的计算方法会得出特别多的0,这个时候在去算p0*p1..pn时就会出现乘0的情况
        #因此为了解决这个情况,采用下面的初始化方式
        #这种方式最终对于概率计算没有影响,因为大家的基数都是一样的
        p0Num = ones(numWords)
        p1Num = 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 = p1Num/p1Denom
        #p0Vect = p0Num/p0Denom
        #这里有个问题由于上面这种方式计算出来的概率都是小数位,因此采用这些小概率做p0*p1*....pn就会造成最终得到的概率四舍五入之后=0
        #为了解决这个问题采用对数的方式将所有的结果概率等条件的放大
        #对于log(p1Num/p1Denom) 与 p1Num/p1Denom 具有等效性 
        p1Vect = log(p1Num/p1Denom)
        p0Vect = log(p0Num/p0Denom)
        return p0Vect, p1Vect, pAbusive

 #Result :
 #p0Vect :
 #[-2.56494936, -2.56494936, -2.56494936, -3.25809654, -3.25809654,
       -2.56494936, -2.56494936, -2.56494936, -3.25809654, -2.56494936,
       -2.56494936, -2.56494936, -2.56494936, -3.25809654, -3.25809654,
       -2.15948425, -3.25809654, -3.25809654, -2.56494936, -3.25809654,
       -2.56494936, -2.56494936, -3.25809654, -2.56494936, -2.56494936,
       -2.56494936, -3.25809654, -2.56494936, -3.25809654, -2.56494936,
       -2.56494936, -1.87180218]
 #p1Vect :
 #[-3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526,
       -3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526,
       -3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526,
       -2.35137526, -2.35137526, -2.35137526, -3.04452244, -1.94591015,
       -3.04452244, -2.35137526, -2.35137526, -3.04452244, -1.94591015,
       -3.04452244, -1.65822808, -3.04452244, -2.35137526, -3.04452244,
       -3.04452244, -3.04452244]
 #pAbusive:
 #0.5

至此我们就得到了通过所有训练样本计算出来的样本特征值的概率分布信息,这个概率分布信息可以用于统计分类执行特征的分类。
例如具体使用Code:

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
            #这里使用的是ln(p(w|ci) * p(ci)) = ln(p(w|ci)) + ln(p(ci))
        #这里的算法公式是 p(ci|w) = p(w|ci) * p(ci)/p(w)
        p1 = sum(vec2Classify*p1Vec) + log(pClass1)
        p0 = sum(vec2Classify*p0Vec) + log(1-pClass1)
        if p1 > p0 :
            return 1
        else:
            return 0

分类算法测试

#对于贝叶斯分类的测试代码
def testingNB():
        listOPosts,listPostClasses = loadDataSet()
        vocabList = createVocabList(listOPosts)
        trainMatrix = []
        for line in listOPosts:
            trainMatrix.append(setOfWords2Vec(vocabList, line))
        p0V,p1V,pAb = trainNB0(trainMatrix, listPostClasses)
        testEntity = ['love', 'my', 'dalmation']
        thisDoc = setOfWords2Vec(vocabList, testEntity)
        print testEntry, "classified as : ", classifyNB(thisDoc,p0V,p1V,pAb)

关于频率的问题

其实在实际使用分类算法时某一个词条出现的次数是有实际统计意义的,在上面的训练算法中并没有考虑频率对概率分布的作用参数。我们可以修改特征向量的生成方式让频率作为一个实际参数进入到我们的分类算法中。

def getVec(dataItem, vecList):
    #初始化特征向量,0 表示特征值不存在 1表示特征值存在
    vecResult = [0] * len(vecList)
    for feature in dataItem:
        if feature in vecList:
            ##如果特征值重复出现,这里对特征向量值做叠加
            vecResult[vecList.index(feature)] += 1

    return vecResult

贝叶斯总结

对于分类而言,使用贝叶斯概率统计方法进行分类比使用硬规则(例如决策树)更为有效,贝叶斯概率准则提供了一种利用已知值来估计未知概率的有效方法。正如我们所做的朴素贝叶斯通过假设各个特征值之间的独立性来降低贝叶斯概率计算的复杂性。虽然独立性的假设并非正确,但是朴素贝叶斯仍是一种有效的分类器。

以上是关于机器学习03-贝叶斯算法的主要内容,如果未能解决你的问题,请参考以下文章

机器学习03-贝叶斯算法

机器学习——朴素贝叶斯算法

机器学习九大算法---朴素贝叶斯分类器

机器学习--贝叶斯分类算法及应用

机器学习回顾篇:朴素贝叶斯算法

机器学习——贝叶斯算法