tf.metrices.auc源码解读
auc是机器学习二分类问题的常用指标,其反映了分类器对正负样本的排序能力,换句话说,auc反映了模型对正负样本的区分能力。常用的auc计算方式有两种,一种是tensorflow的tf.metrics.auc函数,另一种是sklearn中的roc_auc_score()函数。二者都是通过极限逼近的思想,计算roc曲线下面积的一个个小梯形的面积和得到auc。不过有点奇怪的是,这两个函数计算出来的auc值并不总是相似的,有时会差别比较大,所以想分析下。本文先看下tf.metrics.auc的实现。
首先看下tf.metrics.auc的函数定义:
1 |
|
tf.metrics.auc的参数有三个比较重要。labels和predictions是必须参数,labels就是二分类问题的类别集合,一般就是0/1的集合,predictions就是模型的预测得分集合,是一个0-1的浮点数的集合。num_thresholds参数也很重要,这个参数控制了划分成多少个梯形计算面积。默认是200,可以根据batch大小进行调整。
再看下tf.metrics.auc的具体实现:
1 |
|
tf.metrics.auc函数返回了两个值auc_value和update_op。auc_value就是我们需要的值,但是需要注意的是,只有必须先sess.run(update_op)之后才可以得到auc_value值。怎么理解呢?看下面代码就明白了。原因在后面介绍。
1 | auc_value, update_op = tf.metrics.auc(labels=target, predictions=predictions_score, name="auc_metric", num_thresholds=512) |
看第87-90代码可知,auc_value和update_op是通过一个compute_auc()函数计算得到的,compute_auc()的传入参数貌似是混淆矩阵的四个值。
而在compute_auc函数中,57-61行中计算了真正例率rec和假正例率fp_rate,分别对应roc曲线的横坐标x和纵坐标y。66-72行计算了roc曲线下面积的小梯形之和。也就是说compute_auc()函数传入的是混淆矩阵的四个值,通过计算roc曲线下面积的小梯形之和得到auc返回。
再回过来看下97-90行,auc_value和update_op不同在于values和update_ops变量,update_op貌似是个op,而auc_value是标量值。values, update_ops的计算在25行的_confusion_matrix_at_thresholds()函数,其传入的参数是分别是labels, predictions, thresholds, weights(默认值None)。labels, predictions不多说,而thresholds是在[0-1]中分num_thresholds个段,而首尾加一个微量值避免0和1。可以猜想下,是根据num_thresholds个段作为分类阈值计算混淆矩阵的。
1 | def _confusion_matrix_at_thresholds(labels, |
以values[‘tp’]为例看下values是怎么得到的(72-82行)。可以看到values['tp'] = true_p
,而在73行true_p还没有赋值,仅仅是通过metric_variable()函数创建本地变量(Create variable in GraphKeys.(LOCAL|METRIC_VARIABLES
) collections)。在第75行计算了真正例is_true_positive(真正例简称tp,即真实label是正例且预测也是正例),然后79行将tp和通过函数assign_add()加到了true_p上,然后返回op赋值给update_ops[‘tp’]。最后true_p赋值给了values[‘tp’]。**需要注意的是assign_add()返回的是op,所以如果想得到true_p的值必须先计算op(update_ops[‘tp’])**,这就解释了上文的问题。可以再看下assign_add()定义:
1 |
|
然后看下怎么计算真正例is_true_positive的。math_ops.logical_and(label_is_pos, pred_is_pos)
是对label_is_pos和pred_is_pos进行与操作。现在问题变成了分析label_is_pos和pred_is_pos是怎么来的。看58-59行。array_ops.tile(array_ops.transpose(predictions_2d), [num_thresholds, 1])
得到的是num_thresholds*num_predictions的矩阵A,array_ops.transpose(predictions_2d)是1*n的矩阵,n对应batch大小为n的n个得分。使用tile沿着第0维复制num_thresholds次。thresh_tiled是num_thresholds*num_predictions的阈值矩阵。使用math_ops.greater()函数之后,如果predictions中元素大于阈值则返回True,否则False,最后得到了值为True/False的list,也就是pred_is_pos(预测为正),其它同理。所以在76行通过logical_and得到了is_true_positive真正例。
emm,大概就这样。