分类评价指标有准确率(accuracy)、精确率(precision)、召回率(recall)、AUC面积、F值等。
1. 准确率Accuracy
也称精度, 分类准确的样本数占该类样本总数的比例。
此外,分类错误的样本数占样本总数的比例称为“错误率”(error rate)。
假如,m个样本有 a个样本分类错误。
E = a / m a c c = 1 ? a / m E= a/m \\ acc = 1 - a/m E=a/macc=1?a/m
# 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 Countertotal = Counter(true_labels)keys = np.unique(true_labels)acc = {
}for key in keys:acc[key] = 0for i in range(len(pred_labels)):if pred_labels[i] == true_labels[i]:acc[pred_labels[i]] += 1for 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的含义是,把负类错误预测为正类的个数占总共负类的个数的比例。
F P R = F P T N + F P FPR = \frac{FP}{TN + FP} FPR=TN+FPFP?
TPR
:真正例率/真阳率(ROC的 y 轴);
TPR的含义是,正确预测为正类的个数占总共预测正类的个数的比例。
T P R = T P T P + F N TPR = \frac{TP}{TP+FN} TPR=TP+FNTP?
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的定义, A U C = ∑ F P N ? T P P AUC = \sum \frac{FP}{N}*\frac{TP}{P} AUC=∑NFP??PTP?,离散化,即可得
A U C = ∑ I ( P 正样本 , P 负样本 ) M ? N AUC = \frac{\sum I\left(P_{\text {正样本 }}, P_{\text {负样本 }}\right)}{M^{*} N} AUC=M?N∑I(P正样本 ?,P负样本 ?)?
其中, M , N M,N M,N分布表示正、负样本的个数。这里 I 是指示函数,如果正样本对应的预测概率比负样本预测的概率大,值为1;值相等为0.5, 否则为0。
import numpy as np
from sklearn.metrics import roc_auc_scoredef naive_auc(labels, preds):"""最简单粗暴的方法先排序,然后统计有多少正负样本对满足:正样本预测值>负样本预测值, 计数1;如果相等,计数0.5, 再除以总的正负样本对个数复杂度 O(NlogN), N为样本数auc = I(positive, negative)/M*NM: 正类样本数;N:负类样本数"""n_pos = sum(labels)n_neg = len(labels) - n_postotal_pair = n_pos * n_neglabels_preds = zip(labels, preds)# O((M+N)log(M+N))labels_preds = sorted(labels_preds, key=lambda x: (x[1],x[0]))accumulated_neg = 0satisfied_pair = 0for i in range(len(labels_preds)):if labels_preds[i][0] == 1:# 如果是正类,加上累计的负类样本个数。satisfied_pair += accumulated_negj = 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.5else:breakj -= 1else:accumulated_neg += 1auc = satisfied_pair / float(total_pair)return aucif __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
公式二:
变形:
A U C = ∑ i ∈ positiveclass rank ? i ? M ( 1 + M ) 2 M × N A U C=\frac{\sum_{i \in \text {positiveclass}} \operatorname{rank}_{i}-\frac{M(1+M)}{2}}{M \times N} AUC=M×N∑i∈positiveclass?ranki??2M(1+M)??
同样的,如果是遇到预测值相等的,排序 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) - postotal_pairs = pos*negsatisfied_pair = 0for rank,item in enumerate(sort_label):if item[0] == 1:satisfied_pair += (rank+1)j = rank - 1while j >= 0:if sort_label[j][0] == 0:if item[1] == sort_label[j][1]:satisfied_pair -= 0.5else:breakj -= 1auc = (satisfied_pair - pos*(1+pos)/2)/total_pairsreturn auc
ROC曲线和P-R曲线的区别
P-R曲线是以precision作为纵坐标, recall作为横坐标绘制的曲线图。
当测试集中的负样本数量增加是,P-R曲线会发生明显的变化, ROC曲线形状基本不变:
3. F1 评价指标
F 1 = 2 1 / precision + 1 / recall \mathrm{F}_1=\frac{2}{1 / \text { precision }+1 / \text { recall }} F1?=1/ precision +1/ recall 2?
或者写成:
F 1 = N ? T N N + T P ? T N F_1 = \frac{N- TN}{N + TP - TN} F1?=N+TP?TNN?TN?
其中, N 为总样本个数.
实际中,为了遍历找出使 F 1 F_1 F1?最大的阈值(模型预测的概率,常规是大于0.5预测为pos.)。
import numpy as npdef 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]]) / 2y_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 = 0best_score = 0for 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 = thresholdbest_score = scoresearch_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值分别定义如下。
P macro = 1 n ∑ i = 1 n P i P_{\text {macro}}=\frac{1}{n} \sum_{i=1}^{n} P_{i} Pmacro?=n1?i=1∑n?Pi?
R macro = 1 n ∑ i = 1 n R i R_{\text {macro}}=\frac{1}{n} \sum_{i=1}^{n} R_{i} Rmacro?=n1?i=1∑n?Ri?
F macro = 2 × P macro × R macro P macro + R macro F_{\text {macro}}=\frac{2 \times P_{\text {macro}} \times R_{\text {macro}}}{P_{\text {macro}}+R_{\text {macro}}} Fmacro?=Pmacro?+Rmacro?2×Pmacro?×Rmacro??
微平均micro-averaging:
对每一个类别进行混淆矩阵统计。
微平均(Micro-averaging)是对数据集中的每一个示例不分类别进行统计建立全局混淆矩阵,然后计算相应的指标。其计算公式如下:
P micro = T P ? T P ? + F P ? = ∑ i = 1 n T P i ∑ i = 1 n T P i + ∑ i = 1 n F P i R micro = T P ? T P + F N ? = ∑ i = 1 n T P i ∑ i = 1 n T P i + ∑ i = 1 n F N i F micro = 2 × P micro × R micro P micro + R micro \begin{aligned} P_{\text {micro}} &=\frac{T \overline{P}}{T \overline{P}+\overline{F P}}=\frac{\sum_{i=1}^{n} T P_{i}}{\sum_{i=1}^{n} T P_{i}+\sum_{i=1}^{n} F P_{i}} \\ R_{\text {micro}} &=\frac{\overline{T P}}{T P+F \overline{N}}=\frac{\sum_{i=1}^{n} T P_{i}}{\sum_{i=1}^{n} T P_{i}+\sum_{i=1}^{n} F N_{i}} \\ F_{\text {micro}} &=\frac{2 \times P_{\text {micro }} \times R_{\text {micro }}}{P_{\text {micro }}+R_{\text {micro }}} \end{aligned} Pmicro?Rmicro?Fmicro??=TP+FPTP?=∑i=1n?TPi?+∑i=1n?FPi?∑i=1n?TPi??=TP+FNTP?=∑i=1n?TPi?+∑i=1n?FNi?∑i=1n?TPi??=Pmicro ?+Rmicro ?2×Pmicro ?×Rmicro ???
区别:
宏平均把所有的类别平等对待,导致宏平均偏向于反映数量小的类别分类情况;
微平均把所有样本决策平等对待,导致微平均偏向于反映数量多的类别的评判。
如果类别均衡的情况下,使用两个评价指标都可以。
5. 混淆矩阵
混淆矩阵是把一个包含评估指标precision 、recall 、f1-score 值的矩阵。
import numpy as np
from sklearn.metrics import classification_report, \
confusion_matrixref = 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 support1 0.5000 0.5000 0.5000 22 0.5000 0.5000 0.5000 23 0.5000 0.5000 0.5000 2avg / total 0.5000 0.5000 0.5000 6Accuracy 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:
- blog 非常好解释AUC;
- csdn blog 分类指标;
- csdn blog 分类器的性能评价指标;
- zhihu【机器学习理论】分类问题中常用的性能评估指标;
- csdn blog多类别分类问题由 confusion matrix 到分类准确率(accuracy)的计算;
- csdn blog【scikit-learn】评估分类器性能的度量,像混淆矩阵、ROC、AUC等;
- Mean absolute percentage error;
- MAPE实现;
- AUC详解与python实现;
- 百面机器学习;