目录
sklearn中的贝叶斯分类器
前言
1 分类器介绍
2 高斯朴素贝叶斯GaussianNB
2.1 认识高斯朴素贝叶斯
2.2 高斯朴素贝叶斯建模案例
2.3 高斯朴素贝叶斯擅长的数据集
2.3.1 三种数据集介绍
2.3.2 构建三种数据
2.3.3 数据标准化
2.3.4 朴素贝叶斯处理数据
2.4 高斯朴素贝叶斯的拟合效果与运算速度
2.4.1 交叉验证的概念
2.4.2 ShuffleSplit和learning_curve
2.4.3 交叉验证分析
3 概率模型的评估指标
3.1 前言
3.2 布里尔分数Brier Score
3.3 对数似然函数Log Loss
3.4 可靠性曲线 reliability curve
3.4.1 基础知识
3.4.2 模型评估
1 朴素贝叶斯评估
2 其它模型
3.5 预测概率的直方图
3.6 校准可靠性曲线
3.6.1 基础知识
3.6.2 分析贝叶斯模型
3.6.3 SVC校准效果
4 其它贝叶斯
4.1 多项式朴素贝叶斯MultinomialNB
1 如何判定数据符合多项式朴素贝叶斯
2 MultinomialNB语法格式
3 代码实现
1 基础知识
2 连续型数据处理
2 分类型数据
4.2 伯努利贝叶斯
1 BernoulliNB语法格式
2 代码例子
4.3 类别贝叶斯CategoricalNB
1 CategoricalNB语法格式
2 代码例子
4.4 贝叶斯的样本不平衡问题
4.5 补集贝叶斯
1 ComplementNB语法格式
2 代码例子
sklearn中的贝叶斯分类器
前言
sklearn下各种朴素贝叶斯的分类器的原理可看sklearn之各类朴素贝叶斯原理
1 分类器介绍
Sklearn基于数据分布以及这些分布上的概率估计的改进,为我们提供了四个朴素贝叶斯的分类器。
类 | 含义 |
naive_bayes.BernoulliNB | 伯努利分布下的朴素贝叶斯 |
naive_bayes.GaussianNB | 高斯分布下的朴素贝叶斯 |
naive_bayes.MultinomialNB | 多项式分布下的朴素贝叶斯 |
naive_bayes.ComplementNB | 补集朴素贝叶斯 |
naive_bayes.CategoricalNB | 类别贝叶斯 |
linear_model.BayesianRidge | 贝叶斯岭回归,在参数估计过程中使用贝叶斯回归技术来包括正则化参数 |
贝叶斯有以下特点
- 贝叶斯是从概率角度进行估计,不需要太多的样本量,极端情况下甚至我们可以使用1%的数据作为训练集,依然可以得到很好的拟合效果。当然,如果样本量少于特征数目,贝叶斯的效果就会被削弱。
- 与SVM和随机森林相比,朴素贝叶斯运行速度更快,因为求解本质是在每个特征上单独对概率进行计算,然后再求乘积,所以每个特征上的计算可以是独立并且并行的
- 贝叶斯的运行效果不是那么好,贝叶斯模型预测结果也不是总指向真正的分类结果
2 高斯朴素贝叶斯GaussianNB
2.1 认识高斯朴素贝叶斯
class sklearn.naive_bayes.GaussianNB(*, priors=None, var_smoothing=1e-09)
参数说明:
参数 | 说明 |
---|---|
priors | array-like of shape (n_classes,) 类别的先验概率。一经指定,不会根据数据进行调整。 |
var_smoothing | float, default=1e-9 所有特征的最大方差部分,添加到方差中用于提高计算稳定性。 |
属性说明:
属性 | 说明 |
---|---|
class_count_ | ndarray of shape (n_classes,) 每个类别中保留的训练样本数量。 |
class_prior_ | ndarray of shape (n_classes,) 每个类别的概率。 |
classes_ | ndarray of shape (n_classes,) 分类器已知的类别标签 |
epsilon_ | float 方差的绝对相加值 |
sigma_ | ndarray of shape (n_classes, n_features) 每个类中每个特征的方差 |
theta_ | ndarray of shape (n_classes, n_features) 每个类中每个特征的均值 |
方法说明:
方法 | 说明 |
---|---|
fit (X, y[, sample_weight]) | 根据X,y拟合高斯朴素贝叶斯 |
get_params ([deep]) | 获取这个估计器的参数 |
partial_fit (X, y[, classes, sample_weight]) | 对一批样本进行增量拟合 |
predict (X) | 对测试向量X进行分类。 |
predict_log_proba (X) | 返回针对测试向量X的对数概率估计 |
predict_proba (X) | 返回针对测试向量X的概率估计 |
score (X, y[, sample_weight]) | 返回给定测试数据和标签上的平均准确率。 |
set_params (**params) | 为这个估计器设置参数 |
对X矩阵和y矩阵的要求:
参数 | 说明 |
---|---|
X | array-like of shape (n_samples, n_features) 用于训练的向量,其中n_samples是样本数量,n_features是特征数量。 |
y | array-like of shape (n_samples,) 目标值。 |
2.2 高斯朴素贝叶斯建模案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
# 导入数据集
digits = load_digits()
X = digits.data
y = digits.target
# 划分测试集和训练数据集,划分后,训练数据1257个样本,测试数据集540个样本
xtrain, xtest, ytrain, ytest = train_test_split(X, y, test_size=0.3, random_state=0)
# 查看标签种类
print(f'种类标签为:\n{np.unique(ytrain)}')
# 实例化模型并且训练模型,其中fit()过程就是在计算概率的过程
gnb = GaussianNB().fit(xtrain, ytrain)
# score()接口对于我们的分类型算法,返回预测的精确性,也就是accuracy,使用测试数据集测试
acc_score = gnb.score(xtest, ytest)
print(f'预测的精确性为:{acc_score}')
# 返回所有样本对应的类别,这里的样本标签是用我们下面得到的概率中,
# 选取每一个样本中概率最大的作为此样本的标签
y_pred = gnb.predict(xtest)
print(f'贝叶斯模型对所有样本的预测类别为:\n{y_pred}')
# 查看我们的概率结果
# 可以看到,返回的结果是540*10的二维矩阵,其中应为分类有10个,所以一共返回10列概率
# 取其中概率最大的哪一个分类作为最后的分类结果,并且每一行的概率之和是1
yprob = gnb.predict_proba(xtest)
print(f'查看样本计算的概率结果,结果为:\n{yprob}')
# 注意,ROC曲线是不能用于多分类的。多分类状况下最佳的模型评估指标是混淆矩阵和整体的准确度
# 主对角线上的点是全部分类正确的,而我们的主对角线的数值很大,因此准确率很高
from sklearn.metrics import confusion_matrix as CM
print(f'混淆矩阵为:\n{CM(ytest, y_pred)}')
知识点补充
(1)train_test_split
语法格式如下
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=None, shuffle=True, stratify=None)
- X: 表示要划分的特征数据集。
- y: 表示相应的标签。
- test_size: 测试集的大小。可以是浮点数(表示比例)或整数(表示样本数),默认为 0.25。
- random_state: 随机种子,用于控制随机数生成过程。如果设置为某个整数,则每次划分数据集时将使用相同的随机种子,以确保结果的可重复性。如果不设置,则每次运行时都会生成不同的随机种子。
- shuffle: 是否在划分数据集之前对数据进行洗牌。默认为 True,即洗牌。
- stratify: 可选参数,用于在划分数据集时根据标签的分布来进行分层抽样。如果指定了该参数,并且标签是分类问题的话,将会根据标签的类别进行分层抽样,确保训练集和测试集中各类别的样本比例与原始数据集中的比例相同。默认为 None,表示不进行分层抽样。
(2) load_digits
主要的参数如下:
- data: 包含手写数字的特征数据,每一行代表一个样本,每一列代表一个特征(像素)。
- target: 包含每个样本对应的标签(即手写数字的真实值)。
- target_names: 包含标签的名称,即手写数字的可能取值。
- images: 包含手写数字的图像数据,是 8x8 像素的灰度图像。 DESCR: 数据集的描述信息,包括数据集的来源、特征的含义等。
索引
data
images
其中每个元素是8×8的矩阵
target
每个数据对应的标签
target_names
所有的类别标签
data[1]元素为例
64维的向量
8×8的矩阵,可以大致看出数字 ‘1’ 的轮廓
用plt.imshow()可以将images可视化
该数据的类别
2.3 高斯朴素贝叶斯擅长的数据集
2.3.1 三种数据集介绍
月亮型数据(Moon-shaped data)
月亮型数据是指具有类似月亮形状的数据分布。一般使用sklearn.datasets的make_moons方法,调入方式为:
from sklearn.datasets import make_moons
其语法格式为:
make_moons(n_samples=100, *, shuffle=True, noise=None, random_state=None)
- n_samples: 生成的样本数量,默认为 100。
- shuffle: 是否对数据进行洗牌,默认为 True。如果设置为 False,则生成的数据将按顺序排列。
- noise: 添加到数据集中的高斯噪声的标准差。如果为 None,则不添加噪声。默认为 None。
- random_state: 随机种子,用于控制随机数生成过程。如果设置为某个整数,则每次生成数据集时将使用相同的随机种子,以确保结果的可重复性。如果不设置,则每次运行时都会生成不同的随机种子。
环形数据(Ring-shaped data)
环形数据是指具有环形或圆环形状的数据分布。数据点围绕着一个中心点呈环状排列。一般使用sklearn.datasets的make_circles方法,调入方式为:
from sklearn.datasets import make_circles
其语法格式为:
make_circles(n_samples=100, *, shuffle=True, noise=None, random_state=None, factor=0.8)
- n_samples:生成的样本数,默认为 100。
- shuffle:是否打乱样本,默认为 True。
- noise:添加到数据中的高斯噪声的标准差,若为 None,则表示不添加噪声,默认为 None。
- random_state:随机种子,默认为 None。
- factor:外圈与内圈半径之比,默认为 0.8,取值范围为 0 到 1,越接近 1,内外圈之间的距离越大。
二分型数据(Bimodal data)
二分型数据在机器学习中指的是具有两类标签或类别的数据集。 一般使用sklearn.datasets的make_classification方法,调入方式为:
from sklearn.datasets import make_classification
其语法格式为:
make_classification(n_samples=100, n_features=20, *, n_informative=2, n_redundant=2, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)
- n_samples:生成的样本数,默认为 100。
- n_features:特征数,默认为 20。
- n_informative:具有信息性的特征数,默认为 2。
- n_redundant:冗余特征数,默认为 2。
- n_classes:类别数量,默认为 2。
- n_clusters_per_class:每个类别中的簇数量,默认为 2。
- weights:每个类别的样本权重,默认为 None。
- flip_y:在生成标签时翻转(翻译)标签的比例,默认为 0.01。
- class_sep:类之间的平均距离,默认为 1.0。
- hypercube:如果为 True,则将样本分布在一个多维超立方体中,默认为 True。
- shift:数据集的平移量,默认为 0.0。
- scale:数据集的缩放比例,默认为 1.0。
- shuffle:是否打乱样本,默认为 True。
- random_state:随机种子,默认为 None。
2.3.2 构建三种数据
构建月亮型数据
构建环形数据
构建二分型数据
由上图可知:生成的二分型数据的两个簇离彼此很远,这样不利于我们测试分类器的效果,因为这样的数据无论什么模型都是几乎百分百正确率,因此我们使用np生成随机数组,通过让已经生成的二分型数据点加减0~1之间的随机数,使数据分布变得更散更稀疏
2.3.3 数据标准化
使用sklearn.preprocessing的StandardScaler函数来将数据标准化
- 训练数据,采用fit_transform()函数
- 测试数据,采用tansform()函数
fit_transform()函数的计算过程:计算均值/标准差,使用均值/标准差数据转换;tansform()函数则直接使用fit_transform()函数的均值和标准差来将数据转换
StandardScaler原理
标准差标准化(standardScale)使得经过处理的数据符合标准正态分布,即均值为0,标准差为1,其转化函数为:
其中μ为所有样本数据的均值,σ为所有样本数据的标准差
数据标准化的好处是:在实际数据中,不同特征的取值范围可能会相差较大,这会导致某些特征在模型训练过程中对结果产生更大的影响。通过标准化,可以消除这种量纲差异,使得各个特征在模型训练中对结果的影响更加平衡。
2.3.4 朴素贝叶斯处理数据
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB, ComplementNB
h = .02
# 模型的名字
names = ["Multinomial", "Gaussian", "Bernoulli", "Complement"]
# 创建我们的模型对象
classifiers = [MultinomialNB(), GaussianNB(), BernoulliNB(), ComplementNB()]
# 构造数据
# 创建二分型数据集
X, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
random_state=1, n_clusters_per_class=1)
# 处理二分型数据集,使数据更加稀疏
rng = np.random.RandomState(2) #生成一种随机模式
X += 2 * rng.uniform(size=X.shape) #加减0~1之间的随机数
linearly_separable = (X, y) #生成了新的X,依然可以画散点图来观察一下特征的分布
# 准备数据集合,包含了月亮型数据,环型数据,二分型数据
datasets = [make_moons(noise=0.3, random_state=0),
make_circles(noise=0.2, factor=0.5, random_state=1),
linearly_separable
]
# 创建画布,宽高比为6*9
figure = plt.figure(figsize=(6, 9))
#设置用来安排图像显示位置的全局变量i
i = 1
for ds_index, ds in enumerate(datasets):
# ——————————————第一步:对测试集和训练集进行可视化——————————————
X, y = ds
# 对X中的数据进行标准化
X = StandardScaler().fit_transform(X)
# 区分测试集和训练集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.4, random_state=42)
# 找出数据集中两个特征的最大值和最小值,让最大值+0.5,最小值-0.5,创造一个比两个特征的区间本身更大一点的区间
x1_min, x1_max = X[:, 0].min() - .5, X[:, 0].max() + .5
x2_min, x2_max = X[:, 1].min() - .5, X[:, 1].max() + .5
# 函数meshgrid用以生成网格数据;函数np.arange在给定的两个数之间返回均匀间隔的值,0.2为步长
# 生成的网格数据,是用来绘制决策边界的,因为绘制决策边界的函数contourf要求输入的两个特征都必须是二维的
array1, array2 = np.meshgrid(np.arange(x1_min, x1_max, 0.2),
np.arange(x2_min, x2_max, 0.2))
# 创建一种颜色映射(colormap),值较大则显示蓝色,值较小则显示红色,中间值为白色
cm = plt.cm.RdBu
# 用ListedColormap为画布创建颜色,#FF0000正红,#0000FF正蓝
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
# 在画布上加上一个子图,数据为len(datasets)行,2列,放在位置i上
ax = plt.subplot(len(datasets), 2, i)
# 只需要在第一个坐标系上有标题,因此设定if ds_index==0这个条件
if ds_index == 0:
ax.set_title("Input data")
# 将数据集的分布放到我们的坐标系上
# 使用cm_bright画布,其中类别为0是正红,类别为1是正蓝
# 放训练集
ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train,
cmap=cm_bright, edgecolors='k')
# 放测试集
ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test,
cmap=cm_bright, alpha=0.6, edgecolors='k')
# 为图设置坐标轴的最大值和最小值
ax.set_xlim(array1.min(), array1.max())
ax.set_ylim(array2.min(), array2.max())
# 设置没有坐标轴
ax.set_xticks(())
ax.set_yticks(())
# 每次循环之后,改变i的取值让图每次位列不同的位置
i += 1
# ——————————————第二步:从这里开始是贝叶斯模型,对贝叶斯模型处理的数据可视化——————————————
# 在画布上加上一个子图,数据为len(datasets)行,2列,放在位置i上
# 这里的i取值分别为2,4,6
ax = plt.subplot(len(datasets), 2, i)
# 高斯朴素贝叶斯的建模过程:fit训练 → score接口得到预测的准确率
clf = GaussianNB().fit(X_train, y_train)
score = clf.score(X_test, y_test)
# 绘制决策边界,为此,我们将为网格中的每个点指定一种颜色[x1_min,x1_max] x [x2_min,x2_max]
# ravel()能够将一个多维数组转换成一维数组,np.c_是能够将两个数组组合起来的函数
# 使用ravel()和np.c_为网格中每个点指定一种颜色
# 使用高斯朴素贝叶斯的predict_proba,返回每一个输入的数据点所对应的标签类概率,[:, 1]代表只取标签类为1的概率
Z = clf.predict_proba(np.c_[array1.ravel(), array2.ravel()])[:, 1]
# 将返回的类概率作为数据,放到contourf里面绘制去绘制轮廓
# 由于取了类别1的类概率,当类概率越高时,代表类1,则显示正蓝;类概率越低时,代表类0,则显示正红。与图中的训练集和测试集的颜色相对应
Z = Z.reshape(array1.shape)
ax.contourf(array1, array2, Z, cmap=cm, alpha=.8)
# 同理,由于i+1,在新位置的图上也要放置测试集和训练集
ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright,
edgecolors='k')
ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright,
edgecolors='k', alpha=0.6)
# 为图设置坐标轴的最大值和最小值
ax.set_xlim(array1.min(), array1.max())
ax.set_ylim(array2.min(), array2.max())
# 设置没有坐标轴
ax.set_xticks(())
ax.set_yticks(())
# 只需要在第一个坐标系上有标题,因此设定if ds_index==0这个条件
if ds_index == 0:
ax.set_title("Gaussian Bayes")
# (array1.max() - .3, array2.min() + .3)位置坐标
# ('{:.1f}%'.format(score * 100)将其转换为百分比
# horizontalalignment='right'右对齐方式
ax.text(array1.max() - .3, array2.min() + .3, ('{:.1f}%'.format(score * 100)),
size=15, horizontalalignment='right')
i += 1
# 对当前图像进行调整,使得子图参数自动适应图像区域,避免了子图之间或子图与标签之间的重叠。
plt.tight_layout()
# 显示图片
plt.show()
其代码结果如下:
从图上来看,高斯贝叶斯属于比较特殊的一类分类器,其分类效果在二分数据和月亮型数据上表现优秀,有85%以上的准确率,但是环形数据不太擅长。
2.4 高斯朴素贝叶斯的拟合效果与运算速度
2.4.1 交叉验证的概念
交叉验证(Cross-validation):一种将数据集分割成多个小部分,然后多次对模型进行训练和验证的过程。通过多次进行这个过程,可以评估模型的泛化性能和稳定性。
蒙特卡罗交叉验证,也称为Shuffle Split交叉验证,是一种非常灵活的交叉验证策略。在这种技术中,数据集被随机划分为训练集和验证集。在这种交叉认证方式中,自由决定要用做训练集和验证集的百分比(训练集和验证集的百分比加起来不一定是100%),自由决定训练次数。
2.4.2 ShuffleSplit和learning_curve
(1)ShuffleSplit
引入方式:
from sklearn.model_selection import ShuffleSplit
语法格式:
ShuffleSplit(n_splits=10, *, test_size=None, train_size=None, random_state=None)
- n_splits:划分训练集、测试集的次数,默认为10。
- test_size:测试集的大小。可以是浮点数(表示测试集占总样本的比例)或整数(表示测试集的绝对大小)。如果未提供,则默认为 0.1。
- train_size:训练集的大小。可以是浮点数(表示训练集占总样本的比例)或整数(表示训练集的绝对大小)。如果未提供,则默认为 1 - test_size。
- random_state:随机种子,用于控制随机打乱的过程。
例子如下:
# 创建了一个ShuffleSplit对象
cv = ShuffleSplit(n_splits=50 # 划分训练集、测试集的次数,这里是50次
, test_size=0.2 # 其中有20%数据作为测试集
, random_state=0) # 在交叉验证的时候进行的随机抽样的模式
这里例子中的蒙特卡罗交叉验证有20%的数据作为测试集,80%作为训练集,并且重复的次数是50次
(2)learning_curve
2.1 语法格式
引入方式:
from sklearn.model_selection import learning_curve
语法格式:
learning_curve(estimator, X, y, *, train_sizes=None, cv=None, scoring=None, exploit_incremental_learning=False, n_jobs=None, pre_dispatch='all', verbose=0, shuffle=False, random_state=None, error_score='raise', return_times=False)
- estimator:用于拟合数据的模型对象。
- X:特征数据。
- y:目标数据。
- train_sizes:用于生成学习曲线的训练集大小。可以是整数、浮点数或者序列。如果是整数,表示用于学习曲线的每个点的训练集大小;如果是浮点数,表示训练集大小相对于整个数据集的比例;如果是序列,表示具体的训练集大小列表。
- cv:用于交叉验证的交叉验证迭代器,默认为 5 折交叉验证。
- scoring:用于评估模型性能的指标。默认情况下,使用模型的 score 方法来计算。可以指定为字符串(例如 'accuracy'、'precision' 等),也可以传入一个自定义的评估函数。
- n_jobs:并行运行的作业数量。如果设置为 -1,则使用所有可用的 CPU 核心。默认为 None,表示不并行运行。
- shuffle:在每次迭代前对数据进行洗牌,以防止每次划分的数据不同。默认为 False。 random_state:随机种子,用于控制洗牌过程的随机性。
- error_score:当模型在某些子集上无法拟合时,用于替代分数的值。
- return_times:是否返回每次拟合模型的时间。默认为 False。
- learning_curve 函数返回一个元组,其中包含训练样本大小、训练集得分、验证集得分等信息。
例子如下:
from sklearn.naive_bayes import GaussianNB
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
datas = load_digits()
X = datas.data
y = datas.target
clf = GaussianNB()
# 交叉验证的模式
# 这里使用的训练集数据比例是默认的,5次
cv = ShuffleSplit(n_splits=50 # 划分训练集、测试集的次数,这里是50次
, test_size=0.2 # 其中有20%的数据会被作为测试集
, random_state=0) # 交叉验证所进行的随机抽样的模式
# 默认进行5次验证,每次验证会划分50次训练集和测试集进行测试,其中20%作为测试集
# train_sizes 表示每次分训练集和测试集建模之后,训练集上的样本数量
# train_scores 训练集上的分数 test_scores 测试集上的分数
train_sizes, train_scores, test_scores = learning_curve(clf # 标示分类器
, X, y # 特征矩阵和标签
, cv=cv # 设置交叉验证的模式
, n_jobs=4) # 每一次运行可以使用的线程数目,表示允许算法使用多少运算资源
learning_curve会进行5次建模,每次建模的训练集样本如下:
train_scores和test_scores返回的结果是(5,50),这是因为ShuffleSplit是蒙特卡罗交叉验证,将划分测试集和训练集进行了50次,相当于进行了50次训练
2,2 学习曲线常见三种情况
学习曲线能判定偏差(bias)和方差(validation)问题
第一种:当训练集和测试集的误差收敛但却很高时,为高偏差-----欠拟合。
如左上图所示,训练集准确率与验证集准确率收敛,但是两者收敛后的准确率远小于我们的期望准确率(上面那条红线),因此该模型属于欠拟合(underfitting)问题。由于欠拟合,所以需要增加模型的复杂度,比如,增加特征、减小正则项等
第二种:当训练集和测试集的误差之间有大的差距时,为高方差----过拟合。
如右上图所示,训练集准确率高于期望值,验证集则低于期望值,两者之间有很大的间距,误差很大,对于新的数据集模型适应性较差,因此该模型属于过拟合(overfitting)问题。由于过拟合,所以需要降低模型的复杂度,比如增大样本数、减少特征数等等。
第三种:一个比较理想的学习曲线图应当是:低偏差、低方差,即收敛且误差小。
如左下图所示,训练集准确率与验证集准确率收敛于期望值,则为低偏差、低方差,这是比较理想的学习曲线图
2.4.3 交叉验证分析
# ————第一步:导入所需要的库————
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.tree import DecisionTreeClassifier as DTC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
from time import time
import datetime
# ————第二步:定义绘制学习曲线的函数————
def plot_learning_curve(estimator, title, X, y, # estimator标示使用的分类器
ax, # 选择子图
ylim=None, # 设置纵坐标的取值范围
cv=None, # 交叉验证
n_jobs=None # 设定索要使用的线程
):
# 使用learning_curve返回必要参数
train_sizes, train_scores, test_scores = learning_curve(estimator, X, y
, cv=cv, n_jobs=n_jobs)
# 设置标题
ax.set_title(title)
# 设置y坐标的量纲形式一样,即把多个子图的y坐标设置为一样
# 使用*号解包
if ylim is not None:
ax.set_ylim(*ylim)
# 设置横纵坐标名字
ax.set_xlabel("Training examples")
ax.set_ylabel("Score")
# 显示网格作为背景,不是必须
ax.grid()
# 分别画训练集和测试集的学习曲线
# 横坐标是train_sizes,纵坐标分别是训练分数的均值和测试分数的均值
ax.plot(train_sizes, np.mean(train_scores, axis=1), 'o-'
, color="r", label="Training score") # 画训练数据集的图像
ax.plot(train_sizes, np.mean(test_scores, axis=1), 'o-'
, color="g", label="Test score") # 画出测试集图像
# 在图上标明一个图例,用于说明每条曲线的文字显示
ax.legend(loc="best")
# 返回子图
return ax
# ————第三步:导入数据,定义循环————
# 导入数据
digits = load_digits()
X, y = digits.data, digits.target
# 定义标题
title = ["Naive Bayes","DecisionTree","SVM, RBF kernel","RandomForest","Logistic"]
# 定义五个算法模型
model = [GaussianNB(),DTC(),SVC(gamma=0.001)
,RFC(n_estimators=50),LR(C=.1,solver="lbfgs")]
# 定义交叉验证模式
cv = ShuffleSplit(n_splits=50, test_size=0.2, random_state=0)
# ————第四步: 进入循环,绘制学习曲线————
# 设置画布一行五列,尺寸是30×6
# fig是画布,axex是子图对象
fig, axes = plt.subplots(1,5,figsize=(30,6))
# ind代表每次循环的索引,title代表每次循环的标题,estimator代表每次循环的算法模型
for ind,title_,estimator in zip(range(len(title)),title,model):
# 记录当前时间
times = time()
# 调用学习曲线函数
plot_learning_curve(estimator, title_, X, y,
ax=axes[ind], ylim = [0.7, 1.05],n_jobs=4, cv=cv)
# 记录使用时间
print("{}:{}".format(title_,datetime.datetime.fromtimestamp(time()-
times).strftime("%M:%S:%f")))
# 显示图像
plt.show()
查看zip(range(len(title)),title,model
代码结果如下:
对结果进行如下分析:
- 各个代码运行时间。决策树和贝叶斯不相伯仲(如果你没有发现这个结果,可以多运行几次,最终发现贝叶斯和决策树的运行时间逐渐变得差不多)。决策树之所以能够运行非常快速是因为sklearn中的分类树在选择特征时有所“偷懒”,没有计算全部特征的信息熵而是随机选择了一部分特征来进行计算,因此速度快,但决策树的运算效率随着样本量逐渐增大会越来越慢;朴素贝叶斯可以在很少的样本上获得不错的结果,可以预料,随着样本量的逐渐增大贝叶斯会逐渐变得比决策树更快;朴素贝叶斯计算速度远远胜过SVM,随机森林这样复杂的模型,逻辑回归的运行受到最大迭代次数的强烈影响和输入数据的影响(逻辑回归一般在线性数据上运行都比较快,但在这里是受到了稀疏矩阵的影响)。综上在运算时间上,朴素贝叶斯还是十分有优势的。
- 训练集上的拟合结果。手写数字数据集是一个较为简单的数据集,决策树,森林,SVC和逻辑回归都成功拟合了100%的准确率,但贝叶斯的最高训练准确率都没有超过95%,说明朴素贝叶斯的分类效果不如其他分类器,贝叶斯天生学习能力比较弱;随着训练样本量的逐渐增大,其他模型的训练拟合都保持在100%的水平,但贝叶斯的训练准确率却逐渐下降,这说明样本量越大,贝叶斯需要学习的东西越多,对训练集的拟合程度也越来越差。
- 过拟合问题。所有模型在样本量很少的时候都是出于过拟合状态的(训练集上表现好,测试集上表现糟糕),但随着样本的逐渐增多,过拟合问题都逐渐消失了。SVM,随机森林和逻辑归,决策树通过提高模型在测试集上的表现来减轻过拟合问题,而朴素贝叶斯是依赖训练集上的准确率下降,测试集上的准确率上升来逐渐解决过拟合问题。
- 测试集上的拟合结果,即泛化误差的大小。贝叶斯和决策树在测试集上的表现远远不如SVM,随机森林和逻辑回归,VM在训练数据量增大到1500个样本左右的时候,测试集上的表现已经非常接近100%,随机森林和逻辑回归的表现也在95%以上,而决策树和朴素贝叶斯还徘徊在85%左右;决策树虽然测试结果不高,但是依然具有潜力,因为它的过拟合现象非常严重,可以通过减枝来让决策树的测试结果逼近训练结果;贝叶斯的过拟合现象在训练样本达到1500左右的时候已经几乎不存在了,训练集上的分数和测试集上的分数非常接近;综上判断,85%左右就是贝叶斯在这个数据集上的极限了,如果我们进行调参,决策树最后应该可以达到90%左右的预测准确率。
综上所述,可以得出结论:
- 贝叶斯速度很快,但分类效果一般
- 如果数据十分复杂,或者是稀疏矩阵,则使用贝叶斯
3 概率模型的评估指标
3.1 前言
混淆矩阵和精确性可以帮助我们了解贝叶斯的分类结果。然而,选择贝叶斯进行分类,大多数时候不是为了单单追求效果,而是希望看到预测的相关概率。这种概率给出预测的可信度,所以对于概率类模型,我们希望能够由其他的模型评估指标来帮助我们判断,模型在“概率预测”这项工作上,完成得如何。
3.2 布里尔分数Brier Score
(1)布里尔分数的概念
概率预测的准确程度被称为“校准程度”,是衡量算法预测出的概率和真实结果的差异的一种方式。一种比较常用的指标叫做布里尔分数,它被计算为是概率预测相对于测试样本的均方误差,表示为:
其中N是样本数量,是朴素贝叶斯预测出的概率,是样本所对应的真实结果,只能取0或1,如果事件发生则为1,否则为0。这个指标衡量了我们的概率距离真实标签结果的差异,看起来非常像是均方误差。布里尔分数的范围是从0到1,分数越高则预测结果越差劲,校准程度越差,因此布里尔分数越接近0越好。
注意:布里尔分数可以用于任何可以使用predict_proba接口调用概率的模型
(2)brier_score_loss语法格式
brier_score_loss(y_true, y_prob, sample_weight=None, pos_label=None)
- y_true:实际的标签,通常是一个包含了真实标签的数组。
- y_prob:模型对每个样本属于正类的预测概率,通常是一个二维数组,每行对应一个样本,每列对应一个类别的预测概率。
- sample_weight:可选参数,样本权重,用于加权计算 Brier 分数。
- pos_label:可选参数,正类的标签值。默认情况下,pos_label 是1。
注意:新版sklearn的brier_score_loss不再支持多分类,只能用于二分类
(3)例子
由于brier_score_loss不能用于多分类,因此使用只有二分类的乳腺癌数据进行评估,后续再思考如何解决多分类的问题
import pandas as pd
from sklearn.metrics import brier_score_loss
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_breast_cancer
# 第一步---载入数据集---
datas =load_breast_cancer()
X = datas.data
y = datas.target
# 划分测试集和训练数据集
xtrain, xtest, ytrain, ytest = train_test_split(X, y, test_size=0.3, random_state=0)
# 第二步---训练模型---
# 训练高斯朴素贝叶斯模型
gnb = GaussianNB().fit(xtrain, ytrain)
# 训练svc模型
# 我们将SVM模型的probability参数设置为True。这样做是为了在模型预测时可以输出样本的概率。
# 使用了probability参数后,就可以使用predict_proba接口,进而可以使用布里尔分数评估
svc = SVC(probability=True)
svc.fit(xtrain, ytrain)
prob_svc = svc.predict_proba(xtest)
# 训练逻辑归模型
logi = LR(C=1., solver='lbfgs',max_iter=3000,multi_class="auto").fit(xtrain,ytrain)
# 第三步---每个分类器每个标签类别下的布里尔分数可视化---
# 定义三种模型以及颜色
name = ["Bayes","Logistic","SVC"]
color = ["red","black","orange"]
# 由于数据集的类别有两种,有三种模型,因此设定DataFrame为两行三列
df = pd.DataFrame(index=range(2),columns=name)
# 通过循环记录每个类别的布里尔分数
for i in range(2):
df.loc[i, name[0]] = brier_score_loss(ytest, gnb.predict_proba(xtest)[:, i], pos_label=i)
df.loc[i, name[1]] = brier_score_loss(ytest, logi.predict_proba(xtest)
[:, i], pos_label=i)
df.loc[i, name[2]] = brier_score_loss(ytest, svc.predict_proba(xtest)[:, i], pos_label=i)
# 输出DataFrame
print(f'三种模型每个类别的布里尔分数为:\n{df}')
# 类别数为横坐标,每种模型为纵坐标
# 分别为三种模型都画图
for i in range(df.shape[1]):
plt.plot(range(2), df.iloc[:, i], c=color[i],label=name[i])
# 添加图标标题
plt.legend(loc='best')
# 展示结果
plt.show()
可以看到:逻辑回归的布里尔分数是最好的,但是逻辑回归在迭代过程中没有收敛(这里没有放出警告,后续可能会思考解决这个收敛问题),贝叶斯效果比SVC和逻辑回归都差,但总的来说效果也不错
3.3 对数似然函数Log Loss
(1)对数似然函数的概念
一种常用的概率损失衡量是对数损失(log_loss),又叫做对数似然,逻辑损失或者交叉熵损失,它是多元逻辑回归以及一些拓展算法(比如神经网络中)使用的损失函数。它被定义为,对于一个给定的概率分类器,在预测概率为条件的情况下,真实概率发生的可能性的负对数。由于是损失,因此对数似然函数的取值越小,则证明概率估计越准确,模型越理想。值得注意得是,对数损失只能用于评估分类型模型
对于一个样本,如果样本的真实标签在{0,1}中取值,并且这个样本在类别1下的概率估计为
,则这个样本所对应的对数损失是:
注意:这里的 表示以 为底的自然对数。
(2)log_loss语法格式
log_loss(y_true, y_pred, eps=1e-15, normalize=True, sample_weight=None, labels=None)
- y_true:实际的标签,通常是一个包含了真实标签的数组。
- y_pred:模型对每个样本属于每个类别的预测概率,通常是一个二维数组,每行对应一个样本,每列对应一个类别的预测概率。
- eps:用于防止概率值为 0 或 1 时的数值稳定性,避免取对数时出现无穷大。默认值为 1e-15。
- normalize:可选参数,指定是否将计算的损失值归一化。如果设置为 True(默认),则返回的是每个样本的平均对数损失;如果设置为 False,则返回的是每个样本的总对数损失。
- sample_weight:可选参数,样本权重,用于加权计算对数损失。
- labels:可选参数,用于指定类别标签。如果不提供,则函数会自动识别标签。
(3)例子
Log Loss可以用于多分类情况,因此使用手写数据集来测试,当然也可以用乳腺癌数据
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_digits
from sklearn.metrics import log_loss
from sklearn.datasets import load_breast_cancer
# 第一步---载入数据集---
datas = load_digits()
X = datas.data
y = datas.target
# 划分测试集和训练数据集
xtrain, xtest, ytrain, ytest = train_test_split(X, y, test_size=0.3, random_state=0)
# 第二步---训练模型---
# 训练高斯朴素贝叶斯模型
gnb = GaussianNB().fit(xtrain, ytrain)
# 训练svc模型
# 我们将SVM模型的probability参数设置为True。这样做是为了在模型预测时可以输出样本的概率。
# 使用了probability参数后,就可以使用predict_proba接口,进而可以使用布里尔分数评估
svc = SVC(probability=True)
svc.fit(xtrain, ytrain)
prob_svc = svc.predict_proba(xtest)
# 训练逻辑归模型
logi = LR(C=1., solver='lbfgs',max_iter=3000,multi_class="auto").fit(xtrain,ytrain)
# 第三步---求log_loss分数---
# 定义三种模型以及颜色
name = ["Bayes","Logistic","SVC"]
color = ["red","black","orange"]
# 创建DataFrame
df = pd.DataFrame(index=['log_loss'],columns=name)
# 记录每个模型的log_loss分数
df.loc['log_loss', name[0]] = log_loss(ytest, gnb.predict_proba(xtest))
df.loc['log_loss', name[1]] = log_loss(ytest, logi.predict_proba(xtest))
df.loc['log_loss', name[2]] = log_loss(ytest, svc.predict_proba(xtest))
# 输出DataFrame
print(f'三种模型的log_loss分数为:\n{df}')
结果如下:
无论是乳腺癌数据集还是手写数据集,朴素贝叶斯的效果都是最差的。因为逻辑回归和SVC都是以最优化为目的来求解模型,然后进行分类的算法。而朴素贝叶斯中,却没有最优化的过程。对数似然函数直接指向模型最优化的方向,甚至就是逻辑回归的损失函数本身,因此在逻辑回归和SVC上表现得更好。
(4)什么时候使用对数似然
对数似然函数是概率类模型评估的黄金指标,往往是我们评估概率类模型的优先选择。但是它也有缺点:
- 它没有界,不像布里尔分数有上限,可以作为模型效果的参考
- 它的解释性不如布里尔分数,很难与非技术人员去交流对数似然存在的可靠性和必要性
- 它在以最优化为目标的模型上明显表现更好。而且,它还有一些数学上的问题,比如不能接受为0或1的概率,否则的话对数似然就会取到极限值(考虑以e为底的自然对数在取到0或1的时候的情况)
综上所述,有如下规则:
需求 | 优先使用对数似然 | 优先使用布里尔分数 |
衡量模型 | 要对比多个模型,或者衡量模型的不同变化 | 衡量单一模型的表现 |
可解释性 | 机器学习和深度学习之间的行家交流,学术论文 | 商业报告,老板开会,业务模型的衡量 |
最优化指向 | 逻辑回归,SVC | 朴素贝叶斯 |
数学问题 | 概率只能无限接近于0或1,无法取到0或1 | 概率可以取到0或1,比如树,随机森林 |
贝叶斯的原理简单,根本没有什么可用的参数。但是产出概率的算法有自己的调节方式,就是调节概率的校准程度。校准程度越高,模型对概率的预测越准确,算法在做判断时就越有自信,模型就会更稳定。如果我们追求模型在概率预测上必须尽量贴近真实概率,那就可以使用可靠性曲线来调节概率的校准程度
3.4 可靠性曲线 reliability curve
3.4.1 基础知识
(1)概念
可靠性曲线(reliability curve),又叫做概率校准曲线(probability calibration curve),可靠性图(reliability diagrams),这是一条以预测概率为横坐标,真实标签为纵坐标的曲线。我们希望预测概率和真实值越接近越好,最好两者相等,因此一个模型/算法的概率校准曲线越靠近对角线越好。校准曲线因此也是我们的模型评估指标之一。和布里尔分数相似,概率校准曲线是对于标签的某一类来说的,因此一类标签就会有一条曲线,或者我们可以使用一个多类标签下的平均来表示一整个模型的概率校准曲线。但通常来说,曲线用于二分类的情况最多
(2)make_classification语法格式
sklearn.datasets.make_classification(n_samples=100, n_features=20, *, n_informative=2, n_redundant=2, n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)
- n_samples:生成的样本数量,默认值为100
- n_features:生成的特征数量,默认值为20
- n_informative:指定分类问题中有多少个特征是有用的(即与目标变量相关),默认值为2
- n_redundant:指定分类问题中有多少个特征是冗余的(即与目标变量无关,但与其他特征相关),默认值为2
- n_repeated:指定分类问题中有多少个特征是重复的(即完全相同的特征数),默认值为0
- n_classes:指定分类问题中有多少个类别,默认值为2
- n_clusters_per_class:指定每个类别中有多少个簇,默认值为2
- weights:指定各个类别的权重,默认为None,即类是平衡的
- flip_y:随机将标签翻转的概率,默认为0.01
- class_sep:指定类别之间的距离,默认为1.0
- hypercube:指定是否在超立方体中生成数据,默认为True
- shift:指定生成数据的移位量,默认为0
- scale:指定生成数据的缩放比例,默认为1
- shuffle:指定是否对数据进行随机洗牌,默认为True
- random_state:随机种子,默认为None
返回值为:
- X:生成的样本,array of shape [n_samples, n_features]
- y:每个样本的类成员的整数标签,array of shape [n_samples]
例子如下:
from sklearn.datasets import make_classification as mc
# 创建数据集
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
print(X.shape)
print(y.shape)
代码结果如下:
(3)calibration_curve
sklearn.calibration.calibration_curve(y_true, y_prob, normalize=False, n_bins=5, strategy='uniform')
- y_true:真实标签的数组或序列。
- y_prob:预测标签为正类的概率数组或序列。
- normalize:可选参数,默认为 False。如果为 True,则将每个箱中的样本数除以总样本数,使每个箱的值在 [0, 1] 范围内。
- n_bins:可选参数,默认为 5。将概率分成的箱的数量。
- strategy:可选参数,默认为 "uniform"。指定分箱策略,可选值为 "uniform" 或 "quantile"。
返回值为:
- trueproba:可靠性曲线的纵坐标,结构为(n_bins, ),是每个箱子中少数类(Y=1)的占比
- predproba :可靠性曲线的横坐标,结构为(n_bins, ),是每个箱子中概率的均值
3.4.2 模型评估
1 朴素贝叶斯评估
先对朴素贝叶斯进行评估,代码如下:
# 可靠性曲线
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.metrics import brier_score_loss
from sklearn.model_selection import train_test_split
import pandas as pd
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 朴素贝叶斯训练集很小,效果也很好,因此仅用1%的样本作为训练集
xtrain,xtest,ytrain,ytest=train_test_split(X,y,test_size=0.99,random_state=0)
# 第二步---建立模型---
gnb = GaussianNB()
gnb = gnb.fit(xtrain,ytrain)
# 导出分类的结果
y_pred = gnb.predict(xtest)
# 导出类别为1的预测概率
y_prob = gnb.predict_proba(xtest)[:,1]
# 导出精确性
acc_score = gnb.score(xtest, ytest)
# 第三步---绘制图像---
# 利用字典创建DateFrame,({"列的名称":[列的值]})
# 选取一部分测试集,创建DateFrame,一列是真实值ytrue,一列是预测概率probability
df = pd.DataFrame({"ytrue": ytest[:500], "probability": y_prob[:500]})
# 在我们的横纵表坐标上,概率是由顺序的(由小到大),所以对数据按照预测概率进行一个排序
# 对df中的数据进行排序,按照probability进行排序
df = df.sort_values(by="probability")
# 排序后,索引会乱,要恢复索引,让索引按顺序排序
df.index = range(df.shape[0])
# 紧接着我们就可以画图了
fig = plt.figure() # 建立画布
ax1 = plt.subplot() # 建立一个子图
ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated") # 得做一条对角线来对比呀
# 以df["probability"]即预测概率为横坐标,df["ytrue"]即真实类别为纵坐标
ax1.plot(df["probability"], df["ytrue"], "s-", label="%s (%1.3f)" % ("Bayes", acc_score))
ax1.set_ylabel("True label")
ax1.set_xlabel("predcited probability")
# 设置纵坐标范围
ax1.set_ylim([-0.05, 1.05])
ax1.legend()
plt.show()
代码结果如下:
按照预测概率的顺序进行排序的,而预测概率从0开始到1的过程中,真实取值不断在0和1之间变化,而我们是绘制折线图,因此无数个纵坐标分布在0和1的被链接起来了,所以看起来如此混乱。
换成散点图后,就可以看得很清晰了
所谓可靠性曲线的横纵坐标:横坐标是预测概率,而纵坐标是真实值,真实值应该是概率,而不是标签,事实上,真实概率是不可能获取的,因此,一个简单的做法是,将数据进行分箱,然后规定每个箱子中真实的少数类所占的比例为这个箱上的真实概率trueproba,这个箱子中预测概率的均值为这个箱子的预测概率predproba,然后以trueproba为纵坐标,predproba为横坐标,来绘制我们的可靠性曲线。分箱之后样本点的特征被聚合到了一起,曲线可以变得单调且平滑
,可以通过绘制可靠性曲线的类calibration_curve来实现
# 可靠性曲线
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
import pandas as pd
from sklearn.calibration import calibration_curve
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 朴素贝叶斯训练集很小,效果也很好,因此仅用1%的样本作为训练集
xtrain,xtest,ytrain,ytest=train_test_split(X,y,test_size=0.99,random_state=0)
# 第二步---建立模型---
gnb = GaussianNB()
gnb = gnb.fit(xtrain,ytrain)
# 导出分类的结果
y_pred = gnb.predict(xtest)
# 导出类别为1的预测概率
y_prob = gnb.predict_proba(xtest)[:,1]
# 导出精确性
acc_score = gnb.score(xtest, ytest)
# 分成10箱,ytest代表是真实标签,y_prob标示返回的概率
trueproba, predproba = calibration_curve(ytest, y_prob, n_bins=10)
# 第三步---绘制图像---
# 利用字典创建DateFrame,({"列的名称":[列的值]})
# 选取一部分测试集,创建DateFrame,一列是真实值ytrue,一列是预测概率probability
df = pd.DataFrame({"ytrue": ytest[:500], "probability": y_prob[:500]})
# 在我们的横纵表坐标上,概率是由顺序的(由小到大),所以对数据按照预测概率进行一个排序
# 对df中的数据进行排序,按照probability进行排序
df = df.sort_values(by="probability")
# 排序后,索引会乱,要恢复索引,让索引按顺序排序
df.index = range(df.shape[0])
# 紧接着我们就可以画图了
fig = plt.figure() # 建立画布
ax1 = plt.subplot() # 建立一个子图
ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated") # 得做一条对角线来对比呀
# 以df["probability"]即预测概率为横坐标,df["ytrue"]即真实类别为纵坐标
ax1.plot(predproba, trueproba, "s-", label="%s (%1.3f)" % ("Bayes", acc_score))
ax1.set_ylabel("True label")
ax1.set_xlabel("predcited probability")
# 设置纵坐标范围
ax1.set_ylim([-0.05, 1.05])
ax1.legend()
plt.show()
结果如下:
那么在不同箱数的情况下会有什么变化呢,把箱数分别设置为3,10,100,查看结果,代码如下:
# 可靠性曲线
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
import pandas as pd
from sklearn.calibration import calibration_curve
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 朴素贝叶斯训练集很小,效果也很好,因此仅用1%的样本作为训练集
xtrain,xtest,ytrain,ytest=train_test_split(X,y,test_size=0.99,random_state=0)
# 第二步---建立模型---
gnb = GaussianNB()
gnb = gnb.fit(xtrain,ytrain)
# 导出分类的结果
y_pred = gnb.predict(xtest)
# 导出类别为1的预测概率
y_prob = gnb.predict_proba(xtest)[:,1]
# 导出精确性
acc_score = gnb.score(xtest, ytest)
# 第三步---绘制图像---
# 利用字典创建DateFrame,({"列的名称":[列的值]})
# 选取一部分测试集,创建DateFrame,一列是真实值ytrue,一列是预测概率probability
df = pd.DataFrame({"ytrue": ytest[:500], "probability": y_prob[:500]})
# 在我们的横纵表坐标上,概率是由顺序的(由小到大),所以对数据按照预测概率进行一个排序
# 对df中的数据进行排序,按照probability进行排序
df = df.sort_values(by="probability")
# 排序后,索引会乱,要恢复索引,让索引按顺序排序
df.index = range(df.shape[0])
# 紧接着我们就可以画图了
# 我们绘制出的图像,越接近y=x这条直线,越好
fig, axes = plt.subplots(1,3,figsize=(18,4))
for ind,i in enumerate([3,10,100]):
ax = axes[ind]
# 画y=x直线
ax.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")
# 分箱后的真实值和预测值
trueproba, predproba = calibration_curve(ytest, y_prob,n_bins=i)
ax.plot(predproba, trueproba,"s-",label="n_bins = {}".format(i))
ax.set_ylabel("True probability for class 1")
ax.set_xlabel("Mean predcited probability")
ax.set_ylim([-0.05, 1.05])
ax.legend()
plt.show()
结果如下:
从上述图可以得出以下结论:
- n_bins越大,箱子越多,概率校准曲线就越精确,但是太过精确的曲线不够平滑(即剧烈震荡),无法和我们希望的完美概率密度曲线相比较。
- n_bins越小,箱子越少,概率校准曲线就越粗糙(无法反应真实情况),虽然靠近完美概率密度曲线,但是无法真实地展现模型概率预测的结果。
- 需要取适中的箱子个数,让概率校准曲线是一条相对平滑,又可以反应出模型对概率预测的趋势的曲线。通常来说,建议先试试看箱子数等于10的情况。箱子的数目越大,所需要的样本量也越多,否则曲线就会太过精确
2 其它模型
建立其它模型比较一下效果,分别为逻辑回归,SVC,贝叶斯。代码如下:
# 可靠性曲线
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.calibration import calibration_curve
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.metrics import brier_score_loss
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 朴素贝叶斯训练集很小,效果也很好,因此仅用1%的样本作为训练集
xtrain,xtest,ytrain,ytest=train_test_split(X,y,test_size=0.99,random_state=0)
# 第二步---建立模型---
# 训练贝叶斯模型
gnb = GaussianNB()
# 训练svc模型
svc = SVC(probability=True)
# 训练逻辑归模型
logi = LR(C=1., solver='lbfgs',max_iter=3000,multi_class="auto").fit(xtrain,ytrain)
# 第三步---绘制图像---
name = ["GaussianBayes","Logistic","SVC"]
# 紧接着我们就可以画图了
fig, ax1 = plt.subplots(figsize=(8,6))
ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated")
for clf, name_ in zip([gnb,logi,svc],name):
# 训练模型
clf.fit(xtrain,ytrain)
# 预测标签
y_pred = clf.predict(xtest)
# 预测概率
prob_pos = clf.predict_proba(xtest)[:,1]
# 返回布里尔分数
clf_score = brier_score_loss(ytest, prob_pos, pos_label=y.max())
# 分箱数为10
trueproba, predproba = calibration_curve(ytest, prob_pos,n_bins=10)
ax1.plot(predproba, trueproba,"s-",label="%s (%1.3f)" % (name_, clf_score))
ax1.set_ylabel("True probability for class 1")
ax1.set_xlabel("Mean predcited probability")
ax1.set_ylim([-0.05, 1.05])
ax1.legend()
ax1.set_title('Calibration plots (reliability curve)')
plt.show()
代码结果如下:
从图中可以看出:
- 相对来说,高斯朴素贝叶斯的结果比较糟糕
- 高斯朴素贝叶斯呈现和Sigmoid函数相反的形状。对于贝叶斯,如果概率校准曲线呈现sigmoid函数的镜像的情况,则说明数据集中的特征不是相互条件独立的。贝叶斯原理中的”朴素“原则:特征相互条件独立原则被违反了(我们设定了10个冗余特征,这些特征就是噪音,他们之间不可能完全独立),因此贝叶斯的表现不够好。
3.5 预测概率的直方图
(1)基础知识
可以通过绘制直方图来查看模型的预测概率的分布。直方图是以样本的预测概率分箱后的结果为横坐标,每个箱中的样本数量为纵坐标的一个图像。
- 注意:这里的分箱和在可靠性曲线中的分箱不同,这里的分箱是将预测概率均匀分为一个个的区间
(2)matplotlib.pyplot.hist参数详解
ax.hist(x, bins=None, range=None, density=False, weights=None,cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None,label=None, stacked=False, *, data=None, **kwargs)
- x: 绘制直方图所需的一维数组或序列。
- bins: 直方图的条形数目,默认值为 10。
- range: 直方图的取值范围,默认值为 None,即 (min(x), max(x))。
- density: 布尔值,默认为False。若为True,则绘制频率分布直方图,若为False,则绘制频数分布直方图。
- weights: 每个数据点的权重,默认值为 None。
- cumulative: 布尔值,默认为False。若为True,当density为False时直方图显示累计频数,当density为True时直方图显示累计频率。
- bottom: 数值或数组序列,默认为None。若为数值,则直方图的柱子相对于y=0向上/向下偏移相同的量。若为数组序列,则根据数组元素取值每根柱子偏移相应的量。
- histtype: 直方图类型,可选值为 'bar', 'barstacked', 'step', 'stepfilled'。'bar'是传统的条形直方图,'barstacked'是堆叠的条形直方图,'step'是未填充的阶梯直方图,只有外边框,'stepfilled'是有填充的阶梯直方图。
- align: 直方图条形的对齐方式,可选值为 'left', 'mid', 'right'。
- orientation: 直方图的方向,可选值为 'horizontal', 'vertical'。'horizontal'表示柱子水平排列, 'vertical'表示柱子垂直排列。
- rwidth: 直方图条形宽度,浮点数或 None,默认为 None。
- log: 是否绘制对数刻度的直方图,默认值为 False。
- color: 直方图颜色。
- label: 直方图标签。
- stacked: 布尔值,默认为False。当图中有多个数据集时使用该参数,若取值为True,则输出数据集累计堆叠的结果,若取值为False,则多个数据集柱子并排排列。
(3)例子
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.calibration import calibration_curve
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.metrics import brier_score_loss
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 朴素贝叶斯训练集很小,效果也很好,因此仅用1%的样本作为训练集
xtrain,xtest,ytrain,ytest=train_test_split(X,y,test_size=0.99,random_state=0)
# 第二步---建立模型---
# 训练贝叶斯模型
gnb = GaussianNB()
# 训练svc模型
svc = SVC(probability=True)
# 训练逻辑归模型
logi = LR(C=1., solver='lbfgs',max_iter=3000,multi_class="auto").fit(xtrain,ytrain)
# 第三步---绘制图像---
name = ["GaussianBayes","Logistic","SVC"]
# 紧接着我们就可以画图了
fig, ax1 = plt.subplots(figsize=(8,6))
for clf, name_ in zip([gnb,logi,svc],name):
# 训练模型
clf.fit(xtrain,ytrain)
# 预测标签
y_pred = clf.predict(xtest)
# 预测概率
prob_pos = clf.predict_proba(xtest)[:,1]
ax1.hist(prob_pos
, bins=10
, label=name_
, histtype="step" # 设置直方图为透明
, lw=2 # 设置直方图每个柱子描边的粗细
)
# 纵坐标为样本数
ax1.set_ylabel("Distribution of probability")
# 样本预测概率的均值
ax1.set_xlabel("Mean predicted probability")
# 设定x取值
ax1.set_xlim([-0.05, 1.05])
# 限制x轴刻度
ax1.set_xticks([0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])
ax1.legend(loc=9)
plt.show()
结果如下:
根据结果分析如下:
- 逻辑回归。看橙色线,0到0.1之间有很多的样本数,这可以说明很大可能这些样本是类别0;0.9-1之间也有很多的样本数,这可以说明很大可能这些样本是类别1。几乎90%以上的样本都在0和1的附近(这些样本可以充分预测其类别),是置信度最高的算法
- 高斯贝叶斯。看蓝色线,同理,高斯贝叶斯的概率分布是两边非常高,中间非常低,几乎90%以上的样本都在0和1的附近,是置信度最高的算法。但实际上,高斯贝叶斯算法太多样本数在0和1的附近,置信度太高了,可能有一些类别1的样本在0附近,类别0的样本在1附近,这也是它可靠性曲线表现不好的原因。
- 支持向量机,当使用了predict_proba接口后,跟上述算法同理,是置信度最高的算法
- 综上,当聚集在0和1附近的样本数过多,置信度过高,其效果也不一定好
- 如果有一个算法,处于中间的样本数很多,即中间高两边低,则这些样本的类别不确定性很大,算法需要改进
注意:使用的预测概率是类别为1情况下的预测概率
3.6 校准可靠性曲线
3.6.1 基础知识
(1)基本介绍
等近似回归有两种回归可以使用,一种是基于Platt的Sigmoid模型的参数校准方法,一种是基于等渗回归(isotoniccalibration)的非参数的校准方法。概率校准应该发生在测试集上,必须是模型未曾见过的数据。在数学上,使用这两种方式来对概率进行校准的原理十分复杂,而此过程我们在sklearn中无法进行干涉,大家不必过于去深究。
(2)CalibratedClassifierCV语法格式
class sklearn.calibration.CalibratedClassifierCV(base_estimator=None, method='sigmoid', cv=None)
- base_estimator: 使用的基本分类器,默认为 None。可以是任何有 predict_proba() 接口或decision_function接口的分类器。
- method: 校准概率的方法。可选值为 'sigmoid' 和 'isotonic',默认='sigmoid'。输入'sigmoid',使用基于Platt的Sigmoid模型来进行校准;输入'isotonic',使用等渗回归来进行校准。样本数过少(小于等于1000)时建议使用sigmoids,如果使用等渗回归会过拟合
- cv: 用于交叉验证的折数。默认为 None,表示使用默认的 5 折交叉验证。可输入整数,代表指定折数;可输入已经使用其他类建好的交叉验证模式或生成器cv;可输入可迭代的,已经分割完毕的测试集和训练集索引数组;输入"prefit",则假设已经在分类器上拟合完毕数据。在这种模式下,使用者必须手动确定用来拟合分类器的数据与即将被校准的数据没有交集
CalibratedClassifierCV 的主要方法包括:
- fit(X, y, sample_weight=None): 训练模型,并对概率估计进行校准。其中,X 和 y 分别为训练集的特征和标签。
- predict(X): 对测试集进行预测,并返回预测结果的类别。
- predict_proba(X): 对测试集进行预测,并返回每个类别的概率估计。
- predict_log_proba(X): 对测试集进行预测,并返回每个类别的对数概率估计。
- get_params(deep=True): 获取模型的超参数。
- set_params(**params): 设置模型的超参数。
3.6.2 分析贝叶斯模型
这里分别使用了两种校准方式,分别为:'sigmoid' 和 'isotonic'
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.calibration import calibration_curve
from sklearn.linear_model import LogisticRegression as LR
from sklearn.metrics import brier_score_loss
from sklearn.calibration import CalibratedClassifierCV
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 朴素贝叶斯训练集很小,效果也很好,因此仅用1%的样本作为训练集
xtrain,xtest,ytrain,ytest = train_test_split(X,y,test_size=0.99,random_state=0)
# 第二步---建立模型---
# 定义贝叶斯模型
gnb = GaussianNB()
# 所有模型放在models列表中,定义了两种校准方式
models = [gnb
, LR(C=1., solver='lbfgs', max_iter=3000, multi_class="auto")
# 定义两种校准方式
, CalibratedClassifierCV(gnb, cv=2, method='isotonic')
, CalibratedClassifierCV(gnb, cv=2, method='sigmoid')]
# 第三步---定义画图函数---
def plot_calib(models, name, Xtrain, Xtest, Ytrain, Ytest, n_bins=10):
ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated") # 画出y=x的直线
for clf, name_ in zip(models, name):
clf.fit(xtrain, ytrain)
y_pred = clf.predict(Xtest)
prob_pos = clf.predict_proba(Xtest)[:, 1]
# 返回布里尔分数
clf_score = brier_score_loss(Ytest, prob_pos, pos_label=y.max())
# 可靠性曲线
trueproba, predproba = calibration_curve(Ytest, prob_pos, n_bins=n_bins)
ax1.plot(predproba, trueproba, "s-", label="%s (%1.3f)" % (name_, clf_score))
ax2.hist(prob_pos, range=(0, 1), bins=n_bins, label=name_, histtype="step", lw=2)
# 第四步---绘制图像---
name = ["GaussianBayes", "Logistic", "Bayes+isotonic", "Bayes+sigmoid"]
# 建立画布以及子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 6))
# 调用画图函数
plot_calib(models, name, xtrain, xtest, ytrain, ytest)
# 子图2的一些配置
ax2.set_ylabel("Distribution of probability")
ax2.set_xlabel("Mean predicted probability")
ax2.set_xlim([-0.05, 1.05])
ax2.legend(loc=9)
ax2.set_title("Distribution of probablity")
# 子图1的一些配置
ax1.set_ylabel("True probability for class 1")
ax1.set_xlabel("Mean predcited probability")
ax1.set_ylim([-0.05, 1.05])
ax1.legend()
ax1.set_title('Calibration plots(reliability curve)')
plt.show()
# 第四步---输出准确率和布里尔分数---
print(f'贝叶斯的准确率为:{gnb.score(xtest,ytest)},'
f'布里尔分数为{brier_score_loss(ytest,gnb.predict_proba(xtest)[:,1],pos_label = 1)}')
gnbisotonic = CalibratedClassifierCV(gnb, cv=2, method='isotonic').fit(xtrain,ytrain)
print(f'使用isotonic后的贝叶斯的准确率为:{gnbisotonic.score(xtest,ytest)},'
f'布里尔分数为{brier_score_loss(ytest,gnbisotonic.predict_proba(xtest)[:,1],pos_label = 1)}')
结果如下:
对于曲线图和直方图分析结果如下:
- 从曲线图来看,Isotonic等渗校正大大改善了曲线的形状,几乎让贝叶斯的效果与逻辑回归持平,Sigmoid校准的方式也对曲线进行了稍稍的改善,不过效果不明显
- 从直方图来看,Isotonic校正让高斯朴素贝叶斯的效果接近逻辑回归,而Sigmoid校正后的结果依然和原本的高斯朴素贝叶斯更相近
- 综上所述,当数据的特征之间不是相互条件独立的时候,使用Isotonic方式来校准概率曲线,可以得到不错的结果,让模型在预测上更加谦虚
对于贝叶斯模型和准确率和布尔分数的分析:
- 校准概率后,布里尔分数明显变小了,但整体的准确率却略有下降,这证明算法在校准之后,尽管对概率的预测更准确了,但模型的判断力略有降低
- 这是因为,在朴素贝叶斯中,有各种各样的假设,除了“朴素”假设,还有我们对概率分布的假设(比如说高斯),在这些假设下,这种概率估计其实不是那么准确和严肃。通过校准,让模型的预测概率更贴近于真实概率,本质是在统计学上让算法更加贴近我们对整体样本状况的估计,这样的一种校准在一组数据集上可能表现出让准确率上升,也可能表现出让准确率下降,这取决于测试集有多贴近估计的真实样本的面貌
- 深层的可能原因是:概率校准过程中的数学细节如何影响了我们的校准,类calibration_curve中是如何分箱,如何通过真实标签和预测值来生成校准曲线使用的横纵坐标,这些过程中也可能有着让布里尔分数和准确率向两个方向移动的过程
- 当两者相悖的时候,以准确率为标准,但概率类模型几乎没有参数可以调整,概率校准曲线非常好的针对概率提升模型的方法,所以仍然可以考虑使用这些方法
3.6.3 SVC校准效果
使用SVCpredict_proba接口来进行处理,代码如下:
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification as mc
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.calibration import calibration_curve
from sklearn.linear_model import LogisticRegression as LR
from sklearn.svm import SVC
from sklearn.metrics import brier_score_loss
from sklearn.calibration import CalibratedClassifierCV
# 第一步---创建数据集---
X, y = mc(n_samples=100000,n_features=20 #总共20个特征
,n_classes=2 #标签为2分类
,n_informative=2 #其中两个代表较多信息,即比较重要的特征
,n_redundant=10 #10个都是冗余特征
,random_state=42)
# 仅用45%的样本作为训练集
xtrain,xtest,ytrain,ytest = train_test_split(X,y,test_size=0.55,random_state=0)
# 第二步---建立模型---
# 定义SVC
svc = SVC(kernel = "linear",gamma=1,probability=True)
# 所有模型放在models列表中,定义了两种校准方式
models = [svc
,LR(C=1., solver='lbfgs',max_iter=3000,multi_class="auto")
#依然定义两种校准方式
,CalibratedClassifierCV(svc, cv=2, method='isotonic')
,CalibratedClassifierCV(svc, cv=2, method='sigmoid')]
# 第三步---定义画图函数---
def plot_calib(models, name, Xtrain, Xtest, Ytrain, Ytest, n_bins=10):
ax1.plot([0, 1], [0, 1], "k:", label="Perfectly calibrated") # 画出y=x的直线
for clf, name_ in zip(models, name):
clf.fit(xtrain, ytrain)
y_pred = clf.predict(Xtest)
prob_pos = clf.predict_proba(Xtest)[:, 1]
# 返回布里尔分数
clf_score = brier_score_loss(Ytest, prob_pos, pos_label=y.max())
# 可靠性曲线
trueproba, predproba = calibration_curve(Ytest, prob_pos, n_bins=n_bins)
ax1.plot(predproba, trueproba, "s-", label="%s (%1.3f)" % (name_, clf_score))
ax2.hist(prob_pos, range=(0, 1), bins=n_bins, label=name_, histtype="step", lw=2)
# 第四步---绘制图像---
name = ["SVC","Logistic","SVC+isotonic","SVC+sigmoid"]
# 建立画布以及子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 6))
# 调用画图函数
plot_calib(models, name, xtrain, xtest, ytrain, ytest)
# 子图2的一些配置
ax2.set_ylabel("Distribution of probability")
ax2.set_xlabel("Mean predicted probability")
ax2.set_xlim([-0.05, 1.05])
ax2.legend(loc=9)
ax2.set_title("Distribution of probablity")
# 子图1的一些配置
ax1.set_ylabel("True probability for class 1")
ax1.set_xlabel("Mean predcited probability")
ax1.set_ylim([-0.05, 1.05])
ax1.legend()
ax1.set_title('Calibration plots(reliability curve)')
plt.show()
当使用45%的样本作为训练集时,效果如下:
4 其它贝叶斯
4.1 多项式朴素贝叶斯MultinomialNB
1 如何判定数据符合多项式朴素贝叶斯
根据多项分布的概念,以词袋模型为例,在文本分类中有如下假设:
- 不考虑文本中单词之间的次序,即认为这两个文本‘ Love and Peace’和‘ Peace and Love’最后建模出来对应于同一个特征向量。不考虑文本单词之间的次序,会导致文本语义丢失。
- 在类别为Y=c的文本中,每个单词的出现是相互独立。即在类别为Y=c的文本中,每次随机试验为随机从词表中抽取一个单词,进行n次独立重复试验。
第2个假设只有满足了才保证了文本的特征随机向量满足多项式分布,即给定类别Y=c的文本,满足:
即满足了上述要求,可用多项式朴素贝叶斯推导过程里的方法求条件概率 (一般使用词频法和TF-IDF表示法,详情见词袋模型)
2 MultinomialNB语法格式
class sklearn.naive_bayes.MultinomialNB(*, alpha=1.0, fit_prior=True, class_prior=None)
参数说明:
参数 | 说明 |
---|---|
alpha | float, default=1.0 附加的(Laplace/Lidstone)平滑参数(0表示不平滑) |
fit_prior | bool, default=True 是否学习类别先验概率。如果为False,将使用统一的先验。 |
class_prior | array-like of shape (n_classes,), default=None 类别的先验概率。一经指定先验概率不能随着数据而调整。 |
属性说明:
属性 | 说明 |
---|---|
class_count_ | ndarray of shape (n_classes,) 拟合期间每个类别遇到的样本数。此值由提供的样本权重加权。 |
class_log_prior_ | ndarray of shape (n_classes, ) 每个类别的经验对数概率(平滑)。 |
classes_ | ndarray of shape (n_classes,) 分类器已知的类别标签 |
coef_ | ndarray of shape (n_classes, n_features) 用于将MultinomialNB解释为线性模型的镜像 feature_log_prob_ 。 |
feature_count_ | ndarray of shape (n_classes, n_features) 拟合期间每个(类别,特征)遇到的样本数。此值由提供的样本权重加权。 |
feature_log_prob_ | ndarray of shape (n_classes, n_features) 给定一类特征的经验对数概率 P(xi|y) |
intercept_ | ndarray of shape (n_classes, ) 用于将MultinomialNB解释为线性模型的镜像 class_log_prior_ 。 |
n_features_ | int 每个样本的特征数量。 |
方法说明:
方法 | 说明 |
---|---|
fit (X, y[, sample_weight]) | 根据X,y拟合朴素贝叶斯分类器 |
get_params ([deep]) | 获取这个估计器的参数 |
partial_fit (X, y[, classes, sample_weight]) | 对一批样本进行增量拟合 |
predict (X) | 对测试向量X进行分类。 |
predict_log_proba (X) | 返回针对测试向量X的对数概率估计 |
predict_proba (X) | 返回针对测试向量X的概率估计 |
score (X, y[, sample_weight]) | 返回给定测试数据和标签上的平均准确率。 |
set_params (**params) | 为这个估计器设置参数 |
对X矩阵和y矩阵的要求如下:
参数 | 说明 |
---|---|
X | {array-like, sparse matrix} of shape (n_samples, n_features) 用于训练的向量,其中n_samples是样本数量,n_features是特征数量。 |
y | array-like of shape (n_samples,) 目标值。 |
3 代码实现
1 基础知识
(1)make_blobs语法格式
sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=3, cluster_std=1.0, center_box=(-10.0, 10.0), shuffle=True, random_state=None)
- n_samples:生成的样本总数,默认为100。
- n_features:每个样本的特征数,默认为2。
- centers:要生成的聚类中心数量或者中心坐标,默认为3。
- cluster_std:每个聚类的标准差,默认为1.0。
- center_box:生成的聚类中心的范围,默认为(-10.0, 10.0)。
- shuffle:是否打乱数据顺序,默认为True。
- random_state:随机数种子,默认为None。
(2)KBinsDiscretizer语法格式
sklearn.preprocessing.KBinsDiscretizer(n_bins=5, encode='onehot', strategy='uniform')
参数 | 含义和输入 |
---|---|
n_bins | 每个特征要分的箱子数量,默认为5,可以是单个整数或者数组形式(对每个特征分别指定) |
encode | 编码方式,默认为'onehot'(独热编码)。可选值包括: 'onehot':做哑变量,返回一个稀疏矩阵,每一列是一个特征的一个类别,含有该类别的样本为1,否则为0 'ordinal':每个特征的每个箱都被编码为一个整数,返回每一列是一个特征,每个特征下有不同整数编码的箱的矩阵 'onehot-dense':做哑变量,返回密集矩阵 |
strategy | 分箱策略,默认为'quantile'。可选值包括: 'uniform':等宽分箱,即每个特征中每个箱子的最大值之间的差为(特征.max()-特征.min())/(n_bins) 'quantile':等位分箱,即每个特征中每个箱子的样本数量相同 'kmeans':聚类分箱,每个箱子的值到最近的一维k均值聚类的簇心的距离都相同 |
(3)MinMaxScaler语法格式
MinMaxScaler(feature_range=(0, 1), copy=True)
- feature_range:要缩放特征的范围,默认为(0, 1)
- copy:是否在转换时复制输入数据,默认为True
常用方法有:
创建 MinMaxScaler 对象
scaler = MinMaxScaler(feature_range=(0, 1))
fit(X, y=None):计算训练数据 X 的最小值和最大值,用于后续的数据缩放
scaler.fit(X)
transform(X):根据之前计算的最小值和最大值来缩放数据 X
X_scaled = scaler.transform(X)
fit_transform(X, y=None):相当于先调用 fit 方法然后再调用 transform 方法
X_scaled = scaler.fit_transform(X)
inverse_transform(X):执行反向缩放,将缩放后的数据转换回原始范围
X_original = scaler.inverse_transform(X_scaled)
2 连续型数据处理
sklearn中的多项式分布也可以处理连续型变量,但实际上想要处理连续型 变量,应当使用高斯朴素贝叶斯
# 导入库
from sklearn.preprocessing import MinMaxScaler
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.metrics import brier_score_loss
import numpy as np
# **********第一步:建立数据集**********
class_1 = 500
class_2 = 500 # 两个类分别设置500个样本
centers = [[0, 0], [2, 2]] # 设置数据集的中心
clusters_std = [0.5, 0.5] # 设置两个类别的均值和方差
# 使用make_blobs生成样本,y的类别只有0和1
X, y = make_blobs(n_samples=[class_1, class_2], centers=centers, cluster_std=clusters_std
, random_state=0, shuffle=False)
# 划分数据集测试集
xtrain, xtest, ytrain, ytest = train_test_split(X, y, random_state=0, test_size=0.3)
# **********第二步:归一化,确保矩阵不带有负数**********
mms = MinMaxScaler().fit(xtrain)
xtrain_ = mms.transform(xtrain)
xtest_ = mms.transform(xtest)
# **********第三步:建立多项式朴素贝叶斯分类器**********
# 训练的时候使用归一化之后的特征矩阵
mnb = MultinomialNB().fit(xtrain_, ytrain)
# 获取每一个特征的先验概率
# 返回每一个标签类对应的先验概率
print(f'对数先验概率矩阵为:\n{mnb.class_log_prior_}')
# 使用np.exp求出真正的概率,因为对数是以e为底
print(f'先验概率矩阵为:\n{np.exp(mnb.class_log_prior_)}\n')
# 返回每一个固定类别标签下的每一个特征的对数概率log(p(xi|y))
# 矩阵形状是2*2,因为有2个特征,2个类别
print(f'对数条件概率矩阵为:\n{mnb.feature_log_prob_}')
# 使用np.exp求出真正的概率,因为对数是以e为底
print(f'条件概率矩阵为:\n{np.exp(mnb.feature_log_prob_)}\n')
# 在fit时每一个标签类别下包含的样本数
# 当fit时候接口中sample_weight被设置的时候,该接口返回的值也会受到加权的影响
print(f'0类和1类的样本数分别为:\n{mnb.class_count_}\n')
# 返回预测结果
print(f'预测结果为:\n{mnb.predict(xtest_)}\n')
# 返回每一个样本在每一个标签下面的取值概率
print(f'标签的取值概率矩阵为:\n{mnb.predict_proba(xtest_)}\n')
# 返回我们的准确度
print(f'准确度为:{mnb.score(xtest_, ytest)}\n')
# 返回我们的布里尔分数
print(f'布里尔分数为:{brier_score_loss(ytest, mnb.predict_proba(xtest_)[:, 1], pos_label=1)}')
结果如图所示(有些矩阵过大,只截取一部分):
由准确度和布里尔分数可知,多项式朴素贝叶斯处理连续型数据准确率不高,因此考虑使用分箱法来将连续型数据转成分类型数据
2 分类型数据
使用KBinsDiscretizer进行onehot编码方式分箱后,代码如下
# 导入库
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.metrics import brier_score_loss
import numpy as np
# **********第一步:建立数据集**********
class_1 = 500
class_2 = 500 # 两个类分别设置500个样本
centers = [[0, 0], [2, 2]] # 设置数据集的中心
clusters_std = [0.5, 0.5] # 设置两个类别的均值和方差
# 使用make_blobs生成样本,y的类别只有0和1
X, y = make_blobs(n_samples=[class_1, class_2], centers=centers, cluster_std=clusters_std
, random_state=0, shuffle=False)
# 划分数据集测试集
xtrain, xtest, ytrain, ytest = train_test_split(X, y, random_state=0, test_size=0.3)
# **********第二步:数据转换**********
# 把Xtiain转换成分类型数据
# 注意我们的Xtrain没有经过归一化,因为做哑变量之后自然所有的数据就不会有负数了
# 在这里,2个特征中,每个特征分了十个箱,相当于2个特征裂变成20个特征
kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(xtrain)
xtrain_ = kbs.transform(xtrain)
xtest_ = kbs.transform(xtest)
# **********第三步:建立多项式朴素贝叶斯分类器**********
# 训练的时候使用归一化之后的特征矩阵
mnb = MultinomialNB().fit(xtrain_, ytrain)
# 获取每一个特征的先验概率
# 返回每一个标签类对应的先验概率
print(f'对数先验概率矩阵为:\n{mnb.class_log_prior_}')
# 使用np.exp求出真正的概率,因为对数是以e为底
print(f'先验概率矩阵为:\n{np.exp(mnb.class_log_prior_)}\n')
# 返回每一个固定类别标签下的每一个特征的对数概率log(p(xi|y))
# 矩阵形状是2*2,因为有2个特征,2个类别
print(f'对数条件概率矩阵为:\n{mnb.feature_log_prob_}')
# 使用np.exp求出真正的概率,因为对数是以e为底
print(f'条件概率矩阵为:\n{np.exp(mnb.feature_log_prob_)}\n')
# 在fit时每一个标签类别下包含的样本数
# 当fit时候接口中sample_weight被设置的时候,该接口返回的值也会受到加权的影响
print(f'0类和1类的样本数分别为:\n{mnb.class_count_}\n')
# 返回预测结果
print(f'预测结果为:\n{mnb.predict(xtest_)}\n')
# 返回每一个样本在每一个标签下面的取值概率
print(f'标签的取值概率矩阵为:\n{mnb.predict_proba(xtest_)}\n')
# 返回我们的准确度
print(f'准确度为:{mnb.score(xtest_, ytest)}\n')
# 返回我们的布里尔分数
print(f'布里尔分数为:{brier_score_loss(ytest, mnb.predict_proba(xtest_)[:, 1], pos_label=1)}')
结果如下:
可以看到,性能大大提升
4.2 伯努利贝叶斯
1 BernoulliNB语法格式
多元伯努利分布的特点是数据集中可以存在多个特征,但每个特征都是二分类的,可以以布尔变量表示,也可以表示为{0,1}或者{-1,1}等任意二分类组合。因此,这个类要求将样本转换为二分类特征向量,如果数据不是二分类的,可以使用类中专门用来二值化的参数binarize来改变数据。
class sklearn.naive_bayes.BernoulliNB(*, alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None)
参数说明:
参数 | 说明 |
---|---|
alpha | float, default=1.0 附加的平滑参数(Laplace/Lidstone),0是不平滑 |
binarize | float or None, default=0.0 用于将样本特征二值化(映射为布尔值)的阈值(例如小于该阈值设为0,大于该阈值设为1)。如果为None,则假定输入已经由二分类向量组成。 |
fit_prior | bool, default=True 是否学习类别先验概率。如果为False,将使用统一的先验。 |
class_prior | array-like of shape (n_classes,), default=None 类别的先验概率。一经指定先验概率不能随着数据而调整。 |
属性说明:
属性 | 说明 |
---|---|
class_count_ | ndarray of shape (n_classes) 拟合期间每个类别遇到的样本数。此值由提供的样本权重加权。 |
class_log_prior_ | ndarray of shape (n_classes) 每个类别的对数概率(平滑)。 |
classes_ | ndarray of shape (n_classes,) 分类器已知的类别标签 |
feature_count_ | ndarray of shape (n_classes, n_features) 拟合期间每个(类别,特征)遇到的样本数。此值由提供的样品权重加权。 |
feature_log_prob_ | ndarray of shape (n_classes, n_features) 给定一类P(xi|y)的特征的经验对数概率。 |
n_features_ | int 每个样本的特征数量。 |
常用方法如下:
方法 | 说明 |
---|---|
fit (X, y[, sample_weight]) | 根据X,y拟合朴素贝叶斯分类器 |
get_params ([deep]) | 获取这个估计器的参数 |
partial_fit (X, y[, classes, sample_weight]) | 对一批样本进行增量拟合 |
predict (X) | 对测试向量X进行分类。 |
predict_log_proba (X) | 返回针对测试向量X的对数概率估计 |
predict_proba (X) | 返回针对测试向量X的概率估计 |
score (X, y[, sample_weight]) | 返回给定测试数据和标签上的平均准确率。 |
set_params (**params) | 为这个估计器设置参数 |
对X矩阵和y矩阵的要求如下:
参数 | 说明 |
---|---|
X | {array-like, sparse matrix} of shape (n_samples, n_features) 用于训练的向量,其中n_samples是样本数量,n_features是特征数量。 |
y | array-like of shape (n_samples,) 目标值。 |
2 代码例子
仍然使用多项式朴素贝叶斯的数据集
# 导入库
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.metrics import brier_score_loss
from sklearn.preprocessing import MinMaxScaler
from sklearn.naive_bayes import BernoulliNB
# **********第一步:建立数据集**********
class_1 = 500
class_2 = 500 # 两个类分别设置500个样本
centers = [[0, 0], [2, 2]] # 设置数据集的中心
clusters_std = [0.5, 0.5] # 设置两个类别的均值和方差
# 使用make_blobs生成样本,y的类别只有0和1
X, y = make_blobs(n_samples=[class_1, class_2], centers=centers, cluster_std=clusters_std
, random_state=0, shuffle=False)
# 划分数据集测试集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, random_state=0, test_size=0.3)
# **********第二步:数据转换**********
# 普通来说我们应该使用二值化的类sklearn.preprocessing.Binarizer来将特征一个个二值化
# 然而这样效率过低,因此我们选择归一化之后直接设置一个阈值
mms = MinMaxScaler().fit(Xtrain)
Xtrain_ = mms.transform(Xtrain)
Xtest_ = mms.transform(Xtest)
# **********第三步:建立伯努利朴素贝叶斯分类器**********
# 不设置二值化
bnl_ = BernoulliNB().fit(Xtrain_, Ytrain)
print(f'不设置二值化时,准确度为:{bnl_.score(Xtest_,Ytest)}')
print(f'不设置二值化时,布里尔分数为:{brier_score_loss(Ytest,bnl_.predict_proba(Xtest_)[:,1],pos_label=1)}')
# 设置二值化阈值为0.5
bnl = BernoulliNB(binarize=0.5).fit(Xtrain_, Ytrain)
print(f'设置二值化时,准确度为:{bnl.score(Xtest_,Ytest)}')
print(f'设置二值化时,布里尔分数为:{brier_score_loss(Ytest,bnl.predict_proba(Xtest_)[:,1],pos_label=1)}')
代码结果如下:
通过代码结果可知,二值化时伯努利朴素贝叶斯拥有非常高的准确度
4.3 类别贝叶斯CategoricalNB
1 CategoricalNB语法格式
class sklearn.naive_bayes.CategoricalNB(*, alpha=1.0, fit_prior=True, class_prior=None)
参数说明:
参数 | 说明 |
---|---|
alpha | float, default=1.0 附加的平滑参数(Laplace/Lidstone),0是不平滑 |
fit_prior | bool, default=True 是否学习类别先验概率。若为False,将使用统一的先验(概率相等) |
class_prior | array-like of shape (n_classes,), default=None 类别的先验概率。一经指定先验概率不能随着数据而调整。 |
属性说明:
属性 | 说明 |
---|---|
category_count_ | list of arrays of shape (n_features,) 为每个要素保存形状的数组(n_classes,各个要素的n_categories)。每个数组为每个类别和分类的特定特征提供遇到的样本数量。 |
class_count_ | ndarray of shape (n_classes,) 拟合期间每个类别遇到的样本数。此值由提供的样本权重加权。 |
class_log_prior_ | ndarray of shape (n_classes,) 每个类别的对数先验概率(平滑)。 |
classes_ | ndarray of shape (n_classes,) 分类器已知的类别标签 |
feature_log_prob_ | list of arrays of shape (n_features,) 为每个特征保形状的数组(n_classes,各个要素的n_categories)。每个数组提供了给定各自特征和类别的分类的经验对数概率log(p(xi|y)) |
n_features_ | int 每个样本的特征数量。 |
方法说明:
方法 | 说明 |
---|---|
fit(X, y[, sample_weight]) | 根据X,y拟合朴素贝叶斯分类器。 |
get_params([deep]) | 获取这个估计器的参数 |
partial_fit(X, y[, classes, sample_weight]) | 对一批样本进行增量拟合。 |
predict(X) | 对测试向量X进行分类 |
predict_log_proba(X) | 返回针对测试向量X的对数概率估计 |
predict_proba(X) | 返回针对测试向量X的概率估计 |
score(X, y[, sample_weight]) | 返回给定测试数据和标签上的平均准确率 |
set_params(**params) | 为这个估计器设置参数 |
对于X矩阵和y矩阵的要求如下:
参数 | 说明 |
---|---|
X | {array-like, sparse matrix} of shape (n_samples, n_features) 样本的特征矩阵,其中n_samples是样本数量,n_features是特征数量。在此,假设X的每个特征都来自不同的分类分布。进一步假设每个特征的所有类别均由数字0,…,n-1表示,其中n表示给定特征的类别总数。例如,这可以在顺序编码(OrdinalEncoder)的帮助下实现。 |
y | array-like of shape (n_samples,) 每个样本所属的标签类别 |
2 代码例子
以多项式朴素贝叶斯的连续型数据为例,对其进行Ordinal分箱,代码如下:
# 导入库
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.metrics import brier_score_loss
from sklearn.naive_bayes import CategoricalNB
import numpy as np
# **********第一步:建立数据集**********
class_1 = 500
class_2 = 500 # 两个类分别设置500个样本
centers = [[0, 0], [2, 2]] # 设置数据集的中心
clusters_std = [0.5, 0.5] # 设置两个类别的均值和方差
# 使用make_blobs生成样本,y的类别只有0和1
X, y = make_blobs(n_samples=[class_1, class_2], centers=centers, cluster_std=clusters_std
, random_state=0, shuffle=False)
# 划分数据集测试集
xtrain, xtest, ytrain, ytest = train_test_split(X, y, random_state=0, test_size=0.3)
# **********第二步:数据转换**********
# 在这里,2个特征中,将每个特征定义为有五个类别,即分成五个箱
# 矩阵每一列是特征,每一列的值是箱的整数编号,该整数编号相当于该特征的所属类别
kbs = KBinsDiscretizer(n_bins=5, encode='ordinal').fit(xtrain)
xtrain_ = kbs.transform(xtrain)
xtest_ = kbs.transform(xtest)
# **********第三步:建立类别朴素贝叶斯分类器**********
# 训练数据
mnb = CategoricalNB().fit(xtrain_, ytrain)
# 获取每一个特征的先验概率
# 返回每一个标签类对应的先验概率
print(f'对数先验概率矩阵为:\n{mnb.class_log_prior_}')
# 使用np.exp求出真正的概率,因为对数是以e为底
print(f'先验概率矩阵为:\n{np.exp(mnb.class_log_prior_)}\n')
# 返回每一个固定类别标签下的每一个特征的对数概率log(p(xi|y))
# 矩阵形状是2*2,因为有2个特征,2个类别
print(f'对数条件概率矩阵为:\n{mnb.feature_log_prob_}')
# 使用np.exp求出真正的概率,因为对数是以e为底
print(f'条件概率矩阵为:\n{np.exp(mnb.feature_log_prob_)}\n')
# 在fit时每一个标签类别下包含的样本数
# 当fit时候接口中sample_weight被设置的时候,该接口返回的值也会受到加权的影响
print(f'0类和1类的样本数分别为:\n{mnb.class_count_}\n')
# 返回预测结果
print(f'预测结果为:\n{mnb.predict(xtest_)}\n')
# 返回每一个样本在每一个标签下面的取值概率
print(f'标签的取值概率矩阵为:\n{mnb.predict_proba(xtest_)}\n')
# 返回我们的准确度
print(f'准确度为:{mnb.score(xtest_, ytest)}\n')
# 返回我们的布里尔分数
print(f'布里尔分数为:{brier_score_loss(ytest, mnb.predict_proba(xtest_)[:, 1], pos_label=1)}')
最终的准确度和布里尔分数为:
4.4 贝叶斯的样本不平衡问题
探讨一个分类算法永远都逃不过的核心问题:样本不平衡。贝叶斯由于分类效力不算太好,因此对样本不平衡极为敏感
- 注意:对于召回率的概念可看混淆矩阵-ROC曲线、召回率、精确率、准确率
接下来看一个例子,以少数类样本为正类,代码如下:
from sklearn.naive_bayes import MultinomialNB, GaussianNB, BernoulliNB,CategoricalNB
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.metrics import brier_score_loss as BS,recall_score,roc_auc_score as AUC
# 第一步:创建数据集
# 其中,少数类为正类即为类别1
class_1 = 50000 #多数类为50000个样本
class_2 = 500 #少数类为500个样本
centers = [[0.0, 0.0], [5.0, 5.0]] #设定两个类别的中心
clusters_std = [3, 1] #设定两个类别的方差
X, y = make_blobs(n_samples=[class_1, class_2],
centers=centers,
cluster_std=clusters_std,
random_state=0, shuffle=False)
# 查看所有贝叶斯样本不均衡的表现
name = ["Multinomial","Gaussian","Bernoulli","Categorical"]
models = [MultinomialNB(),GaussianNB(),BernoulliNB(),CategoricalNB()]
for name,clf in zip(name,models):
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y
,test_size=0.3
,random_state=420)
if name == "Categorical":
kbs = KBinsDiscretizer(n_bins=5, encode='ordinal').fit(Xtrain)
Xtrain = kbs.transform(Xtrain)
Xtest = kbs.transform(Xtest)
if name != "Gaussian" and name !="Categorical":
kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(Xtrain)
Xtrain = kbs.transform(Xtrain)
Xtest = kbs.transform(Xtest)
clf.fit(Xtrain,Ytrain)
y_pred = clf.predict(Xtest)
proba = clf.predict_proba(Xtest)[:,1]
score = clf.score(Xtest,Ytest)
print(name)
print("\tBrier:{:.3f}".format(BS(Ytest,proba,pos_label=1)))
print("\tAccuracy:{:.3f}".format(score))
# 返回召回率
print("\tRecall:{:.3f}".format(recall_score(Ytest,y_pred)))
# 返回AUC
print("\tAUC:{:.3f}".format(AUC(Ytest,proba)))
代码结果如下:
从结果得到如下结论:
- 多项式朴素贝叶斯判断出了所有的多数类样本,但放弃了全部的少数类样本,受到样本不均衡问题影响最严重
- 类别贝叶斯和多项式朴素贝叶斯一样
- 高斯比多项式在少数类的判断上更加成功一些,至少得到了43.8%的recall
- 伯努利贝叶斯虽然整体的准确度和布里尔分数不如多项式和高斯朴素贝叶斯和,但至少成功捕捉出了77.1%的少数类
从上述结果可知,这四类贝叶斯结果都不好,因此出现了新兴贝叶斯算法:补集朴素贝叶斯
4.5 补集贝叶斯
1 ComplementNB语法格式
补集朴素贝叶斯(complement naive Bayes,CNB)算法是标准多项式朴素贝叶斯算法的改进。补集贝叶斯可以解决样本不平衡问题,并且能够一定程度上忽略朴素假设
class sklearn.naive_bayes.ComplementNB(*,alpha = 1.0,fit_prior = True,class_prior = None,norm = False )
参数说明
参数 | 说明 |
---|---|
alpha | float, default=1.0 附加的(Laplace/Lidstone)平滑参数(0表示不平滑) |
fit_prior | bool, default=True 是否学习类别先验概率。如果为False,将使用统一的先验。 |
class_prior | array-like of shape (n_classes,), default=None 类别的先验概率。一经指定先验概率不能随着数据而调整。 |
norm | bool, default=False 在计算权重的时候是否适用L2范式来规范权重的大小。默认不进行规范,即不跟从补集朴素贝叶斯算法的全部内容,如果希望进行规范,请设置为True。 |
属性说明:
属性 | 说明 |
---|---|
class_count_ | ndarray of shape (n_classes,) 拟合期间每个类别遇到的样本数。此值由提供的样本权重加权。 |
class_log_prior_ | ndarray of shape (n_classes,) 每个类别的对数概率(平滑)。 |
classes_ | ndarray of shape (n_classes,) 分类器已知的类别标签 |
feature_all_ | ndarray of shape (n_features,) 拟合期间每个特征遇到的样本数。此值由提供的样本权重加权。 |
feature_count_ | ndarray of shape (n_classes, n_features) 拟合期间每个(类别,特征)遇到的样本数。此值由提供的样本权重加权。 |
feature_log_prob_ | ndarray of shape (n_classes, n_features) 类别补充的经验权重 |
n_features_ | int 每个样本的特征数量。 |
常用方法如下:
方法 | 说明 |
---|---|
fit (X, y[, sample_weight]) | 根据X,y拟合朴素贝叶斯分类器 |
get_params ([deep]) | 获取这个估计器的参数 |
partial_fit (X, y[, classes, sample_weight]) | 对一批样本进行增量拟合 |
predict (X) | 对测试向量X进行分类。 |
predict_log_proba (X) | 返回针对测试向量X的对数概率估计 |
predict_proba (X) | 返回针对测试向量X的概率估计 |
score (X, y[, sample_weight]) | 返回给定测试数据和标签上的平均准确率。 |
set_params (**params) | 为这个估计器设置参数 |
对X矩阵和y矩阵要求如下:
参数 | 说明 |
---|---|
X | {array-like, sparse matrix} of shape (n_samples, n_features) 用于训练的向量,其中n_samples是样本数量,n_features是特征数量。 |
y | array-like of shape (n_samples,) 目标值 |
2 代码例子
接下来看一个例子,以少数类样本为正类,代码如下:
from sklearn.naive_bayes import MultinomialNB, GaussianNB, BernoulliNB,CategoricalNB, ComplementNB
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.metrics import brier_score_loss as BS,recall_score,roc_auc_score as AUC
# 第一步:创建数据集
# 其中,少数类为正类即为类别1
class_1 = 50000 #多数类为50000个样本
class_2 = 500 #少数类为500个样本
centers = [[0.0, 0.0], [5.0, 5.0]] #设定两个类别的中心
clusters_std = [3, 1] #设定两个类别的方差
X, y = make_blobs(n_samples=[class_1, class_2],
centers=centers,
cluster_std=clusters_std,
random_state=0, shuffle=False)
# 查看所有贝叶斯样本不均衡的表现
name = ["Multinomial","Gaussian","Bernoulli","Categorical","Complement"]
models = [MultinomialNB(),GaussianNB(),BernoulliNB(),CategoricalNB(),ComplementNB()]
for name,clf in zip(name,models):
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y
,test_size=0.3
,random_state=420)
if name == "Categorical":
kbs = KBinsDiscretizer(n_bins=5, encode='ordinal').fit(Xtrain)
Xtrain = kbs.transform(Xtrain)
Xtest = kbs.transform(Xtest)
if name != "Gaussian" and name !="Categorical":
kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(Xtrain)
Xtrain = kbs.transform(Xtrain)
Xtest = kbs.transform(Xtest)
clf.fit(Xtrain,Ytrain)
y_pred = clf.predict(Xtest)
proba = clf.predict_proba(Xtest)[:,1]
score = clf.score(Xtest,Ytest)
print(name)
print("\tBrier:{:.3f}".format(BS(Ytest,proba,pos_label=1)))
print("\tAccuracy:{:.3f}".format(score))
# 返回召回率
print("\tRecall:{:.3f}".format(recall_score(Ytest,y_pred)))
# 返回AUC
print("\tAUC:{:.3f}".format(AUC(Ytest,proba)))
代码结果如下:
从代码结果可以得出:
- 补集朴素贝叶斯牺牲了部分整体的精确度和布里尔指数,但是得到了十分高的召回率Recall,捕捉出了98.7%的少数类,并且在此基础上维持了和原本的多项式朴素贝叶斯一致的AUC分数。
- 和其他的贝叶斯算法比起来,补集朴素贝叶斯的运行速度也十分优秀。如果目标是捕捉少数类,则选择补集朴素贝叶斯。