一 高斯朴素贝叶斯分类器代码实现
网上搜索不调用 sklearn 实现的朴素贝叶斯分类器基本很少, 即使有也是结合文本分类的多项式或伯努利类型, 因此自己写了一遍能直接封装的高斯类型 NB 分类器, 当然与真正的源码相比少了很多属性和方法, 有兴趣的可以自己添加. 代码如下(有详细注释):
- class NaiveBayes():
- '''高斯朴素贝叶斯分类器'''
- def __init__(self):
- self._X_train = None
- self._y_train = None
- self._classes = None
- self._priorlist = None
- self._meanmat = None
- self._varmat = None
- def fit(self, X_train, y_train):
- self._X_train = X_train
- self._y_train = y_train
- self._classes = np.unique(self._y_train) # 得到各个类别
- priorlist = []
- meanmat0 = np.array([[0, 0, 0, 0]])
- varmat0 = np.array([[0, 0, 0, 0]])
- for i, c in enumerate(self._classes):
- # 计算每个种类的平均值, 方差, 先验概率
- X_Index_c = self._X_train[np.where(self._y_train == c)] # 属于某个类别的样本组成的 "矩阵"
- priorlist.append(X_Index_c.shape[0] / self._X_train.shape[0]) # 计算类别的先验概率
- X_index_c_mean = np.mean(X_Index_c, axis=0, keepdims=True) # 计算该类别下每个特征的均值, 结果保持二维状态[[3 4 6 2 1]]
- X_index_c_var = np.var(X_Index_c, axis=0, keepdims=True) # 方差
- meanmat0 = np.append(meanmat0, X_index_c_mean, axis=0) # 各个类别下的特征均值矩阵罗成新的矩阵, 每行代表一个类别.
- varmat0 = np.append(varmat0, X_index_c_var, axis=0)
- self._priorlist = priorlist
- self._meanmat = meanmat0[1:, :] #除去开始多余的第一行
- self._varmat = varmat0[1:, :]
- def predict(self,X_test):
- eps = 1e-10 # 防止分母为 0
- classof_X_test = [] #用于存放测试集中各个实例的所属类别
- for x_sample in X_test:
- matx_sample = np.tile(x_sample,(len(self._classes),1)) #将每个实例沿列拉长, 行数为样本的类别数
- mat_numerator = np.exp(-(matx_sample - self._meanmat) ** 2 / (2 * self._varmat + eps))
- mat_denominator = np.sqrt(2 * np.pi * self._varmat + eps)
- list_log = np.sum(np.log(mat_numerator/mat_denominator),axis=1)# 每个类别下的类条件概率相乘后取对数
- prior_class_x = list_log + np.log(self._priorlist) # 加上类先验概率的对数
- prior_class_x_index = np.argmax(prior_class_x) # 取对数概率最大的索引
- classof_x = self._classes[prior_class_x_index] # 返回一个实例对应的类别
- classof_X_test.append(classof_x)
- return classof_X_test
- def score(self, X_test, y_test):
- j = 0
- for i in range(len(self.predict(X_test))):
- if self.predict(X_test)[i] == y_test[i]:
- j += 1
- return ('accuracy: {:.10%}'.format(j / len(y_test)))
- 对于手动实现的高斯型 NB 分类器, 利用鸢尾花数据进行测试, 与调用 sklearn 库的分类器结果差不多, 基本在 93-96 徘徊. 这是由于多次进行二八切分, 相当于多次留出法. 为计算更准确精度, 可进行交叉验证并选择多个评价方法, 这里不再实现.
- import numpy as np
- from sklearn import datasets
- from sklearn.model_selection import train_test_split
- from sklearn import preprocessing
- # 获取数据集, 并进行 8:2 切分
- iris = datasets.load_iris()
- X = iris.data
- y = iris.target
- # print(X)
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
- nb = NaiveBayes()
- nb.fit(X_train,y_train)
- print(nb.predict(X_test))
- print(nb.score(X_test,y_test))
- # 输出结果如下:
- [0, 2, 1, 1, 1, 2, 1, 0, 2, 0, 1, 1, 1, 0, 2, 2, 2, 2, 0, 1, 1, 0, 2, 2, 2, 0, 1, 0, 1, 0]
- accuracy: 96.6666666667%
- 二 其他
- 基于属性条件独立性假设的朴素贝叶斯, 在现实中往往很难成立, 因此产生了 "半朴素贝叶斯分类器". 其基本思想是适当考虑一部分属性间的相互依赖信息, 从而既不需要进行完全联合概率计算, 又不至于彻底忽略比较强的属性依赖关系."独依赖估计" 是最常用的一种策略, 即假设每个属性在类别之外最多依赖一个其他属性. 包括 SPODE 方法, TAN 方法, AODE 方法等.
- np.unique(): 返回原来 array 中不重复元素组成的新 array, 元素从小到大.
- y = np.array([1, 2, 9, 1,2,3])
- classes = np.unique(y) # 返回 y 中所有不重复的元素组成的新 array([1,2,3,9])
- print(classes) # 结果为 np.array([1,2,3,9])
- np.where(): 对 array 进行操作
- '''
- 1. np.where(condition, x, y)
- 满足条件(condition), 满足进行 x 操作, 不满足进行 y 操作
- '''
- a= np.array([[9, 7, 3], [4, 5, 2], [6, 3, 8]])
- b=np.where(a> 5, 1, 0) #对于 a 中的元素如果大于 5, 则改写成 1, 否则写成 0.
- print(b)
- 输出结果:
- [[1 1 0]
- [0 0 0]
- [1 0 1]]
- '''
- 2. np.where(condition)
- 只有条件 (condition), 没有 x 和 y, 则输出满足条件元素的坐标 (等价于 numpy.nonzero).
- 这里的坐标以 tuple 的形式给出, 通常原数组有多少维, 输出的 tuple 中就包含几个数组, 分别对应符合条件元素的各维坐标.
- '''
- c = np.array([[9, 7, 3], [4, 5, 2], [6, 3, 8]])
- d = np.where(c> 5) #条件为元素大于 5
- print(d)
输出结果如下(元组):
(array([0, 0, 2, 2], dtype=int64), array([0, 1, 0, 2], dtype=int64)) 表示下表为 00,01 20,22 的元素满足条件.
- a = np.array([1,3,6,9,0])
- b = np.where(a> 5)
- print(b)
输出结果 (array([2, 3], dtype=int64),) 表示坐标为 2 和 3 的元素满足, 注意末尾的逗号, 表明一维时实质输出元组为二维, 2_,3_只不过后面没有而已, a 维数大于等于 2 时, 元组和 a 维数相同.
输出的结果是可以直接作为数组下标.
x = np.array([[1, 5, 8, 1], [2, 4, 6, 8], [3, 6, 7, 9], [6, 8, 3, 1]])
print(x[b]) 结果为 x 的第 2,3 行组成的数组 [[3 6 7 9] [6 8 3 1]], 等价于 x[[2,3]],x[2,3] 为输出为元素 9,x[[2],[3]]输出数组[9].
来源: https://www.cnblogs.com/lyxML/p/9526089.html