如何在GPU上进行训练时处理非确定性问题?

时间:2018-06-07 15:05:33

标签: python tensorflow machine-learning

在调整超参数以使我的模型更好地执行时,我注意到每次运行代码时我得到的分数(以及因此创建的模型)都不同,尽管为随机操作修复了所有种子。如果我在CPU上运行,则不会发生此问题。

我用Google搜索并发现使用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在培训NN方面很受欢迎,因此该领域的人们必须有办法处理非决定论,因为我无法看到你如何能够可靠地调整超参数。使用GPU时处理非确定性的标准方法是什么?

2 个答案:

答案 0 :(得分:7)

<强> TL; DR

  • 先验确定性操作的非确定性来自concurent(多线程)实现。
  • 尽管不断取得进展,但tensorflow目前还不能保证其所有运营的确定性。在互联网上快速搜索后,情况似乎与其他主要工具包类似。
  • 在培训期间,除非您正在调试问题,否则可以在运行之间产生波动。不确定性属于培训的本质,测量它并在比较结果时将其考虑在内是明智的 - 即使工具包最终在培训中达到完美的决定论

那,但更长

当您将神经网络操作视为数学运算时,您会发现一切都是确定性的。卷积,激活,交叉熵 - 这里的一切都是数学方程式,应该是确定性的。甚至伪随机操作,例如改组,辍学,噪音等,完全由种子决定。

另一方面,当您从计算实现中看到这些操作时,您会将它们视为大规模并行化计算,除非您非常小心,否则这些计算可能是随机性的来源。

问题的核心在于,当您在多个并行线程上运行操作时,通常不知道哪个线程将首先结束。当线程对自己的数据进行操作时并不重要,因此,例如,将激活函数应用于张量应该是确定性的。但是当这些线程需要同步时,例如计算总和时,结果可能取决于求和的顺序,进而取决于首先结束的线程的顺序。

从那里,你可以广泛地说两个选择:

  • 保持与更简单实施相关的非确定性。

  • 在设计并行算法时要格外小心,以减少或消除计算中的非确定性。添加的约束通常会导致算法速度变慢

哪条路由需要CuDNN?好吧,主要是确定性的。在最近的版本中,确定性操作是常态而非例外。但它过去常常提供许多非确定性操作,更重要的是,它曾经不提供某些操作,如减少,人们需要在CUDA中实现自己,并对可靠性有不同程度的考虑。

一些像theano这样的图书馆在这些主题上提前做得更早,通过尽早公开用户可以打开或关闭的deterministic标志 - 但正如你从其描述中看到的那样,它远非提供任何保证。

  

如果more,有时我们会选择一些更确定但更慢的实现。特别是在GPU上,我们将避免使用AtomicAdd。有时我们仍然会使用非确定性的实现,例如当我们没有确定性的GPU实现时。另请参阅dnn.conv.algo *标志以涵盖更多案例。

在张量流中,对确定性的需求的实现已经相当晚了,但它的发展速度正在减缓 - 这也得益于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的非确定性算法的少数操作之一。该算法在张量流中仍然在the list of possible choices for the backward filter。从the choice of the filter seems to be based on performance开始,如果它更有效,它确实可以被选中。 (我对tensorflow的C ++代码不是那么熟悉,所以请稍等一下。)

这很重要吗?

如果您正在调试问题,确定性并不重要:它是强制性的。您需要重现导致问题的步骤。这是目前像tensorflow这样的工具包的真正问题。为了缓解这个问题,您唯一的选择是调试实时,在正确的位置添加检查和断点 - 不是很好。

部署是事物的另一个方面,通常需要具有确定性行为,部分是为了人类接受。虽然没有人会合理地期望医疗诊断算法永远不会失败,但计算机可能会根据运行情况给同一患者不同的诊断,这是很尴尬的。 (虽然医生本身也不能免受这种变化的影响)。

这些原因是修正神经网络中非确定性的正确动机。

对于所有其他方面,我会说,如果不接受,我们需要接受神经网络训练的非确定性。出于所有目的,培训随机的。我们使用随机梯度下降,随机数据,使用随机初始化和丢失 - 更重要的是,训练数据本身是一个随机的数据样本。从这个角度来看,计算机只能用种子生成伪随机数这一事实就是一件神器。当你训练时,你的损失是一个值,由于这种随机性,也带有置信区间。比较这些值以优化超参数而忽略那些置信区间并没有多大意义 - 因此,在我看来,花费太多精力修复非确定性,以及其他许多情况,这是徒劳的。

答案 1 :(得分:0)

要在我的GPU(1050Ti)上确定性地训练MNIST网络(https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py):

  • 设置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时向后CudnnPooling
  • cudnnSpatialTfSamplerBackward
  • cudnnCTCLoss和cudnnCTCLoss_v8何时 使用了CUDNN_CTC_LOSS_ALGO_NON_DETERMINSTIC