机器学习系列七:朴素贝叶斯

一、算法原理

贝叶斯公式描述了两个相关的随机事件或随机变量之间的概率关系。贝叶斯分类器使用贝叶斯公式计算样本属于某一类的条件概率值,并将样本判定为概率值最大的那个类。

贝叶斯公式为:

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

P(A|B)称为"后验概率"(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。

P(B|A)/P(B)称为"可能性函数"(Likelyhood),这是一个调整因子,使得预估概率更接近真实概率。

这一结论可以推广到随机变量。分类问题中样本的特征向量取值x与样本属性y具有因果关系。因为样本属于y,所以具有特征值x。贝叶斯分类器是在已知样本的特征向量为x的条件下反推样本所属的类别。根据贝叶斯公式有:

p(y|x) = \frac{p(x|y)p(y))}{p(x)}

只要知道特征向量的概率分布p(x),每一个类出现的概率p(y),以及每一个类样本的条件概率p(y|x),就可以计算出样本属于每一个类的概率p(y|x)。分类问题只要预测类别,比较样本属于每一个类的概率的大小,找出该值最大的那一个类即可,因此可以忽略p(x),因为它对所有类都是相同的。简化后分类器的判别函数为

   arg max _{y} p(x|y)p(y)

贝叶斯和朴素贝叶斯的概念是不同的,区别就在于“朴素”二字,朴素贝叶斯对条件个概率分布做了条件独立性的假设。朴素贝叶斯分类器假设特征向量的分量之间相互独立。

二、算法实践

1.离散型特征

