Python机器学习——模型评估与选择

第6关:准确度的陷阱与混淆矩阵

任务描述

本关任务:填写 python 代码,完成 confusion_matrix 函数实现二分类混淆矩阵的构建。

相关知识

准确度的缺陷

准确度这个概念相信对于大家来说肯定并不陌生,就是正确率。例如模型的预测结果与数据真实结果如下表所示:

编号预测结果真实结果
112
222
333
411
523

很明显,连小朋友都能算出来该模型的准确度为 3/5。

那么准确对越高就能说明模型的分类性能越好吗?非也!举个例子,现在我开发了一套癌症检测系统,只要输入你的一些基本健康信息,就能预测出你现在是否患有癌症,并且分类的准确度为 0.999。您认为这样的系统的预测性能好不好呢?

您可能会觉得,哇,这么高的准确度!这个系统肯定很牛逼!但是我们知道,一般年轻人患癌症的概率非常低,假设患癌症的概率为 0.001,那么其实我这个癌症检测系统只要一直输出您没有患癌症,准确度也可能能够达到 0.999。

假如现在有一个人本身已经患有癌症,但是他自己不知道自己患有癌症。这个时候用我的癌症检测系统检测发现他没有得癌症,那很显然我这个系统已经把他给坑了(耽误了治疗)。看到这里您应该已经体会到了,一个分类模型如果光看准确度是不够的,尤其是对这种样本极度不平衡的情况( 10000 条健康信息数据中,只有 1 条的类别是患有癌症,其他的类别都是健康)。

混淆矩阵

想进一步的考量分类模型的性能如何,可以使用其他的一些性能指标,例如精准率和召回率。但这些指标计算的基础是混淆矩阵

继续以癌症检测系统为例,癌症检测系统的输出不是有癌症就是健康,这里为了方便,就用 1 表示患有癌症,0 表示健康。假设现在拿 10000 条数据来进行测试,其中有 9978 条数据的真实类别是 0,系统预测的类别也是 0,有 2 条数据的真实类别是 1 却预测成了 0,有 12 条数据的真实类别是 0 但预测成了 1,有 8 条数据的真实类别是 1,预测结果也是 1。

如果我们把这些结果组成如下矩阵,则该矩阵就成为混淆矩阵

真实预测01
0997812
128

混淆矩阵中每个格子所代表的的意义也很明显,意义如下:

真实预测01
0预测0正确的数量预测1错误的数量
1预测0错误的数量预测1正确的数量

如果将正确看成是 True,错误看成是 False, 0 看成是 Negtive, 1 看成是 Positive。然后将上表中的文字替换掉,混淆矩阵如下:

真实预测01
0TNFP
1FNTP

因此 TN 表示真实类别是 Negtive,预测结果也是 Negtive 的数量; FP 表示真实类别是 Negtive,预测结果是 Positive 的数量; FN 表示真实类别是 Positive,预测结果是 Negtive 的数量;TP 表示真实类别是 Positive,预测结果也是 Positive 的数量。

很明显,当 FN 和 FP 都等于 0 时,模型的性能应该是最好的,因为模型并没有在预测的时候犯错误。即如下混淆矩阵:

真实预测01
099780
1022

所以模型分类性能越好,混淆矩阵中非对角线上的数值越小。

代码示例:

def confusion_matrix(y_true, y_predict):
	def TN(y_true, y_predict):
		return np.sum((y_true == 0) & (y_predict == 0))
	
	def FP(y_true, y_predict):
		return np.sum((y_true == 0) & (y_predict == 1))

	def FN(y_true, y_predict):
		return np.sum((y_true == 1) & (y_predict == 0))

	def TP(y_true, y_predict):
		return np.sum((y_true == 1) & (y_predict == 1))

	return np.array([
		[TN(y_true, y_predict), FP(y_true, y_predict)],
		[FN(y_true, y_predict), TP(y_true, y_predict)]
	])

编程要求

根据提示,在 Begin-End 区域填写 python 代码,完成 confusion_matrix 函数实现二分类混淆矩阵的构建。
confusion_matrix 函数中的参数:

  • y_true:数据的真实类别,类型为 ndarray;
  • y_predict:模型预测的类别,类型为 ndarray。

测试输入:
{‘y_true’:[1, 0, 0, 1, 0, 1, 0], ‘y_predict’:[0, 1, 0, 1, 0, 1, 0]}
预期输出:
[[3 1]
[1 2]]

