发新帖

tensorboard中auc_precision_recall曲线y轴值的理解

[复制链接]
1558 9

快来加入 TensorFlowers 大家庭!

您需要 登录 才可以下载或查看,没有帐号?加入社区

x
为了更清楚的表达我的问题,我将我在stackoverflow上提交的问题在这里再次用中文发一下吧。

版本:
TensorFlow:1.6.0
TensorBoard:1.6.0

我所做的事情:
1. 用 tf.estimator.DNNClassifier 训练一个二分类模型,且所用的训练数据是非常的不对称的(准确的说是预测某个用户点击广告的可能性。显然绝大多数的数据都是:不点击(noclick),而只有很少数的数据是:点击(click))。
2. 因此这里我们评价模型的好坏标准不能用传统的AUC值,而应该用PR-AUC值。
3. 在模型训练到一定的时候,其准确率就不会再提高了,因此这时就需要试着从训练数据中尽可能找出存在的噪音特征(noise feature),将其剔除。
4. 而找噪音特征的过程就是用的迭代方式。假设我们用于训练的特征有30个(feat_1, feat_2, feat_3, ... feat_30),当用所有30个特征数据训练时,我们的auc=0.7。现在我们的模型准确率停留在了0.7不会再提高了。那么我们现在就用迭代的方式找找看有没有噪音:
  1. a. 去掉特征feat_1,然后用剩下的29个特征(feat_2, feat_3, ..., feat_30)重新进行训练。
  2. - 假设最后得到的auc=0.65,那么我们可以得出的结论是:feat_1是包含有信息量的(informative)。我们应该保留feat_1。
  3. - 而如果最后得到的auc=0.75,那么我们可以得出的结论是:feat_1是包含有噪音(uninformative)。feat_1正是我们想找的噪音。
  4. b. 重复过程a:去掉特征feat_2,然后用剩下的29个特征(feat_1, feat_3, feat_4, ..., feat_30)重新进行训练。
复制代码
在寻找噪音特征的过程中,我从tensorboard得到30个auc_precision_recall曲线图,其中有3个图片如下:(我们依次称三个图片为figure A, figure B, figure C)

a

a

b

b

c

c

关于图片的描述:
  • x轴:训练步数
  • y轴:0〜1的区间值(这个值代表什么意思正是我的疑问所在。目前感觉可能是代表PR-AUC,但由于figure Afigure B的起始值很大,所以我表示怀疑)

我的问题是:
  • 上图中的y轴到底代表什么?
  • 如果这个y轴值代表PR-AUC值,那为什么在训练的第一步就会有如此大的值(如图figure A:的第一个点(x, y)=(1, 0.5009))?
  • 除了上述两个问题外。关于figure A的疑问:图里为什么会有一个向下的峰值(y值接近0)?


yunhai_luo已获得悬赏 10 金币+20 金币

最佳答案

更新: 之前(下面)可能没有说清楚,我强调一下重点。 首先,最重要的整体考虑:我感觉现在还是找问题的阶段,所以最后先不要下结论解释。所谓“插值错误”只是一个例子,个人感觉很有可能还有其他我不知道 ...
本楼点评(0) 收起

精彩评论9

