机器学习算法-朴素贝叶斯(附源码)

前言

贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。
分类问题综述:
对于分类问题,其实谁都不会陌生,我们每个人每天都在执行分类操作,只是我们没有意识到罢了。例如,当你看到一个陌生人,你的脑子下意识判断TA是男是女。其实这就是一种分类操作。
从数学角度来说,分类问题可做如下定义:已知集合:∁={y_(1,) ┤ ├ y_(2, ) y_(3,…., ) y_n }和      

I={x_(1,) ┤├ x_(2, ) x_(3,…., ) x_(m,…) },确定映射规则y=f(x),使得任意x_i∈I,有且仅有一个y_j∈C

使得y_j=f(x_i)成立。

 其中C叫做类别集合,其中每一个元素是一个类别,而I叫做项集合,其中每一个元素是一个待分类项,f叫做分类器。分类算法的任务就是构造分类器f 。


贝叶斯定理


要理解贝叶斯推断,必须先理解贝叶斯定理。后者实际上就是计算"条件概率"的公式。
所谓"条件概率"(Conditionalprobability),就是指在事件B发生的情况下,事件A发生的概率,用P(A|B)来表示。 
机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

根据文氏图,可以很清楚地看到在事件B发生的情况下,事件A发生的概率就是P(A∩B)

     除以P(B),即P(A│B)=P(A∩B)/P(B) 。

因此:P(A∩B)=P(A│B)P(B),同理可得:P(A∩B)=P(B│A)P(A),所以:

     P(A│B)P(B)=P(B│A)P(A),即:P(A│B)=(P(B│A)P(A))/P(B) ,这就是条件概率的计算公式。 

全概率公式:假定样本空间S,是两个事件A与A'的和
机器学习算法-朴素贝叶斯(附源码)机器学习算法-朴素贝叶斯(附源码)
机器学习算法-朴素贝叶斯(附源码)      机器学习算法-朴素贝叶斯(附源码)
图中,红色部分是事件A,绿色部分是事件A‘它们共同构成了样本空间S,这种情况,事件B可以划分成两个部分。
即:P(B)=P(B∩A)+P(B∩A′),已知:P(B∩A)=P(B│A)P(A),所以:P(B)=P(B│A)P(A)+P(B│A’)P(A′)
这就是全概率公式。它的含义是:如果A和A'构成样本空间的一个划分,那么事件B的概率,就等于A和A'的概率分

      别乘以B对这两个事件的条件概率之和。

      将这个公式代入条件概率公式,就得到了条件概率的另一种写法:P(A│B)=(P(B│A)P(A))/(P(B│A)P(A)"+" P(B│A’)P(A′) )

对条件概率公式进行变形,可以得到如下形式:

    P(A│B)=P(A)  (P(B│A))/P(B)

把P(A)称为"先验概率"即在B事件发生之前,我们对A事件概率的一个判断。

      P(A|B)称为“后验概率”即在B事件发生之后,对A事件概率的重新评估。P(B|A)/P(B)称为“可能性函数”这是一

      个调整因子,使得预估概率更接近真实概率。

所以,条件概率可以理解成下面的式子:后验概率 = 先验概率 x 调整因子
这就是贝叶斯推断的含义。我们先预估一个"先验概率",然后加入实验结果,看这个实验到底是增强

      还是削弱了"先验概率",由此得到更接近事实的"后验概率"。

在这里,如果"可能性函数"P(B|A)/P(B)>1,意味着"先验概率"被增强,事件A的发生的可能性变大;如果"可能性函数"=1,意味着B事件

无助于判断事件A的可能性;如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。

贝叶斯决策论

