目录
一、决策边界(Decision Boundary)
二、程序绘制决策边界(逻辑回归)
1、定义决策边界绘制函数
2、数据准备
3、参数定义
4、模型训练及结果
5、绘制决策边界
三、特征的可解释性
1、数据准备
2、参数设定
3、结果显示
四、分类模型的评价指标
1、混淆矩阵(Confusion matrix)
2、召回率(Recall)
3、 精确度(Precision)
4、F1-Score
5、识别0类来构建评估指标
(1)特异度(Specificity)
(2)阴性预测值(negative predictive value)
(3)伪阳率(false positive rate)
6、多分类混淆矩阵
7、ROC-AUC
(1)ROC曲线的绘制原理
(2)ROC-AUC的基本性质
(3)程序绘制ROC曲线
(4)ROC的概率敏感特性与偏态数据判别
(5)ROC的类别对称性
利用训练好的模型对样本空间所有的坐标点进行预测,然后观察样本空间所有点的不同类别之间的边界,最终就是模型的决策边界。
在二分类问题中,决策边界或决策表面是超曲面,其将基础向量空间划分为两个集合,一个集合。 分类器将决策边界一侧的所有点分类为属于一个类,而将另一侧的所有点分类为属于另一个类。可以通过绘制模型决策边界,来辅助判别分类模型的模型性能。
不同模型的决策边界并不相同,逻辑回归在二维样本空间中的决策边界是一条直线,KNN模型决策边界实际上是一个个圆圈叠加而成的拥有一定幅度的边界,而对于决策树模型来说,其决策边界实际上是一条条折线。
基于反向传播的人工神经网络或感知器的情况下,网络可以学习的决策边界的类型由网络具有的隐藏层的数量来确定。如果它没有隐藏层,那么它只能学习线性问题。如果它有一个隐藏层,则它可以学习Rn的紧致子集上的任何连续函数 ,如通用近似定理所示,因此它可以具有任意的决策边界。
神经网络试图学习决策边界,最小化经验误差,而支持向量机试图学习决策边界,最大化决策边界和数据点之间的经验边际。
只看1和5就可以,2,3,4参考上篇。
def logit_DB(X,w,y):
'''
逻辑回归决策边界绘制函数
:param X: 特征
:param w: 参数
:param y: 标签
:return: 逻辑回归决策边界绘制图
'''
from matplotlib.colors import ListedColormap
#以X1,2两列即两个特征的+1/-1作为边界,并在其中添加1000个点
x1,x2=np.meshgrid(np.linspace(X[:,0].min()-1,X[:,0].max()+1,1000).reshape(-1,1),
np.linspace(X[:,1].min()-1,X[:,1].max()+1,1000).reshape(-1,1))
#将所有点的横纵坐标转换为二维数组
X_temp=np.concatenate([x1.reshape(-1,1),x2.reshape(-1,1),np.ones(shape=(1000000,1))],1)
#对所有点进行逻辑回归预测
y_hat_temp=logit_cla(sigmiod(X_temp.dot(w)))
yhat=y_hat_temp.reshape(x1.shape) #转变为x1这样的形状,便于对不同颜色的点进行区分的时候,有满足x1形状的参数
custom_cmap = ListedColormap(['#EF9A9A', '#90CAF9'])
plt.contourf(x1, x2, yhat, cmap=custom_cmap)
plt.show()
# 设置随机数种子
np.random.seed(24)
# 创建数据
f, l = arrayGenCla(num_class = 2, deg_dispersion = [6, 2], bias = True) # 离散程度较小
# 设置随机数种子
np.random.seed(24)
# 数据切分
Xtrain, Xtest, ytrain, ytest = array_split(f, l)
mean_ = Xtrain[:, :-1].mean(axis=0)
std_ = Xtrain[:, :-1].std(axis=0)
#数据归一化
Xtrain[:, :-1] = (Xtrain[:, :-1] - mean_) / std_
Xtest[:, :-1] = (Xtest[:, :-1] - mean_) / std_
# 观察数据集整体情况
plt.scatter(f[:, 0], f[:, 1], c=l)
# 设置随机数种子
np.random.seed(24)
# 参数初始值
n = f.shape[1]
w = np.random.randn(n, 1)
# 核心参数
batch_size = 50
num_epoch = 200
lr_init = 0.2
lr_lambda = lambda epoch: 0.95 ** epoch #定义匿名函数来执行学习率衰减
# 记录迭代过程模型准确率计算结果
train_acc = []
test_acc = []
for i in range(num_epoch):
w = sgd_cal(Xtrain, w, ytrain, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
train_acc.append(logit_acc(Xtrain, w, ytrain, thr=0.5))
test_acc.append(logit_acc(Xtest, w, ytest, thr=0.5))
# 观察计算结果
plt.plot(list(range(num_epoch)), np.array(train_acc).flatten(), label='train_acc')
plt.plot(list(range(num_epoch)), np.array(test_acc).flatten(), label='test_acc')
plt.xlabel('epochs')
plt.ylabel('Accuracy')
plt.legend(loc = 4)
plt.show()
# 训练集上的决策边界
logit_DB(Xtrain, w, ytrain)
plt.scatter(Xtrain[(ytrain == 0).flatten(), 0], Xtrain[(ytrain == 0).flatten(), 1], color='yellow')
plt.scatter(Xtrain[(ytrain == 1).flatten(), 0], Xtrain[(ytrain == 1).flatten(), 1], color='green')
plt.show()
我们既然已经求得逻辑回归的方程系数和决策边界,是否可以由此判断不同特征之间哪一个更重要。不可以
iris_df = pd.read_csv('D:\iris.csv')
features_temp = iris_df.iloc[:, 1: 3].values
labels_temp = iris_df.iloc[:, -1].values
#构建二分类布尔索引
labels_temp[labels_temp != 'Iris-setosa'] = 0
labels_temp[labels_temp == 'Iris-setosa'] = 1
labels = labels_temp.astype(float).reshape(-1, 1)
features = np.concatenate([features_temp, np.ones(shape=labels.shape)], 1)
# 设置随机数种子
np.random.seed(24)
# 核心参数
batch_size = 10
num_epoch = 200
lr_init = 0.5
# 参数初始值
n = features.shape[1]
w = np.random.randn(n, 1) #参数的初始取值不同,最终的特征系数不同
# 定义学习率衰减匿名函数
lr_lambda = lambda epoch: 0.95 ** epoch
# 模型训练
for i in range(num_epoch):
w = sgd_cal(features, w, labels, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
# 计算准确率
logit_acc(features, w, labels, thr=0.5)
# 绘制决策边界
logit_DB(features, w, labels)
plt.scatter(features[(labels == 0).flatten(), 0], features[(labels == 0).flatten(), 1], color='red')
plt.scatter(features[(labels == 1).flatten(), 0], features[(labels == 1).flatten(), 1], color='blue')
plt.xlabel('sepal_width')
plt.ylabel('petal_length')
plt.title('Iris-setosa or not')
plt.show()
我们无法通过系数w的取值来判定特征的重要性,正负只能表示一种对最终结果正负向的促进作用。绝对值类似也无法判定两者贡献相近,某种程度上我们可以仅通过petal_length来做区分。
不同初始参数的取值会获得不同的特征系数,但均可以在训练好的模型中取得较好的预测结果。特征系数本身与特征贡献度并不对应。
单分类模型 | 多分类模型 |
准确率 Accuracy | F1-score |
精确率 Precision | ROC曲线 |
召回率 Recall | AUC |
类别划分时将重点识别的样本类划为类别1,其他样本划为类别0。如果0、1两类在业务判断上并没有任何重要性方面的差异,那么我们可以将样本更少的哪一类划为1类。
如下图10个样本,文字标注为某一模型的预测结果:
Actual condition:样本真实标签;
Predicated condition:模型预测标签;
Actual condition positive(P):样本中阳性样本总数,一般也就是真实标签为1的样本总数;【P=6,有6个蓝】
Actual condition negative(N):样本中阴性样本总数,一般也就是真实标签为0的样本总数;【N=4,有四个黄】
Predicted condition positive(PP):预测中阳性样本总数,一般也就是预测标签为1的样本总数;【PP=5,有五个被预测为蓝色】
Predicted condition negative(PN):预测中阳性样本总数,一般也就是预测标签为0的样本总数【PN=5,有五个被预测为黄色】
样本总数=P+N=PP+NN=10
在上面的例子中:
别称:sensitivity(敏感度)、hit rate(命中率)、true positive rate (TPR)、查全率
positive即1类样本中准确识别的概率。
本例中6个蓝色的1类样本,准确预测了4个。以召回率作为模型评估指标,则会使得模型非常重视是否把1全部识别了出来,甚至是牺牲掉一些0类样本判别的准确率来提升召回率,即哪怕是错判一些0样本为1类样本,也要将1类样本识别出来。
使用Scikit-Learn利用网格搜索进行模型调参时,对于某些本身分类性能比较强(本身对两类都能进行较好识别)的模型,我们可以适时采用召回率作为网格搜索评价指数,来提升1类识别准确率。
别称:positive predictive value (PPV)、查准率
关注每一次出手(对1类样本的识别)能否成功(准确识别出1)的概率,精确度计算公式为:
本例中5条样本被判定为蓝色,其中4条是判定正确的以精确度作为模型判别指标时,模型整体对1的判别会趋于保守,只对那些大概率确定为1的样本进行1类的判别,从而会一定程度牺牲1类样本的准确率,在每次判别成本较高、而识别1样本获益有限的情况可以考虑使用精确度。
附上很经典的一个图:
召回率和精确度其实是一对相对的概念,在围绕1类样本的识别过程中,召回率力求尽可能更多的将1识别出来,而精确度则力求每次对1样本的判别都能获得一个正确的结果。这时候可以考虑使用二者的调和平均数(harmonic mean)作为模型评估指标,即F1-Score。
用TPR表示Recall、PPV表示Precision,此时F1-Score可表示如下:
用混淆矩阵的一级指标来进行表示,则:
F1-Score是一个介于[0,1]之间的计算结果,当FP+FN=0时候(即没有误判样本时),F1-Score计算结果为1。
除了F1-Score以外,还有一种更为一般的、可以自主调整召回率和精确度在参与调和平均数计算过程中的权重的评估指标,,其计算公式如下:
F1-Score并不是类别对称的,也就是说,如果我们将0类和1类数据标签互换,最终算得的F1-Socre结果会有所不同。因此其实F1-Score虽然是一个更加均衡的评估指标,但其实也只是均衡了在识别1类样本时“激进”或者“保守”的倾向性,但本质上还是一个围绕模型对1类样本识别能力所构建的评估指标。
衡量0类被正确识别比例的特异度,该指标类似召回率。特异度往往也被称为true negative rate (TNR)。
类似Precision,也被称为NPV(negative predictive value)。其计算公式如下:
表示在所有预测为0类的样本中,错误样本所占比例。
3分类就是一个3*3的矩阵。
如果要进行Recall、Precision的计算,则需要先采用此前介绍的OVR策略进行“划分”、然后采用均值策略进行“集成”,依次将A、B、C视为1类,其余类别视为0类来进行计算。
ROC(全称为Receiver operating characteristic,意为受试者特征曲线)是一个二维平面空间中一条曲线,而AUC则是曲线下方面积(Area Under Curve)的计算结果,是一个具体的值。
找个图:
它的横坐标是FPR伪阳率,纵坐标为TPR(真阳性率,又称recall召回率)。
真实场景中ROC曲线一般都会在这条直线的上方,所以AUC的取值一般在0.5~1之间。AUC的值越大,说明该模型的性能越好。
ROC曲线正是通过不断移动分类器的“阈值”来生成曲线上的一组关键点的。
参考网图:
我们指定一个阈值为0.9,那么只有第一个样本(0.9)会被归类为正例,而其他所有样本都会被归为负例,因此,对于0.9这个阈值,我们可以计算出FPR为0,TPR为0.1(因为总共10个正样本,预测正确的个数为1),那么我们就知道曲线上必有一个点为(0, 0.1)。依次选择不同的阈值(或称为“截断点”),画出全部的关键点以后,再连接关键点即可最终得到ROC曲线如下图所示。
ROC曲线上的点分布在横纵坐标都在[0,1]范围内的二维平面区间内。
对于任意模型来说,ROC曲线越靠近左上方、ROC曲线下方面积越大,则模型分类性能越好。
根据点的移动轨迹构成ROC曲线角度来理解,刚开始移动时,是朝向X还是Y轴正向移动,其实是有模型输出概率最高的几个样本决定的,如果这几个样本被判别错了(即实际样本类别为0),则刚开始从原点移动就将朝着X轴正方向移动,此时曲线下方面积会相对更小(相比刚开始朝着Y轴正方向移动的情况),并且根据此前介绍的理论,此时由于模型对于“非常肯定”的样本都判错了,证明模型本身判别性能欠佳;而反之,如果输出概率最高的头部几条样本都判断正确,样本真实类别确实属于1,则点开始移动时将朝向Y轴正方向移动,此时曲线下方面积就将相对更大,模型判别性能也将相对较好。
# 定义阈值取值范围
thr_l = np.linspace(1, 0, 100)
# 输入两个模型预测结果和数据真实标签
yhat_A = np.array([0.9, 0.8, 0.6, 0.4, 0.3]).reshape(-1, 1)
y_A = np.array([1, 1, 0, 1, 0]).reshape(-1, 1)
yhat_B = np.array([0.9, 0.8, 0.6, 0.4, 0.3]).reshape(-1, 1)
y_B = np.array([1, 0, 1, 1, 0]).reshape(-1, 1)
# TPR计算过程
y_cla = logit_cla(yhat_A, thr=0.5) #逻辑回归类别输出函数,以0.5为阈值将预测结果转换为0,1标签
P = y_cla[y_A == 1] #将预测的0,1标签转换为布尔类型True、False,再用布尔类型对原来的真实结果进行索引,索引预测标签为1的真实组合为P
TPR = P.mean() #P为【1,1,0】,对这个求mean即为(1+1+0)/3
# FPR计算过程
N = y_cla[y_A == 0]
FPR = N.mean()
# 定义ROC曲线绘制函数
def ROC_curve(yhat, y, thr_l, label='ROC_curve'):
"""
ROC绘制曲线函数:
:param yhat: 模型输出的类别概率判别结果
:param y: 样本真实类别
:param thr_l:阈值取值列表
:param label:折线图的图例
:return :ROC曲线绘制图
"""
TPR_l = []
FPR_l = []
for i in thr_l:
y_cla = logit_cla(yhat, thr=i)
P = y_cla[y == 1]
TPR = P.mean()
TPR_l.append(TPR)
N = y_cla[y == 0]
FPR = N.mean()
FPR_l.append(FPR)
plt.plot(FPR_l, TPR_l, label=label)
# 绘制ROC曲线
ROC_curve(yhat_A, y_A, thr_l, label='Model A')
ROC_curve(yhat_B, y_B, thr_l, label='Model B')
plt.plot([0, 1], [0, 1], 'r--')
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.title('ROC_curve')
plt.legend(loc = 4)
plt.show()
如果数据是偏态数据,由于ROC是对概率敏感的判别曲线(根据概率结果而非类别判别结果进行识别),因此ROC能够对模型对于偏态数据中少量样本的识别能力进行评估。
ROC和F1-Score类似,少数的1类样本的判别结果会很大程度影响AUC的计算结果,因此ROC-AUC也能用于判别模型在偏态样本上的分类能力。
如果我们将数据中的0和1类互换,而模型原先预测1的类概率就变成了现在预测0类的概率,此时ROC曲线会参照x+y=1的直线进行对称变换,但AUC面积不变,即模型性能评估数值仍然不会发生变化。
F1-Score更加倾向于判别模型对1类样本的识别能力,而ROC-AUC则没有这方面的倾向性。因此,ROC-AUC和F1-Score之间的选取问题,同样也需要根据业务需要来进行选择,如果需要重点考虑1类是否被正确识别,则更加倾向选择F1-Score,但如果没有其他特殊要求,则一般会考虑使用ROC-AUC作为模型评估指标。
参考:机器学习基础(1)- ROC曲线理解 - 简书 (jianshu.com)