M丶Sulayman  TF豆豆  发表于 2018-4-26 15:03:33 | 显示全部楼层
额,首先0.5是初始值,AUC不管是哪种,它的取值都是在[0.5,1]。出现小于0.5的分类器,你直接把结果取反。懂我意思不?反正是二分类问题,输出正样本的可能性大,它最后就是输出的正样本标签,可能性小,输出的就是正样本的取反(负样本标签)。因为recall就是只关注正样本预测准确度的指标,它小于0.5只是说明该分类器对负样本敏感,不是说它性能不好。你可以试试随机森林,它不会对样本太过敏感。额,啰嗦了一大堆,希望对你有用。
本楼点评(14) 收起
  • rz828请问你能给我一些关于你说的这两个观点的文档看看吗?也许能帮助我理解
       *AUC不管是哪种,它的取值都是在[0.5,1]*
       *0.5是初始值*

    (AUC我能理解其初值应该是0.5,但是PR-AUC我不太理解)
    2018-4-26 15:26 回复
  • M丶Sulayman回复 rz828 :http://www.360doc.com/content/15/0326/20/17553313_458302508.shtml
    2018-4-26 15:28 回复
  • rz828回复 M丶Sulayman : 你好,我想要的是对你上述两个观点的说明文档,你给的链接我好好看了,完全没有你所说这两个观点的文字。
    2018-4-26 15:39 回复
  • rz828请问下,你有用tensorboard的经验吗?
    2018-4-26 15:39 回复
  • rz828如果你没有tensorboard的使用经验,你肯定理解不了我贴的三张图的。
    2018-4-26 15:40 回复
  • M丶Sulayman回复 rz828 :额,好吧,我刚刚入门TF,我以为你问的是数学方面的东西,抱歉~
    2018-4-26 15:51 回复
  • rz828回复 M丶Sulayman :依然谢谢你的慷慨解答
    2018-4-26 16:02 回复
  • yunhai_luo回复 M丶Sulayman :请教M丶Sulayman一个数学问题:能否详解一下当AUC_PR小于0.5时,取反可以得到大于0.5的AUC_PR。我明白AUC_ROC取反可证曲线以(0.5, 0.5)为中心旋转180度,从而必然得到大于0.5的AUC_ROC。
    2018-4-27 15:21 回复
  • M丶Sulayman回复 yunhai_luo :取反得到的当然不是PR了,这不是曲线旋转的问题。PR代表的是正样本被模型正确的判为正样本的概率,你先去看下混淆矩阵......你就知道了
    2018-4-27 15:37 回复
  • yunhai_luo回复 M丶Sulayman : 其实我想求教的是为什么AUC_PR的取值必然可以大于0.5,如果取反得到的不是PR,那为什么说可以取反得到能使AUC_PR大于0.5的分类器呢?希望大神给个证明链接,查了混淆矩阵没能找到相关内容,让大神见笑了。正样本被模型正确的判为正样本的概率是recall吗?如果是,那为什么precision-recall曲线的AUC会代表recall呢?希望大神也能给个相关链接,麻烦了,先谢过了!
    2018-4-27 16:06 回复
yunhai_luo  TF豆豆  发表于 2018-4-28 15:52:49 | 显示全部楼层
真心请教楼主两个问题:
1)你的auc_precision_recall曲线是怎么得到的?我猜测是通过tf.metrics.auc或者某个canned estimator的metrics,如果是这样,那似乎没有理由怀疑y轴不是PR-AUC(除非tensorflow里面有bug)。
2)我大概可以理解看到PR-AUC太大或者跳到0的心情,但是有没有可能这是真的而不是bug?或者楼主还有什么其他怀疑理由?如果你导出相应步骤的参数,搭好模型,应该可以用你确信的算法计算PR-AUC,检验是真是假吧?我没有别的意思,我也对这个现象背后的原因感兴趣。不过既然参数初始化是随机的,而你三幅图选取的特征又不一样,另外模型优化也不是以PR-AUC为目标,所以我感觉理论上出现较大初始PR-AUC和PR-AUC跳动还是有可能的。
本楼点评(1) 收起
  • rz828其实我写的很清楚了,可能你们没有人用过这些现成的东西:tf.estimator.DNNClassifier,再也没有别的东西了,关于这个estimator的api链接我主楼也贴出来了。
    1. 可以肯定这不是bug。因为Precision-Recall曲线的跳动就是很剧烈的,原因是:Precision-Recall曲线就是对二分类中,占据比例很少的数据特别敏感,因为画这个曲线只需要计算PPV(也称Precision)和TPR(也称Recall)。
    2. 按道理说,这个y轴的值应该就是PR-AUC。所以我最大的问题就是:为什么我的PR-AUC的初始值会是0.5?(因为我判断模型的好坏的时候,同样是根据这个y轴值来决定的,那么从我的图上来看:初值0.5就是最大值,难道说我刚训练第一步的结果就是最好结果?这显然说不过去)
    2018-4-28 16:17 回复
