版本:2.0.1 CPU环境
一、 binary_cross_entropy_with_logits、binary_cross_entropy和 softmax_with_cross_entropy、cross_entropy是两对非常重要的基础损失函数,这两套函实现机制应该是一致的。
我实际测试发现是这样的:
binary_cross_entropy_with_logits 是先做sigmoid,再计算交叉墒,接着mean reduction
binary_cross_entropy是直接计算交叉墒,再做mean reduction
这与预期完全一致
二、而 softmax_with_cross_entropy、cross_entropy的实现机制却与预想完全不一样(与API文档中描述也不一样)
softmax_with_cross_entropy机制是先做softmax,再计算交叉墒,但是没有做mean reduction
cross_entropy机制是先做exp运算,再计算交叉墒,接着mean reduction, 这样做显然造成了混乱与不一致!!!
以下测试数据:
logits:
Tensor(shape=[2, 10], dtype=float32, place=CPUPlace, stop_gradient=False,
[[-2.99181128, -1.29089355, -0.49017018, 1.25062561, -3.17891693, -4.10819149, -12.85970783, 11.72850990, -4.76343298, 1.11044443],
[ 1.47004175, 2.04185462, 7.62971449, 1.82590401, -8.92507839, -2.46311164, 0.17321450, -8.56867886, -0.19891697, -8.65551376]])
labels:
Tensor(shape=[2, 1], dtype=int64, place=CPUPlace, stop_gradient=True,
[[7],
[2]])
softmax_with_cross_entropy的结果(未做reduction):
Tensor(shape=[2, 1], dtype=float32, place=CPUPlace, stop_gradient=False,
[[0.00006080],
[0.00984089]])
mean的结果是:0.00495084
softmax的结果:
Tensor(shape=[2, 10], dtype=float32, place=CPUPlace, stop_gradient=False,
[[0.00000040, 0.00000222, 0.00000494, 0.00002815, 0.00000034, 0.00000013, 0.00000000, 0.99993920, 0.00000007, 0.00002447],
[0.00209225, 0.00370637, 0.99020737, 0.00298650, 0.00000006, 0.00004097, 0.00057202, 0.00000009, 0.00039427, 0.00000008]])
再对softmax结果做cross_entropy的结果是:
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[1.46536016])
从上面可以看出, cross_entropy是做了reduction的,但是结果与预期明显不一致
以下是验证:
softmax_with_cross_entropy(logits, labels).mean():
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[0.00495084])
cross_entropy(paddle.log(F.softmax(logits)), labels):
Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=False,
[0.00495084])
这就要求在cross_entropy之前要先做一次softmax,再做一次log或者直接做log_softmax,这显然是不合适的。
因此,强烈要求做以下两点修复:
1、对softmax_with_cross_entropy结果自动做mean运算
2、删除cross_entropy中的exp运算
8条答案
按热度按时间kupeojn61#
您好,我们已经收到了您的问题,会安排技术人员尽快解答您的问题,请耐心等待。请您再次检查是否提供了清晰的问题描述、复现代码、环境&版本、报错信息等。同时,您也可以通过查看官网API文档、常见问题、历史Issue、AI社区来寻求解答。祝您生活愉快~
Hi! We've received your issue and please be patient to get responded. We will arrange technicians to answer your questions as soon as possible. Please make sure that you have posted enough message to demo your request. You may also check out the API,FAQ,Github Issue and AI community to get the answer.Have a nice day!
ycl3bljg2#
@jesse01
cross_entropy
的计算就是log_softmax
+nll_loss
的结合,删除exp的运算是不合理的。softmax_with_cross_entropy
是paddle 1.x起就使用的算子,为了向下兼容,无法直接在内部求mean。如果想求mean,可以计算完求mean即可。cross_entropy
和softmax_with_cross_entropy
在不设置weight
的情况下,计算是一致的,不用paddle.log(F.softmax(logits))
这样计算。b4qexyjb3#
我不认同回复的观点,理由如下:
1、paddle 2.0设计明显是吸收tensorflow 2.0 + pytorch的特点,目标可以说是既实现tensorflow强大的计算和部署能力,同时又体现keras的易用性以及pytorch面向对象编程的程序员友好性,更大的目标应该是成为全中国乃至全世界最好用的深度学习框架!
目前在github上 paddle的star是14.6K, pythorch是46.9K, tensorflow是154, 差距还是很明显。
本人是去年底开始接触paddle,当时还没有发布2.0, 根据我简单的了解,paddle 1.x似乎是在完全仿制tensorflow 1.x,而tensorflow 1.x 这种静态图的做法太繁琐,在像我这样的老程序员眼中,简直是反人类的设计,根本就没有学习的兴趣!而我坚定地认为tensorflow 2.0如果不是改用与keras结合,并且底层采用所见即所得的动态图优先策略,未来肯定是死路一条!所以在我初次接触paddle 1.x,就果断放弃了它。paddle 2.0正式推出,让我眼前一亮,我真的希望paddle能早日在国内普及。随着人工智能应用的深入人心,paddle想要快速提高认可度,扩大用户群,必须让学生们觉得简单易用,不易困惑,更重要的是要符合广大的程序员逻辑思维习惯。
2、paddle2的cross_entropy看名称应该与pytorch的CrossEntropyLoss有参照性,而pytorch的CrossEntropyLoss相当于现在paddle2的softmax_with_cross_entropy + mean方法,先抛开mean的问题,这样做是可以理解的,毕竟paddle2是提供了两个方法,使用起来更方便。但是cross_entropy实现做成了log_softmax+nll_loss这个就很难理解了,因为从名称看就是: softmax_with_cross_entropy = softmax + cross_entropy, 而 binary_cross_entropy_with_logits = sigmoid + binary_cross_entropy也正是这种逻辑。同一套框架里,标准的一致性是至关重要的,只有标准一致,用户才不会混乱!
另外请看paddle 2 API中关于 cross_entropy的描述:
https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/functional/loss/cross_entropy_cn.html#cross-entropy
其中关于“硬标签交叉熵算法”描述就是直接取负对数的,根本就没有exp运算,也没有关于log_softmax+nll_loss相关的描述,这说明连paddle2的内部团队人员都已经开始混淆了!
3、softmax_with_cross_entropy为了和paddle 1.x兼容就不做mean运算思路也是不对的,softmax_with_cross_entropy、cross_entropy、binary_cross_entropy_with_logits、binary_cross_entropy这四个方法中,后三个都做了mean运算,只有softmax_with_cross_entropy不做mean运算,这标准的一致性何在,让用户如何理解? 我们看到python 2.x 升到 python 3.x 为了保持语法的严谨一致, 是直接抛弃了print语句而改用print()方法,而不是兼容print语句,这才是正确的做法!对于paddle 1.x老用户来说,只要在paddle 2.0升级需知中告知不兼容变化就行。如果他们paddle 1.x已用于生产环境,他们是不会轻易升级的,升级时也会做充分测试的!还有很多老用户估计代码中都是对softmax_with_cross_entropy结果做mean运算的,如果paddle2加了mean运算,对于这些代码来说相当于对单个数据又做了一次mean运算,结果并不受影响,代码也不需改动。
最后我想强调的是,现在paddle的用户体量还不是很大,扩大用户群的核心就是要吸引新用户,而吸引新用户,设计标准的一致性是至关重要的!我是paddle2的坚定拥护者,希望paddle2越做越好!
7kjnsjlb4#
我不认同回复的观点,理由如下: 1、paddle 2.0设计明显是吸收tensorflow 2.0 + pytorch的特点,目标可以说是既实现tensorflow强大的计算和部署能力,同时又体现keras的易用性以及pytorch面向对象编程的程序员友好性,更大的目标应该是成为全中国乃至全世界最好用的深度学习框架! 目前在github上 paddle的star是14.6K, pythorch是46.9K, tensorflow是154, 差距还是很明显。 本人是去年底开始接触paddle,当时还没有发布2.0, 根据我简单的了解,paddle 1.x似乎是在完全仿制tensorflow 1.x,而tensorflow 1.x 这种静态图的做法太繁琐,在像我这样的老程序员眼中,简直是反人类的设计,根本就没有学习的兴趣!而我坚定地认为tensorflow 2.0如果不是改用与keras结合,并且底层采用所见即所得的动态图优先策略,未来肯定是死路一条!所以在我初次接触paddle 1.x,就果断放弃了它。paddle 2.0正式推出,让我眼前一亮,我真的希望paddle能早日在国内普及。随着人工智能应用的深入人心,paddle想要快速提高认可度,扩大用户群,必须让学生们觉得简单易用,不易困惑,更重要的是要符合广大的程序员逻辑思维习惯。 2、paddle2的cross_entropy看名称应该与pytorch的CrossEntropyLoss有参照性,而pytorch的CrossEntropyLoss相当于现在paddle2的softmax_with_cross_entropy + mean方法,先抛开mean的问题,这样做是可以理解的,毕竟paddle2是提供了两个方法,使用起来更方便。但是cross_entropy实现做成了log_softmax+nll_loss这个就很难理解了,因为从名称看就是: softmax_with_cross_entropy = softmax + cross_entropy, 而 binary_cross_entropy_with_logits = sigmoid + binary_cross_entropy也正是这种逻辑。同一套框架里,标准的一致性是至关重要的,只有标准一致,用户才不会混乱! 另外请看paddle 2 API中关于 cross_entropy的描述: https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/functional/loss/cross_entropy_cn.html#cross-entropy 其中关于“硬标签交叉熵算法”描述就是直接取负对数的,根本就没有exp运算,也没有关于log_softmax+nll_loss相关的描述,这说明连paddle2的内部团队人员都已经开始混淆了! 3、softmax_with_cross_entropy为了和paddle 1.x兼容就不做mean运算思路也是不对的,softmax_with_cross_entropy、cross_entropy、binary_cross_entropy_with_logits、binary_cross_entropy这四个方法中,后三个都做了mean运算,只有softmax_with_cross_entropy不做mean运算,这标准的一致性何在,让用户如何理解? 我们看到python 2.x 升到 python 3.x 为了保持语法的严谨一致, 是直接抛弃了print语句而改用print()方法,而不是兼容print语句,这才是正确的做法!对于paddle 1.x老用户来说,只要在paddle 2.0升级需知中告知不兼容变化就行。如果他们paddle 1.x已用于生产环境,他们是不会轻易升级的,升级时也会做充分测试的!还有很多老用户估计代码中都是对softmax_with_cross_entropy结果做mean运算的,如果paddle2加了mean运算,对于这些代码来说相当于对单个数据又做了一次mean运算,结果并不受影响,代码也不需改动。 最后我想强调的是,现在paddle的用户体量还不是很大,扩大用户群的核心就是要吸引新用户,而吸引新用户,设计标准的一致性是至关重要的!我是paddle2的坚定拥护者,希望paddle2越做越好! 原始邮件 发件人: Guanghua***@***.***> 收件人:***@***.***> 抄送:***@***.***>;***@***.***> 发送时间: 2021年3月15日(周一) 18:51 主题: Re: [PaddlePaddle/Paddle] 关于softmax_with_cross_entropy和cross_entropy代码实现的严重错误 (#31610) @jesse01 cross_entropy的计算就是log_softmax + nll_loss的结合,删除exp的运算是不合理的。 softmax_with_cross_entropy是paddle 1.x起就使用的算子,为了向下兼容,无法直接在内部求mean。如果想求mean,可以计算完求mean即可。 — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.
bxjv4tth5#
@jesse01
非常感谢你对Paddle 2.0的关注,并提出宝贵的建议。
对于cross_entropy和softmax_with_cross_entropy造成的困扰,我们表示歉意。
我解释下这个问题:
再次感谢宝贵建议!如果有其他问题或者建议,欢迎再反馈给我们!
wlzqhblo6#
感谢回复,cross_entropy包含softmax,那就是和pytorch完全一样了,这样设计是没有问题的。 这次给我的主要困惑由三个方面原因造成: 1、softmax_with_cross_entropy命名很容有误会,应该尽快打上deprecated标志 2、cross_entropy API说明有问题,1是没有写上softmax运算,2是没有说明是取代softmax_with_cross_entropy函数 3、paddle 2.0教程误导, 教程图像分类关于lenet的代码:https://www.paddlepaddle.org.cn/tutorials/projectdetail/1525452中 在该段代码中仍在使用softmax_with_cross_entropy函数,导致paddle 2.0使用方式的误解 原始邮件 发件人:***@***.***> 收件人:***@***.***> 抄送:***@***.***>;***@***.***> 发送时间: 2021年3月16日(周二) 19:15 主题: Re: [PaddlePaddle/Paddle] 关于softmax_with_cross_entropy和cross_entropy代码实现的严重错误 (#31610) @jesse01 非常感谢你对Paddle 2.0的关注,并提出宝贵的建议。 对于cross_entropy和softmax_with_cross_entropy造成的困扰,我们表示歉意。 我解释下这个问题: softmax_with_cross_entropy是历史原因保留的api,2.0下并不推荐用户使用,我们会在下一个版本会打印deprecated警告,并在后续的版本中删除,所以请忽略softmax_with_cross_entropy API在2.0下的行为。 Paddle 2.0下推荐用户使用cross_entropy这个API。这个api默认是会计算softmax的,输入其实是softmax归一化之前的logits。这一点跟cross_entropy的数学定义并不一致,之所以这样设计,是为了减少softmax exp计算带来的数值溢出,并提升性能;我们会再下一个版本的cross_entropy函数中增加with_softmax的参数开关,用来支持不需要计算softmax的情况。 文档确实有问题,我们尽快更新。 再次感谢宝贵建议!如果有其他问题,欢迎再次反馈给我们! — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.
2sbarzqh7#
各位好! 因为github打不开,只好直接发邮件了,见谅! 版本: V2.0.1 场景描述: 在自定义模型中想用以下方法进行简便遍历: def forward(self, x): for layer in self.sublayers(include_sublayers=False): x = layer(x) return x 结果self.sublayers返回的列表为空,如果include_sublayers参数设为True, sublayers方法则会递归返回所有下级子层,导致代码出错。改用以下方法调用内部字典可以正常运行: def forward(self, x): for _, layer in self._sub_layers.items(): x = layer(x) return x 经过排查,发现self.sublayers 是通过 self.named_sublayers 方法间接查找 self._sub_layers 字典,其中include_sublayers参数用于控制是否递归查找下级layer,但是named_sublayers方法有逻辑错误,当include_sublayers=False时,本级layer也不会查找。具体问题代码如下: if include_sublayers: for key, layer in self._sub_layers.items(): if layer is None: continue layer_prefix = prefix + ('.' if prefix else '') + key for p, l in layer.named_sublayers( prefix=layer_prefix, include_sublayers=include_sublayers, include_self=True, layers_set=layers_set): yield p, l
4bbkushb8#
这个看起来是个很严重的问题,我们高优修复下。