以在线社区留言为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或者侮辱性的语言,那么就将该留言标志为内容不当。过滤这类内容是一个很常见的需求。对此问题建立两个类型:侮辱类和非侮辱类,使用1和0分别表示。(来自《机器学习实战》转载自http://blog.csdn.net/c406495762 )

# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce

"""
函数说明:创建实验样本

Parameters:
	无
Returns:
	postingList - 实验样本切分的词条
	classVec - 类别标签向量
Author:
	Jack Cui
Blog:
	http://blog.csdn.net/c406495762
Modify:
	2017-08-11
"""
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																#返回实验样本切分的词条和类别标签向量

"""
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表

Parameters:
	dataSet - 整理的样本数据集
Returns:
	vocabSet - 返回不重复的词条列表,也就是词汇表
Author:
	Jack Cui
Blog:
	http://blog.csdn.net/c406495762
Modify:
	2017-08-11
"""
def createVocabList(dataSet):
	vocabSet = set([])  					#创建一个空的不重复列表
	for document in dataSet:				
		vocabSet = vocabSet | set(document) #取并集
	return list(vocabSet)

"""
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:
	vocabList - createVocabList返回的列表
	inputSet - 切分的词条列表
Returns:
	returnVec - 文档向量,词集模型
Author:
	Jack Cui
Blog:
	http://blog.csdn.net/c406495762
Modify:
	2017-08-11
"""
def setOfWords2Vec(vocabList, inputSet):
	returnVec = [0] * len(vocabList)			#创建一个其中所含元素都为0的向量
	for word in inputSet:					    #遍历每个词条
		if word in vocabList:					#如果词条存在于词汇表中,则置1
			returnVec[vocabList.index(word)] = 1
		else: print("the word: %s is not in my Vocabulary!" % word)
	return returnVec							#返回文档向量


"""
函数说明:朴素贝叶斯分类器训练函数

Parameters:
	trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
	trainCategory - 训练类别标签向量,即loadDataSet返回的classVec
Returns:
	p0Vect - 侮辱类的条件概率数组
	p1Vect - 非侮辱类的条件概率数组
	pAbusive - 文档属于侮辱类的概率
Author:
	Jack Cui
Blog:
	http://blog.csdn.net/c406495762
Modify:
	2017-08-12
"""
def trainNB0(trainMatrix,trainCategory):
	numTrainDocs = len(trainMatrix)							#计算训练的文档数目
	numWords = len(trainMatrix[0])							#计算每篇文档的词条数
	pAbusive = sum(trainCategory)/float(numTrainDocs)		#文档属于侮辱类的概率
	p0Num = np.zeros(numWords); p1Num = np.zeros(numWords)	#创建numpy.zeros数组,
	p0Denom = 0.0; p1Denom = 0.0                        	#分母初始化为0.0
	for i in range(numTrainDocs):
		if trainCategory[i] == 1:							#统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
			p1Num += trainMatrix[i]
			p1Denom += sum(trainMatrix[i])
		else:												#统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
			p0Num += trainMatrix[i]
			p0Denom += sum(trainMatrix[i])
	p1Vect = p1Num/p1Denom									#相除        
	p0Vect = p0Num/p0Denom          
	return p0Vect,p1Vect,pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率

"""
函数说明:朴素贝叶斯分类器分类函数

Parameters:
	vec2Classify - 待分类的词条数组
	p0Vec - 侮辱类的条件概率数组
	p1Vec -非侮辱类的条件概率数组
	pClass1 - 文档属于侮辱类的概率
Returns:
	0 - 属于非侮辱类
	1 - 属于侮辱类
Author:
	Jack Cui
Blog:
	http://blog.csdn.net/c406495762
Modify:
	2017-08-12
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
	p1 = reduce(lambda x,y:x*y, vec2Classify * p1Vec) * pClass1    			#对应元素相乘
	p0 = reduce(lambda x,y:x*y, vec2Classify * p0Vec) * (1.0 - pClass1)
	print('p0:',p0)
	print('p1:',p1)
	if p1 > p0:
		return 1
	else: 
		return 0

"""
函数说明:测试朴素贝叶斯分类器

Parameters:
	无
Returns:
	无
Author:
	Jack Cui
Blog:
	http://blog.csdn.net/c406495762
Modify:
	2017-08-12
"""
def testingNB():
	listOPosts,listClasses = loadDataSet()						  #创建实验样本
	myVocabList = createVocabList(listOPosts)					  #创建词汇表
	trainMat=[]
	for postinDoc in listOPosts:
		trainMat.append(setOfWords2Vec(myVocabList, postinDoc))				#将实验样本向量化
	p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))		#训练朴素贝叶斯分类器
	testEntry = ['love', 'my', 'dalmation']									#测试样本1
	thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))				#测试样本向量化
	if classifyNB(thisDoc,p0V,p1V,pAb):
		print(testEntry,'属于侮辱类')										#执行分类并打印分类结果
	else:
		print(testEntry,'属于非侮辱类')										#执行分类并打印分类结果
	testEntry = ['stupid', 'garbage']										#测试样本2

	thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))				#测试样本向量化
	if classifyNB(thisDoc,p0V,p1V,pAb):
		print(testEntry,'属于侮辱类')										#执行分类并打印分类结果
	else:
		print(testEntry,'属于非侮辱类')										#执行分类并打印分类结果

if __name__ == '__main__':
	testingNB()

2.连续型特征

如果特征向量的分量是连续型随机变量,可以假设它们服从正态分布(高斯分布)。根据训练样本集可以计算出正态分布的均值和方差,这可以通过最大似然估计得到。连续型随机变量不能计算它在某一点的概率,因为它在任何一点处的概率为0。我们可以直接用概率密度函数的值作为概率值,得到各类别的概率。

我们通过高斯贝叶斯模型对Iris数据集进行花的类别识别。该数据集包含Iris花的三个品种(Iris setosa, Iris virginica and Iris versicolor)各50个样本,每个样本还有4个特征参数(分别是萼片<sepals>的长宽和花瓣<petals>的长 宽,以厘米为单位)。

(来自《统计学习方法》的python实现)

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter
import math

#data
def create_data():
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
    #data = df.iloc[:100,:]
    data = np.array(df)
    #print (data)
    return data[:,:-1],data[:,-1]

class NaiveBayes:
    def __init__(self):
        self.model = None

    #数学期望
    @staticmethod
    def mean(X):
        return sum(X) / float(len(X))

    #标准差(方差)
    def stdev(self, X):
        avg = self.mean(X)
        return math.sqrt(sum([pow(x-avg,2) for x in X]) / float(len(X)))

    #概率密度函数
    def gaussian_probability(self, 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_train
    def summarize(self, train_data):
        summarizes = [(self.mean(i), self.stdev(i)) for i in zip(*train_data)]
        return summarizes

    #分类别求出数学期望和标准差
    def fit(self, X, y):
        labels = list(set(y))
        data = {label:[] for label in labels}
        for f, label in zip(X, y):
            data[label].append(f)
        self.model = {label: self.summarize(value) for label, value in data.items()}
        return 'guassianNB train done'

    #计算概率
    def calculate_probabilities(self, input_data):
        probabilities = {}
        for label, value in self.model.items():
            probabilities[label] = 1
            for i in range(len(value)):
                mean, stdev = value[i]
                probabilities[label] *= self.gaussian_probability(input_data[i], mean, stdev)
        return probabilities
    #类别
    def predict(self, X_test):
        label = sorted(self.calculate_probabilities(X_test).items(), key=lambda x: x[-1])[-1][0]
        return label

    def score(self, X_test, y_test):
        right = 0
        for X,y in zip(X_test, y_test):
            label = self.predict(X)
            if label == y:
                right += 1
        return right / float(len(X_test))

if __name__ == '__main__':
    X,y = create_data()
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    model = NaiveBayes()
    model.fit(X_train, y_train)
    score = model.score(X_test, y_test)
    print(score)
    pre_label = model.predict([4.4, 3.2, 1.3, 0.2])
    print(pre_label)

3.sklearn实现

朴素贝叶斯是一类比较简单的算法,scikit-learn中朴素贝叶斯类库的使用也比较简单。在scikit-learn中,一共有3个朴素贝叶斯的分类算法类。分别是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB就是先验为高斯分布的朴素贝叶斯,MultinomialNB就是先验为多项式分布的朴素贝叶斯,而BernoulliNB就是先验为伯努利分布的朴素贝叶斯。

官方英文文档地址:http://scikit-learn.org/dev/modules/generated/sklearn.naive_bayes.MultinomialNB.html

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB #高斯模型、伯努利模型、多项式模型

#data
def create_data():
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
    #data = df.iloc[:100,:]
    data = np.array(df)
    #print (data)
    return data[:,:-1],data[:,-1]

if __name__ == '__main__':
    X,y = create_data()
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    clf = GaussianNB()
    clf.fit(X_train, y_train)
    score = clf.score(X_test, y_test)
    print(score)
    pre_label = clf.predict([[4.4, 3.2, 1.3, 0.2]])
    print(pre_label)

三、算法总结

朴素贝叶斯优点:

  • 生成式模型,通过计算概率来进行分类,可以用来处理多分类问题。
  • 对小规模的数据表现很好,适合多分类任务,适合增量式训练,算法也比较简单。
  • 对数据的训练快,分类也快

朴素贝叶斯缺点:

  • 对输入数据的表达形式很敏感。
  • 由于朴素贝叶斯的“朴素”特点,所以会带来一些准确率上的损失。
  • 需要计算先验概率,分类决策存在错误率。

应用场景:

  •  文本分类/垃圾文本过滤/情感判别
  • 多分类实时预测
  • 推荐系统

四、面试题

1.什么是贝叶斯决策理论?                                                                                                                                                                     贝叶斯决策理论是主观贝叶斯派归纳理论的重要组成部分。贝叶斯决策就是在不完全情报下。对部分未知的状态用主观概率估计,然后用贝叶斯公式对发生的概率进行修正,最后利用期望值和修正概率做出最优决策(选择概率最大的类别)。其基本思想是:(1)已知类条件概率密度参数表达式和先验概率(2)利用贝叶斯公式转化为后验概率(3)根据后验概率大小进行决策分类

2.朴素贝叶斯算法的前提假设是什么?                                                                                                                                                   朴素贝叶斯则是建立在两个前提假设上的:(1)特征之间相互独立(2)每个特征同等重要

3.什么是朴素贝叶斯的零概率问题?                                                                                                                                                       零概率问题,就是在计算实例的概率时,如果某个量x,在训练集中未出现过,会导致整个实例的概率结果为0。可以使用拉普拉斯平滑解决,具体做法是给分子分母同时加上一个正数。如果特征分量的取值有k种情况,将分母加上k,每个类的分子加上1,这样可以保证所有类的条件概率加起来还是1。

4.当数据的属性是连续变量时,朴素贝叶斯算法如何处理?                                                                                                                   当数据的属性是连续变量时,有两种方法可以计算属性的条件概率。(1)把一个连续的属性离散化,然后用相应的离散区间替换连续值。(2)假设连续变量服从某种概率分布,然后使用训练数据估计分布的参数,例如使用高斯分布。

5.朴素贝叶斯有哪几种常用的分类模型?                                                                                                                                              朴素贝叶斯的三个常用模型:高斯、多项式、伯努利

6.高度相关的特征对朴素贝叶斯有什么影响?                                                                                                                                       假设有两个特征高度相关,相当于该特征在模型中发挥了两次作用(计算两次条件概率),使得朴素贝叶斯获得的结果向该特征所希望的方向进行了偏移,影响了最终结果的准确性,所以朴素贝叶斯算法应先处理特征,把相关特征去掉。                                                           

猜你喜欢

转载自blog.csdn.net/qq_36086861/article/details/85625084
今日推荐