AirLRJ  TF荚荚  发表于 2018-4-28 16:19:16 | 显示全部楼层
可以把代码粘出来嘛?PS如果可以的话,可以看看precisoin 和 recall的变化可以更容易帮助debug。

PPS, 训练和验证的时候请shuffle data
本楼点评(2) 收起
  • rz828当然可以,代码看下楼。
    2018-4-28 16:23 回复
  • rz828再提一句:验证的时候,不需要shuffle data。例子请看tensorflow官方教程https://www.tensorflow.org/get_started/get_started_for_beginners
    2018-5-2 09:31 回复
rz828  TF荚荚  发表于 2018-4-28 16:26:14 | 显示全部楼层
用于训练模型的代码,没有任何特殊点(input_fn我想就没必要贴了,至于shuffle,那是最基本的操作,肯定不能少):
  1. train_spec = tf.estimator.TrainSpec(
  2.         input_fn=lambda: train_input_fn(
  3.             args.train_files,
  4.             args.train_batch_size
  5.         ),
  6.         max_steps=train_max_step
  7.     )

  8.     exporter = tf.estimator.LatestExporter(
  9.         name='ctr',
  10.         serving_input_receiver_fn=serving_fn,
  11.         as_text=True,
  12.         exports_to_keep=20
  13.     )

  14.     eval_spec = tf.estimator.EvalSpec(
  15.         input_fn=lambda: eval_input_fn(
  16.             args.eval_files,
  17.             args.eval_batch_size
  18.         ),
  19.         name='eval',
  20.         exporters=[exporter],
  21.         start_delay_secs=120,
  22.         throttle_secs=300
  23.     )

  24.     run_config = tf.estimator.RunConfig(
  25.         model_dir=args.job_dir,
  26.         save_summary_steps=50,
  27.         save_checkpoints_steps=5000,
  28.         keep_checkpoint_every_n_hours=1,
  29.         keep_checkpoint_max=20
  30.     )

  31.     estimator = tf.estimator.DNNClassifier(
  32.         hidden_units=args.hidden_units,
  33.         feature_columns=FEATURE_SPEC,
  34.         config=run_config
  35.     )

  36.     print_last_ckpt(estimator.latest_checkpoint())

  37.     tf.estimator.train_and_evaluate(
  38.         estimator, train_spec, eval_spec)
复制代码


本楼点评(0) 收起
yunhai_luo  TF豆豆  发表于 2018-4-29 16:27:56 | 显示全部楼层
本帖最后由 yunhai_luo 于 2018-5-3 02:06 编辑

更新:

之前(下面)可能没有说清楚,我强调一下重点。
       首先,最重要的整体考虑:我感觉现在还是找问题的阶段,所以最后先不要下结论解释。所谓“插值错误”只是一个例子,个人感觉很有可能还有其他我不知道的情况,最起码“插值错误”就有很多“变体现象”,比如predictions = tf.cast(np.random.choice([.1, .9], size=n), tf.float32),AUC PR得到0.45407957,得到曲线是两个点组成的0.2左右的线。具体发生了什么是跟你的数据有关的,即使是在第一个步骤,尽管参数是随机的、跟数据无关的,但预测是跟你的特征作用后的结果,所以不同设定都是不同的。从这个角度讲,个人觉得"画出PR曲线看一看"不仅是没有办法的办法,而是其中一种正确思路。
       其次,关于你这个问题中我感兴趣的部分主要是两个,第一是训练过程一直保持0.5,另一个是跳低过一次。下面算是回答你点评中的问题:
       1. 这里想说的是第几个步骤(是不是第一个点)跟你出现0.5这个问题很可能没有太直接的关系,具体能不能忽略掉应该需要看看PR曲线甚至是其他统计结果决定,不好直接下结论。
       2. 同样的,我也不确定跳低过的那一次是不是就“没错”。对于跳低过的那一次,我感兴趣的点在于到底有多低。这篇文章讲到了PR空间的禁区问题,我不是完全理解其中的证明,但大概一个思路是当recall100%的时候,precision不可能低于偏移度比例,因为分类找到了所有正值,最差也是所有的负值都是一类错误。但是,随机分类的AUC PR是要大于这个曲线的。你的三幅图最低值到了0.002,好像真的很低,说明你的偏移度可能真的很大,我好奇你这个值是不是低于了随机分类,如果是,感性上说还是挺难得的。
       最后,提一个1.8.0的更新。今天重跑下面代码是发现有警告:“WARNING:tensorflow:Trapezoidal rule is known to produce incorrect PR-AUCs; please switch to "careful_interpolation" instead.”。仔细看过应该是1.8.0的更新,“careful_interpolation”也是新的,参考文章就是之前点评中提到的那篇。这个选项在DNNClassifier里好像还没用到。如果用这个方法计算下面的例子,得到的是0.23976201而不是0.6105。