import numpy as np

def confusion_matrix(y_true, y_predict):
	'''
	构建二分类的混淆矩阵,并将其返回
	:param y_true: 真实类别,类型为ndarray
	:param y_predict: 预测类别,类型为ndarray
	:return: shape为(2, 2)的ndarray

	'''
	#********* Begin *********#
	TN = np.sum((y_true == 0) & (y_predict == 0))
	FP = np.sum((y_true == 0) & (y_predict == 1))
	FN = np.sum((y_true == 1) & (y_predict == 0))
	TP = np.sum((y_true == 1) & (y_predict == 1))

	return np.array([
		[TN, FP],
		[FN, TP]
	])
	#********* End *********#

第7关:精准率与召回率

任务描述

本关任务:填写 python 代码,完成 precision_score 函数和 recall_score 函数分别实现计算精准率和召回率。

相关知识

精准率

精准率(Precision) 指的是模型预测为 Positive 时的预测准确度,其计算公式如下:

P r e c i s i o n = T P T P + F P Precision = \frac{TP}{TP+FP} Precision=TP+FPTP

假如癌症检测系统的混淆矩阵如下:

真实预测01
0997812
128

则该系统的精准率 = 8/(8+12) = 0.4。
0.4 这个值表示癌症检测系统的预测结果中如果有 100 个人被预测成患有癌症,那么其中有 40 人是真的患有癌症。也就是说,精准率越高,那么癌症检测系统预测某人患有癌症的可信度就越高。

召回率

召回率(Recall) 指的是我们关注的事件发生了,并且模型预测正确了的比值,其计算公式如下:

R e c a l l = T P F N + T P Recall = \frac{TP}{FN+TP} Recall=FN+TPTP

假如癌症检测系统的混淆矩阵如下:

真实预测01
0997812
128

则该系统的召回率 = 8/(8+2) = 0.8。
从计算出的召回率可以看出,假设有 100 个患有癌症的病人使用这个系统进行癌症检测,系统能够检测出 80 人是患有癌症的。也就是说,召回率越高,那么我们感兴趣的对象成为漏网之鱼的可能性越低。

精准率与召回率之间的关系

假设有这么一组数据,菱形代表 Positive,圆形代表 Negtive 。

在这里插入图片描述
现在需要训练一个模型对数据进行分类,假如该模型非常简单,就是在数据上画一条线作为分类边界。模型认为边界的左边是 Negtive,右边是 Positive。如果该模型的分类边界向左或者向右移动的话,模型所对应的精准率和召回率如下图所示:

在这里插入图片描述
从上图可知,模型的精准率变高,召回率会变低,精准率变低,召回率会变高

应该选精准率还是召回率作为性能指标?

到底应该使用精准率还是召回率作为性能指标,其实是根据具体业务来决定的

比如我现在想要训练一个模型来预测我关心的股票是涨( Positive )还是跌( Negtive ),那么我们应该主要使用精准率作为性能指标。因为精准率高的话,则模型预测该股票要涨的可信度就高(很有可能赚钱!)。

比如现在需要训练一个模型来预测人是( Positive )否( Negtive )患有艾滋病,那么我们应该主要使用召回率作为性能指标。因为召回率太低的话,很有可能存在漏网之鱼(可能一个人本身患有艾滋病,但预测成了健康),这样就很可能导致病人错过了最佳的治疗时间,这是非常致命的。

编程要求

根据提示,在 Begin-End 区域填写 python 代码,完成 precision_score 函数和 recall_score 函数分别实现计算精准率和召回率。

precision_score 函数中的参数:

  • y_true :数据的真实类别,类型为 ndarray;
  • y_predict :模型预测的类别,类型为 ndarray。

recall_score 函数中的参数:

  • y_true:数据的真实类别,类型为 ndarray;
  • y_predict:模型预测的类别,类型为 ndarray。

测试输入:
{‘y_true’:[1, 0, 0, 1, 0, 1, 0], ‘y_predict’:[0, 1, 0, 1, 0, 1, 0]}

预期输出:
0.666667, 0.666667

import numpy as np

def precision_score(y_true, y_predict):
	'''
	计算精准率并返回
	:param y_true: 真实类别,类型为ndarray
	:param y_predict: 预测类别,类型为ndarray
	:return: 精准率,类型为float
	'''
	#********* Begin *********#
	TP = np.sum((y_true == 1) & (y_predict == 1))
	FP = np.sum((y_true == 0) & (y_predict == 1))
	Precision = TP/(TP+FP)
	return Precision
	#********* End *********#

