在调整超参数以使我的模型更好地执行时,我注意到每次运行代码时我得到的分数(以及因此创建的模型)都不同,尽管为随机操作修复了所有种子。如果我在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时处理非确定性的标准方法是什么?
答案 0 :(得分:7)
<强> TL; DR 强>
那,但更长
当您将神经网络操作视为数学运算时,您会发现一切都是确定性的。卷积,激活,交叉熵 - 这里的一切都是数学方程式,应该是确定性的。甚至伪随机操作,例如改组,辍学,噪音等,完全由种子决定。
另一方面,当您从计算实现中看到这些操作时,您会将它们视为大规模并行化计算,除非您非常小心,否则这些计算可能是随机性的来源。
问题的核心在于,当您在多个并行线程上运行操作时,通常不知道哪个线程将首先结束。当线程对自己的数据进行操作时并不重要,因此,例如,将激活函数应用于张量应该是确定性的。但是当这些线程需要同步时,例如计算总和时,结果可能取决于求和的顺序,进而取决于首先结束的线程的顺序。
从那里,你可以广泛地说两个选择:
保持与更简单实施相关的非确定性。
在设计并行算法时要格外小心,以减少或消除计算中的非确定性。添加的约束通常会导致算法速度变慢
哪条路由需要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):
python_random.seed(42)
np.random.seed(42)
tf.set_random_seed(42)
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)