贝叶斯分类器

来源:互联网 发布:怎么编程app 编辑:程序博客网 时间:2024/06/03 18:27

本部分内容来源《数据挖掘导论》4.4节

1、朴素贝叶斯分类器原理

贝叶斯分类器是基于贝叶斯定理构建出来的分类器,是一个统计分类器。对分类方法进行比较的有关研究表明,简单贝叶斯分类器在分类性能上与决策树和神经网络都是可比拟的。在处理大规模数据时,贝叶斯分类器已经表现出较高的准确性和运算性能。
简单贝叶斯分类器的假设是:指定类别中,样本各特征之间相互独立,即某一特征不同取值的概率与其他特征的取值没有任何关系。
假定:每个数据样本均是由一个n维特征向量Xx1,x2,,xn来表示与属性(A1,A2,...,An)对应的值,所有的样本共有m个类别,C1,C2,...,Cm。对于任一个样本,我们要预测其属于哪一类样本。
我们的目标是计算P(Ci/X),式中Ci表示某类样本,P(Ci/X)表示已知样本X的基础上样本为Ci假设发生的概率,比如X={“红色”,”圆形”},Ci:这是一个苹果,P(Ci/X)则表示在已知某物品是红色和圆形的前提下这是一个苹果的概率;在分类问题中,将P(Ci/X)最大值对应的Ci作为样本X的最终类别。
如何求取max(P(Ci/X))
根据贝叶斯定理,P(Ci/X)P(X/Ci)P(Ci)/P(X),这也是此类分类器被称为贝叶斯分类器的原因。
上式中,对于给定样本X,P(X)是定值,故max(P(Ci/X))等价于求max(P(X/Ci)P(Ci)),对于给定训练样本集,P(Ci)可利用统计方法求得,而P(X/Ci)可在类别Ci中求X的概率,但是在样本量较大、特征数目较多的时候,该方法求X会带来维数灾难,而且也不利于将这种算法拓展到分布式实现。这里通过引入贝叶斯假设:在同一类别中,不同特征之间相互独立。该假设的引入简化了计算P(X/Ci)P(x1/Ci)P(x2/Ci)P(xn/Ci)。即一个样本在某一类样本出现的概率就变成了计算该样本各特征的值在该类样本中出现概率。

2、计算步骤

2.1、计算类别Ci的先验概率

P(Ci)=sis
式中si表示类别为Ci的样本数目,s表示样本总体数目。

2.2、计算类别Ci中属性Ak取值为vj的概率

一、若属性Ak是符号变量,则:
P(xk/Ci)=siksi
式中sik表示类别Ci的样本中属性Ak取值为vj的样本数目,si表示类别Ci的样本数。
二、若属性Ak是连续变量,在假设属性具有高斯分布的情况下,有:
P(xk/Ci)=g(xk,μCi,ρCi)=12πρCie(xμCi)22ρ2Ci
式中μCiρCi分别表示类别Ci的样本中属性Ak的均值和方差。

2.3、计算P(X/Ci)

P(X/Ci)=kk=1P(xk/Ci)
对每一类样本都计算P(X/Ci)

2.4、确定样本类别

样本X归属类别CI当且仅当:
P(X/CI)>P(X/Cj)
式中, 1Im,1jm
贝叶斯算法流程图

从理论上讲与其它分类器相比,贝叶斯分类器具有最小的错误率。但实际上由于其所依据的类别独立性假设和缺乏某些数据的准确概率分布,从而使得贝叶斯分类器预测准确率受到影响。

3、算法实现

算法来源网络,没找到具体链接,可以参考该文。
数据存在本地,文件名为”pima-indians-diabetes.data.csv”。
数据大致如下,共768行9列,最后一列为标签列:

6   148 72  35  0   33.6    0.627   50  11   85  66  29  0   26.6    0.351   31  08   183 64  0   0   23.3    0.672   32  11   89  66  23  94  28.1    0.167   21  00   137 40  35  168 43.1    2.288   33  1

