Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Hackathon 4th No.13】为 Paddle 新增 Bernoulli API #52244

Merged
merged 10 commits into from
Apr 12, 2023

Conversation

megemini
Copy link
Contributor

@megemini megemini commented Mar 28, 2023

PR types

New features

PR changes

APIs

Describe

[used AI Studio]

  • 飞桨黑客马拉松第四期 No.13:为 Paddle 新增 Bernoulli API
  • 涉及以下5个文件:
    • __init__.py: 将 Bernoulli 添加到 distribution 目录下
    • bernoulli.py: Bernoulli API 的具体实现
    • kl.py: 添加 Bernoulli 对应的 kl_divergence 方法
    • test_distribution_bernoulli.py: 动态图单元测试,含kl.py
    • test_distribution_bernoulli_static.py: 静态图单元测试,含kl.py

其他说明:

  • 相应的设计文档已经合并 【PR链接】#453
  • 以上代码通过 pre-commit
  • 以上代码已经在 AI Studio 测试通过
  • 自测代码覆盖率 90%+

谢谢评审!

p.s. 2023-04-09 CI情况反馈

根据评审意见修改:

  • 初始化条件改为 float|Tensor,并且优化初始化逻辑。
  • 根据修改后的初始化条件,增加相应的异常单测。
  • EPS 使用 paddle.finfo 代替,并且修改单测中 BernoulliNumpyentropy 方法。
  • 支持0D的初始化条件,并增加相应单测,temperature 使用 paddle.full(shape=(), fill_value=...)
  • 增加 rsample 的反向单测。
  • 修改 doc string 中的例子,增加0D的情况。
  • 调整 doc string 中的空行。

请评审,谢谢!

p.s. 2023-04-06 CI情况反馈

重新提交之后,CI只剩下两个必须需要手工确认的了:

  • 修改了单测中 BernoulliNumpy 的初始化方式,规避掉 scipy 版本的问题。
    统一用 float64 初始化,然后在各个返回值中(如 meanvariance 等)再转换为原始数据类型( astype ,如 float32float64)。这样可以在不破坏原有测试用例的基础上规避掉 scipy 版本的问题。
  • 优化了 test_distribution_bernoulli_static 的测试流程,较少了测试时间。
    将原有的 parameterize_cls 中多个测试参数合并为一个,并且将 test_xxx 统一转换为私有方法 _test_xxx,再由 test_all 一并调用。这样可以在不减少测试用例的基础上,省掉每次测试的初始化过程,最终静态图的测试时间约为动态图的一半。

请评审,谢谢!

p.s. 2023-04-03 CI情况反馈

通过修改 unittest_py/requirements.txtscipy 的版本,已经可以正常跑通 PR-CI-Coverage 中动态图单测,但是 PR-CI-Coverage 中静态图的单测提示超时 15s 了。

在 AI Studio 中静态图的单测大约在 12s 左右,CI 环境比较慢。

请指导一下,是否可以通过修改 CMakeLists.txt 的方式延长单测时间,修改哪一部分?60s 应该足够。

非常感谢!:)

p.s. 2023-03-29 CI情况反馈

今天查看CI的执行情况,发现单测通过不了,具体看了一下,应该是scipy版本的问题。

CI环境中的scipy版本是scipy==1.7.3,这个版本对于float32的处理存在问题:

  import numpy as np
  import scipy.stats
  p = np.array(0.3, dtype='float32')
  rv = scipy.stats.bernoulli(p)
  print(rv.mean())    
  # 0.0

而对于其他版本,如scipy==1.7.1,或最新的1.10.x版本,结果是正确的:

  import numpy as np
  import scipy.stats
  p = np.array(0.3, dtype='float32')
  rv = scipy.stats.bernoulli(p)
  print(rv.mean())    
  # 0.30000001192092896

单测中的np.testing.assert_allclose错误,进而导致CI无法通过。

以上问题,在scipyissue中已有类似的反馈:
BUG: scipy.stats.beta and bernoulli fails with float32 inputs #15961
BUG: Overflow when using stats.beta.mean #16478

另外,1.7.3 版本已经不在 scipy 正式文档列表中了,是否可以更新一下CI环境重新测试。

还请知悉,非常感谢!

@paddle-bot
Copy link

paddle-bot bot commented Mar 28, 2023

你的PR提交成功,感谢你对开源项目的贡献!
请关注后续CI自动化测试结果,详情请参考Paddle-CI手册
Your PR has been submitted. Thanks for your contribution!
Please wait for the result of CI firstly. See Paddle CI Manual for details.

@cxxly
Copy link
Contributor

cxxly commented Apr 3, 2023