def recall_score(y_true, y_predict):
	'''
	计算召回率并召回
	:param y_true: 真实类别,类型为ndarray
	:param y_predict: 预测类别,类型为ndarray
	:return: 召回率,类型为float
	'''
	#********* Begin *********#
	TP = np.sum((y_true == 1) & (y_predict == 1))
	FN = np.sum((y_true == 1) & (y_predict == 0))
	Recall = TP/(TP+FN)
	return Recall
	#********* End *********#

第8关:F1 Score

任务描述

本关任务:填写 python 代码,完成 f1_score 函数实现计算 F1 Score。

相关知识

F1 Score

上一关中提到了精准率变高,召回率会变低,精准率变低,召回率会变高。那如果想要同时兼顾精准率和召回率,这个时候就可以使用 F1 Score 来作为性能度量指标了。

F1 Score 是统计学中用来衡量二分类模型精确度的一种指标。它同时兼顾了分类模型的准确率和召回率。 F1 Score 可以看作是模型准确率和召回率的一种加权平均,它的最大值是 1,最小值是 0。其公式如下:

F 1 = 2 ∗ p r e c i s i o n ∗ r e c a l l p r e c i s i o n + r e c a l l F1 = \frac{2*precision*recall}{precision+recall} F1=precision+recall2precisionrecall

  • 假设模型 A 的精准率为 0.2,召回率为 0.7,那么模型 A 的 F1 Score 为 0.31111。
  • 假设模型 B 的精准率为 0.7,召回率为 0.2,那么模型 B 的 F1 Score 为 0.31111。
  • 假设模型 C 的精准率为 0.8,召回率为 0.7,那么模型 C 的 F1 Score 为 0.74667。
  • 假设模型 D 的精准率为 0.2,召回率为 0.3,那么模型 D 的 F1 Score 为 0.24。

从上述 4 个模型的各种性能可以看出,模型C的精准率和召回率都比较高,因此它的 F1 Score 也比较高。而其他模型的精准率和召回率要么都比较低,要么一个低一个高,所以它们的 F1 Score 比较低。

这也说明了只有当模型的精准率和召回率都比较高时 F1 Score 才会比较高。这也是 F1 Score 能够同时兼顾精准率和召回率的原因。

编程要求

根据提示,在 Begin-End 区域填写 python 代码,完成 f1_score 函数实现计算 F1 Score。

f1_score 函数中的参数:

  • precision:模型的精准率,类型为 float;
  • recall:模型的召回率,类型为 float。

一个测试用例(列表中的第一个数字表示精准率,第二个数字表示召回率):
测试输入:[0.7, 0.2]
预期输出:0.311111

import numpy as np

def f1_score(precision, recall):
	'''
	计算f1 score并返回
	:param precision: 模型的精准率,类型为float
	:param recall: 模型的召回率,类型为float
	:return: 模型的f1 score,类型为float
	'''
	#********* Begin *********#
	f1 = (2*precision*recall)/(precision+recall)
	return f1
	#********* End ***********#

第9关:ROC曲线与AUC

任务描述

本关任务:填写 python 代码,完成 AUC 函数实现计算 AUC。

相关知识

ROC曲线

ROC曲线( Receiver Operating Cha\fracteristic Curve )描述的 TPR ( True Positive Rate )与 FPR ( False Positive Rate )之间关系的曲线。

TPR 与 FPR 的计算公式如下:

T P R = T P T P + F N TPR = \frac{TP}{TP+FN} TPR=TP+FNTP

F P R = F P F P + T N FPR = \frac{FP}{FP+TN} FPR=FP+TNFP

其中 TPR 的计算公式您可能有点眼熟,没错!就是召回率的计算公式。也就是说 TPR 就是召回率。所以 TPR 描述的是模型预测 Positive 并且预测正确的数量占真实类别为 Positive 样本的比例。而 FPR 描述的模型预测 Positive 并且预测错了的数量占真实类别为 Negtive 样本的比例。

