数值精度¶
在现代计算机中,浮点数使用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张量核心,而对于卷积启用它们,尽管大多数神经网络工作负载在使用TF32时与使用fp32具有相同的收敛行为。如果您的网络不需要完整的float32精度,我们建议通过torch.backends.cuda.matmul.allow_tf32 = True为矩阵乘法启用TF32张量核心。如果您的网络需要对矩阵乘法和卷积都使用完整的float32精度,则也可以通过torch.backends.cudnn.allow_tf32 = False为卷积禁用TF32张量核心。
有关更多信息,请参见 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
在AMD Instinct MI200设备上使用降低精度的FP16和BF16 GEMMs和卷积¶
在AMD Instinct MI200 GPU上,FP16和BF16 V_DOT2和MFMA矩阵指令将输入和输出的非规格化值刷新为零。FP32和FP64 MFMA矩阵指令不会将输入和输出的非规格化值刷新为零。受影响的指令仅由rocBLAS(GEMM)和MIOpen(卷积)内核使用;所有其他PyTorch操作都不会遇到这种行为。所有其他支持的AMD GPU也不会遇到这种行为。
rocBLAS 和 MIOpen 为受影响的 FP16 操作提供了替代实现。BF16 操作的替代实现未提供;BF16 数字具有比 FP16 数字更大的动态范围,因此不太可能遇到非正规值。对于 FP16 的替代实现,FP16 输入值被转换为中间 BF16 值,然后在累积 FP32 操作后重新转换回 FP16 输出。通过这种方式,输入和输出类型保持不变。
当使用FP16精度进行训练时,某些模型可能会因为FP16非规格化数被冲零而无法收敛。非规格化值在训练的反向传播过程中计算梯度时更频繁地出现。PyTorch默认会在反向传播期间使用rocBLAS和MIOpen的替代实现。默认行为可以通过环境变量ROCBLAS_INTERNAL_FP16_ALT_IMPL和MIOPEN_DEBUG_CONVOLUTION_ATTRIB_FP16_ALT_IMPL进行覆盖。这些环境变量的行为如下:
前向 |
反向传播 |
|
|---|---|---|
环境变量未设置 |
原始 |
备用 |
环境设置为1 |
备用 |
备用 |
环境设置为0 |
原始 |
原始 |
以下是可能使用rocBLAS的操作列表:
torch.addbmm
torch.addmm
torch.baddbmm
torch.bmm
torch.mm
torch.nn.GRUCell
torch.nn.LSTMCell
torch.nn.Linear
torch.sparse.addmm
以下torch._C._ConvBackend实现:
slowNd
slowNd_transposed
slowNd_dilated
slowNd_dilated_transposed
以下是MIOpen可能被使用的操作列表:
torch.nn.Conv[Transpose]Nd
以下torch._C._ConvBackend实现:
ConvBackend::Miopen
ConvBackend::MiopenDepthwise
ConvBackend::MiopenTranspose