根据《统计学习方法》第四章朴素贝叶斯算法流程写成,引入贝叶斯估计(平滑处理)
《机器学习实战》的朴素贝叶斯算法,是针对文本处理(垃圾邮件过滤)的算法,是二元分类(y=0或y=1),且特征的取值也是二元(x=0或x=1)的特殊算法,因此用numpy数组相加的方法,可以快速算出各特征取值的概率。但是推广的一般情况,即不同特征的取值范围各不相同时,无法把所有情况整合到一个数组中,故在实现时,使用字典来存储各种情况的概率,代价是空间和时间复杂度更大了,而且看上去不是那么简洁。应该还有更好的办法。 以下用简单的测试数据测试通过。
import numpy as np
class NB():
'''
朴素贝叶斯(Naive Bayes)
'''
def trainNB(self, dataSet, lamb=1):
'''
根据输入数据,训练NB模型
:param dataSet: 训练集 m*(n+1),m是样本数,n是特征数, np.array
:return: 各类别的信息字典 组成的列表
'''
labels = set(list(dataSet[:, -1])) # 获取类别的取值
m, n = dataSet.shape[0], dataSet.shape[1] - 1
featureValues = [] # 所有特征所有可能的特征取值
for featureindex in range(n):
values = set(list(dataSet[:, featureindex]))
featureValues.append(values) # 元素为集合的列表,集合存储相应索引位特征的所有可能取值。
NBlist = [] # 最终要返回的列表,即训练好的NB模型
for label in labels:
labeldict = {} # 存储该类别的必要信息
labeldict['label'] = label # 类别标签信息
labeldict['feature'] = {} # 类别的特征信息, 因为特征有n个,这里还是用字典存储特征信息
for i in range(n):
labeldict['feature'][i] = {} # 特征的值的信息,因为特征的取值个数>=2,这里还是用字典存储特征信息
for value in featureValues[i]:
labeldict['feature'][i][value] = 0 # 初始化每个特征出现的次数为0
numlabel = 0 # 记录该类别出现的次数
for example in dataSet:
if example[-1] == label: # 找到属于该类别的样本
numlabel += 1
for i in range(n):
labeldict['feature'][i][example[i]] += 1 # 找到对应的特征值,使其出现次数+1
labeldict['plabel'] = (numlabel + lamb) / (m + lamb * len(labels)) # 计算先验概率p,并做平滑处理
for i in range(n):
for value in featureValues[i]:
labeldict['feature'][i][value] = (labeldict['feature'][i][value] + lamb) / (numlabel + lamb * len(featureValues[i])) # 根据该类别的总样本数,把出现次数转变成计算条件概率,并做平滑处理
NBlist.append(labeldict)
return NBlist
def predict(self, NBlist, testData):
'''
输入待测数据,输出预测类别
:param NBlist: 训练好的模型
:param testData: shape=(n,)的待测数据,np.array
:return: 预测结果
'''
prob = {}
for nb in NBlist:
p = np.log(nb['plabel']) # 把连乘换成取对数相加,防止下溢(即太多小于1的数相乘,结果会变成0)
for index, val in enumerate(testData): # 计算每个类别的“可能性”
p += np.log(nb['feature'][index][val])
prob[nb['label']] = p
return max(prob, key=lambda x: prob[x]) # 取键值最大的键
# 以下是测试数据
dataSet = np.array([[1, 5, -1],
[1, 6, -1],
[1, 6, 1],
[1, 5, 1],
[1, 5, -1],
[2, 5, -1],
[2, 6, -1],
[2, 6, 1],
[2, 7, 1],
[2, 7, 1],
[3, 7, 1],
[3, 6, 1],
[3, 6, 1],
[3, 7, 1],
[3, 7, -1]])
nb = NB()
NBlist = nb.trainNB(dataSet)
prob = nb.predict(NBlist, np.array([1, 7]))