在平时计算auc的时候,大都是使用 sklearn.metrics.roc_auc_score 来计算。一般操作是将每个batch预测出来的结果 拼接起来,然后扔到该函数中计算。
但是如果测试集量级过大(比如 10亿量级),每个样本的预测结果拼接起来之后至少需要 3G内存。这个开销显然不是我们想要的。有什么方法可以不用拼接batch预测出来的结果就可以计算auc呢?
回想auc的概率含义:从正例采出一个样本,从负例采出一个样本,这两个样本的模型预测值,正例排在负例前面的概率。对应下面这个公式
。
一个比较简单的方法,我们对连续的score进行分桶,然后统计 每个桶里的 正负例 数量,经过计算就可以得到auc。这个方法的好处是,因为只需要统计 每个桶里的正负例数量,所以就不用保存每个样本的预测值。很适合 样本量级很大的 auc计算。python代码如下(翻译自paddlepaddle计算auc的代码~)。
import numpy as np
import sklearn.metrics as sklearn_metrics
class Auc(object):
def __init__(self, num_buckets):
self._num_buckets = num_buckets
self._table = np.zeros(shape=[2, self._num_buckets])
def Reset(self):
self._table = np.zeros(shape=[2, self._num_buckets])
def Update(self, labels: np.ndarray, predicts: np.ndarray):
"""
:param labels: 1-D ndarray
:param predicts: 1-D ndarray
:return: None
"""
labels = labels.astype(np.int)
predicts = self._num_buckets * predicts
buckets = np.round(predicts).astype(np.int)
buckets = np.where(buckets < self._num_buckets,
buckets, self._num_buckets-1)
for i in range(len(labels)):
self._table[labels[i], buckets[i]] += 1
def Compute(self):
tn = 0
tp = 0
area = 0
for i in range(self._num_buckets):
new_tn = tn + self._table[0, i]
new_tp = tp + self._table[1, i]
# self._table[1, i] * tn + self._table[1, i]*self._table[0, i] / 2
area += (new_tp - tp) * (tn + new_tn) / 2
tn = new_tn
tp = new_tp
if tp < 1e-3 or tn < 1e-3:
return -0.5 # 样本全正例,或全负例
return area / (tn * tp)
if __name__ == '__main__':
label = np.random.randint(low=0, high=2, size=[1000])
predict = np.random.uniform(0, 1, size=[1000])
auc = Auc(num_buckets=102400)
auc.Update(label, predict)
print(auc.Compute())
print(sklearn_metrics.roc_auc_score(label, predict))
参考资料
https://github.com/PaddlePaddle/Paddle/blob/v1.8.5/paddle/fluid/framework/fleet/box_wrapper.h#L51
https://github.com/PaddlePaddle/Paddle/blob/v1.8.5/paddle/fluid/framework/fleet/box_wrapper.cc#L32