本文部分总结内容摘自李航老师的《统计学习方法》及其配套课件
原文代码作者:https://github.com/wzyonggege/statistical-learning-method
在开始朴素贝叶斯的学习之前,先弄清楚几个概念:
先验概率:
事件发生前的预判概率。可以是基于历史数据的统计,可以由背景常识得出,也可以是人的主观观点给出。一般都是单独事件概率,如P(x),P(y)。
后验概率:
事件发生后求的反向条件概率;或者说,基于先验概率求得的反向条件概率。概率形式与条件概率相同。
条件概率:
一个事件发生后另一个事件发生的概率。一般的形式为P(x|y)表示y发生的条件下x发生的概率。
基本假设
朴素贝叶斯,英文叫Naive Bayes。
Naive?那是有原因的,朴素贝叶斯对输入变量做了一个很强的假设——条件独立
条件独立
输入变量之间是相互独立的,没有概率依存关系。(若相互依存,那叫贝叶斯网络)
即,用于分类的特征(xj)在类(y=ck)确定的条件下,都是相互独立的,即
P(X=x|Y=ck)=P(X1=x2,X2=x2...Xn=xn|Y=ck)
=P(X1=x1|Y=ck)P(X2=x2|Y=ck)...P(Xn=xn|Y=ck)
训练数据集:由X和Y的联合概率分布P(X,Y)独立同分布产生
朴素贝叶斯通过训练数据集学习联合概率分布P(X,Y) ,
即先验概率分布:
及条件概率分布:
条件独立性假设:
“朴素” 贝叶斯名字由来, 牺牲分类准确性。
如下:
-
后验概率最大化的含义:
朴素贝叶斯法将实例分到后验概率最大的类中。 这等价于期望风险最小化。 假设选择0-1损失函数:
式中f(X)是分类决策函数。 这时, 期望风险函数为
期望是对联合分布P(X,Y)取的。 由此取条件期望
注意:
为了使期望风险最小化, 只需对X=x逐个极小化, 由此得到:
这样一来, 根据期望风险最小化准则就得到了后验概率最大化准则:
即朴素贝叶斯法所采用的原理。
-
极大似然估计
在朴素贝叶斯法中, 学习意味着估计P(Y=)和P(=|Y=)。 可以应用极大似然估计法估计相应的概率。 先验概率P(Y=)的极大似然估计是
设第j个特征可能取值的集合为{,,…,}, 条件概率P(=|Y=)的极大似然估计
是
式中, 是第i个样本的第j个特征; 是第j个特征可能取的第l个值; I为指示函数。
-
朴素贝叶斯法的学习与分类算法
-
贝叶斯估计
用极大似然估计可能会出现所要估计的概率值为0的情况。 这时会影响到后验概率的计算结果, 使分类产生偏差。 解决这一问题的方法是采用贝叶斯估计。 具体地, 条件概率的贝叶斯估计是
式中 ≥0。 等价于在随机变量各个取值的频数上赋予一个正数 >0。 当 =0时就是极大似然估计。 常取 =1, 这时称为拉普拉斯平滑(Laplace smoothing) 。 显然, 对任何l=1,2,…,Sj, K=1,2,…,K, 有
表明式(4.10) 确为一种概率分布。 同样, 先验概率的贝叶斯估计是
-
朴素贝叶斯的优缺点
优点:
-
测试数据集的类预测简单又快速,在多类预测表现良好;
-
当变量假设独立成立时,相比Logistic回归等其他分类方法,朴素贝叶斯分类器性能更优,需要的训练数据较少;
-
相较于数值变量,朴素贝叶斯分类器在多个分类变量的情况下表现更好。若是数值变量,需要正态分布假设。
缺点:
-
如果分类变量的类别(在测试数据集中)没有在训练数据集总被观察到,那这个模型会分配一个0(零)概率给它,同时也会无法进行预测。这通常被称为“零频率”。
-
朴素贝叶斯的另一个限制是独立预测的假设。在现实生活中,这几乎是不可能的,各变量间或多或少都会存在相互影响。
基于贝叶斯定理与特征条件独立假设的分类方法。
模型:
- 高斯模型
- 多项式模型
- 伯努利模型
GaussianNB 高斯朴素贝叶斯
GaussianNB假设特征的先验概率为正态分布,即如下式:
其中Ck为Y的第k类类别。μ和σ^2为需要从训练集估计的值。
GaussianNB会根据训练集求出μ和σ^2。 μ为在样本类别Ck中,所有Xj的平均值。σ^2为在样本类别Ck中,所有Xj的方差。
- 数据准备
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
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 = np.array(df.iloc[:100, :])
# print(data)
return data[:,:-1], data[:,-1]
X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
- 函数:
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):
#zip(*x),解压,保证解压后参数长度一致,这里得到的是x的每一列作为一组
summaries = [(self.mean(i), self.stdev(i)) for i in zip(*train_data)]
#所以这里得到四组,每一组是每个特征的期望和方差
return summaries
# 分类别求出数学期望和标准差
def fit(self, X, y):
labels = list(set(y)) #构建一个不重复的集合
# labels = [0.0, 1.0]
#创建字典eg: {label :[]},此处应该只有{0:[], 1:[]}
data = {label:[] for label in labels}
#data ={0.0: [], 1.0: []}
#zip() 多个参数压缩到一起,该函数返回一个以元组为元素的列表
for f, label in zip(X, y):
data[label].append(f) #添加数据字典
#{0.0: [array1,array2……],1.0:....]
#求出每个类对应的数学期望和标准差
#这里的summarize函数将X的每一列特征计算了期望和标准差
self.model = {label: self.summarize(value) for label, value in data.items()}
return 'gaussianNB train done!'
# 计算概率
def calculate_probabilities(self, input_data):
# summaries:{0.0: [(5.0, 0.37),(3.42, 0.40)], 1.0: [(5.8, 0.449),(2.7, 0.27)]}
# input_data:[1.1, 2.2]
probabilities = {}
#每一类
for label, value in self.model.items():
probabilities[label] = 1 #保证非零
for i in range(len(value)): #每个value[i]中存放期望与方差
mean, stdev = value[i]
#累乘
probabilities[label] *= self.gaussian_probability(input_data[i], mean, stdev)
return probabilities
# 类别
def predict(self, X_test):
#probabilities ={0.0: 1.5639826885594645, 1.0: 2.3090969013303393e-17}
#排序sorted(d.items(), key=lambda x: x[-1]) 按照前面对象的第-1维数据(即value)的值进行排序
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))
- 模型计算:
model = NaiveBayes()
model.fit(X_train, y_train)
'gaussianNB train done!'
print(model.predict([4.4, 3.2, 1.3, 0.2]))
# 0.0
model.score(X_test, y_test)
#1.0
个人有个疑惑好像在上述代码中没有计算对应的各个类别的先验概率 P(Y=Ck)P(Y=Ck),累乘中的计算似乎没有算上这个不知道为什么。。不过采用的数据iris数据集中的各个类别的数量是相等的。如果是其他的训练数据应该要再乘上去吧。
-
scikit-learn实例
在scikit-learn中,一共有3个朴素贝叶斯的分类算法类。分别是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB就是先验为高斯分布的朴素贝叶斯,MultinomialNB就是先验为多项式分布的朴素贝叶斯,而BernoulliNB就是先验为伯努利分布的朴素贝叶斯。
这三个类适用的分类场景各不相同,一般来说,如果样本特征的分布大部分是连续值,使用GaussianNB会比较好。如果如果样本特征的分大部分是多元离散值,使用MultinomialNB比较合适。而如果样本特征是二元离散值或者很稀疏的多元离散值,应该使用BernoulliNB。
GaussianNB类的主要参数仅有一个,即先验概率priors ,对应Y的各个类别的先验概率P(Y=Ck)P(Y=Ck)。这个值默认不给出,如果不给出此时P(Y=Ck)=mk/m。其中m为训练集样本总数量,mk为输出为第k类别的训练集样本数。如果给出的话就以priors 为准。
在使用GaussianNB的fit方法拟合数据后,我们可以进行预测。此时预测有三种方法,包括predict,predict_log_proba和predict_proba。
predict方法就是我们最常用的预测方法,直接给出测试集的预测类别输出。
predict_proba则不同,它会给出测试集样本在各个类别上预测的概率。容易理解,predict_proba预测出的各个类别概率里的最大值对应的类别,也就是predict方法得到类别。
predict_log_proba和predict_proba类似,它会给出测试集样本在各个类别上预测的概率的一个对数转化。转化后predict_log_proba预测出的各个类别对数概率里的最大值对应的类别,也就是predict方法得到类别。
- sklearn.naive_bayes
from sklearn.naive_bayes import GaussianNB
clf = GaussianNB() #注:建立一个高斯朴素贝叶斯分类器的实例,这里默认没有预设先验概率,根据训练集自动设置。
#注:分类器的fit方法,输入数据集的特征x和分类标签y,进行分类器的训练。然后用predict方法,输入特征x,输出分类器对样本的预测标签。
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
clf.predict([[4.4, 3.2, 1.3, 0.2]])
from sklearn.naive_bayes import BernoulliNB, MultinomialNB
# 伯努利模型和多项式模型