程序代码如下:

# -*- coding: UTF-8 -*-import csvimport randomimport mathimport numpy as np'''1.处理数据首先加载数据文件。CSV格式的数据没有标题行和任何引号。我们可以使用csv模块中的open函数打开文件,使用reader函数读取行数据。我们也需要将以字符串类型加载进来属性转换为我们可以使用的数字。下面是用来加载匹马印第安人数据集(Pima indians dataset)的loadCsv()函数。''''''1.处理数据首先加载数据文件。CSV格式的数据没有标题行和任何引号。我们可以使用csv模块中的open函数打开文件,使用reader函数读取行数据。我们也需要将以字符串类型加载进来属性转换为我们可以使用的数字。下面是用来加载匹马印第安人数据集(Pima indians dataset)的loadCsv()函数。'''def loadCsv(filename):    lines = csv.reader(open(filename, encoding='iso-8859-1'))    dataset = list(lines)    for i in range(len(dataset)):        dataset[i] = [float(x) for x in dataset[i]]#将字符串类型转换成浮点型数据    return dataset##我们可以通过加载皮马印第安人数据集,然后打印出数据样本的个数,以此测试这个函数。#filename = './pima-indians-diabetes.data.csv'#dataset = loadCsv(filename)#print(('Loaded data file {0} with {1} rows').format(filename, len(dataset)))#打印结果如下#'Loaded data file ./pima-indians-diabetes.data.csv with 768 rows''''下一步,我们将数据分为用于朴素贝叶斯预测的训练数据集,以及用来评估模型精度的测试数据集。我们需要将数据集随机分为包含67%的训练集合和包含33%的测试集(这是在此数据集上测试算法的通常比率)。下面是splitDataset()函数,它以给定的划分比例将数据集进行划分。dataset为数据集,splitRatio为分裂的比例即训练集合在总数据中的比例'''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]##我们可以定义一个具有5个样例的数据集来进行测试,首先它分为训练数据集和测试数据集,然后打印出来,看看每个数据样本最终落在哪个数据集。#dataset = [[1], [2], [3], [4], [5]]#splitRatio = 0.67#train, test = splitDataset(dataset, splitRatio)#print(('Split {0} rows into train with {1} and test with {2}').format(len(dataset), train, test))#打印结果如下:#'Split 5 rows into train with [[5], [2], [3]] and test with [[1], [4]]''''提取数据特征朴素贝叶斯模型包含训练数据集中数据的特征,然后使用这个数据特征来做预测。所收集的训练数据的特征,包含相对于每个类的每个属性的均值和标准差。举例来说,如果如果有2个类和7个数值属性,然后我们需要每一个属性(7)和类(2)的组合的均值和标准差,也就是14个属性特征。也就是求取特征的概率分布在对特定的属性归属于每个类的概率做计算、预测时,将用到这些特征。我们将数据特征的获取划分为以下的子任务:1    按类别划分数据2    计算均值3    计算标准差4    提取数据集特征5    按类别提取属性特征''''''按类别划分数据首先将训练数据集中的样本按照类别进行划分,然后计算出每个类的统计数据。我们可以创建一个类别到属于此类别的样本列表的的映射,并将整个数据集中的样本分类到相应的列表。下面的SeparateByClass()函数可以完成这个任务:'''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##可以看出,函数假设样本中最后一个属性(-1)为类别值,返回一个类别值到数据样本列表的映射。##我们可以用一些样本数据测试如下:#dataset = [[1,20,1], [2,21,0], [3,22,1]]#separated = separateByClass(dataset)#得到一字典,以类别标签为key,以样本为元素组成的列表作为值#print(('Separated instances: {0}').format(separated))#测试结果如下:#Separated instances: {1: [[1, 20, 1], [3, 22, 1]], 0: [[2, 21, 0]]}'''计算均值、方差我们需要计算在每个类中每个属性的均值。均值是数据的中点或者集中趋势,在计算概率时,我们用它作为高斯分布的中值。我们也需要计算每个类中每个属性的标准差。标准差描述了数据散布的偏差,在计算概率时,我们用它来刻画高斯分布中,每个属性所期望的散布。标准差是方差的平方根。方差是每个属性值与均值的离差平方的平均数。注意我们使用N-1的方法(译者注:参见无偏估计),也就是在在计算方差时,属性值的个数减1。'''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)##通过计算从1到5这5个数的均值来测试函数。#numbers = [1,2,3,4,5]#print(('Summary of {0}: mean={1}, stdev={2}').format(numbers, mean(numbers), stdev(numbers)))#测试结果如下:#Summary of [1, 2, 3, 4, 5]: mean=3.0, stdev=1.5811388300841898#其实没有必要自己来定义这些基础的函数,虽然math里没有但numpy里就有这些函数'''提取数据集的特征现在我们可以提取数据集特征。对于一个给定的样本列表(对应于某个类),我们可以计算每个属性的均值和标准差。zip函数将数据样本按照属性分组为一个个列表,然后可以对每个属性计算均值和标准差。'''def summarize(dataset):#几个属性就得到几个均值、方差对,形式为[(2.0, 1.0), ... ,(21.0, 1.0)]    summaries = [(mean(attribute), stdev(attribute)) for attribute in zip(*dataset)]    del summaries[-1]    return summaries##我们可以使用一些测试数据来测试这个summarize()函数,测试数据对于第一个和第二个数据属性的均值和标准差显示出显著的不同。#dataset = [[1,20,0], [2,21,1], [3,22,0]]#summary = summarize(dataset)#print(('Attribute summaries: {0}').format(summary))#测试结果如下:#Attribute summaries: [(2.0, 1.0), (21.0, 1.0)]'''按类别提取属性特征合并代码,我们首先将训练数据集按照类别进行划分,然后计算每个属性的摘要。'''def summarizeByClass(dataset):#不同类别及各属性的均值方差,形式为{1: [(2.0, 1.4142135623730951), ... ,(21.0, 1.4142135623730951)], 0: [(3.0, 1.4142135623730951), ... ,(21.5, 0.7071067811865476)]}    separated = separateByClass(dataset)    summaries = {}    for classValue, instances in separated.items():        summaries[classValue] = summarize(instances)    return summaries##使用小的测试数据集来测试summarizeByClass()函数。#dataset = [[1,20,1], [2,21,0], [3,22,1], [4,22,0]]#summary = summarizeByClass(dataset)#print(('Summary by class value: {0}').format(summary))#测试结果如下:#Summary by class value: {1: [(2.0, 1.4142135623730951), (21.0, 1.4142135623730951)], 0: [(3.0, 1.4142135623730951), (21.5, 0.7071067811865476)]}'''预测我们现在可以使用从训练数据中得到的摘要来做预测。做预测涉及到对于给定的数据样本,计算其归属于每个类的概率,然后选择具有最大概率的类作为预测结果。我们可以将这部分划分成以下任务:1    计算高斯概率密度函数2    计算对应类的概率3    单一预测4    评估精度''''''计算高斯概率密度函数给定来自训练数据中已知属性的均值和标准差,我们可以使用高斯函数来评估一个给定的属性值的概率。已知每个属性和类值的属性特征,在给定类值的条件下,可以得到给定属性值的条件概率。关于高斯概率密度函数,可以查看参考文献。总之,我们要把已知的细节融入到高斯函数(属性值,均值,标准差),并得到属性值归属于某个类的似然(译者注:即可能性)。在calculateProbability()函数中,我们首先计算指数部分,然后计算等式的主干。这样可以将其很好地组织成2行。'''import mathdef 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##使用一些简单的数据测试如下:#x = 71.5#mean = 73#stdev = 6.2#probability = calculateProbability(x, mean, stdev)#print(('Probability of belonging to this class: {0}').format(probability))#测试结果如下:#Probability of belonging to this class: 0.06248965759370005'''计算所属类的概率既然我们可以计算一个属性属于某个类的概率,那么合并一个数据样本中所有属性的概率,最后便得到整个数据样本属于某个类的概率。使用乘法合并概率,在下面的calculClassProbilities()函数中,给定一个数据样本,它所属每个类别的概率,可以通过将其属性概率相乘得到。结果是一个类值到概率的映射。'''def calculateClassProbabilities(summaries, inputVector):    probabilities = {}    for classValue, classSummaries in summaries.items():#逐个类别内部计算某样本属于某一类的概率        probabilities[classValue] = 1#初始化样本属于某类的概率为1        for i in range(len(classSummaries)):#逐个属性计算某样本属于某一类的概率            mean, stdev = classSummaries[i]            x = inputVector[i]            probabilities[classValue] *= calculateProbability(x, mean, stdev)#连乘,会直接改变probabilities[classValue]的值    return probabilities##测试calculateClassProbabilities()函数。#summaries = {0:[(1, 0.5)], 1:[(20, 5.0)]}#inputVector = [1.1, '?']#probabilities = calculateClassProbabilities(summaries, inputVector)#print(('Probabilities for each class: {0}').format(probabilities))#测试结果如下:#Probabilities for each class: {0: 0.7820853879509118, 1: 6.298736258150442e-05}'''单一预测既然可以计算一个数据样本属于每个类的概率,那么我们可以找到最大的概率值,并返回关联的类。下面的predict()函数可以完成以上任务。'''def predict(summaries, inputVector):    probabilities = calculateClassProbabilities(summaries, inputVector)    bestLabel, bestProb = None, -1    for classValue, probability in probabilities.items():        if bestLabel is None or probability > bestProb:            bestProb = probability            bestLabel = classValue    return bestLabel##测试predict()函数如下:#summaries = {'A':[(1, 0.5)], 'B':[(20, 5.0)]}#inputVector = [1.1, '?']#result = predict(summaries, inputVector)#print(('Prediction: {0}').format(result))#测试结果如下:#Prediction: A'''多重预测最后,通过对测试数据集中每个数据样本的预测,我们可以评估模型精度。getPredictions()函数可以实现这个功能,并返回每个测试样本的预测列表。'''def getPredictions(summaries, testSet):    predictions = []    for i in range(len(testSet)):        result = predict(summaries, testSet[i])        predictions.append(result)    return predictions##测试getPredictions()函数如下。#summaries = {'A':[(1, 0.5)], 'B':[(20, 5.0)]}#testSet = [[1.1, '?'], [19.1, '?']]#predictions = getPredictions(summaries, testSet)#print(('Predictions: {0}').format(predictions))##测试结果如下:##Predictions: ['A', 'B']'''计算精度预测值和测试数据集中的类别值进行比较,可以计算得到一个介于0%~100%精确率作为分类的精确度。getAccuracy()函数可以计算出这个精确率。'''def getAccuracy(testSet, predictions):    correct = 0    for x in range(len(testSet)):        if testSet[x][-1] == predictions[x]:            correct += 1    return (correct/float(len(testSet))) * 100.0##我们可以使用如下简单的代码来测试getAccuracy()函数。#testSet = [[1,1,1,'a'], [2,2,2,'a'], [3,3,3,'b']]#predictions = ['a', 'a', 'a']#accuracy = getAccuracy(testSet, predictions)#print(('Accuracy: {0}').format(accuracy))#测试结果如下:#Accuracy: 66.66666666666666#以上就完成了相应的功能模块,下面用这些模块来完成一个预测def main():    filename = './pima-indians-diabetes.data.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()#组合后测试结果如下:#Split 768 rows into train=514 and test=254 rows#Accuracy: 75.59055118110236%
原创粉丝点击