之前的发言:

我感觉AirLRJ的建议是很好的,楼主应该考虑把可以的模型或者相应步骤的预测值提取出来,画出PR曲线看一看。到底出了什么问题,或者是不是真的AUC PR是可以检验的。个人认为仅凭楼主的代码分析是不够的,问题很有可能跟楼主的数据特征有关。我对PR曲线的了解确实不够深入,但是我能想到的一个特例就是当预测概率为定值,或者说所有的预测概率在一个相对于阈值采样区间很小的范围中时,PR曲线变成了一个precision为偏移度、recall为1的点,这样在接下来的AUC数值计算中可能会导致0.5左右的AUC,但这是完全无意义的伪值。这个特例在tensorflow中可以的到证实:
  1. import numpy as np
  2. import tensorflow as tf
  3. from tensorboard.plugins.pr_curve import summary

  4. n = 1000
  5. np.random.seed(408)
  6. labels = tf.cast(np.random.choice(2, size=n, p=(0.8, 0.2)), tf.bool)
  7. predictions = tf.random_uniform(shape=[n], minval=0.1, maxval=0.1005, seed=408)
  8. auc = tf.metrics.auc(labels, predictions, curve='PR')
  9. summary_op = summary.op('Big_AUC-PR', labels, predictions)

  10. with tf.Session() as sess:
  11.     init = tf.local_variables_initializer()
  12.     sess.run(init)
  13.     auc_value, summ = sess.run((auc, summary_op))
  14.     with tf.summary.FileWriter('Big_AUC-PR') as writer:
  15.         writer.add_summary(summ)
  16.     print(auc_value)
复制代码


得到的AUC为0.6105,tensorboard中的曲线图为

AUC-PR ~ 0.5

AUC-PR ~ 0.5

当然这只是一个特例,很可能还有其他特例,或者其他可能性,我水平有限,说不上来。不过还是那句话,画出PR曲线看看没准有帮助。


另外,想在这里老话重提一遍。之前向楼主和@M丶Sulayman请教过AUC-PR大于0.5的问题,但一直没有下文,不知道是不是言语不周有所得罪,在这里先道个歉,请见谅。这次想再提的是以下代码,分类器是最差的均匀随机分类器,我用1-预测值的方法取反,但取反前后的AUC-PR都小于0.5。
  1. import numpy as np
  2. import tensorflow as tf
  3. from tensorboard.plugins.pr_curve import summary

  4. n = 1000
  5. labels = tf.cast(np.random.choice(2, size=n, p=(0.8, 0.2)), tf.bool)
  6. predictions = tf.random_uniform([n])
  7. ops = []
  8. for name, pred in zip(['Prediction', '1-prediction'],
  9.                       [predictions, 1-predictions]):
  10.     ops.append(tf.metrics.auc(labels, pred, curve='PR'))
  11.     summary_op = summary.op(name, labels, pred)
  12. ops.append(tf.summary.merge_all())

  13. with tf.Session() as sess:
  14.     init = tf.local_variables_initializer()
  15.     sess.run(init)
  16.     auc1, auc2, merged_summary = sess.run(ops)
  17.     with tf.summary.FileWriter('PR_curve') as writer:
  18.         writer.add_summary(merged_summary)
  19.     print('Prediction: ', auc1[1])
  20.     print('1-prediction: ', auc2[1])