和精准率与召回率一样, TPR 与 FPR 之间也存在关系。假设有这么一组数据,菱形代表 Positive,圆形代表 Negtive。
在这里插入图片描述
现在需要训练一个逻辑回归的模型对数据进行分类,假如将从 0 到 1 中的一些值作为模型的分类阈值。若模型认为当前数据是 Positive 的概率小于分类阈值则分类为 Negtive ,否则就分类为 Positive (假设分类阈值为 0.8,模型认为这条数据是 Positive 的概率为 0.7, 0.7 小于 0.8,那么模型就认为这条数据是 Negtive)。在不同的分类阈值下,模型所对应的 TPR 与 FPR 如下图所示(竖线代表分类阈值,模型会将竖线左边的数据分类成 Negtive,竖线右边的分类成 Positive ):
在这里插入图片描述
从图中可以看出,当模型的 TPR 越高 FPR 也会越高, TPR 越低 FPR 也会越低。这与精准率和召回率之间的关系刚好相反。 并且,模型的分类阈值一但改变,就有一组对应的 TPR 与 FPR 。
假设该模型在不同的分类阈值下其对应的 TPR 与 FPR 如下表所示:

TPRFPR
0.20.08
0.350.1
0.370.111
0.510.12
0.530.13
0.560.14
0.710.21
0.820.26
0.920.41
0.930.42

若将 FPR 作为横轴, TPR 作为纵轴,将上面的表格以折线图的形式画出来就是 ROC曲线 。
在这里插入图片描述
假设现在有模型 A 和模型 B ,它们的 ROC 曲线如下图所示(其中模型 A 的 ROC曲线 为黄色,模型 B 的 ROC 曲线 为蓝色):
在这里插入图片描述
**那么模型 A 的性能比模型 B 的性能好,因为模型 A 当 FPR 较低时所对应的 TPR 比模型 B 的低 FPR 所对应的 TPR 更高。**由于随着 FPR 的增大, TPR 也会增大。所以 ROC 曲线与横轴所围成的面积越大,模型的分类性能就越高。而 ROC曲线 的面积称为AUC

AUC

很明显模型的 AUC 越高,模型的二分类性能就越强。AUC 的计算公式如下:

A U C = ∑ i ∈ p o s i t i v e c l a s s r a n k i − M ( M + 1 ) 2 M ∗ N AUC = \frac{ \sum_{i \in positiveclass} rank_i - \frac{M(M+1)}{2} }{M*N} AUC=MNipositiveclassranki2M(M+1)

其中 M 为真实类别为 Positive 的样本数量,N 为真实类别为 Negtive 的样本数量。ranki 代表了真实类别为 Positive 的样本点额预测概率从小到大排序后,该预测概率排在第几。

举个例子,现有预测概率与真实类别的表格如下所示(其中 0 表示 Negtive, 1 表示 Positive ):

编号预测概率真实类别
10.10
20.40
30.31
40.81

想要得到公式中的 rank,就需要将预测概率从小到大排序,排序后如下:

编号预测概率真实类别
10.10
30.31
20.40
40.81

排序后的表格中,真实类别为 Positive 只有编号为 3 和编号为 4 的数据,并且编号为 3 的数据排在第 2 ,编号为 4 的数据排在第 4。所以 rank=[2, 4] 。又因表格中真是类别为 Positive 的数据有 2 条, Negtive 的数据有 2 条。因此 M 为2, N 为2。所以根据 AUC 的计算公式可知:

A U C = ( 2 + 4 ) − 2 ( 2 + 1 ) 2 2 ∗ 2 AUC = \frac{(2 + 4) - \frac{2(2+1)}{2}} {2*2} AUC=22(2+4)22(2+1)

编程要求

根据提示,在 Begin-End 区域填写 python 代码,完成 calAUC 函数实现计算 AUC 并返回。

calAUC 函数中的参数:

  • prob:模型预测样本为 Positive 的概率列表,类型为 ndarray;
  • labels:样本的真实类别列表,其中 1 表示 Positive ,0 表示 Negtive ,类型为 ndarray 。

一个测试用例(字典中的 probs 部分代表模型认为样本是 Positive 的概率,labels 部分代表样本的真实类别,1 表示 Positive, 0 表示 Negtive ):
测试输入: {‘probs’:[0.1, 0.4, 0.3, 0.8], ‘labels’:[0, 0, 1, 1]}
预期输出: 0.75

import numpy as np

