統(tǒng)計(jì)學(xué)習(xí)方法 | 樸素貝葉斯 | python實(shí)現(xiàn)
本文實(shí)現(xiàn)了李航教授的《統(tǒng)計(jì)學(xué)習(xí)方法》一書中第4章樸素貝葉斯法中的算法,包括算法4.1(樸素貝葉斯算法)和在此基礎(chǔ)上改進(jìn)的貝葉斯估計(jì)。文末整理了在實(shí)現(xiàn)算法過程中遇到問題記錄的筆記。
樸素貝葉斯法

首先訓(xùn)練樸素貝葉斯模型,對應(yīng)算法4.1(1),分別計(jì)算先驗(yàn)概率及條件概率,分別存在字典priorP和condP中(初始化函數(shù)中定義)。其中,計(jì)算一個(gè)向量各元素頻率的操作反復(fù)出現(xiàn),定義為count函數(shù)。
# 初始化函數(shù)定義了先驗(yàn)概率和條件概率字典,并訓(xùn)練模型
def __init__(self, data, label):
self.priorP = {}
self.condP = {}
self.train(data, label)
count函數(shù),輸入一個(gè)向量,輸出一個(gè)字典,包含各元素頻率
# 給一個(gè)向量,返回字典,包含不同元素的頻率。可以引用collections中的Counter函數(shù)來實(shí)現(xiàn)
# 這個(gè)函數(shù)可以改進(jìn),懶得弄了
def count(self, vec):
np.array(vec)
keys = np.unique(vec)
p = {}
for key in keys:
n = np.sum(np.isin(vec, key) + 0) # 加0可以使布爾向量變?yōu)?-1向量
p[key] = n/len(vec) # 計(jì)算頻率
return p
訓(xùn)練函數(shù),關(guān)于condP的保存下面有詳細(xì)說明
def train(self, data, label):
m, n = np.shape(data)
# 計(jì)算先驗(yàn)概率
self.priorP = self.count(label)
print("priorP:", self.priorP)
# 計(jì)算條件概率
classes = np.unique(label)
for c in classes:
subset = [data[i] for i in range(m) if label[i] == c] # 取Y=ck的子集
for j in range(n): # 遍歷每一個(gè)特征,分別求條件概率
self.condP[str(c)+" "+str(j)] = self.count([x[j] for x in subset])
print("condP:", self.condP)
對于條件概率condP的保存,將每個(gè)特征關(guān)于Y=ck的條件概率都存為一個(gè)字典,再存入字典condP中,key設(shè)為 “ck j” ,其中 ck 為 Y 的類別,j 表示第 j 個(gè)特征。訓(xùn)練例4.1得到的模型如下, condp中的'-1 0' 項(xiàng)即表示Y=-1條件下,P(x0=1)=0.5, P(x0=2)=0.333, P(x0=3)=0.166. 為了顯示方便,只給出了小數(shù)點(diǎn)后3位。
priorP: {-1: 0.4, 1: 0.6}
condP: {'-1 0': {1: 0.5, 2: 0.333, 3: 0.166},
'-1 1': {'L': 0.166, 'M': 0.333, 'S': 0.5},
'1 0': {1: 0.222, 2: 0.333, 3: 0.444},
'1 1': {'L': 0.444, 'M': 0.444, 'S': 0.111}}
訓(xùn)練之后,對給定X進(jìn)行預(yù)測,結(jié)果保存在字典preP中
def predict(self, x):
preP = {}
for c in self.priorP.keys():
preP[c] = self.priorP[c]
for i, features in enumerate(x):
preP[c] *= self.condP[str(c)+" "+str(i)][features]
print("probability: ", preP)
print("prediction: ", max(preP, key=preP.get))
結(jié)果:
probability: {-1: 0.06666666666666667, 1: 0.02222222222222222}
prediction: -1
貝葉斯分類
考慮到概率可能為0,在隨機(jī)變量各個(gè)取值的頻數(shù)上賦予一個(gè)正數(shù)lamda,lamda為0時(shí)即為極大似然估計(jì);lamda取1時(shí)稱為拉普拉斯平滑。
先驗(yàn)概率變?yōu)椋╝)

條件概率變?yōu)椋╞)

在之前的算法基礎(chǔ)上改進(jìn),添加一個(gè)字典變量rangeOfFeature來保存每個(gè)特征的取值個(gè)數(shù),定義在初始化函數(shù)中。
self.rangeOfFeature = {} # 保存每個(gè)特征的取值個(gè)數(shù)
公式(a)和(b)形式相同,將 K 或Sj 作為參數(shù)傳入count函數(shù),lamda缺省為0:
def count(self, vec, classNum, lamda=0):
keys = set(vec)
p = {}
for key in keys:
n = np.sum(np.isin(vec, key) + 0)
p[key] = (n+lamda)/(len(vec)+classNum*lamda)
return p
訓(xùn)練函數(shù)變?yōu)?/p>
def train(self, data, label, lamda=0):
m, n = np.shape(data)
# 計(jì)算rangeOfFeature
for j in range(n):
self.rangeOfFeature[j] = len(set([x[j] for x in data]))
classes = set(label)
# 計(jì)算先驗(yàn)概率
self.priorP = self.count(label, len(classes), lamda)
print("priorP:", self.priorP)
# 計(jì)算條件概率
for c in classes:
subset = [data[i] for i in range(m) if label[i] == c]
for j in range(n):
self.condP[str(c)+" "+str(j)] = self.count([x[j] for x in subset], self.rangeOfFeature[j], lamda)
print("condP:", self.condP)
其他不變,對之前的實(shí)例運(yùn)行
bayes = Bayes(dataSet, labels, 1)
bayes.predict([2, "S"])
結(jié)果如下:
probability: {1: 0.0326797385620915, -1: 0.06100217864923746}
prediction: -1
筆記
-
貝葉斯定理
-
max(dict, key = dict.get)獲得字典dict中value最大的值的鍵 -
bool型列表轉(zhuǎn)換為0-1
- 變量后加0
booldata.astype(int)類型轉(zhuǎn)換
-
for i, value in enumerate(['A', 'B', 'C']):把list變成索引-元素對用enumerate函數(shù),這樣就可以在for循環(huán)中同時(shí)迭代索引和元素本身 -
[a[j] for a in data]取data第 j 列 -
np.isin(a,b)判斷a中每個(gè)元素是否在b中,返回與a形狀相同的bool數(shù)組 -
np.unique(a)去重并排序
代碼下載:3-Bayes.py


浙公網(wǎng)安備 33010602011771號(hào)