复制代码


没有设随机种子,我得到的结果是0.20946483和0.19511054。PR曲线图如下:

AUC-PR < 0.5

AUC-PR < 0.5

AUC-PR取反 < 0.5

AUC-PR取反 < 0.5

恳请楼主和@M丶Sulayman帮忙看看算法/代码有什么问题,或者能详细指点一下为什么AUC-PR必然大于0.5,真的非常感谢!




本楼点评(4) 收起
  • rz8281.  "画出PR曲线看一看",这貌似也是个没有办法的办法,为什么这么说呢:因为我的疑问是第一步就得到接近0.5的PR-AUC值,是很奇怪的。而且,才仅仅训练了1个step,就去做evaluation并且画PR曲线,真的只能说是没有办法的办法。
    2. "AUC为0.6105,tensorboard中的曲线图":你这句话下面的图上只有一个点,无法表明AUC值为0.6105,是不是你图贴错了(因为AUC是指面积,一个点无法构成面积值)?
    3. 关于"AUC-PR必然大于0.5":这个观点是这个层主@M丶Sulayman的观点。不是我的观点。你可能没有仔细看楼上的回复。
    2018-5-2 09:48 回复
  • yunhai_luo回复 rz828 :2. 确实没有贴错图,楼主可能比较忙,没时间测试代码,如果试过得到不同结果请告知。一个点确实无法构成面积值,楼主说的很对,所以我才说是“完全无意义的伪值”,但在数值求解过程中会进行“错误”插值,得到0.6105的,完全没有报错。引用这篇文章可能没有可比性(https://www.biostat.wisc.edu/~page/rocpr.pdf),但其中也谈到了插值有误的问题。
    2018-5-2 12:47 回复
  • rz828回复 yunhai_luo :
    1. 貌似你的这个解释可以说得通,那么这样子的话:对于tensorboard上显示的auc_precision_recall图,第一个点就直接当成"错误插值",把它忽略掉是不是就可以了?
    2. 但是还有一点不好解释:关于图figure A,除了一个点以外,其所有点的值都是0.5。这个就不好解释了,因为如果用"错误插值"来解释的话,那figure A里面的点只有一个不是"错误插值"了?
    依然困惑。。。
    2018-5-2 16:32 回复
  • yunhai_luo回复 rz828 :之前可能没有说清楚,说的有点儿多,更新了帖子,请查收。
    2018-5-3 01:47 回复
M丶Sulayman  TF豆豆  发表于 2018-5-2 16:18:51 | 显示全部楼层
yunhai_luo 发表于 2018-4-29 16:27
我感觉AirLRJ的建议是很好的,楼主应该考虑把可以的模型或者相应步骤的预测值提取出来,画出PR曲线看一看。 ...

没有没有,上次是我看的有问题。混淆矩阵里面的计算很简单的,就是求个分数。但是我看代码好像计算很复杂啊......我用sklearn试过了是对的,但是和TF的auc_precision_recall对比区别很大。是我没搞清楚原理,所以也就不好意思给你发消息了。
本楼点评(0) 收起
M丶Sulayman  TF豆豆  发表于 2018-5-2 16:20:31 | 显示全部楼层
抱歉抱歉......实在解释不了了......TF算这个怎么和原始的数学公式不一样啊......原始的混淆矩阵很简单的
本楼点评(2) 收起
  • yunhai_luo您客气了,我只是想搞清楚问题,也确实是新手,所以有点着急、刨根问底啥的请别忘心里去,完全没有别的意思。您肯跟花时间我讨论我感激还来不及呢,来这儿也是想通过这种方式学习,没有人讨论提高才是失败、失望。无论对错成败都谢谢您!
    2018-5-3 01:53 回复
  • M丶Sulayman回复 yunhai_luo :https://www.tensorflowers.cn/t/445
    2018-5-3 08:43 回复
rz828  TF荚荚  发表于 2018-5-3 16:37:38 | 显示全部楼层
本帖最后由 rz828 于 2018-5-3 16:42 编辑
yunhai_luo 发表于 2018-4-29 16:27
更新:

之前(下面)可能没有说清楚,我强调一下重点。

非常感谢您的回复!看到你更新回复的timestamp,更是被你感动了!(Do not tell me if you have jetlag with me.)
吐槽下论坛的通知系统,在楼层里的点评没有通知,所以不能及时看到在楼层里面的回复。

我认真看了你的更新回复。
1. 很可能如你所说:就是这个1.6.0版本下,计算PR-AUC时,利用trapezoidal rule所导致的问题。所以造成得到的图形比较奇怪。因为我查看了tensorflow代码库,DNNClassifier的确是用默认的trapezoidal  rule来计算PR-AUC值的(代码来源见以下链接)。
2. 我会先更新到1.8.0,然后再尝试(可能会过一段时间,因为google ml-engine的确太贵了),如果有新的结果,我会再来更新的。
3. 你更新前的关于AUC-PR大于0.5的问题:其实正如你在这个贴子6楼https://www.tensorflowers.cn/t/355#pid1353 的回复”AUC_PR值似乎没有特别直观的类似AUC_ROC那样的概率解释”,所以PR-AUC没有必须要大于0.5这么一说,也没有取反(即用1减所得的结果)这么一说。例子如下,下图为均匀分布(Balanced)和不均匀分布(Imbalanced)的数据集的PR-AUC数据比较:

balance-imbalance

balance-imbalance


上图来源:https://classeval.wordpress.com/ ... mbalanced-datasets/
正如这个表格和这篇博文所说:
  • 只有均匀分布的数据集其ROC-AUC值和PR-AUC值是一致的。(即PR-AUC的结果0.5和ROC-AUC一样也表示随机猜想(random guess),所以对于这种均匀的数据集,我们就直接用ROC-AUC来判断模型的好坏就好了。)
  • 而对于分类为不均匀的数据集:其PR-AUC和ROC-AUC会有很大的不同,如例子中,0.09的PR-AUC值可能代表随机猜想(和0.5的ROC-AUC值一样的意义),而0.51的PR-AUC可能已经表示一个很不错的结果了(相当于ROC-AUC=0.84),所以对于不均匀的数据,我们绝不能简单的把某个PR-AUC值想像成和同值的ROC-AUC的意义相同(如上表中0.23的PR-AUC,其实表示一个也还不错的结果)。



再次表示感谢!



本楼点评(1) 收起
  • yunhai_luo-1. 确有时差,不必客气。楼主看的很仔细,点赞(表情里没有点赞?)
    0. 我大概没有出现过点评没通知的情况,如果确有bug应该可以去反馈。顺便问一下,楼主知道这里怎么@人吗?
    2. 有结果请不吝更新,共同学习。另外1.8.0的DNNClassifier也没用“careful_interpolation”,如果楼主有代码实现(hook等)也请分享一下,先行谢过。
    3. 非常感谢你的解释,对正确理解使用PR-AUC真的很有帮助。
    2018-5-4 00:54 回复
rz828  TF荚荚  发表于 2018-6-5 10:25:37 | 显示全部楼层
@yunhai_luo自从上次我们找到问题的来源后,我就针对这个问题,对tensorflow库提交了一个合并请求,在经过一个月的等待后,结果如下:
tensorflow社区人员也讨论过将 careful_interpolation 这种方式作为计算AUC的默认方式,但是最后发现这依然会造成向后兼容的问题。所以这个AUC的计算问题暂时不会修改。
同时社区开发人员也给我提出了另一个方法,就是在tensorflow/contrib/estimator/python/estimator/head.py 里面作改动。但是我经过思考以后,觉得可能还是不这样做比较好。
所以我们还是期待这个问题能在下一个大的版本里面得到修正吧。


最后还是感谢你能发现这个问题的线索!
本楼点评(1) 收起
  • yunhai_luo多谢楼主的反馈,确实很有帮助!也非常感谢楼主提出并维护这样一个好问题。
    2018-6-5 12:16 回复
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

主题

帖子

25

积分
快速回复 返回顶部 返回列表