1.贝叶斯公式
我们先来看看贝叶斯公式:
其实这个公式并不难推导,只需要利用到高中所学的条件概率对其进行相应的变行即可。下面对于这个公式,先验概率,后验概率等,引用了知乎里一段很通俗易懂的段落进行解释。https://www.zhihu.com/answer/241988854
我的朋友小鹿说,他的女神每次看到他的时候都冲他笑,他想知道女神是不是喜欢他呢?下面我们一起用贝叶斯帮小鹿预测下女神喜欢他的概率有多大,这样小鹿就可以根据概率的大小来决定是否要表白女神。
首先,我分析了给定的已知信息和未知信息:
1)要求解的问题:女神喜欢你,记为A事件
2)已知条件:女神经常冲你笑,记为B事件
所以说,P(A|B)是女神经常冲你笑这个事件(B)发生后,女神喜欢你(A)的概率。
从公式来看,我们需要知道这么3个事情:
1)先验概率
我们把P(A)称为"先验概率"(Prior probability),即在不知道B事件的前提下,我们对A事件概率的一个主观判断。这个例子里就是在不知道女神经常对你笑的前提下,来主观判断出女神喜欢一个人的概率,这里我们假设是50%,也就是不能喜欢你,可能不喜欢还你的概率都是一半。
2)可能性函数
P(B|A)/P(B)称为"可能性函数"(Likelyhood),这是一个调整因子,即新信息B带来的调整,作用是使得先验概率更接近真实概率。
可能性函数你可以理解为新信息过来后,对先验概率的一个调整。比如我们刚开始看到“人工智能”这个信息,你有自己的理解(先验概率/主观判断),但是当你学习了一些数据分析,或者看了些这方面的书后(新的信息),然后你根据掌握的最新信息优化了自己之前的理解(可能性函数/调整因子),最后重新理解了“人工智能”这个信息(后验概率)
如果"可能性函数"P(B|A)/P(B)>1,意味着"先验概率"被增强,事件A的发生的可能性变大;
如果"可能性函数"=1,意味着B事件无助于判断事件A的可能性;
如果"可能性函数"<1,意味着"先验概率"被削弱,事件A的可能性变小。
还是刚才的例子,根据女神经常冲你笑这个新的信息,我调查走访了女神的闺蜜,最后发现女神平日比较高冷,很少对人笑。所以我估计出"可能性函数"P(B|A)/P(B)=1.5(具体如何估计,省去1万字,后面会有更详细科学的例子)
3)后验概率
P(A|B)称为"后验概率"(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。这个例子里就是在女神冲你笑后,对女神喜欢你的概率重新预测。
带入贝叶斯公式计算出P(A|B)=P(A)* P(B|A)/P(B)=50% *1.5=75%
因此,女神经常冲你笑,喜欢上你的概率是75%。这说明,女神经常冲你笑这个新信息的推断能力很强,将50%的"先验概率"一下子提高到了75%的"后验概率"。
在得到预测概率后,小鹿自信满满的发了下面的表白微博
稍后,果然收到了女神的回复。预测成功。
现在我们再来看一遍贝叶斯公式,你现在就能明白这个公式背后的最关键思想了:我们先根据以往的经验预估一个"先验概率"P(A),然后加入新的信息(实验结果B),这样有了新的信息后,我们对事件A的预测就更加准确。
因此,贝叶斯定理可以理解成下面的式子:
后验概率(新信息出现后A发生的概率) = 先验概率(A发生的概率) x 可能性函数(新信息带出现来的调整)
贝叶斯的底层思想就是:
如果我能掌握一个事情的全部信息,我当然能计算出一个客观概率(古典概率、正向概率)。可是生活中绝大多数决策面临的信息都是不全的,我们手中只有有限的信息。既然无法得到全面的信息,我们就在信息有限的情况下,尽可能做出一个好的预测。也就是,在主观判断的基础上,可以先估计一个值(先验概率),然后根据观察的新信息不断修正(可能性函数)。
上面已经对这个公式有了一定的了解。在生活中贝叶斯也经常会用到,比如要预测明天的是否下雨,我们不可能去统计每个明天是否下雨(即通过完整的信息)来得到下雨的概率,这时就可以用贝叶斯的思想,借助以往的天气情况的经验来预测天气。
2.朴素贝叶斯分类模型
贝叶斯分类算法是利用统计学里的概率统计知识进行分类的算法。这里设Y = {C1,C2,C3,…,Cm}是有m个不同类别的集合,X(x1,x2…,xn)表示一个n维的特征向量
贝叶斯决策准则是根据给定特征X求出该特征属于每个类别(C1,C2,C3,…,Cm)的概率,X被判为概率最大这个类别,这时也可以使得预测所产生的损失(预测错误)最小化了。对于每个要预测类别的X,P(X)都是一样的,所以只要求
朴素贝叶斯有一个很严格的假设:在类别Ci情况下特征属性之间相互独立(这里是特征属性条件独立假设而不是特征属性独立假设!),这样一来P( X | Ci )可以由之前的求解联合概率转变为单个属性相关概率的乘积,可以更好地估算P( X | Ci )(但实际往往无法做到完全相互独立)。这里其实还有一个假设,每个特征属性等重要,不然还要在每个P( Xk | Ci )上乘个概率。
所以最终的的表达式是
接下来进行求解:
P( Ci ) = Di / D , Di 表示类别Ci的个数,D表示样本总个数
- 若P (xk | Ci)中的特征属性为离散型,P (xk | Ci)=Ski / Si, Ski表示类别Ci样本中该特征属性取值为xk 的个数,Si表示类别Ci样本个数;
- 若P (xk | Ci)中的特征属性为连续型,则一般假设该特征属性服从高斯分布,先得到类别Ci样本中该特征属性的方差与均值,利用概率密度函数求解。
这里还可能出现一个问题,倘若P (xk | Ci)=Ski / Si中有一个 为0时,也就是在类别Ci样本中没有所求特征属性值为xk的情况,整个式子都会等于0将会出现很大的偏差。这时可以进行平滑处理,(这里叫拉普拉斯修正)其实就是加个相关的常数,下面的N表示类别数,Ni表示属性xi的可能取值的个数。当然这个问题只会在属性是离散的情况下
朴素贝叶斯有高斯模型、多项式模型、伯努利模型这三个模型,下面就对高斯模型、多项式模型进行简单实现
3.朴素贝叶斯–高斯模型
当特征属性是连续型的时候,就可以用高斯模型进行分类了,实际就是利用上面的概率密度函数
下面简单地实现一下:
#导入包
from sklearn import datasets
import random
import numpy as np
这是一个用于数据集分割的函数
def train_test_split(x,y,rate=0.25):
indexs = np.array(range(0,len(x)))
random.shuffle(indexs) #打乱索引
n = len(x) - int(rate*len(x))#train的数据量
x_train, x_test, y_train, y_test = x[indexs[:n]], x[indexs[n:]], y[indexs[:n]], y[indexs[n:]]
return x_train, x_test, y_train, y_test
下面是高斯模型
#p(c/x) = p(c)*p(x/c)
class GaussianNB:
def __init__(self):
self._means = None#储存每个类别下每个属性的均值的一个数组
self._stds = None#储存每个类别下每个属性的方差的一个数组
self._Pc = None
self._n = None#全部的类别
self.prob_ =None
def fit(self,x,y):
n = list(set(y))#得到全部的类别
y = y.reshape(-1,1)#转化为二维矩阵便于ndarray合并
#得到每个类别数据样本对应的std和mean
X = np.hstack([x,y])#合并,便于按类分割数据
stds = np.ones((len(n),np.shape(x)[1]))
means = np.ones((len(n),np.shape(x)[1]))
for i in n:
inde = X[X[:,np.shape(X)[1]-1]==i]
inde = inde[:,:np.shape(X)[1]-1]#得到每个类型的数据样本
a = np.mean(inde[:,:np.shape(inde)[1]],axis=0)#每列
b = np.std(inde[:,:np.shape(inde)[1]],axis=0)
means[i] = a
stds[i] = b
#求一个关于P(c)的向量 便于后边运算
Pc = np.array([sum(y==i)/len(y) for i in n]).reshape((len(n),))
self.means = means
self.stds = stds
self.Pc = Pc
self.n = n
return self
def predict(self,x_test):
#预测
prob = []
for k in x_test:
pro = []
for i in range(0,len(self.n)): #每个类别
mult = 1
for j in range(0,np.shape(x_test)[1]): #特征数
mult *= np.exp((-1)*(k[j] - self.means[i,j])**2/(self.stds[i,j])*2)/(np.sqrt(2*np.pi*self.stds[i,j]))
pro.append(mult)
prob.append(pro)
prob = [self.Pc*prob[i] for i in range(len(prob))]
pre = np.argmax(prob,axis=1)
prob = np.array(prob)
self.prob_ = prob
return pre
def accuracy_score(self,x_test,y_test):
accuracy_score = sum(self.predict(x_test)==y_test)/len(y_test)
return accuracy_score
还是用包里的鸢尾花数据集进行模型实现
iris =datasets.load_iris()
x = iris.data
y = iris.target
x_train, x_test, y_train, y_test = train_test_split(x,y)
model = GaussianNB()
model.fit(x_train,y_train)
pred = model.predict(x_test)
prob_ = model.prob_ #这里必须要先进行预测
print('accuracy_score: {}'.format(model.accuracy_score(x_test,y_test)))
进行多次预测效果都比较好,当然与这份数据太干净也有一定关系!
accuracy_score: 1.0
accuracy_score: 0.972972972972973
accuracy_score: 0.9459459459459459
哒哒哒哒哒哒
4.朴素贝叶斯–多项式模型
当特征属性是离散型的时候,这时可以建立多项式模型。朴素贝叶斯–多项式模型用来进行文本分类比较好,(顺便说句,伯努利也可以用来文本分类)根据一篇文章里的单词情况判断这篇文章是科学篇,还是文学篇等等。
接下来进行一个简单的文本分类,这里用到的公式和上面特征属性所讲到的公式有点不一样。
在多项式模型中, 设某文档d=(t1, t2, …, tk),tk是该文档中出现过的单词,允许重复,则:
- 先验概率P(c)= 类c下单词总数/整个训练样本的单词总数
- 类条件概率P (tk | c) =(类c下单词tk在各个文档中出现过的次数之和+1)/(类c下单词总数+|V|)
(V是训练样本的单词表(即抽取单词,单词出现多次,只算一个))
额 由于没找到相关数据,也比较懒 就还是用鸢尾花数据集进行模型实现 这的确有点不太适合 凑合下吧
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
#这里就预测一个样本了,要多个直接套个循环就可了 上面的高斯模型可以预测多个
def Multi(x_train,y_train,x_test,y_test):
yn = list(set(y_train))#利用集合唯一性得到有多少的个类别,再转化为list
#总单词数
N = np.sum([len(x_train[i]) for i in range(len(x_train))])
#求P(c)
y_train = y_train.reshape(-1,1)
X = np.hstack([x_train,y_train])
Nc = np.ones(len(yn))
j = 0
for i in yn:
indes = X[X[:,np.shape(X)[1]-1]==i]#每个类别的样本
indes = indes[:,:np.shape(X)[1]-1]#除去label
sum = np.sum([len(indes[i]) for i in range(len(indes))])
Nc[j] = sum
j +=1
Pc = Nc/N
#求P(tk|c)
prob = np.ones(len(yn))
j = 0
for i in yn: #遍历每个类别
multi = 1
for k in x_test: #遍历文档的每个单词
indes = X[X[:,np.shape(X)[1]-1]==i]#每个类别的样本
indes = indes[:,:np.shape(X)[1]-1]#除去label
sum = np.sum(k==indes)#统计单词在C类下出现的次数
multi *= (sum+1)/(Nc[i] + len(set(x_train.flatten())))
prob[j] = Pc[j]*multi
j += 1
pre = np.argmax(prob,axis=0)
print('属于类别: ',yn[pre],pre==y_test)
iris = datasets.load_iris()
x = iris.data
y = iris.target
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.3)
for i in range(10): #进行了10次预测
Multi(x_train,y_train,x_test[i],y_test[i])
跑了两遍程序
属于类别: 2 True
属于类别: 1 True
属于类别: 1 True
属于类别: 2 True
属于类别: 0 False
属于类别: 2 True
属于类别: 0 True
属于类别: 0 True
属于类别: 0 True
属于类别: 2 True
属于类别: 2 True
属于类别: 2 True
属于类别: 0 True
属于类别: 2 False
属于类别: 0 True
属于类别: 2 True
属于类别: 1 False
属于类别: 0 True
属于类别: 1 True
属于类别: 0 True
数据不太恰当,效果还好 这还得归于这么数据比较简单、干净 但让算法也是功劳 这里就可以看出,有时数据表面上虽然和算法不太友好、不太适合,但效果却不一定 。