贝叶斯决策论是概率框架下实施决策的基本方法。对分类任务来说,在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。
假设有N种可能的类别标记,即:Y={c_(1,) ┤├ c_(2, ) c_(3,….,) c_n },λ_ij是将一个真实标记为c_j的样本误分类为c_i所产生的损失。λ_ij={█(0,  [email protected],  i≠ j)┤,基于后验概率P(c_i│x)可获得将样本x分类为c_i所产生的期望损失,即在样本x上的“条件风险”

                                         R(c_i│x)=∑_(j=1)^N▒λ_ij  P(c_j│x)

我们目的是寻找一个判定准则h:X→y以最小化总体风险

                                         R(h)=E_X [R(h(x)│x)]

显然,对每个样本x ,若h能最小化风险R(h(x)│x),则总体风险R(h)也将被最小化。这就产生贝叶斯判定准则:为最小化总体风险,只需在每个样本上选择那个能使条件风险R(c│x)最小的类别标记,即:

                                        h^∗ (x)=(arg min)┬(c∈y)⁡〖R(c│x)〗

此时,h^∗称为贝叶斯最优分类器,与之对应的总体风险R(h^∗ )称为贝叶斯风险。1-R(h^∗ )反映了分类器所能达到的最好性能。
此时条件风险:R(c│x)=1-P(c│x),于是,最小化分类错误率的贝叶斯最优分类器为:h^∗ (x)=(arg max)┬(c∈y)⁡〖P(c│x)〗,即对每个样本x,选择能使后验概率P(c│x)最大的类别标记。
贝叶斯决策论——举例

假设:类别标记Y={c_(1,) ┤ ├ c_(2, ) c_(3, )c_(4,) c_5 },

             后验概率P(c_1│x)=P(c_2│x)=P(c_4│x)=P(c_5│x)=0.15,P(c_3│x)=0.4

   R(c_i│x)=∑_(j=1)^N▒λ_ij  P(c_j│x)             λ_ij={█(0,  [email protected],  i≠ j)┤

   i=1:R(c_1│x)=λ_11 P(c_1│x)+λ_12 P(c_2│x)+λ_13 P(c_3│x)+λ_14 P(c_4│x)+λ_15 P(c_5│x)=0.85

   i=2:R(c_2│x)=λ_21 P(c_1│x)+λ_22 P(c_2│x)+λ_23 P(c_3│x)+λ_24 P(c_4│x)+λ_25 P(c_5│x)=0.85

   i=3:R(c_3│x)=λ_31 P(c_1│x)+λ_32 P(c_2│x)+λ_33 P(c_3│x)+λ_34 P(c_4│x)+λ_35 P(c_5│x)=0.6

   i=4:R(c_4│x)=λ_41 P(c_1│x)+λ_42 P(c_2│x)+λ_43 P(c_3│x)+λ_44 P(c_4│x)+λ_45 P(c_5│x)=0.85

   i=5:R(c_5│x)=λ_51 P(c_1│x)+λ_52 P(c_2│x)+λ_53 P(c_3│x)+λ_54 P(c_4│x)+λ_55 P(c_5│x)=0.85

R(c_3│x)=1-P(c_3│x),条件风险最小,故:欲使用贝叶斯准则来最小化决策风险,首先要获得后验概率,使其最大化

                                                                               h^∗ (x)=(arg max)┬(c∈y)⁡〖P(c│x)〗

朴素贝叶斯分类器

基于贝叶斯公式来估计后验概率P(c│x),类条件概率〖P(x│c)〗_ (注:P(x│c)是样本x相对于类标记c的类条件概率,或称为“似然”)是所有属性上的联合概率,难以从有限的训练样本直接估计而得。为避开这个障碍,朴素贝叶斯分类器采用了“属性条件独立性假设”:对已知类别,假设所有属性相互独立。
基于属性条件独立性假设,贝叶斯公式可写为:P(c│x)=(P(c)P(x│c))/P(x) =P(c)/P(x) ∏_(i=1)^d▒〖P(x_i│c)〗    

      (其中d为属性数目,x_ix在第i个属性上的取值)

由于对所有类别来说P(x)相同,因此基于贝叶斯准则有

                          〖       h〗_nb (x)=arg  max┬(c∈y)⁡〖P(c) ∏_(i=1)^d▒〖P(x_i│c)〗〗

这就是朴素贝叶斯分类器的表达式。

朴素贝叶斯分类器——训练过程

朴素贝叶斯分类器的训练过程就是基于训练集D来估计类先验概率P(c),并为每个属性估计条件概率:P(x_i│c)
令D_c表示训练集D中第c类样本组成的集合,若有充足的独立同分布样本,则可以容易地估计出类先验概率:P(c)=|D_c |/|D|
对离散属性而言:令D_c,x_i表示D_c中第i个属性上取值为x_i的样本组成的集合,则条件概率P(x_i│c)可估计为:P(x_i│c)=|D_c,x_i |/|D_c |
对连续属性可考虑概率密度函数:假定P(x_i│c)~Ν(μ_(c,i),σ_(c,i)^2 ),其中μ_(c,i)和σ_(c,i)^2分别是第c类样本在第i个属性上取值的均值和方差,则有:

                       P(x_i│c)=1/(σ_(c,i) √2π) e^((-(x_i-μ_(c,i) )^2/(2σ_(c,i)^2)) )

朴素贝叶斯分类器——算法流程

机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

朴素贝叶斯分类器——举例

采用西瓜数据集训练一个朴素贝叶斯分类器,对测试例1”进行分类:

机器学习算法-朴素贝叶斯(附源码)

测试用例:

机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

拉普拉斯平滑

为避免其他属性携带的信息被训练集中未出现的属性值“抹去”,在估计概率值时通常要进行“平滑”,常使用“拉普拉斯修修正”
具体来说,令N表示训练集D中可能的类别数,N_i表示第i个属性可能的取值数,修改公式为:

                                     P(c)=|D_c |/|D|                                P(x_i│c)=|D_c,x_i |/|D_c |

例中,类先验概率可估计为

机器学习算法-朴素贝叶斯(附源码)

机器学习算法-朴素贝叶斯(附源码)

类似地,P青绿|是和P青绿|否可估计为:

机器学习算法-朴素贝叶斯(附源码)


机器学习算法-朴素贝叶斯(附源码)

同时,概率P_(清脆|是) 可估计为:P_(清脆|是)=P(敲声=清脆|好瓜=是)=(0+1)/(8+3)≈0.091

实验数据

数据集:皮马印第安人糖尿病问题
数据集描述:这个问题包括768个对于皮马印第安患者的医疗观测细节,记录所描述的瞬时测量取自诸如患者的年纪,怀孕和血液检查的次数。所有患者都是21岁以上(含21岁)的女性,所有属性都是数值型,而且属性的单位各不相同。
属性:
1.怀孕次数。
2.2小时口服葡萄糖耐量测试中得到的血糖浓度。
3.舒张期血压(mmHg)。
4.三头肌皮脂厚度(mm)。
5.2小时血清胰岛素(muU/ml)。
6.身体质量指数(体重kg/(身高inm)^2)。
7.糖尿病家系作用。
8.年龄。
code:

import csv
import random
import math


def loadCsv(filename):
    lines = csv.reader(open(filename, "rb"))
    dataset = list(lines)
    for i in range(len(dataset)):
        dataset[i] = [float(x) for x in dataset[i]]
    return dataset


def splitDataset(dataset, splitRatio):
    trainSize = int(len(dataset) * splitRatio)
    trainSet = []
    copy = list(dataset)
    while len(trainSet) < trainSize:
        index = random.randrange(len(copy))
        trainSet.append(copy.pop(index))
    return [trainSet, copy]


def separateByClass(dataset):
    separated = {}
    for i in range(len(dataset)):
        vector = dataset[i]
        if (vector[-1] not in separated):
            separated[vector[-1]] = []
        separated[vector[-1]].append(vector)
    return separated


def mean(numbers):
    return sum(numbers) / float(len(numbers))


def stdev(numbers):
    avg = mean(numbers)
    variance = sum([pow(x - avg, 2) for x in numbers]) / float(len(numbers) - 1)
    return math.sqrt(variance)


def summarize(dataset):
    summaries = [(mean(attribute), stdev(attribute)) for attribute in zip(*dataset)]
    del summaries[-1]
    return summaries


def summarizeByClass(dataset):
    separated = separateByClass(dataset)
    summaries = {}
    for classValue, instances in separated.iteritems():
        summaries[classValue] = summarize(instances)
    return summaries


def calculateProbability(x, mean, stdev):
    exponent = math.exp(-(math.pow(x - mean, 2) / (2 * math.pow(stdev, 2))))
    return (1 / (math.sqrt(2 * math.pi) * stdev)) * exponent


def calculateClassProbabilities(summaries, inputVector):
    probabilities = {}
    for classValue, classSummaries in summaries.iteritems():
        probabilities[classValue] = 1
        for i in range(len(classSummaries)):
            mean, stdev = classSummaries[i]
            x = inputVector[i]
            probabilities[classValue] *= calculateProbability(x, mean, stdev)
    return probabilities


def predict(summaries, inputVector):
    probabilities = calculateClassProbabilities(summaries, inputVector)
    bestLabel, bestProb = None, -1
    for classValue, probability in probabilities.iteritems():
        if bestLabel is None or probability > bestProb:
            bestProb = probability
            bestLabel = classValue
    return bestLabel


def getPredictions(summaries, testSet):
    predictions = []
    for i in range(len(testSet)):
        result = predict(summaries, testSet[i])
        predictions.append(result)
    return predictions


def getAccuracy(testSet, predictions):
    correct = 0
    for i in range(len(testSet)):
        if testSet[i][-1] == predictions[i]:
            correct += 1
    return (correct / float(len(testSet))) * 100.0


def main():
    filename = 'sj.csv'
    splitRatio = 0.67
    dataset = loadCsv(filename)
    trainingSet, testSet = splitDataset(dataset, splitRatio)
    print('Split {0} rows into train={1} and test={2} rows').format(len(dataset), len(trainingSet), len(testSet))
    # prepare model
    summaries = summarizeByClass(trainingSet)
    # test model
    predictions = getPredictions(summaries, testSet)
    accuracy = getAccuracy(testSet, predictions)
    print('Accuracy: {0}%').format(accuracy)


main()
算法改进

朴素贝叶斯分类有一个限制条件,就是特征属性必须有条件独立或基本独立(实际上在现实应用中几乎不可能做到完全独立)。当这个条件成立时,朴素贝叶斯分类法的准确率是最高的,但不幸的是,现实中各个特征属性间往往并不条件独立,而是具有较强的相关性,这样就限制了朴素贝叶斯分类的能力。贝叶斯分类中更高级、应用范围更广的一种算法——贝叶斯网络(又称贝叶斯信念网络或信念网络)
不同的密度函数(伯努利或者多项式):使用了高斯朴素贝叶斯,可以尝试下其他分布。实现一个不同的分布诸如多项分布、伯努利分布或者内核朴素贝叶斯,他们对于属性值的分布和/或与类值之间的关系有不同的假设。