def calAUC(prob, labels):
	'''
	计算AUC并返回
	:param prob: 模型预测样本为Positive的概率列表,类型为ndarray
	:param labels: 样本的真实类别列表,其中1表示Positive,0表示Negtive,类型为ndarray
	:return: AUC,类型为float
	'''
	#********* Begin *********#
	f = list(zip(prob, labels))
	rank = [values2 for values1, values2 in sorted(f, key=lambda x:x[0])]
	rankList = [i+1 for i in range(len(rank)) if rank[i] == 1]
	
	posNum = 0
	negNum = 0
	for i in range(len(labels)):
		if(labels[i] == 1):
			posNum += 1
		else:
			negNum += 1
	auc = (sum(rankList) - (posNum*(posNum+1))/2)/(posNum*negNum)
	return auc
	#********* End *********#

第10关:sklearn中的分类性能指标

任务描述

本关任务:使用 sklearn 完成对模型分类性能的评估。

相关知识

accu\fracy_score

sklearn 提供了计算准确度的接口 accu\fracy_score。其中参数如下:

  • y_true:为样本真实标签,类型为一维的 ndarray 或者 list;
  • y_pred:为模型预测标签,类型为一维的 ndarray 或者 list。

示例代码如下:

from sklearn.metrics import accu\fracy_score

precision_score

sklearn 提供了计算精准率的接口 precision_score 。其中参数如下:

  • y_true:为样本真实标签,类型为一维的 ndarray 或者 list;
  • y_pred:为模型预测标签,类型为一维的 ndarray 或者 list;
  • pos_label:用什么值表示 Positive,默认为 1。

示例代码如下:

from sklearn.metrics import precision_score

recall_score

sklearn 提供了计算召回率的接口 recall_score 。其中参数如下:

  • y_true:为样本真实标签,类型为一维的 ndarray 或者 list;
  • y_pred:为模型预测标签,类型为一维的 ndarray 或者 list;
  • pos_label:用什么值表示 Positive ,默认为 1。

示例代码如下:

from sklearn.metrics import recall_score

f1_score

sklearn 提供了计算 F1 Score 的接口 f1_score 。其中参数如下:

  • y_true:为样本真实标签,类型为一维的 ndarray 或者 list;
  • y_pred:为模型预测标签,类型为一维的 ndarray 或者 list;
  • pos_label:用什么值表示 Positive ,默认为 1。

示例代码如下:

from sklearn.metrics import f1_score

roc_auc_score

sklearn 提供了计算 AUC 的接口 roc_auc_score 。其中参数如下:

  • y_true:为样本真实标签,类型为一维的 ndarray 或者 list;
  • y_score:为模型预测样本为 Positive 的概率,类型为一维的 ndarray 或者 list。

示例代码如下:

import numpy as np
from sklearn.metrics import roc_auc_score

#y_true为真实标签,y_predict为预测标签
y_true = [1, 0, 0, 1]
y_predict = [1, 0, 1, 0]

print(accu\fracy_score(y_true, y_predict))

编程要求

在 Begin-End 区域填写 classification_performance(y_true, y_pred, y_prob) 函数分别计算模型的准确度、精准率、召回率、F1-Score 和 AUC 并将其返回,其中:

  • y_true :样本的真实类别,类型为 ndarray;
  • y_pred :模型预测出的类别,类型为 ndarray;
  • y_prob :模型预测样本为 Positive 的概率,类型为 ndarray。

测试说明

代码根据输入来按顺序返回正确的准确度、精准率、召回率、 F1-Score 和 AUC。
以下为其中一个测试用例(字典中的 y_prob 部分代表模型认为样本是 Positive 的概率;y_true 部分代表样本的真实类别,1 表示 Positive, 0 表示 Negtive;y_pred 部分代表模型预测的类别):
测试输入:
{‘y_prob’:[0.7, 0.2, 0.9, 0.8],’y_true’:[0, 0, 1, 1],’y_pred’:[1, 0, 1, 1]}
预期输出:
0.750000, 0.666667, 1.000000, 0.800000, 1.000000

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

def classification_performance(y_true, y_pred, y_prob):
	'''
	返回准确度、精准率、召回率、f1 Score和AUC
	:param y_true:样本的真实类别,类型为`ndarray`
	:param y_pred:模型预测出的类别,类型为`ndarray`
	:param y_prob:模型预测样本为`Positive`的概率,类型为`ndarray`
	:return:
	'''
	#********* Begin *********#
	return accuracy_score(y_true, y_pred), precision_score(y_true, y_pred), recall_score(y_true, y_pred), f1_score(y_true, y_pred,y_prob), roc_auc_score(y_true, y_prob)
	#********* End *********#
  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值