数值精度¶
在现代计算机中,浮点数使用IEEE 754标准表示。 有关浮点算术和IEEE 754标准的更多详细信息,请参见 浮点算术 特别注意的是,浮点数提供的精度有限(单精度浮点数约为7位小数,双精度浮点数约为16位小数),并且浮点加法和乘法不具有结合性,因此操作顺序会影响结果。 由于这一点,PyTorch不能保证对数学上相同的浮点计算产生位级相同的结果。同样,位级相同的结果也不能保证跨PyTorch版本、个人提交或不同平台。特别是,CPU和GPU的结果可能不同,即使对于位级相同的输入,在控制随机源后也是如此。
批量计算或切片计算¶
PyTorch中的许多操作支持批量计算,即对输入批次的元素执行相同的操作。一个例子是torch.mm()和
torch.bmm()。可以将批量计算实现为对批量元素的循环,
并根据需要对各个批量元素应用数学运算,但由于效率原因我们没有这样做,并且通常对整个批量进行计算。我们调用的数学库以及PyTorch内部的操作实现,在这种情况下可能会产生与非批量计算略有不同的结果。特别是,
令A和B为适合批量矩阵乘法的三维张量。
那么(A@B)[0](批量结果的第一个元素)并不保证与A[0]@B[0](输入批次第一个元素的矩阵乘积)
在位上完全相同,尽管从数学上讲这是一个相同的计算。
同样,对张量切片应用的操作不一定能产生与对该完整张量执行相同操作后的结果切片完全相同的结果。例如,设
A 是一个二维张量。A.sum(-1)[0] 不保证与
A[:,0].sum() 在位运算上相等。
极值¶
当输入包含大值,使得中间结果可能超出所用数据类型的范围时,最终结果也可能溢出,即使它在原始数据类型中是可以表示的。例如:
import torch
a=torch.tensor([1e20, 1e20]) # fp32 type by default
a.norm() # produces tensor(inf)
a.double().norm() # produces tensor(1.4142e+20, dtype=torch.float64), representable in fp32
Nvidia Ampere 设备上的 TensorFloat-32(TF32)¶
在Ampere架构的Nvidia GPU上,pytorch默认使用TensorFloat32(TF32)来加速数学运算密集型操作,特别是矩阵乘法和卷积。当使用TF32张量核心执行操作时,只会读取输入尾数的前10位。这会导致结果精度降低,并产生一些令人意外的结果,例如将矩阵与单位矩阵相乘后得到的结果与输入不同。
大多数神经网络任务在使用tf32时的收敛行为与使用fp32时相同,但若需要更高的精度,可以通过
torch.backends.cuda.matmul.allow_tf32 = False
关闭TF32。
For more information see TensorFloat32
FP16 GEMMs的精度降低¶
半精度GEMM操作通常使用单精度进行中间累积(归约)以提高数值精度和防止溢出。为了性能,某些GPU架构,尤其是较新的架构,允许对中间累积结果进行一些截断到较低精度(例如,半精度)。从模型收敛的角度来看,这种变化通常是无害的,但它可能会导致意外的结果(例如,当最终结果应该可以用半精度表示时出现inf值)。
如果降低精度的归约存在问题,可以通过
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False关闭它们。
For more information see allow_fp16_reduced_precision_reduction