Created by: Aurelius84
PR types
Others
PR changes
Others
Describe
Fix diff of cycle GAN model on GPU
The used algorithm of GradKernel in BatchNorm
is cudnnBatchNormalizationBackward
which is non-deterministic. In this PR, we set use_global_stats=True
to use the default implement in Paddle to get deterministic result.
However, even we set use_global_stats=True
, the result of dygraph on GPU is not always exactly same. After many iterations like 20 batches, it will have some diff.
1. cuDNN不确定性算子
cuDNN库中实现了一些深度学习中常用的算子,且经过专门的优化以更好的适配Nvidia的GPU芯片,包括但不限于Conv、Pooling、BatchNorm等。这些算子在GPU或者CPU多线程下可能存在不确定性结果。
2. 可复现性
在具有相同体系结构和相同数量的SM的GPU上执行时,给定版本的大多数cuDNN接口在运行时会生成相同的按位结果。
但是,由于特定op实现有升级,无法保证各个版本之间结果的按位重复性(相等)。另外一类是原子操作,比如atomicAdd
,不保证运行结果总是一致,如下常见的算子具有不确定性:
-
cudnnConvolutionBackwardFilter
- 当使用
CUDNN_CONVOLUTION_BWD_FILTER_ALGO_0
或CUDNN_CONVOLUTION_BWD_FILTER_ALGO_3
时
- 当使用
-
cudnnConvolutionBackwardData
- 使用
CUDNN_CONVOLUTION_BWD_DATA_ALGO_0
时
- 使用
-
cudnnPoolingBackward
- 使用
CUDNN_POOLING_MAX
时
- 使用
cudnnSpatialTfSamplerBackward
3. 原子操作
GPU上的浮点型原子操作存在精度问题。在对一组数据进行计算时,执行的顺序会不一样,导致结果也可能不一样。比如AutomicAdd
操作4个变量a,b,c,d,返回的结果可能是a+b+c+d
,也可能是d+c+a+b
。由于float精度的原因,两种顺序加完的结果有轻微差别。
GPU计算结果的不确定性是线程调度不确定性引起的。这种不确定性现象在多核CPU下也会出现,这是硬件层面的问题。在并行计算中,从所有线程收集部分计算结果的顺序是无法确定,因此浮点型的加减乘除的顺序也会不一致,导致了结果的不确定性。
在tensorflow
中,reduce_sum
的GPU实现导致这个op的计算结果是不确定性的(用到了AtomicAdd
)。
On GPU, small amount of non-deterministic results is expected.
TensorFlow uses the Eigen library, which uses Cuda atomics to implement reduction operations,
such as tf.reduce_sum etc. Those operations are non-determnistical.
Each operation can introduce a small difference. If your model is not stable,
it could accumulate into large errors, after many steps.
If you see a large difference, after one or two operations,
it would be problematic. Otherwise, it is somewhat expected.
Regularizers such as dropout helps the model tolerate that.
GPU上少量的不确定性结果浮动是符合预期的。
Tensorflow使用了`Eigen`库,里面借助Cuda的原子操作实现`reduce`操作。
这些原则操作包含不确定性,每个操作都会引入一小部分diff。
如果你的模型不稳定,在很多轮迭代后,将会累加成很大的误差。
4. 总结
目前发现GPU上的浮点型数据操作不确定大致可分为两个层面:
- cudnn底层算子引入的不确定性算法选择
- 原子操作和并行计算带来的浮点型操作顺序不确定性
前者可以通过升级或改写cudnn算子来消除,如英伟达提供的framework-determinism;