Pytorch: RFC:添加 torch.deterministic 标志以强制确定性算法

创建于 2018-12-18  ·  67评论  ·  资料来源: pytorch/pytorch

🚀 功能

我们应该添加一个全局变量来强制 PyTorch 使用按位确定性算法。 Soumith 建议将标志添加到torch.experimental子包中,因为我们不确定某些细节。

动机

运行之间的按位确定性有时对调试很有用。 但是,很难为某些操作编写有效的确定性算法。

沥青

torch.experimental.deterministicFalse (默认值)时,PyTorch 应该使用可用于给定操作的最快算法。 当torch.experimental.deterministicTrue ,PyTorch 应该只使用确定性算法。 如果我们没有可用于给定操作的确定性算法并且torch.experimental.deterministicTrue PyTorch 应该发出警告。

神经网络

我们已经有一个torch.backends.cudnn.deterministic标志来控制 cuDNN 算法的选择。 我们现在应该保留这个标志,如果torch.backends.cudnn.deterministictorch.experimental.deterministic为真,则将 cuDNN 限制为确定性算法。

非目标

我们只针对在具有相同架构和配置的机器上运行的按位确定性。 例如,即使torch.experimental.deterministic为 True,我们也不会在以下任何情况发生变化时实现按位确定:

  • PyTorch 版本
  • CPU 架构(例如 x86 with AVX vs. ARM)
  • GPU 架构(例如 AMD 与 NVIDIA 或 P100 与 V100)
  • 库依赖项(例如 OpenBLAS 与 MKL)
  • OpenMP 线程数

实施建议

我建议分两步添加此功能。 第一步是添加torch.backends.cudnn.deterministic标志并向任何非确定性操作添加警告。 第二步是为非确定性操作添加确定性实现。

PyTorch 文档中有部分非确定性操作列表。

开放问题

torch.experimental.deterministic应该如何与 RNG 种子交互? 如果没有设置手动种子,是否应该设置默认种子? 如果没有设置手动种子,它是否应该发出警告?

抄送@ezyang @gchanan @zou3519

feature high priority determinism internals triaged

最有用的评论

嗨,我想谈谈torch.deterministic未来计划。 我们需要回答几个高级问题:

  1. torch.deterministic的语义是什么? 用户期望什么? 尽力而为真的对用户有用吗? 如果它没有用,那么根据它控制什么操作来定义torch.deterministic更好吗?
  2. 现在我们有了torch.deterministic标志,从面向公众的 API 中完全消除deterministic=关键字参数是否有意义( bmm ,我在看着你)。
  3. 这项工作的最终结果是什么? 与通用社区相比,您(@kurtamohler)将在其中进行多少工作,当我们在这里结束您的工作时,合理的状态是什么样的?

