性能度量是指模型泛化能力的衡量,泛化能力可以理解为对于未知数据的预判能力
1、回归场景
(1)均方误差MSE
回归预测最常用的性能度量是“均方误差”(MSE mean squared error), 又称为L2范数损失。
预测值与实际值的差值平方求和求平均的过程
实践测试
调用python的sklearn快速计算一下。显然y_pred_2的预测效果比y_pred_1效果好,与y_true更接近。
from sklearn.metrics import mean_squared_error
y_true = [3000,1323,4234,6567]
y_pred_1 = [4212,2000,5002,6004]
y_pred_2 = [4124,1252,4341,6534]
print(mean_squared_error(y_true,y_pred_1)) # 708516.5
print(mean_squared_error(y_true,y_pred_2)) # 320238.75
看来MSE值越小说明预测能力越好(其实看公式也能看出来的)
(2)RMSE
提到均方误差(MSE,mean squared error)就不能不说说均方根误差(RMSE,root mean squared error)
两者有什么差异吗?说是RMSE消除了对量纲的影响。
import numpy as np
print(np.sqrt(mean_squared_error(y_true,y_pred_1))) # 841.7342217113428
print(np.sqrt(mean_squared_error(y_true,y_pred_2))) # 565.8964127824102
算法评估为什么要消除量纲?误差相关值不是越小就可以了吗?而且做完根号以后值还是很大的啊?不多运行比较几次都不知道拟合效果好不好?(解答待定)
(3)
R²(亦称可决系数、确定系数),取值范围在0-1之间,值越大模型的越好。
from sklearn.metrics import r2_score
y_true = [3000,1323,4234,6567]
y_pred_1 = [4212,2000,5002,6004]
y_pred_2 = [4124,1252,4341,6534]
r2_score(y_true, y_pred_1) # 0.8061345958233034
r2_score(y_true, y_pred_2) # 0.9123757672520116
既然R2那么好用,但实际上我们比较常见的还是mse、rmse这类性能度量指标,R2有什么局限吗?(解答待定)
2、分类场景
(1)错误率和精度
错误率(error rate) = 分类错误数a/总数m
精度(accuracy) = 分类正确数1-a/总数m
- 虽然常用,但局限比较大
- 适用于二分类、多分类
# 分为5类:0,1,2,3,4
from sklearn.metrics import accuracy_score
y_true = [0, 2, 1, 3,4,4,3,1,0]
y_pred = [1, 2, 1, 4,2,4,3,2,0] # 4个分错
accuracy_score(y_true, y_pred) # 0.55555556
(2)TP、FP、TN、FN
预测正例 | 预测反例 | |
---|---|---|
实际正例 | TP(true positive) | FN(false negative) |
实际反例 | FP(false positive) | TN(true negative) |
在TP、FP、TN、FN做文章的相关性能度量有:查准率precision、查全率recall、P-R曲线、ROC曲线、F1
(3)查准率与查全率
查准率又叫准确率,查全率又叫召回率。
查准率(准确率)precision = ,指所有预测为正例的数据中,真正例所占的比例(越大越好)
查全率(召回率)recall = ,指预测为正例的数据占所有真正例数据的比例(越大越好)
from sklearn import metrics
y_true = [0, 1, 1, 0,0,1,0,1,0,0,0,0,1,0,0,0,1]
y_pred = [0, 0, 0, 0,0,1,0,1,0,0,0,0,0,0,1,0,1]
metrics.precision_score(y_true, y_pred, average='binary') # 0.75
metrics.recall_score(y_true, y_pred, average='binary') # 0.5
在准确率和召回率的基础上扩建的概念有P-R曲线和F1两个概念,与P-R曲线类似的有ROC曲线。
(4)P-R曲线
以precision为纵轴,recall为横轴绘制P-R曲线,一般用于模型选择和参数调优。
【疑问】上面计算precision和recall不是都只有一个值吗?怎么能绘制曲线?
算法对样本进行分类时,都会有阈值(threshold),我们一般设置阈值为0.5,如果预测为1的概率大于0.5,则判断分类结果为1,否则分类为0。
通过调节阈值,改变预测正例的数量,从而改变precision和recall的值。详细案例可参考P-R曲线过程如何理解一文。
【参数调优】因此P-R权限调优的参数是这个“阈值”threshold,选择使P-R曲线面积最大的阈值。
【模型选择】根据面积与BEP选择最优模型
1)例如模型C的P-R曲线完全被A,B包裹,显然AB模型对于该样本更合适。
2)对于P-R曲线相交的情况,我们比较曲线的平衡点(Break-Event Point, BEP),是准确率与召回率一致时的得分,显然A模型在该样本上优于B模型。
(5)F1
F1是precision和recall的调和平均,同时兼顾了分类模型的精确率和召回率,取值范围在0-1之间,当然还是越大越好。
# P = 0.75; R = 0.5
metrics.f1_score(y_true, y_pred, average='binary') # 0.6
(6)ROC曲线
基本原理与P-R曲线相似,也是通过改变阈值threshold来获得横轴与纵轴值的变更,主要在于横轴与纵轴的变化。
【纵轴】纵轴为真正例率(TPR,True positive rate,真阳率)
可以发现Recall = TPR(当然P-R曲线纵轴是P,横轴是R),这里TPR为纵轴。真正例率,越大越好。
【横轴】横轴为假正例率(FPR,False positive rate,假阳率)
假正例率,当然是值越小越好
【ROC曲线】
怎样理解ROC曲线呢?先来看一下四个顶点(0,0),(0,1),(1,0),(1,1)
(0,0),FPR=0,TPR=0,即TP=0,FP=0,实际为正例的预测为反例,实际为反例的也全预测为反例,即该模型对正例的预测无效。
(0,1),FPR=0,TPR=1,即FP=0,FN=0,那就是实际为正例的全部预测为正例,实际为反例的全部预测为反例,最完美的模式。
(1,0),FPR=1,TPR=0,即TN=0,TP=0,不管正例反例都没判断对
(1,1),FPR=1,TPR=1,即TN=0,FN=0,实际为正例的预测为正例,实际为反例的也全预测为正例,即该模型对反例的预测无效。
使用AUC(Area Under ROC Curve)描述曲面下的面积,AUC值越大表示模型的预测能力越好。
三、多分类场景
对于多分类场景,可以使用哪些性能衡量指标呢?
性能度量指标 | 描述 | 是否适用多分类 |
---|---|---|
accuracy | 预测正确/总量 | yes |
error | 预测错误/总量 | yes |
precision | 转型升级 | yes |
recall | 转型升级 | yes |
F1 | 转型升级 | yes |
ROC | 转型升级 | yes |
多分类问题除了accuracy和error的衡量外,也有precision、recall、F1的度量,不过需要做个转型升级。
(1)macro宏
先计算出每个分类的precision、recall、F1,然后计算平均值,作为该模型对多分类的macro-P、macro-R、macro-F1。
结合代码按步骤进行计算
计算案例的macro-P、macro-R、macro-F1
1、把目标类设置为1,其他为0,模拟每个类的二分类场景
2、计算每个类的P、R、F1
3、macro-P、macro-R、macro-F1为均值
具体代码参见附录macro-P、macro-R、macro-F1,计算结果为macro-P = 0.6389,macro-R= 0.5633、macro-F1=0.5790
from sklearn.metrics import confusion_matrix
from sklearn.metrics import precision_score,recall_score,f1_score
y_true = [0, 2, 1, 3,4,4,3,1,0,0,0,1,1,2,3,2,2,4,2,1]
y_pred = [1, 2, 1, 4,2,4,3,2,0,0,0,1,2,3,2,2,2,2,2,1]
confusion_matrix(y_true,y_pred)
precision_score(y_true,y_pred,labels=[0,1,2,3,4],average='macro') # 0.638888888888889
recall_score(y_true,y_pred,labels=[0,1,2,3,4],average='macro') # 0.5633333333333335
f1_score(y_true,y_pred,labels=[0,1,2,3,4],average='macro') # 0.5790476190476189
和直接调用sklearn结果是一样的。
(2)micro微
先计算出每个分类的TP,TN,FP,FN,然后获得TP,TN,FP,FN的平均值,带入precision、recall、F1的计算公式即可获得micro-P、micro-R、micro-F1
结合代码按步骤进行计算
计算案例的micro-P、micro-R、micro-F1
1、把目标类设置为1,其他为0,模拟每个类的二分类场景
2、计算每个类的TP,TN,FP,FN
3、计算每个类的TP,TN,FP,FN均值
4、带入公式,计算micro-P、micro-R、micro-F1
具体代码参见附录macro-P、macro-R、macro-F1,计算结果为micro-P = 0.6,micro-R= 0.6、micro-F1=0.6,与直接调用sklearn计算结果一致
# micro微
precision_score(y_true,y_pred,labels=[0,1,2,3,4],average='micro')
recall_score(y_true,y_pred,labels=[0,1,2,3,4],average='micro')
f1_score(y_true,y_pred,labels=[0,1,2,3,4],average='micro')
(3)多分类的ROC曲线
多分类问题可以把目标类设置为1,其他为0,模拟每个类的二分类场景。
- macro-roc:对n条ROC曲线取平均,即可得到最终的ROC曲线
- micro-roc:对每一类TP,TN,FP,FN取均值,然后计算TPR,FPR
多分类ROC曲线一文写的挺清楚的
但感觉多分类的roc曲线用起来一点也不方便,不像二分类的ROC一个函数解决,有点麻烦不是很想用
【问题】
- 某些性能度量是不是只有二分类场景可以用?
- 准确率与精度的概念紊乱
accuracy = 分类正确数/总数,叫精度
precision = TP/(TP+FP) ,叫准确率 - 那么多性能衡量指标要怎么选?适用什么场景?一般使用那个数值衡量模型?
例如PR曲线的两个指标(TPR真正例率,FPR假正例率)都聚焦于正例,主要关心正例,对于类别不平衡、比较关系正例判断能力的时候比较适用
四、聚类场景
聚类算法的性能衡量指标是什么?不像有监督学习那样可以根据标签计算accuracy、error等值。
聚类是将样本划分为若干互不相交的子集,簇内相似度高,簇间相似度低。聚类的性能度量指标分为外部指标和内部指标
聚类性能度量 | 指标名称 |
---|---|
外部指标 | jaccard系数(jaccard coefficient,JC) |
FM指数(fowlkes and Mallows Index, FMI) | |
Rand指数(Rand Index, RI) | |
内部指标 | DB指数 |
Dunn指数 |
详细性能度量指标可以参考聚类性能度量和距离计算一文。
参考资料
[1]《机器学习》周志华
[2] P-R曲线过程如何理解:https://www.zhihu.com/question/348073311/answer/837781413
[3] 聚类的性能度量:https://blog.csdn.net/qq_36396104/article/details/78166317
附录
1、precision与recall的计算
import numpy as np
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
y_true = [0, 1, 1, 0,0,1,0,1,0,0,0,0,1,0,0,0,1]
y_pred = [0, 0, 0, 0,0,1,0,1,0,0,0,0,0,0,1,0,1]
accuracy_score(y_true, y_pred)
confusion_matrix(y_true,y_pred)
# 判断为1实际也为1
TP = np.sum(np.logical_and(np.equal(y_true,1),np.equal(y_pred,1)))
# 判断为0实际也为0
TN = np.sum(np.logical_and(np.equal(y_true,0),np.equal(y_pred,0)))
# 判断为1实际为0
FP = np.sum(np.logical_and(np.equal(y_true,0),np.equal(y_pred,1)))
# 判断为0实际为1
FN = np.sum(np.logical_and(np.equal(y_true,1),np.equal(y_pred,0)))
precision = TP/(TP+FP) # 0.75
recall = TP/(TP+FN) # 0.5
2、macro-P、macro-R、macro-F1
# 数据处理:把目标类设置为1,其他为0,模拟该类的二分类
def deal_list(y,labels):
new_list = []
for i in y:
if i==labels:
new_list.append(1)
else:
new_list.append(0)
return new_list
# 计算目标类的P、R、F1
def count_p_r_f1(y_true,y_pred,labels):
new_true = deal_list(y_true,labels)
new_pred = deal_list(y_pred,labels)
a = precision_score(new_true, new_pred, average='binary')
b = recall_score(new_true, new_pred, average='binary')
c = f1_score(new_true, new_pred, average='binary')
return a,b,c
y_true = [0, 2, 1, 3,4,4,3,1,0,0,0,1,1,2,3,2,2,4,2,1]
y_pred = [1, 2, 1, 4,2,4,3,2,0,0,0,1,2,3,2,2,2,2,2,1]
p_score = []
r_score = []
f1 = []
for i in range(5):
a,b,c = count_p_r_f1(y_true,y_pred,i)
p_score.append(a)
r_score.append(b)
f1.append(c)
# 计算macro-P,macro-R,macro-F1
def count_mean(score_list):
s = sum(score_list)/len(score_list)
return s
count_mean(p_score) # 0.638888888888889
count_mean(r_score) # 0.5633333333333335
count_mean(f1) # 0.5790476190476189
3、 计算micro-P,micro-R,micro-F1
import numpy as np
def count_tp_tn_fp_fn(new_true,new_pred):
# 判断为1实际也为1
TP = np.sum(np.logical_and(np.equal(new_true,1),np.equal(new_pred,1)))
# 判断为0实际也为0
TN = np.sum(np.logical_and(np.equal(new_true,0),np.equal(new_pred,0)))
# 判断为1实际为0
FP = np.sum(np.logical_and(np.equal(new_true,0),np.equal(new_pred,1)))
# 判断为0实际为1
FN = np.sum(np.logical_and(np.equal(new_true,1),np.equal(new_pred,0)))
return TP,TN,FP,FN
y_true = [0, 2, 1, 3,4,4,3,1,0,0,0,1,1,2,3,2,2,4,2,1]
y_pred = [1, 2, 1, 4,2,4,3,2,0,0,0,1,2,3,2,2,2,2,2,1]
TP_list,TN_list,FP_list,FN_list = [],[],[],[]
for i in range(5):
new_true = deal_list(y_true,i)
new_pred = deal_list(y_pred,i)
TP,TN,FP,FN = count_tp_tn_fp_fn(new_true,new_pred)
TP_list.append(TP)
TN_list.append(TN)
FP_list.append(FP)
FN_list.append(FN)
TP_mean = count_mean(TP_list)
TN_mean = count_mean(TN_list)
FP_mean = count_mean(FP_list)
FN_mean = count_mean(FN_list)
precision = TP_mean/(TP_mean+FP_mean) # 0.6
recall = TP_mean/(TP_mean+FN_mean) # 0.6
f1 = 2*precision*recall/(precision+recall) # 0.6