scipy基础版本升级可能比较麻烦,建议 1)你可以修改Paddle/python/unittest_py/requirements.txt 中的scipy版本尝试,如果CI出现大范围报错,则 2) 用numpy实现个bernoulli作为基准进行测试

@megemini
Copy link
Contributor Author

megemini commented Apr 3, 2023

scipy基础版本升级可能比较麻烦,建议 1)你可以修改Paddle/python/unittest_py/requirements.txt 中的scipy版本尝试,如果CI出现大范围报错,则 2) 用numpy实现个bernoulli作为基准进行测试

收到!我先试一下第一个方法,谢谢!:)

@luotao1
Copy link
Contributor

luotao1 commented Apr 3, 2023

1)你可以修改Paddle/python/unittest_py/requirements.txt 中的scipy版本尝试

如果CI没有问题的话,建议单独提一个PR把scipy版本做升级

@@ -8,7 +8,7 @@ hypothesis
opencv-python<=4.2.0.32
visualdl
paddle2onnx>=0.9.6
scipy>=1.6
scipy>=1.6, !=1.7.2, !=1.7.3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2023-04-03 14:06:08 Collecting scipy!=1.7.2,!=1.7.3,>=1.6
2023-04-03 14:06:09   Using cached scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (34.5 MB)

可以直接写 1.10.1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2023-04-03 14:06:08 Collecting scipy!=1.7.2,!=1.7.3,>=1.6
2023-04-03 14:06:09   Using cached scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (34.5 MB)

可以直接写 1.10.1?

可以,AI STUDIO 上就是1.10.1,可以过测。

只是这样的话会绑定python版本,以后scipy更新也要修改,所以就写成我这样了。

@megemini
Copy link
Contributor Author

megemini commented Apr 3, 2023

1)你可以修改Paddle/python/unittest_py/requirements.txt 中的scipy版本尝试

如果CI没有问题的话,建议单独提一个PR把scipy版本做升级

  1. 通过修改这个文件已经能够过测 PR-CI-Py3,但是 PR-CI-Coverage 这个环境比较慢,其中的静态图超时了,是不是可以修改一下 timeout

  2. 单独提PR修改 unittest_py/requirements.txt 这个文件吗?是不是先提PR修改scipy版本,merge之后我再拉取新的版本并提这个API的PR?这样就不需要在这个API中修改scipy版本了?

谢谢!

@luotao1
Copy link
Contributor

luotao1 commented Apr 3, 2023

只是这样的话会绑定python版本,以后scipy更新也要修改,所以就写成我这样了。

你可以写 >=1.10.1

单独提PR修改 unittest_py/requirements.txt 这个文件吗?是不是先提PR修改scipy版本,merge之后我再拉取新的版本并提这个API的PR?这样就不需要在这个API中修改scipy版本了?

意思就是先提一个单独的PR修改unittest_py/requirements.txt 中的scipy版本(因为CI环境的变化需要其他审核人看),merge之后再更新这个API的PR(你可以先同步保留这个PR中scipy版本的修改,便于 @cxxly 继续审核,不然你这个PR的CI都过不去)

但是 PR-CI-Coverage 这个环境比较慢,其中的静态图超时了,是不是可以修改一下 timeout

@cxxly 看下是否可以修改 timeout,还是单测写法上可以改进下。

@megemini
Copy link
Contributor Author

megemini commented Apr 3, 2023

只是这样的话会绑定python版本,以后scipy更新也要修改,所以就写成我这样了。

你可以写 >=1.10.1

单独提PR修改 unittest_py/requirements.txt 这个文件吗?是不是先提PR修改scipy版本,merge之后我再拉取新的版本并提这个API的PR?这样就不需要在这个API中修改scipy版本了?

意思就是先提一个单独的PR修改unittest_py/requirements.txt 中的scipy版本(因为CI环境的变化需要其他审核人看),merge之后再更新这个API的PR(你可以先同步保留这个PR中scipy版本的修改,便于 @cxxly 继续审核,不然你这个PR的CI都过不去)

但是 PR-CI-Coverage 这个环境比较慢,其中的静态图超时了,是不是可以修改一下 timeout

@cxxly 看下是否可以修改 timeout,还是单测写法上可以改进下。

收到,那我先去提个PR修改scipy版本。

@megemini
Copy link
Contributor Author

megemini commented Apr 3, 2023

已经提交PR,#52476

  1. 这里没有写 scipy>=1.10.1,因为 scipy1.8.0 版本以后需要 python3.8,而CI有的环境中还是 python3.7版本,为了保持兼容性,还是写成了 scipy>=1.6, !=1.7.2, !=1.7.3
  2. PR-CI-Mac-Python3 没有通过,还是之前一样的问题,日志中没有显示 scipy 的安装版本,是不是这个CI不受 unittest_py/requirements.txt 的影响?