从 (1) 开始,torch.deterministic 的当前文档说:

     r"""Sets a global flag to force all operations to use a deterministic
    implementation if available. If an operation that does not have a
    deterministic implementation is called while this setting is True, the
    operation will throw a RuntimeError.

    Note that deterministic operations tend to have worse performance than
    non-deterministic operations.

虽然这对于最终的最终状态可能是正确的,但这不准确地代表了当前的情况,其中许多操作尚未经过审计,对于任何给定的模型,我们不知道torch.deterministic是否真的会做它在罐头上说并使您的模型具有确定性/在您点击 nondet 时引发错误。 因此,基本上,我们的实现在这些语义方面存在缺陷,并且在可预见的未来仍将存在缺陷。 这不是一个很好的状态。

我们可以更改 torch.deterministic 的文档来改善这一点。 一些可能的变化:

  • torch.deterministic 是最好的努力,但如果您发现它没有捕获一些非确定性,请报告错误
  • torch.deterministic 切换这些操作符的行为(然后给出它切换的操作符的详尽列表)

第二个要点引向 (2):如果 torch.deterministic 现在作为切换确定性的一种方式存在,那么直接在用户 API 中支持确定性就没有那么重要了。 所以我们可能不应该向 bmm 添加deterministic参数。 如果您想直接切换某些内容,我们可能会考虑公开一个内部函数,但deterministic不应直接在函数本身上可用。

你怎么认为? 我认为更改文档可能是走上可持续发展道路的最简单方法。 还有一些其他细节,例如如何填充详尽列表,但这些语义可能比实际上不会成为真的“理想”语义更有意义。

抄送@gchanan @mruberry

所有67条评论

这是我的一个赞。 问题主要是如何在代码库中的任何地方实际推出它; 没有什么更糟糕的是声称我们是确定性的,但秘密地它不是:)

我完全赞成,我的方法是在确定性打开时标记操作和错误,而我们知道它们不是。

我认为在非确定性操作上出错太苛刻了。 警告似乎是一种更流畅的体验

我认为默认应该是抛出,但我想我们可以在那里支持多值属性(非确定性可以,警告,抛出)。

我必须承认我并没有真正看到警告的用例。 当人们足够关心确定性以将其打开时,他们可能会预料到错误。 对于某些调用,您始终可以将其关闭,以表示您可以接受其中存在的任何不确定性。

错误、警告、正确的文档...
后者是必须的。
警告还是错误? 我会出错。

投掷似乎很棒。 我同意 Adam 的观点,即提供警告而不是抛出的选项似乎是合理的。

感谢您的参与。最后,三元标志的主要努力是标志本身,这并不难。
我将在 Context.h 中添加一个标志,并(通过实用程序函数)撒上 AT_ERROR 和 AT_CHECK。

你好,
这个标志有消息吗?
决定论至关重要。
根据我的经验,当前版本允许使用固定种子在一个 gpu 上进行确定,精度可达1e-16 。 请注意,无穷小的差异可能会被放大并使结果发散。

请考虑 multigpu 的情况(至少对于固定的K gpus,行为需要是确定性的。我能够实现某种有时会因我的原因而崩溃的确定性暂时不明白(使用每晚构建1.2.0.dev20190616 )。)。 我现在正在为此苦苦挣扎( 1 , 2 )。

谢谢!

@t-vi 你在积极地做这件事吗?

我不想阻止你这样做。

@t-vi 对不起,如果我不清楚,我不打算在这方面工作:)。 只是想了解是否有人在积极地这样做。

时隔将近一年,非确定性插值的问题依然没有解决。

希望社区添加此功能:)

也许确定性插值会给用户带来很大的帮助。

~我还没有真正宣传它,但考虑到用户兴趣似乎比分配的开发人员资源更多,我把它列为一个项目,当我设置它时,你可以在我的github 赞助页面上投票。
我很确定我们可以在年底前取得良好的进展,插值肯定是我计划如何解决的事情之一(类似于我在问题中某处的 fold 伪代码)但只是不在我自己的优先事项清单上。~
结果发现并不有趣。

确定性插值将是一个巨大的帮助。 关联

碰撞优先级,尤其是 CUDA,基于用户反馈

我很高兴它被修复了,谢谢!

@t-vi 公平地说,我不认为“提高优先级”等同于“正在修复”:)。

期待解决方案!

colesbury 提到,确定性算法的一个致命原因并不是因为确定性实际上是问题所在,而是当你打开它时你可以排除它;)

torch.experimental.deterministic应该如何与 RNG 种子交互? 如果没有设置手动种子,是否应该设置默认种子? 如果没有设置手动种子,它是否应该发出警告?

如果用户没有设置种子,我建议不要设置种子。 一方面是因为它耦合了两个不需要的接口(我认为关心确定性的用户会很好地理解 RNG)。 更重要的是,这很难可靠地做到; 可以在多进程/线程应用程序中使用 RNG,还有其他torch.Generator子类,也可以使用numpy.random等。

不确定警告,仅当有一个理智的地方可以设置它时(例如,您是否被迫在determinism=True而不是在使用 RNG 的同一模块/函数中播种?)。

我只是好奇,当我设置torch.backends.cudnn.deterministic=True ,插值运算符仍然无法确定。 pytorch 插值不使用 cudnn 吗?

可能不会。 您可以 nvprof 您的插值运行进行检查。

我想知道一旦实现了torch.experimental.deterministic我们是否应该继续在函数调用中提供deterministic参数。 也许我们应该,因为用户可能更喜欢某些操作的确定性和其他操作的速度。

如果我们保留参数,那么如果torch.experimental.deterministic和函数的deterministic标志相互对立会发生什么。 torch.experimental.deterministic = True表示“无论如何在所有情况下都使用确定性”,还是应该表示“使用确定性作为默认值,但如果在函数调用中指定了deterministic参数,则使用该特定函数调用的设置。” 换句话说,下面的代码应该如何处理? 有人知道torch.backends.cudnn.deterministic标志在类似情况下的作用吗?

torch.experimental.deterministic = True
torch.some_operation(deterministic=False)

@kurtamohler好问题。 我认为最简单的解决方法是将其设为bool? deterministic=None ,然后将None解释torch.experimental.deterministic ”,否则完全使用用户要求的内容。

我们种有卷积类似的情况,但它做的方式有,有一个convolution没有benchmark参数,然后_convolution有一个明确的基准。

我认为这些解决方案中的任何一个都是可以接受的; 然而,卷积方法还有一个额外的好处,即不会将内部deterministic标志泄露给用户可见的 API(除非他们使用内部 API)。

“我想在任何地方都是确定性的,但_不是在这个特定的运营商_”的基本原理是什么? 这真的应该是一个足够常见的用例来保证向我们的许多运算符(以及大多数复杂的运算符)添加额外的输入吗? IMO 最好提供上下文管理器来切换确定性。

@apaszke ,是的,我认为您是对的,最好仅使用上下文管理器来切换确定性。 我不会说我们应该将deterministic参数添加到任何运算符,但有些运算符已经有了它。 最好删除所有这些并破坏 BC,还是最好保留它们并允许它们覆盖torch.experimental.deterministic

我会说我们应该删除它或至少将其设为私有(即下划线前缀或某事)。

我想知道内插函数的确定性功能是否已关闭并且不会实现?

不,我们可以接受 PyTorch 中所有函数的确定性版本

@ezyang哪个 pytorch 版本具有确定性 F.interpolate 功能? 它是从 pytorch 1.6 开始的吗? 还是在最新的稳定版本 (1.5) 中可用? 还是我必须从源代码下载并安装 Pytorch?

我很乐意开始研究这个

上面的commit只加了flag,还不影响任何操作。 如果有人能花几分钟看看它,让我知道我是否做错了什么,或者到目前为止是否有任何可以改进的地方,我将不胜感激。 我基于torch.backends.cudnn.deterministic的实现方式。

这看起来不错,但我觉得内部命名不应该包括实验性的(因为,表面上看,有一天你想让它不是实验性的,这不应该涉及必须重命名所有实现位!)

@ezyang ,是的,这是有道理的,我会重命名。

我添加了一个torch.experimental.deterministic_error_level ,类似于@t-vi 在他之前关于这个问题的工作中所做的。 如果deterministic == True和给定函数没有确定性实现,则deterministic_error_level控制错误/警告行为。 它可以设置为 2(错误)、1(警告)或 0(静默)。

如果用户将其设置为任何其他值,我想抛出一个可捕获的 python 运行时异常。 通常,我会使用TORCH_CHECK()来处理这种行为,但在这种情况下,异常无法捕获,我不确定为什么。 这是TORCH_CHECK()调用:链接

当检查失败时会发生以下情况:

>>> import torch
>>> try:
...     torch.experimental.deterministic_error_level=50
... except:
...     print('exception caught')
... 
terminate called after throwing an instance of 'c10::Error'
  what():  error level 50 is invalid, must be one of 0: None, 1: Warn, or 2: Error
Exception raised from longToErrorLevel at ../aten/src/ATen/Context.cpp:85 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x58 (0x7f53e2cc0878 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libc10.so)
frame #1: at::Context::longToErrorLevel(long) + 0x122 (0x7f53f6d61a82 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libtorch_cpu.so)
frame #2: THPModule_setDeterministicErrorLevel(_object*, _object*) + 0x31 (0x7f53fb5625d1 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libtorch_python.so)
<omitting python frames>
frame #23: __libc_start_main + 0xe7 (0x7f5432d62b97 in /lib/x86_64-linux-gnu/libc.so.6)

Aborted (core dumped)

如果有人知道我如何解决这个问题,请告诉我。

@kurtamohler THPModule_setDeterministicErrorLevel缺少 HANDLE_TH_ERRORS / END_ HANDLE_TH_ERRORS 宏? 他们需要捕获 C++ 异常并将其转换为 Python 错误返回。

啊,就是这样,谢谢@colesbury!

我开始向atomicAdd所有调用者添加非确定性警报。 我注意到有些来电者仅在某些情况下使用atomicAdd 。 例如, adaptive_avg_pool3d_backward仅在(isizeW%osizeW != 0) || (isizeH%osizeH != 0) || (isizeT%osizeT != 0)为真时使用。 我应该只在这些情况下发出警报并尝试在错误消息中传达它们,还是在调用这些函数时发出警报是否可以atomicAdd最终被使用?

如果您无条件地提醒,它可能更容易实施和更容易理解。

@ngimel ,我一直在考虑如何使用CUBLAS_WORKSPACE_CONFIG来确保确定性的流使用,我认为应该考虑两种主要方法。

如果有人正在使用受影响的 CUDA 版本之一(目前为 10.2 或更高版本),并且调用了torch.set_deterministic(True) ,请使用std::getenv确保CUBLAS_WORKSPACE_CONFIG:16:8:4096:8 。 如果没有,请执行 (1) 或 (2):

  1. 抛出一个错误,告诉用户适当地设置变量。

  2. 使用putenv自动设置变量(Windows 上_putenv )。 但是,还有一些与此相关的进一步设计决策。 我们应该选择:16:8 (性能较低,但内存使用较少)还是:4096:8 (性能较高,但内存使用较多)? 此外,如果用户将变量设置为其他一些非确定性值,我们将不得不跟踪原始值并在torch.set_deterministic(False)被调用时恢复它,或者我们可以抛出一个错误告诉用户他们需要取消设置变量或其他一些方案。

此外,我不知道在应用程序已经运行时设置变量是否会产生任何影响,所以我不确定选项 (2) 是否可行。 该变量可能只在 CUDA 运行时启动或 cuBLAS 句柄创建时检查一次。 我找不到关于这方面的信息,所以我可能不得不通过实验来找出(我将不得不使用非确定性流使用再现器来编写测试,所以我会研究这个) . 我还寻找 API 调用,而不是使用环境变量,但 CUDA 似乎没有提供。

您对哪种选择更好有强烈的看法吗? 选项 (2) 可能对用户更友好,但可能不如选项 (1) 透明。

我不知道在应用程序已经运行时设置变量是否真的会产生任何影响

为了跟进这个问题,在 pytorch 脚本中设置环境变量似乎不会影响 CUDA 流的确定性。 我修改了https://github.com/pytorch/pytorch/issues/39849 中的脚本以运行多次并比较训练统计数据以检查非确定性行为。 它尝试设置CUBLAS_WORKSPACE_CONFIG=:4096:8以确保确定性流使用: https :

运行它表明我们没有通过在脚本中设置变量来获得确定性行为:

$ python cuda_stream_nondeterminism.py 
Before setting var: not deterministic
After setting var: not deterministic
After restoring old var: not deterministic

但是使用在脚本之外设置的环境变量运行它确实使它具有确定性:

$ CUBLAS_WORKSPACE_CONFIG=:4096:8 python cuda_stream_nondeterminism.py 
Before setting var: possibly deterministic
After setting var: possibly deterministic
After restoring old var: possibly deterministic

请注意,它打印“可能确定性”,因为我只运行了 5 次训练函数,即使行为不是真正确定性的,也有可能走运。

也许如果我可以重新初始化 cuda 流,这将迫使它遵守更改后的CUBLAS_WORKSPACE_CONFIG变量。 我想尝试一下,但我不知道如何或是否有可能在运行时做到这一点。 如果有人知道,请告诉我。

我发现我可以通过以下方式创建和使用新流:

with  torch.cuda.stream(torch.cuda.Stream()):

但是新流不支持更改的环境变量设置。 我还发现了torch.cuda.init() ,但不幸的是,如果 cuda 已经初始化,那将是一个空操作。

所以除非我们能想到其他尝试,看起来我们可能无法自动更改工作区配置,所以我们可能只需要抛出一个错误告诉用户设置它。

是的,在 cuda 上下文初始化后设置环境变量没有效果,所以不幸的是,它是全有或全无的解决方案。 抛出一个错误告诉用户设置它听起来很合理。

目前,似乎不可能从非 nvcc 编译文件中检查 CUDA 版本,所以我相信我必须将其添加到aten/src/ATen/cuda/detail/CUDAHooks.h (检查 cuDNN 版本是该接口的一部分) . 如果有人知道更好的,请告诉我。

上面的提交添加了错误。 但我现在需要弄清楚如何处理单元测试。 有两个问题:

  • 为了测试在正确情况下抛出的错误(cuda >= 10.2 并且CUBLAS_WORKSPACE_CONFIG设置不正确),测试基础设施必须能够在运行测试之前自动更改环境变量
  • 为了确保现有的torch.set_deterministic测试不会中断,我们需要自动正确设置CUBLAS_WORKSPACE_CONFIG 。 我们可以在所有使用 cuda >= 10.2 的 CI 作业中默认设置这个变量。

我发现我可以从 python 脚本设置环境变量,然后重新加载火炬模块以使其符合新值:

>>> import torch
>>> torch.set_deterministic(True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/work/kurtamohler/development/pytorch-deterministic-flag-cuda-env-var/torch/__init__.py", line 306, in set_deterministic
    _C._set_deterministic(d)
RuntimeError: To enable deterministic behavior with CUDA >= 10.2, you must set environment variable CUBLAS_WORKSPACE_CONFIG=:4096:8 or CUBLAS_WORKSPACE_CONFIG=:16:8. For more information, go to https://docs.nvidia.com/cuda/cublas/index.html#cublasApi_reproducibility
>>> import os
>>> os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
>>> from importlib import reload
>>> torch = reload(torch)
>>> torch.set_deterministic(True)

我不知道重新加载火炬是否也会导致 CUDA 遵守此更改,但至少这为我们提供了一种对错误消息进行单元测试的方法。 虽然我不得不问,在单元测试中重新加载火炬模块会不会有任何问题?

编辑:原来我不需要重新加载火炬来让它看到改变的环境变量。 此外,更改变量后重新加载不会影响 CUDA 运行时。

上述提交解决了我在之前的评论中提到的所有问题。 我添加了一个装饰器来包装任何调用torch.set_deterministic() API 测试,仅在需要时临时设置CUBLAS_WORKSPACE_CONFIG=:4096:8 。 它还将确定性标志和 CUBLAS_WORKSPACE_CONFIG 设置恢复到运行测试之前的状态。

我意识到可重复性文档提到确定性 CuDNN 行为需要:

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

这个线程上有人知道benchmark到底是什么,为什么torch.backends.cudnn.deterministic = True本身是不够的?

我们可能需要强制benchmark到如果被关闭torch.is_deterministic() == True 。 换句话说,不是将ctx.benchmarkCuDNN()直接传递给at::_convolution() ,也许它应该是ctx.benchmarkCuDNN() && !ctx.deterministic()在这一行: https :

如果我们不进行此更改,似乎使用set_deterministic和 CuDNN 的人将不得不这样做:

torch.set_deterministic(True)
torch.backends.cudnn.benchmark = False

这意味着仅set_deterministic()并不能涵盖所有内容,这在我看来令人困惑。

抄送@ezyang @colesbury @t-vi @ngimel

当遇到新的卷积配置时, benchmark=True运行所有可用的 cudnn 实现并选择一个最快的实现,缓存它选择的实现,因此所有后续调用具有相同参数的卷积都将使用它。 因此,如果deterministic也设置为True ,只要此缓存持续存在,即只要您处于同一进程中,结果将是确定性的。 如果有运行时间接近的实现,下次您启动流程并再次运行基准测试时,另一个实现可能会获胜,并且结果(尽管在上述意义上仍然具有确定性)将与之前的运行不同。 因此,为了保证运行之间的确定性,您必须关闭基准测试。

我知道了。 因此,对于某些应用程序而言,也许只有进程内确定性而不是跨进程确定性才是重要的,因此人们会发现如果他们设置了torch.set_deterministic(True)仍然能够使用基准测试很有用。 在那种情况下,我不应该改变当前的行为。 只要我更新文档以说明这一点,我就看不出它有什么问题。

我制作了一个 wiki 页面来帮助 PyTorch 贡献者添加对torch.set_deterministic()https :

欢迎任何改进。

另外,我不确定“当前不支持的功能”部分是否应该在这个 wiki 中,或者作为一个新的 github 问题是否会更好(wiki 页面可以链接到它)。 有人有偏好吗?

嗨,我想谈谈torch.deterministic未来计划。 我们需要回答几个高级问题:

  1. torch.deterministic的语义是什么? 用户期望什么? 尽力而为真的对用户有用吗? 如果它没有用,那么根据它控制什么操作来定义torch.deterministic更好吗?
  2. 现在我们有了torch.deterministic标志,从面向公众的 API 中完全消除deterministic=关键字参数是否有意义( bmm ,我在看着你)。
  3. 这项工作的最终结果是什么? 与通用社区相比,您(@kurtamohler)将在其中进行多少工作,当我们在这里结束您的工作时,合理的状态是什么样的?

从 (1) 开始,torch.deterministic 的当前文档说:

     r"""Sets a global flag to force all operations to use a deterministic
    implementation if available. If an operation that does not have a
    deterministic implementation is called while this setting is True, the
    operation will throw a RuntimeError.

    Note that deterministic operations tend to have worse performance than
    non-deterministic operations.

虽然这对于最终的最终状态可能是正确的,但这不准确地代表了当前的情况,其中许多操作尚未经过审计,对于任何给定的模型,我们不知道torch.deterministic是否真的会做它在罐头上说并使您的模型具有确定性/在您点击 nondet 时引发错误。 因此,基本上,我们的实现在这些语义方面存在缺陷,并且在可预见的未来仍将存在缺陷。 这不是一个很好的状态。

我们可以更改 torch.deterministic 的文档来改善这一点。 一些可能的变化:

  • torch.deterministic 是最好的努力,但如果您发现它没有捕获一些非确定性,请报告错误
  • torch.deterministic 切换这些操作符的行为(然后给出它切换的操作符的详尽列表)

第二个要点引向 (2):如果 torch.deterministic 现在作为切换确定性的一种方式存在,那么直接在用户 API 中支持确定性就没有那么重要了。 所以我们可能不应该向 bmm 添加deterministic参数。 如果您想直接切换某些内容,我们可能会考虑公开一个内部函数,但deterministic不应直接在函数本身上可用。

你怎么认为? 我认为更改文档可能是走上可持续发展道路的最简单方法。 还有一些其他细节,例如如何填充详尽列表,但这些语义可能比实际上不会成为真的“理想”语义更有意义。

抄送@gchanan @mruberry

@zou3519https://github.com/pytorch/pytorch/pull/38683#issuecomment -662590937 也与 Q 相交

我很高兴你提出了这些问题@ezyang,@ zou3519和@mruberry。 我同意我写的文档是对当前状态的错误表示。

我喜欢详尽列出torch.set_deterministic()影响的所有函数的想法,这样我们就不会对用户撒谎。 感谢您将其添加到 1.6.0,@zou3519。

我同意我们不应该提供deterministic设置作为直接函数参数。

至于最终的游戏,我很高兴在必要时继续努力,但它的设置应该让任何人都可以快速学习如何提供帮助。

从长远来看,我认为提供受影响函数的详尽列表是一个有效的决定,但我不认为仅凭这一策略就可以最大限度地提高确定性标志的实用性。 我们可以像这样对函数进行分类(在一个特定的环境中):

  1. 确定性的
  2. 默认情况下是非确定性的,但支持确定性标志(错误或替代实现)
  3. 非确定性且不支持确定性标志

当然,理想的情况是完全消除类别 3,然后类别 2 函数的列表就足够了。 但是,第 3 类函数仍将存在很长一段时间(或者可能永远存在,如果不是所有贡献者都知道确定性问题,或者提交意外删除了函数的确定性等)。 因此,即使我们有所有类别 2 函数的详尽列表,用户也没有简单的方法来知道未出现在列表中的函数是否是确定性的(可能是类别 1 或类别 3)。 例如, torch.add没有出现在列表中,那么用户如何知道它是确定性的呢?

也许我们也可以考虑维护一个第 3 类函数的列表。 但是由于许多原因,手动维护这些列表会非常困难,所以我想知道我们是否可以将其自动化。 我们可能会设置一个 CI 作业,对所有函数运行确定性测试。 不可能 100% 归纳证明函数是确定性的,如果我们不走运,不确定性函数有时可能会多次给出相同的结果。 但是,我们运行这些测试的次数越多,我们就越能确定每个函数属于哪个类别。

还有一个问题是如何最有效地向用户传达我们知道和不知道的关于每个功能和每个平台的一切。 也许我们可以为每个平台上的所有类别 2 和类别 3 功能制作一个表格。 如果确定性测试可以自动验证此表是否正确,那就太好了。

只是头脑风暴,也许这些想法比它们的价值更难。 一个更务实的计划可能更可持续,即使不太理想。

torch.add确定性的吗?

import torch
n = 512
device = 'cuda'
a = torch.arange(n**3, device=device, dtype=torch.float32)
a = a.reshape((n, n, n))
b = torch.arange(n**3, device=device, dtype=torch.float32)
b = b.reshape((n, n, n))
out_zero = torch.zeros((n, n, n), device=device)
out_zero = out_zero.set_(out_zero.storage(), storage_offset=0, size=a.size(), stride=(1,1,1))
out_one = torch.zeros((n, n, n), device=device)
out_one = out_one.set_(out_one.storage(), storage_offset=0, size=a.size(), stride=(1,1,1))

torch.add(a, b, out=out_zero)
torch.add(a, b, out=out_one)
(out_zero == out_one).all()
: tensor(False, device='cuda:0')

我们可能应该记录重叠张量违反了我们想要的任何确定性契约。

列出受“确定性”标志影响的操作听起来不错。 不过,稍微退一步,我们似乎真的在谈论两件事:

  • 请求操作的确定性版本,如果可用( use_deterministic ?)
  • 操作不确定时发出警告

第一件事的标志似乎很简单。 然而,第二个有点棘手。 我担心很难判断像 oneDNN、cuDNN 和 MAGMA 这样的数学库的操作是否是确定性的,尤其是跨版本和硬件。 @kurtamohler,你知道如何最好地解决这个问题吗? 也许我们可以对所有本地非确定性操作发出警告,也可以在进行数学库调用时发出警告? 每个进程警告一次不应该那么麻烦。

这种警告方法需要在上线之前审查大量算法和调用站点,但它不需要阻止标志来选择确定性算法(如果它们可用)。

(正在讨论的第三件事是呈现确定性算法选择的最佳方式(通过全局标志或作为函数的 kwargs),但我认为我们可以推迟讨论,直到我们确定标志的计划?)

我认为我们不应该让完美成为这里美好的敌人。 我不知道什么时候在 PyTorch 中使用自重叠张量是 100% 安全的,我的印象是普通人不会使用它们。

我在论坛上的印象是,大多数人都惊讶于他们运行了两次并从中获得了不同的梯度,这通常是因为 PyTorch 的其中一个本地函数使用 atomicAdd。
如果我们收到警告,我们已经涵盖了人们想知道的大多数情况。 感觉有一半的东西实际上是向后升级的。

我认为我们应该明确指出,就外部库而言,这是尽最大努力,并且我们会在了解问题时添加警告,但我的印象是我们的本地内核实际上才是最重要的。

我不知道什么时候在 PyTorch 中使用自重叠张量是 100% 安全的,我的印象是普通人不会使用它们。

是的,任何有可能被合理归类为错误的程序。 我的意思是我们应该小心地记录我们为这些标志提出的任何合同。

我认为我们应该明确指出,就外部库而言,这是尽最大努力,并且我们在了解问题时添加警告......

该文档可能会说类似“已知不确定的数学库调用......”?

我同意@t-vi(我真的很喜欢所报告的非确定性的一半正在向后放大的观察结果)。 特别是,我认为我们有部分记录的已知为不确定性的函数(甚至部分记录了一些确定性的函数)的状态比我们根本不给出任何指示的状态要好得多——关键是是不声称支持我们不支持的事情! 我同意考虑如何进行确定性测试是一项有用的活动,但我认为这是标记明显不确定的 API 的正交活动。

由于很多想法已经浮出水面,让我谈谈我对其中一些想法的具体想法:

  1. “也许我们也可以考虑维护一个第 3 类功能的列表。” 这似乎是很多工作。 我认为这可能仅对于我们明确为确定性做出一些调整的函数是值得的(最有可能的是,支持确定性标志的函数)
  2. “我们可能会设置一个 CI 作业,对所有函数运行确定性测试。” 我认为这样的事情必须非常小心地完成,因为就其本质而言,它正在测试不确定性的东西,这意味着确定性测试本身是“不稳定的”(有时会通过而其他人会失败) . 我们的 CI 报告工具不能很好地处理这种情况。
  3. “然而,第二个有点棘手。我担心很难判断像 oneDNN、cuDNN 和 MAGMA 这样的数学库的操作是否是确定性的,尤其是跨版本和硬件。” 我们应该尽力做到这一点。 在许多情况下,数学库在其文档中明确指定它们是否具有确定性,我们应该简单地忠实地报告文档所说的内容
  4. “也许我们可以对所有本机非确定性操作发出警告,也可以在进行数学库调用时发出警告?” 我认为我们不应该这样做。 当我们警告不确定性时,应该是因为不确定性正在发生,而不是它可能正在发生。 如果你过度警告,人们会开始忽视警告。

我不认为我们应该担心跨版本/硬件确定性——祝你好运。

当我们警告不确定性时,应该是因为不确定性正在发生,而不是它可能正在发生。 如果你过度警告,人们会开始忽视警告。

这似乎很棘手。 例如,如果我正在运行一些操作并且 PyTorch 实现是确定性的,但是一些扩展已经覆盖了某些东西(通过调度键、火炬函数或其他),现在我不知道。 如果这实际上是我的不确定性的来源,那似乎是一个不值得警告的无赖。

如果这实际上是我的不确定性的来源,那似乎是一个不值得警告的无赖。

当然,但用户也可以不让我们参与他们不确定的恶作剧,然后当然你不会期望收到警告;)

我相信我们现在可以关闭这个问题,因为标志 API 存在并且有据可查。

@kurtamohler很棒的工作。 谢谢你。

这是否意味着,我们可以使用torch.manual_seed(111)来设置一切确定性,包括interpolation操作?

不。请查看重现性/随机性说明。
到目前为止,我们拥有基础设施,标记了非确定性的已知来源,并大大改进了文档,因此您可以了解正在发生的事情。
如果您遇到非确定性操作,您仍然不走运,但现在处理它更合理。

特别是插值似乎可以通过为后向编写一个并非那么复杂的内核来确定性。

@t-vi 嗨,现在 pytorch 1.7 发布了,是否更新了插值向后内核?

因此,CUDA 上采样内核和向后采样位于aten/src/ATen/native/cuda/UpSample* 。 grep 表明线性、双线性、三次向后具有不确定性(它们有一个警告标记),但最近的则没有。
不过,@kurtamohler会是更好的提问人。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

soumith picture soumith  ·  3评论

ikostrikov picture ikostrikov  ·  3评论

bartvm picture bartvm  ·  3评论

cdluminate picture cdluminate  ·  3评论

SeparateReality picture SeparateReality  ·  3评论