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
資料不太恰當,效果還好 這還得歸于這麼資料比較簡單、幹淨 但讓算法也是功勞 這裡就可以看出,有時資料表面上雖然和算法不太友好、不太适合,但效果卻不一定 。