@luotao1
Copy link
Contributor

luotao1 commented Apr 4, 2023

PR-CI-Mac-Python3 没有通过,还是之前一样的问题,日志中没有显示 scipy 的安装版本,是不是这个CI不受 unittest_py/requirements.txt 的影响?

Mac 是在CI配置中写的 unittest_py/requirements.txt ,因此等 PR merge后重新rerun下即可。

@megemini
Copy link
Contributor Author

megemini commented Apr 6, 2023

目前CI还有两个必须需要手动确认的项目了~

请评审,谢谢!

'float32': 1e-03,
'float64': 1e-05,
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paddle.finfo(paddle.float32).eps paddle.finfo(paddle.float64).eps能否满足要求,如果不能,此处eps取值需要通过注释说明原因

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

应该是这样写,可是有几个问题:

  • paddle里面我现在只找到 iinfo ,没有 finfo
  • 如果有,且与 numpyeps 一致的话,单测是过不了的,因为单测里面的 np.testing.assert_allclose 是按照 config 来设置的
RTOL = {
   'float32': 1e-03,
   'complex64': 1e-3,
   'float64': 1e-5,
   'complex128': 1e-5,
}

https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/tests/unittests/distribution/config.py#L25
numpy 的值跟这个不一样

print(np.finfo('float32').eps, np.finfo('float64').eps)
# 1.1920929e-07 2.220446049250313e-16

这咋处理?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1)paddle develop 包含 finfo
2) 你这里eps不是仅仅为了clip用嘛,没太明白allclose阈值有啥关系
3)paddle.finfo 和 numpy finfo 是一样的

Copy link
Contributor Author

@megemini megemini Apr 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1)我在develop里面已经找到 finfo 了,谢谢!
2)我又看了一下当时因为 eps 错误的用例,应该是 scipyentropy 方法与 paddle 的实现有少许区别导致的。假设 probs=1.0:

# --------------------------
# scipy
def entr(x):
    return -x*np.log(x)

def _entropy_scipy(p):
    return entr(p) + entr(1-p)

# --------------------------
# paddle
def _probs_to_logits(probs, is_binary=False):
    return ((paddle.log(probs) -
                paddle.log1p(-probs)) if is_binary else paddle.log(probs))

def _entropy_paddle(logits, probs):
    return paddle.max(logits, 0)-logits*probs+paddle.log(1+paddle.exp(-paddle.abs(logits)))

# --------------------------
# 测试 scipy 与 paddle 的区别
probs = (1.0-np.finfo('float32').eps).astype('float32')
logits = _probs_to_logits(paddle.to_tensor(probs, dtype='float32'), True)

entropy_np = _entropy_scipy(probs)
entropy_paddle = _entropy_paddle(logits, probs)

print(entropy_np)
print(entropy_paddle.numpy())
# 2.0196896973703793e-06
# [2.026558e-06]

这时候如果再用allclose进行声明就会报错:

np.testing.assert_allclose(
    entropy_np,
    entropy_paddle,
    rtol=1e-03,
    atol=0,
)
# AssertionError: 
# Not equal to tolerance rtol=0.001, atol=0

# Mismatched elements: 1 / 1 (100%)
# Max absolute difference: 6.8682766e-09
# Max relative difference: 0.00338913
#  x: array(2.01969e-06)
#  y: array([2.026558e-06], dtype=float32)

这样吧,我把 scipyentropy 改为与 paddle 一致的算法,这样应该就可以了。

谢谢!:)


Args:
probs (float|list|tuple|numpy.ndarray|Tensor): The ``probs`` input of Bernoulli distribution. The data type is float32 or float64. The range must be in [0, 1].
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

无需支持 list/tuple/numpy.ndarray,支持scalar/Tensor即可

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改成 float|Tensor 吧,保持与 _to_tensor 一致,而且包含复数也没什么意义。

# Clip probs from [0, 1] to (0, 1) with smallest representable number `eps`.
self.probs = _clip_probs(self.probs)
self.logits = self._probs_to_logits(self.probs, is_binary=True)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上,支持scalar/Tensor即可,简化代码复杂度

Tensor: Sampled data with shape `sample_shape` + `batch_shape` + `event_shape`.
The shape of the sampled data have ndim lager than 1.

Examples:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此处应该需要空行吧,请确认下

'shape',
(np.ndarray, tensor.Variable, list, tuple),
name,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上

Tensor: Sampled data with shape `sample_shape` + `batch_shape` + `event_shape`.
The shape of the sampled data have ndim lager than 1.

Examples:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上

'temperature',
(float,),
name,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上

shape = shape if isinstance(shape, tuple) else tuple(shape)
shape = self._extend_shape(shape)

temperature = paddle.full(shape=[1], fill_value=temperature)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

飞桨已支持0D Tensor, paddle.full(shape=(), fill_value=...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0D 好像现在支持的还不好,这里如果改成 0D,后面的 paddle.divide 会报错,比如:

paddle.divide(paddle.to_tensor([0.3]), paddle.full(shape=[1], fill_value=0.1))
# Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=True, [3.])

paddle.divide(paddle.to_tensor([0.3]), paddle.full(shape=(), fill_value=0.1))
# ---------------------------------------------------------------------------
# ValueError                                Traceback (most recent call last)
# /tmp/ipykernel_1677/1739005628.py in <module>
# ----> 1 paddle.divide(paddle.to_tensor([0.3]), paddle.full(shape=(), fill_value=0.1))

# /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/tensor/math.py in divide(x, y, name)
#     716     act = None
#     717     if in_dygraph_mode():
# --> 718         return _C_ops.divide( x, y)
#     719     else:
#     720         if _in_legacy_dygraph():

# ValueError: (InvalidArgument) Axis should be less than 1, but received axis is 1.
#   [Hint: Expected axis < max_dim, but received axis:1 >= max_dim:1.] (at /paddle/paddle/phi/kernels/funcs/common_shape.h:53)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

因为to_tensor会产生1D,能否也用full替代,或者reshape到0D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

刚又重新试了下develop版本,之前很多0D的运算问题都没有了,我把整体代码逻辑优化一下。谢谢!:)

temperature,
)
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要测试 rsample的反向

@cxxly
Copy link
Contributor

cxxly commented Apr 7, 2023

只是这样的话会绑定python版本,以后scipy更新也要修改,所以就写成我这样了。

你可以写 >=1.10.1

单独提PR修改 unittest_py/requirements.txt 这个文件吗?是不是先提PR修改scipy版本,merge之后我再拉取新的版本并提这个API的PR?这样就不需要在这个API中修改scipy版本了?

意思就是先提一个单独的PR修改unittest_py/requirements.txt 中的scipy版本(因为CI环境的变化需要其他审核人看),merge之后再更新这个API的PR(你可以先同步保留这个PR中scipy版本的修改,便于 @cxxly 继续审核,不然你这个PR的CI都过不去)

但是 PR-CI-Coverage 这个环境比较慢,其中的静态图超时了,是不是可以修改一下 timeout

@cxxly 看下是否可以修改 timeout,还是单测写法上可以改进下。

可以的,需要定位并说明原因

@megemini
Copy link
Contributor Author

megemini commented Apr 7, 2023

只是这样的话会绑定python版本,以后scipy更新也要修改,所以就写成我这样了。

你可以写 >=1.10.1

单独提PR修改 unittest_py/requirements.txt 这个文件吗?是不是先提PR修改scipy版本,merge之后我再拉取新的版本并提这个API的PR?这样就不需要在这个API中修改scipy版本了?

意思就是先提一个单独的PR修改unittest_py/requirements.txt 中的scipy版本(因为CI环境的变化需要其他审核人看),merge之后再更新这个API的PR(你可以先同步保留这个PR中scipy版本的修改,便于 @cxxly 继续审核,不然你这个PR的CI都过不去)

但是 PR-CI-Coverage 这个环境比较慢,其中的静态图超时了,是不是可以修改一下 timeout

@cxxly 看下是否可以修改 timeout,还是单测写法上可以改进下。

可以的,需要定位并说明原因

不用改了,我这里修改静态图的测试流程,不会 timeout 了。谢谢!:)

@luotao1
Copy link
Contributor

luotao1 commented Apr 10, 2023

@megemini 如果上一轮comment意见都修改完了,请评论告诉我们可以启动下一轮review

@megemini
Copy link
Contributor Author

@megemini 如果上一轮comment意见都修改完了,请评论告诉我们可以启动下一轮review

可以了,请评审!

谢谢!

Copy link
Contributor

@cxxly cxxly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@jeff41404 jeff41404 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@luotao1
Copy link
Contributor

luotao1 commented Apr 11, 2023

@megemini 中文文档链接请附一下

Copy link
Contributor

@sunzhongkai588 sunzhongkai588 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@megemini
Copy link
Contributor Author

@megemini 中文文档链接请附一下

PR链接:#5794

请评审,谢谢!:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API contributor External developers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants