在 GPU 上训练时如何处理不确定性?
Posted
技术标签:
【中文标题】在 GPU 上训练时如何处理不确定性?【英文标题】:How to handle non-determinism when training on a GPU? 【发布时间】:2018-06-07 15:05:32 【问题描述】:在调整超参数以使我的模型表现更好时,我注意到每次运行代码时我得到的分数(以及由此创建的模型)都是不同的,尽管修复了随机操作的所有种子。如果我在 CPU 上运行,则不会发生此问题。
我搜索了一下,发现这是使用 GPU 进行训练时的常见问题。 Here is a very good/detailed example with short code snippets to verify the existence of that problem.
他们将不确定性指向“tf.reduce_sum”函数。但是,对我来说并非如此。这可能是因为我使用了不同的硬件(1080 TI)或不同版本的 CUDA 库或 Tensorflow。似乎 CUDA 库中有许多不同的部分是不确定的,要确切地确定哪一部分以及如何摆脱它似乎并不容易。此外,这一定是设计使然,因此很可能有足够的效率提高来换取不确定性。
所以,我的问题是:
由于 GPU 在训练神经网络方面很受欢迎,因此该领域的人们必须有办法处理不确定性,因为我看不出还有什么方法可以可靠地调整超参数。使用 GPU 时处理不确定性的标准方法是什么?
【问题讨论】:
@jdehesa 这是我问题中的链接 对不起,我想我把标签弄混了,我保存了相同的链接...... 我遇到了同样的问题,我已经找到了解决方案。这是问题的链接:***.com/questions/51542111/… 【参考方案1】:TL;DR
先验的非确定性确定性操作来自并发(多线程)实现。 尽管在这方面不断取得进展,TensorFlow 目前并不能保证其所有操作的确定性。在网上快速搜索了一下,情况似乎和其他主要的工具包差不多。 在训练期间,除非您正在调试问题,否则运行之间有波动是可以的。不确定性是训练的本质,明智的做法是衡量它并在比较结果时将其考虑在内 - 即使工具包最终在训练中达到完美的确定性。那个,但要长得多
当您将神经网络运算视为数学运算时,您会期望一切都是确定性的。卷积、激活、交叉熵——这里的一切都是数学方程,应该是确定性的。甚至是shuffle、dropout、noise等伪随机操作,也完全由一个seed决定。
另一方面,当您从它们的计算实现中看到这些操作时,您会将它们视为大规模并行计算,除非您非常小心,否则它们可能是随机性的来源。
问题的核心在于,当您在多个并行线程上运行操作时,您通常不知道哪个线程将首先结束。线程对自己的数据进行操作时并不重要,例如,将激活函数应用于张量应该是确定性的。但是当这些线程需要同步时,例如当你计算一个和时,那么结果可能取决于求和的顺序,进而取决于线程首先结束的顺序。
从那里,您大致有两种选择:
保持与更简单的实现相关的不确定性。
在设计并行算法时要格外小心,以减少或消除计算中的不确定性。添加的约束通常会导致算法变慢
哪条路线采用 CuDNN?好吧,主要是确定性的。在最近的版本中,确定性操作是常态而不是例外。但它过去提供了许多非确定性操作,更重要的是,它过去不提供一些操作,例如归约,人们需要在 CUDA 中实现自己,并对确定性有不同程度的考虑。
诸如 theano 之类的一些库更领先于这个主题,通过提前暴露用户可以打开或关闭的 deterministic
标志 - 但从它的描述中可以看出,它远未提供任何保证。
如果
more
,有时我们会选择一些更具确定性但速度较慢的实现。特别是在 GPU 上,我们将避免使用 AtomicAdd。有时我们仍然会使用非确定性实现,例如当我们没有确定性的 GPU 实现时。此外,请参阅 dnn.conv.algo* 标志以涵盖更多情况。
在 TensorFlow 中,确定性需求的实现已经相当晚了,但它正在慢慢实现——这也得益于 CuDNN 在这方面的进步。长期以来,减少一直是非确定性的,但现在它们似乎是确定性的。 CuDNN 在 6.0 版中引入了确定性缩减这一事实当然可能有所帮助。
目前看来,the main obstacle for TensorFlow towards determinism is the backward pass of the convolution。这确实是 CuDNN 提出了一种非确定性算法的少数操作之一,标记为CUDNN_CONVOLUTION_BWD_FILTER_ALGO_0
。这个算法还在 TensorFlow 中的the list of possible choices for the backward filter 中。而且由于the choice of the filter seems to be based on performance,如果效率更高,确实可以选择。 (我对 TensorFlow 的 C++ 代码不太熟悉,所以对此持保留态度。)
这很重要吗?
如果您正在调试问题,确定性不仅重要:它是强制性的。您需要重现导致问题的步骤。目前,对于 TensorFlow 等工具包来说,这是一个真正的问题。为了缓解这个问题,你唯一的选择是实时调试,在正确的位置添加检查和断点——这不是很好。
部署是事物的另一个方面,通常需要确定性行为,部分是为了人类接受。虽然没有人会合理地期望医学诊断算法永远不会失败,但如果计算机可以根据运行情况为同一患者提供不同的诊断,那就太尴尬了。 (尽管医生自己也不能幸免于这种变异性。)
这些原因是修复神经网络中的非确定性的正当动机。
对于所有其他方面,我想说我们需要接受(如果不接受)神经网络训练的不确定性。出于所有目的,训练是随机的。我们使用随机梯度下降、混洗数据、使用随机初始化和 dropout——更重要的是,训练数据本身就是数据的随机样本。从这个角度来看,计算机只能生成带有种子的伪随机数这一事实是一种人工制品。当你训练时,你的损失是一个值,由于这种随机性,它也带有置信区间。在忽略这些置信区间的情况下比较这些值以优化超参数并没有多大意义——因此,在我看来,在这种情况以及许多其他情况下花费太多精力来修复非确定性是徒劳的。
【讨论】:
感谢您的详细回答,但我不同意这个结论。由于随机性而导致的变化,例如洗牌是可以理解的。由于比赛条件(这是您所描述的)而导致的变化没有意义,特别是考虑到它在分数上的巨大差异。这也不是浮点波动,而是竞争条件。当我达到最终测试分数时,这些波动会导致模型/分数发生显着变化,准确度约为 +/- 2.5%。在处理 90%-99% 范围内的准确度时,这太大了。我不知道如何以这种方式调整超参数。 比赛条件确实会导致细微的变化。当然,它们很小——但你有很多它们,并且随着培训进度的优化课程出现分歧。如果您迭代足够长的时间,您的损失确实有可能与您从不同种子开始时具有相同的变化。 是的,这就是我的问题。 我已经解决了这个问题。请关注***.com/questions/51542111/… 不能完全复制作品是不行的。我们正处于再现危机中!当模型产生不同的结果时,太多人指责潜在的“不确定性”和非确定性。是的,调试是确定性的目的之一。此外,教师需要能够验证学生的 Kaggle 排行榜分数是否可准确重现。研究人员依靠可重复性作为出发点来推进想法(或验证他人的主张)。统计模型中的按需确定性还有许多其他好的和可靠的理由。【参考方案2】:要让 MNIST 网络 (https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py) 在我的 GPU (1050Ti) 上进行确定性训练:
设置 PYTHONHASHSEED='SOMESEED'。我在启动 python 内核之前这样做。 为随机生成器设置种子(不确定 MNIST 是否需要所有种子) python_random.seed(42)
np.random.seed(42)
tf.set_random_seed(42)
使 TF 选择确定性 GPU 算法
要么:
import tensorflow as tf
from tfdeterminism import patch
patch()
或者:
os.environ['TF_CUDNN_DETERMINISTIC']='1'
import tensorflow as tf
请注意,使用任何一种从 TF 中选择确定性算法的方法都可以重复产生的损失,但这两种方法会导致不同的损失。此外,上面的解决方案不会使我使用的更复杂的模型可重复。
查看https://github.com/NVIDIA/framework-determinism 以获取最新答案。
附注:
对于 cuda cuDNN 8.0.1,存在以下非确定性算法:
(来自https://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html)
cudnnConvolutionBackwardFilter 当 CUDNN_CONVOLUTION_BWD_FILTER_ALGO_0 或 使用 CUDNN_CONVOLUTION_BWD_FILTER_ALGO_3 cudnnConvolutionBackwardData 当 CUDNN_CONVOLUTION_BWD_DATA_ALGO_0 被使用 使用 CUDNN_POOLING_MAX 时,cudnnPoolingBackward cudnnSpatialTfSamplerBackward cudnnCTCLoss 和 cudnnCTCLoss_v8 时 使用了 CUDNN_CTC_LOSS_ALGO_NON_DETERMINSTIC【讨论】:
以上是关于在 GPU 上训练时如何处理不确定性?的主要内容,如果未能解决你的问题,请参考以下文章
用户关闭应用程序时如何处理用户在 Xamarin iOS 上单击本地通知?