分类评价指标有准确率(accuracy)、精确率(precision)、召回率(recall)、AUC面积、F值等。
1. 准确率Accuracy
也称精度, 分类准确的样本数占该类样本总数的比例。
此外,分类错误的样本数占样本总数的比例称为“错误率”(error rate)。
假如,m个样本有 a个样本分类错误。
# calculate accuracy
from sklearn import metrics
print( metrics.accuracy_score(y_test, y_pred) )
复制代码
空准确率 null accuracy
使用样本比例最多的类别进行预测时的准确率。
y_count = np.bincount(y_test)
ii = np.nonzero(y_count)[0]
print(y_count)
zip(ii, y_count[ii])
print('null acc', y_count[0]/ len(y_test) )
复制代码
每类的accuracy
def cal_acc(true_labels, pred_labels):
'''
true_labels: like[2,1,4],
pred_labels: like[1,2,3]
output: the acc of each class.
'''
from collections import Counter
total = Counter(true_labels)
keys = np.unique(true_labels)
acc = {}
for key in keys:
acc[key] = 0
for i in range(len(pred_labels)):
if pred_labels[i] == true_labels[i]:
acc[pred_labels[i]] += 1
for key in keys:
acc[key] = acc[key] / total[key]
return acc
复制代码
2. AUC,Precision,Recall,ROC曲线
AUC是衡量二分类模型优劣的一种评价指标,表示正例的预测值比负例预测值大的概率。
精确率 P
precision = TP/(TP +FP)
,即正类预测对的个数和所有预测成正类样本数比值。
召回率 R
recall = TP/(TP +FN)
,即预测正类样本和原来一共正类样本数的比值。
准确率 Acc
Accuracy = (TP + TN)/(P + N)
ROC曲线 与 AUC面积
FPR
:假正例率/假阳率(ROC的 x 轴);
FPR的含义是,把负类错误预测为正类的个数占总共负类的个数的比例。
TPR
:真正例率/真阳率(ROC的 y 轴);
TPR的含义是,正确预测为正类的个数占总共预测正类的个数的比例。
ROC曲线, Receiver Operating Characteristic Curve, 受试者工作特征曲线, 是以假阳率(FPR)为横坐标,以真阳率(TPR)为纵坐标绘制的曲线。
ROC曲线与X坐标轴所围成的面积叫做AUC面积,这个面积也可以作为分类器的性能评价指标。面积越大,分类器性能越好。 值得注意的是,由于需要获得一系列的TPR,FPR,我们需要给出预测为正样本的概率。
假如我们已经得到了所有样本的概率输出(属于正样本的概率),现在的问题是如何改变“discrimination threashold”?我们根据每个测试样本属于正样本的概率值从大到小排序。
下图是一个示例,图中共有20个测试样本,“Class”一栏表示每个测试样本真正的标签(p表示正样本,n表示负样本),“Score”表示每个测试样本属于正样本的概率4。
按照概率排序:
接下来,我们从高到低,依次将“Score”值作为阈值threshold
,当测试样本属于正样本的概率大于或等于这个threshold
时,我们认为它为正样本,否则为负样本。
举例来说,对于图中的第4个样本,其“Score”值为0.6,那么样本1,2,3,4都被认为是正样本,因为它们的“Score”值都大于等于0.6,而其他样本则都认为是负样本。
每次选取一个不同的threshold
,就可以得到一组FPR
和TPR
,即ROC曲线上的一点。这样一来,一共得到了20组FPR
和TPR
的值,将它们画在ROC曲线的结果如下图:
当我们将threshold
设置为1
和0
时,分别可以得到ROC曲线上的(0,0)
和(1,1)
两个点。将这些(FPR,TPR)对连接起来,就得到了ROC曲线。当threshold
取值越多,ROC曲线越平滑。
这里,阈值为1, 预测为正类的样本为0;
阈值为0, 预测为正类的样本为所有样本。正类样本预测对的个数= 正类样本个数,即TPR=1;负类样本预测成正类样本的个数 = 负类样本个数,FPR = 1.
ROC曲线的几个性质:
- ROC 曲线越靠近左上角,准确度越高;且最高仅左上角的ROC曲线的点对应的阈值是,错误最小的阈值(即FP,FN总数最小);
- ROC曲线不会随类别分布改变而改变,可以用于评价样本不平衡模型的效果.
计算 AUC 代码:
公式一: 根据AUC的定义,,离散化,即可得
其中,分布表示正、负样本的个数。这里 I 是指示函数,如果正样本对应的预测概率比负样本预测的概率大,值为1;值相等为0.5, 否则为0。
import numpy as np
from sklearn.metrics import roc_auc_score
def naive_auc(labels, preds):
"""
最简单粗暴的方法
先排序,然后统计有多少正负样本对满足:正样本预测值>负样本预测值, 计数1;如果相等,计数0.5, 再除以总的正负样本对个数
复杂度 O(NlogN), N为样本数
auc = I(positive, negative)/M*N
M: 正类样本数;N:负类样本数
"""
n_pos = sum(labels)
n_neg = len(labels) - n_pos
total_pair = n_pos * n_neg
labels_preds = zip(labels, preds)
# O((M+N)log(M+N))
labels_preds = sorted(labels_preds, key=lambda x: (x[1],x[0]))
accumulated_neg = 0
satisfied_pair = 0
for i in range(len(labels_preds)):
if labels_preds[i][0] == 1:
# 如果是正类,加上累计的负类样本个数。
satisfied_pair += accumulated_neg
j = i-1
# 由于会出现正类和负类预测概率相等情况,这个时候需要-0.5.
while j>=0:
if labels_preds[j][0] == 0:
if labels_preds[i][1] == labels_preds[j][1]:
satisfied_pair -= 0.5
else:
break
j -= 1
else:
accumulated_neg += 1
auc = satisfied_pair / float(total_pair)
return auc
if __name__ == "__main__":
y_true = np.array([1,1,0,0,1,1,0])
y_scores = np.array([0.8,0.7,0.5,0.5,0.5,0.5,0.3])
auc1 = roc_auc_score(y_true, y_scores)
auc2 = naive_auc(y_true,y_scores)
print("auc1",auc1)
print("auc2",auc2)
复制代码
运行结果:
auc1 0.8333333333333334
auc2 0.8333333333333334
复制代码
公式二:
变形:
同样的,如果是遇到预测值相等的,排序 rank - 0.5. rank的取值是1到M+N.
def cal_auc2(labels,preds):
sort_label = sorted(zip(labels,preds),key=lambda x: (x[1],x[0]))
pos = sum(labels)
neg = len(labels) - pos
total_pairs = pos*neg
satisfied_pair = 0
for rank,item in enumerate(sort_label):
if item[0] == 1:
satisfied_pair += (rank+1)
j = rank - 1
while j >= 0:
if sort_label[j][0] == 0:
if item[1] == sort_label[j][1]:
satisfied_pair -= 0.5
else:
break
j -= 1
auc = (satisfied_pair - pos*(1+pos)/2)/total_pairs
return auc
复制代码
ROC曲线和P-R曲线的区别
P-R曲线是以precision作为纵坐标, recall作为横坐标绘制的曲线图。 当测试集中的负样本数量增加是,P-R曲线会发生明显的变化, ROC曲线形状基本不变:
3. F1 评价指标
或者写成:
其中, N 为总样本个数.
实际中,为了遍历找出使 最大的阈值(模型预测的概率,常规是大于0.5预测为pos.)。
import numpy as np
def f1_smart(y_true, y_pred):
'''
f1 = 2*P*R/(P + R);
P = TP/(TP + FP)
R = TP/(TP + FN)'''
args = np.argsort(y_pred)
tp = y_true.sum()
fs = (tp - np.cumsum(y_true[args[:-1]])) / np.arange(y_true.shape[0] + tp - 1, tp, -1)
res_idx = np.argmax(fs)
return 2 * fs[res_idx], (y_pred[args[res_idx]] + y_pred[args[res_idx + 1]]) / 2
y_true = np.array([1, 1, 0, 0,0])
y_pred = np.array([0.2, 0.3, 0.5, 0.1, 0.1])
f1, threshold = f1_smart(y_true, y_pred)
复制代码
类似的找最佳阈值的方法有,
def threshold_search(y_true, y_proba):
best_threshold = 0
best_score = 0
for threshold in tqdm([i * 0.01 for i in range(100)], disable=True):
score = f1_score(y_true=y_true, y_pred=y_proba > threshold)
if score > best_score:
best_threshold = threshold
best_score = score
search_result = {'threshold': best_threshold, 'f1': best_score}
return search_result
复制代码
4. Averaging
首先,F1 , ROC, AUC这里都是二分类评估指标,但是,也可以应用于多分类。
比如,在三分类中,可以对每个类别进行计算它的精确度 P
,召回率R
,然后平均(macro-averaging)。
或者,我们把这三个类别各自的二分类TN、TP、FN、FP计算出来,最后才求 P
和R
(micro-averaging)。
宏平均Macro-averaging:
宏平均是所有类别每一个统计指标的算术平均值。 宏精度、宏召回率R_macro 、宏F值分别定义如下。
微平均micro-averaging:
对每一个类别进行混淆矩阵统计。
微平均(Micro-averaging)是对数据集中的每一个示例不分类别进行统计建立全局混淆矩阵,然后计算相应的指标。其计算公式如下:
区别:
宏平均把所有的类别平等对待,导致宏平均偏向于反映数量小的类别分类情况; 微平均把所有样本决策平等对待,导致微平均偏向于反映数量多的类别的评判。 如果类别均衡的情况下,使用两个评价指标都可以。
5. 混淆矩阵
混淆矩阵是把一个包含评估指标precision 、recall 、f1-score 值的矩阵。
import numpy as np
from sklearn.metrics import classification_report, \
confusion_matrix
ref = np.array([1,1,2,2,3,3])
pred = np.array([1,2,2,3,3,1])
report = classification_report(ref, pred, digits=4)
print("report", report)
conf_mat = confusion_matrix(ref, pred)
print("Accuracy", np.sum(np.diag(conf_mat))/len(ref))
复制代码
这里,利用classification_report
可以把精确度P
, 召回率 R
, F1
计算出来。
而 准确率 Accuracy
,需要计算混淆矩阵的对角元素个数和(TP
、FN
)与元素总个数之比。
report precision recall f1-score support
1 0.5000 0.5000 0.5000 2
2 0.5000 0.5000 0.5000 2
3 0.5000 0.5000 0.5000 2
avg / total 0.5000 0.5000 0.5000 6
Accuracy 0.5
复制代码
6. MAPE
在实际中,RMSE损失虽然能够刻画回归预测值和真实值的偏离度,但是,如果存在离群点,rmse指标会很差。 MAPE是比RMSE更加鲁棒的指标。
mape, mean absolute percent error, 平均绝对百分比误差。
where At is the actual value and Ft is the forecast value.
相比与RMSE, MAPE相当于把每个点的误差进行了归一化,降低了个别离群点带来的绝对误差的影响。
from sklearn.utils import check_arrays
def mean_absolute_percentage_error(y_true, y_pred):
y_true, y_pred = check_arrays(y_true, y_pred)
## Note: does not handle mix 1d representation
#if _is_1d(y_true):
# y_true, y_pred = _check_1d_array(y_true, y_pred)
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
复制代码
7. SMAPE
SMAPE是对MAPE进行了修正, 对称的平均绝对百分比误差。
如果真实数值本身很小的话,预测偏差一点百分比就会差很多。
where At is the actual value and Ft is the forecast value.
实现:
def smape(A, F):
return 100/len(A) * sum(2 * abs(F - A) / (abs(A) + abs(F)))
复制代码
reference: