评估方法
线性回归算法评估方式
-
均方误差MSE(mean squared error): m s e = ∑ i = 1 m ( y ^ i − y i ) 2 m mse = \frac{\sum_{i=1}^m(\hat y_{i} - y_{i})^2}{m} mse=m∑i=1m(y^i−yi)2
(mse的标量是真实标量的平方,线性回归损失函数也用此公式) -
均方根误差RMSE(root mean squared error): r m s e = ∑ i = 1 m ( y ^ i − y i ) m rmse = \sqrt \frac{\sum_{i=1}^m(\hat y_{i} - y_{i})}{m} rmse=m∑i=1m(y^i−yi)
(预测值与真实值误差为 ± r m s e \pm rmse ±rmse) -
均方绝对误差MAE(mean absolute error): m a e = ∑ i = 1 m ∣ y ^ i − y i ∣ m mae = \frac{\sum_{i=1}^m|\hat y_{i} - y_{i}|}{m} mae=m∑i=1m∣y^i−yi∣
(预测值与真实值误差为 ± m a e \pm mae ±mae) -
R Squared:
- 公式中:分式---->分子即为模型预测产生误差用MSE计算,分母为模型的基准误差(方差也就是最大误差)。预测产生的误差与最大误差的比。
- 因为1减去分式,所以 R 2 < = 1 R^2<=1 R2<=1, R 2 R^2 R2越大越好,为1时表示分式为0,我们模型不犯任何错误。为0时表示预测误差等于基准误差。<0时表示还不如基准模型,很可能我们的数据不存在任何线性关系。
- scikit-learn线性回归方法中score计算准确率,封装评估方式就是 R 2 R^2 R2 。
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import r2_score
mean_squared_error(y_test, y_predict)
mean_absolute_error(y_test, y_predict)
r2_score(y_test,y_predict)
分类算法评估方式
-
准确率(accuracy)
- 公式: a c c u r a c y = 分 类 正 确 样 本 数 / 样 本 总 数 accuracy = 分类正确样本数 / 样本总数 accuracy=分类正确样本数/样本总数
- 是最基本的评估方式,但是对于分布极度偏斜的数据,只是用准确度评估是不够的,就会出现问题。
eg:有100人,其中90个健康,10个患病。假设100人经过模型预测,全部认定为健康。模型一个患病的都没有预测成功,但是模型准确率却有90%,因此将需要有更多的评估方式来评估模型的性能好坏。
-
混淆矩阵
- 最左边一列代表真实值,最上面一行代表预测值。
- 矩阵中按顺序:TN---->预测为0类且真值也为0类的样本。FP---->预测为1类但真值也为0类的样本。
FN---->预测为0类但真值为1类的样本。TP---->预测为1类且真值也为1类的样本。 - 样本总数 = TN+FN+FP+TP
- 通常将1类别作为我们真正关注的对象,eg:医疗中判断是否患病1类作为患病类,金融中判断是否有风险1类作为风险类。
查准率(precision)
- 模型判定为1类的样本中,正确的数量。
- p r e c i s i o n = T P F P + T P precision = \frac{TP}{FP+TP} precision=FP+TPTP
查全率(recall)
- 真实为1类的样本中,模型判定对的数量。
- r e c a l l = T P F N + T P recall = \frac{TP}{FN+TP} recall=FN+TPTP
F1 Score
-
有时候我们注重查准率,例如预测股票是升还是跌,查全率就显得不那么总要,我们不需要查出所有升的股票,我们只要保证我们预测出的升的股票准确率是高的。
-
有时候我们注重查全率,例如医疗领域,最好的就是把所有有病的患者都查出来,相对对于准确率的要求就没有那么高,就算没病判断成有病可再进一步检查,确诊。
-
当然有些情况下,不是完全偏向于某一方,是希望查全率和查准率均衡,兼顾二者。新的衡量标准 F1 Score:
使用了查全率和查准率的调和平均值:1 F 1 = 1 2 ( 1 p r e c i s i o n + 1 r e c a l l l ) \frac{1}{F1} = \frac{1}{2}(\frac{1}{precision}+\frac{1}{recalll}) F11=21(precision1+recalll1)
F 1 = 2 ⋅ p r e c i s i o n ⋅ r e c a l l l p r e c i s i o n + r e c a l l F1 = \frac{2\cdot precision\cdot recalll}{precision+recall} F1=precision+recall2⋅precision⋅recalll
F1的范围[ 0 , 1 ]。只有在二者同时很高的情况下,F1的值才会很高,任何一方值是低的都会拉低F1评估值。
scikit-learn中的API
#混淆矩阵
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, y_predict)
#查准率(percision)
from sklearn.metrics import precision_score
precision_score(y_test, y_predict,average=‘binary’)
#查全率(recall)
from sklearn.metrics import recall_score
recall_score(y_test, y_predict, average=‘binary’)
#F1 Score
from sklearn.metrics import f1_score
f1_score(y_test, y_predict)
'''
其中precision_score和recall_score有参数average,默认binary适用于二分类。
当为多分类任务需调整,主要有'macro'和'weigthed'。
以precision_score为例:'macro':不考虑各类别的样本数量,结果为:各类别p的均值。
'weigthed':考虑各类别的样本数量,结果为:各类别p乘各类别样本数量,求和,再除总样本数 。
若测试的各类别样本数量明显不均衡应采用weigthed。
'''
画混淆矩阵
from sklearn.metrics import confusion_matrix
import seaborn as sn
#多分类(3类)
cfm = confusion_matrix(y_test, y_predict)
>>> array(预测 0 1 2
真实 0 [[147, 6, 0],
1 [ 7, 123, 0],
2 [ 1, 0, 134]])
#每行样本数
row_sums = np.sum(cfm, axis=1)
#新矩阵:每格占样本数的比
err_matrix = cfm / row_sums
#将新矩阵对角线都填成0,因为我们不关心正确的个数,剩下的每个格子都是预测错误的占比。
np.fill_diagonal(err_matrix, 0)
ax1 = sn.heatmap(err_matrix, annot= True ,linewidths = 0.05, xticklabels=['put on',"take off","scratch"],
yticklabels=['put on',"take off","scratch"])
ax1.set_title("Confusion Matrix")
ax1.set_ylabel("True label")
ax1.set_xlabel("Predict label")
- 查全率和查准率是互斥的,通常一个升高另一个便会下降,有时候我们需要查全率高,有时候我们又需要查准率高。
- 接下来要讨论的是一个很厉害的东西,通过决策边界(Threshold)来调整一个训练好的模型的查全率和查准率。
- 那什么是决策边界(Threshold)呢?以LogisticRegression为例:
LogisticRegression是对线性回归模型计算的结果( y = X b ⋅ θ y = X_{b}\cdot \theta y=Xb⋅θ),再用一个函数(sigmoid)进行转化得到[0,1]间的概率,定义以概率0.5为分界线,大于0.5归为1类,小于0.5的归为0类。根据sigmoid函数的性质,概率为0.5时, X b ⋅ θ X_{b}\cdot \theta Xb⋅θ值是为0,所以也就变成 X b ⋅ θ > 0 X_{b}\cdot \theta>0 Xb⋅θ>0 时归为1类, X b ⋅ θ < 0 X_{b}\cdot \theta<0 Xb⋅θ<0 时归为0类。不清楚LogisticRegression算法的可以看我另一篇博客:[LogisticRegression详解]。
因此, X b ⋅ θ = 0 X_{b}\cdot \theta = 0 Xb⋅θ=0 就称为决策边界,0即可替换为任意值的Threshold,当 X b ⋅ θ > T h r e s h o l d X_{b}\cdot \theta > Threshold Xb⋅θ>Threshold 时分为1类,当 X b ⋅ θ < T h r e s h o l d X_{b}\cdot \theta < Threshold Xb⋅θ<Threshold 时分为0类。 - 举个例子:
一共有12个样本,五角星代表我们关注的对象为1类。
当Threshold = 0 ,右边为大于0的部分分为1类, p r e c i s i o n = 0.8 , r e c a l l = 0.67 precision = 0.8, recall = 0.67 precision=0.8,recall=0.67
当Threshold = 2 ,右边为大于2的部分分为1类, p r e c i s i o n = 1.0 , r e c a l l = 0.33 precision = 1.0, recall = 0.33 precision=1.0,recall=0.33
当Threshold = -3 ,右边为大于-3的部分分为1类, p r e c i s i o n = 0.75 , r e c a l l = 1.0 precision = 0.75, recall = 1.0 precision=0.75,recall=1.0
- 那应该如何修改Threshold调整预测结果呢?请看下面。
首先要明确一点,是对已经训练好的模型,在predict时调整决策边界(threshold),改变预测分类结果。
在scikit-learn中LogsticRegression默认的predict就是以 X b ⋅ θ = 0 X_{b}\cdot \theta = 0 Xb⋅θ=0 为分界线,即sigmoid( X b ⋅ θ ) X_{b}\cdot \theta) Xb⋅θ)大于0.5 为1,小于0.5为0。
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)
#默认predict,threshold为0的预测结果
y_predict = log_reg.predict(X_test)
precision_score(y_test, y_predict)
>>> 0.9473549684225426
recall_score(y_test, y_predict)
>>> 0.8000000000000004
'''调整threshold的方式'''
#1.通过API:decision_function,获得未经过sigmoid函数的预测值,即 Xb⋅ θ 的值。
decision_scores = log_reg.decision_function(X_test)
>>> [-22.0315,-33.1241,-0.9412,4.1312,15.2345,......]
#2.不使用sigmoid,通过直接给Threshold值来生成预测结果。decision_scores中大于-5的分为1类,小于-5的分为0类。
y_predict_2 = np.array(decision_scores >= -5, dtype='int')
#3.看查全率和查准率的变化
precision_score(y_test, y_predict_2)
>>> 0.72727272727272729
recall_score(y_test, y_predict_2)
>>> 0.88888888888888884
- 那该如何决定最佳的threshold呢?PR曲线诞生。
'''scikit-learn 中的precision_recall_curve
输入参数:测试集真值(y_test)和未经过sigmoid函数的预测值(由API:decision_function获取)
返回值:查准率数组,记录了每一个threshold对应的precision,数组最后一个值是1,且没有对应的threshold。
查全率数组,记录了每一个threshold对应的recall,数组最后一个值是0,且没有对应的threshold。
threshold数组,记录了每一个threshold,这些threshold是skl自动规划间距得出有意义的threshold。
(自己手动选取阀值,通常取决策分数的最小和最大值,间隔0.1取阀值threshold。)
threshold数组是比查准率数组和查全率数组少一个元素的。
'''
from sklearn.metrics import precision_recall_curve
precisions,recalls,thresholds = precision_recall_curve(y_test, decision_scores)
# 横坐标为threshold,纵坐标为对应的precison(蓝色)和recall(橘色)
# 因为precision数组和recall数组最后一个元素是没有对应的threshold,因此去掉。
plt.plot(thresholds, precisions[:-1])
plt.plot(thresholds, recalls[:-1])
plt.show()
交汇处即为查准率和查全率最平衡的threshold。
# PR曲线
plt.plot(precisions, recalls)
plt.show()
拐点处即为查准率和查全率最平衡的threshold。
7.PR曲线除了可以用来权衡查全率和查准率,还可用于评价不同模型之间的性能。
通常一条PR曲线能够包住另一条曲线,表示此曲线效能优于另一条曲线。
ROC曲线及其面积AUC
-
Receiver Operation Characteristic Curve 描述TPR和FPR之间的关系。
-
TPR = 查全率,预测1且对了的占真实1的多少。
-
FPR,预测1且错了的占真实0的多少。
-
TPR和FPR关系:
TPR升高时,FPR也同时升高。 -
scikit-learn中的ROC
'同precision_recall_curve使用方式一致'
from sklearn.metrics import roc_curve
fprs, tprs, thresholds = roc_curve(y_test, decision_scores)
plt.plot(fprs, tprs)
plt.show()
'''计算ROC曲线下方的面积AUC'''
from sklearn.metrics import roc_auc_score
roc_auc_score(y_test, decision_scores)
- ROC通常用来比较两个模型孰优孰劣,ROC 曲线下,面积更大的模型,效能更好。