Receiver Operating Characteristic Area Under the Curve (ROC and AUC).
如何向别人解释 ROC AUC 对评价机器学习算法的意义:
一个样本集,一半正样本,一半为负样本。如果一个机器学习算法将全部样本均预测为正(或者负),它的准确率即可达到 50%,所以 ROC AUC 实现了对样本类别比例的稳定性;
Precision(精确率):
TPR (True Positive Rate):
FPR (False Positive Rate):
def TPR(y_true, y_pred):
return ((y_true==1)*(y_pred==1)).sum()/(y_true==1).sum()
def FPR(y_true, y_pred):
return ((y_true==0)*(y_pred==1)).sum()/(y_true==0).sum()
对于一个特定的分类器与测试数据集,显然只能得到一个分类结果,即一组TPR与FPR值,而要画出一个曲线,我们便需要一系列的FPR与TPR值。我们来看 Wikipedia对其的定义,
In signal detection theory, a receiver operating characteristic (ROC), or simply ROC curve, is a graphical plot which illustrates the performance of a binary classifier system as its discrimination threshold is varied.
如何理解这里的 discrimination threshold呢?其实我们忽略了分类器的一个重要功能是“概率输出”,即表示分类器某个样本以多大的概率属于正样本或负样本。对于0,1二分类问题,一些分类器得到的其实不是简单的0和1,如神经网络,得到诸如0.5, 0.8这样的分类结果。这时,如果我们人为指定一个阈值(threshold),比如0.4,那么小于0.4的为0类,大于等于0.4的则为1类,则可得一个分类结果(该分类结果再跟真实的结果相结合,可进而计算TPR与FPR)。
阈值不同,可以得到不同的结果,但是由于分类器所决定的统计图始终是不变的。这时候就需要一个独立于阈值,只与分类器有关的评价指标,来衡量特定分类器的好坏。
假如我们已经得到了所有样本的概率输出(属于正样本的概率),现在的问题是如何改变“discrimination threshold”呢?我们根据每个训练样本属于正样本的概率值从大到小进行排序。下图为某一示例,共20个训练样本,”Class”一列表示每个样本真正的标签(p为正样本,n表示负样本),”Score”表示每个测试样本属于正样本的概率。
接下来,我们从高到低,依次将”Score”值作为阈值(threshold)。每次选取一个不同的 threshold,我们就可以得到一组FPR和TPR,这样一来我们共得到20组FPR与TPR的值,即得ROC curve:
def main():
y_true = np.array([1, 1, 0, 1, 1, 1, 0, 0, 1, 0,
1, 0, 1, 0, 0, 0, 1, 0, 1, 0])
y_hat = np.array([.9, .8, .7, .6, .55, .54, .53, .52, .51, .505,
.4, .39, .38, .37, .36, .35, .34, .33, .30, .1])
threshs = sorted(y_hat, reverse=True)
tprs, fprs = [], []
for thresh in threshs:
y_pred = np.where(y_hat < thresh, 0, 1)
# np.where, numpy版的三目运算符
tprs.append(TPR(y_true, y_pred))
fprs.append(FPR(y_true, y_pred))
plt.plot(fprs, tprs, 'k--', lw=2)
plt.scatter(fprs, tprs, c='k', marker='x', s=50)
plt.plot(np.arange(-.05, 1.05, .01), np.arange(-.05, 1.05, .01), '--', color='lightgray')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.xlim([0-.05, 1+.05])
plt.ylim([0-.05, 1+.05])
plt.show()
if __name__ == '__main__':
main()
当threshold取值越多,ROC曲线越光滑。
AUC(Area Under Curve)被定义为ROC曲线下的面积,显然这个面积的数值不会大于1,整个ROC空间为坐标轴上的 [0,1]×[0,1] 。又由于ROC曲线一般都位于 y=x 这条直线的上方(如上图的虚线所示),所以AUC的取值范围在0.5和1之间。使用AUC作为评价指标是因为很多时候,ROC曲线并不能清晰地说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。
这里不妨给出一份AUC的计算代码:
def AUC(y_true, scores):
M = (y_true==1).sum() # M:正样本个数
N = (y_true==0).sum() # N:负样本个数
I = np.argsort(scores)
rank = 0
for i in range(1, 1+len(I)):
if y_true[I[-i]] == 1:
rank += len(I)-i+1
return (rank-M*(M+1))/(M*N)
既然已经存在那么多评价标准,为什么还要使用ROC和AUC呢?因为ROC曲线有个很好的特性:当训练集中的正负样本的分布(np.bincount(y_train))变化的时候,ROC曲线能够保持不变,也即ROC不受训练集类别分布的影响。因为在实际的训练集中经常会出现类不平衡(class imbalance)现象,即正样本占90%,或者相反(此时如果一个将所有样本当预测为正样本的分类器,也能得到90%的准确率,显然这样毫无意义)。
ROC曲线与AUC
ROC和AUC介绍以及如何计算AUC