目录

数值精度

在现代计算机中,浮点数使用IEEE 754标准表示。 有关浮点算术和IEEE 754标准的更多详细信息,请参见 浮点算术 特别注意的是,浮点数提供的精度有限(单精度浮点数约为7位小数,双精度浮点数约为16位小数),并且浮点加法和乘法不具有结合性,因此操作顺序会影响结果。 由于这一点,PyTorch不能保证对数学上相同的浮点计算产生位级相同的结果。同样,位级相同的结果也不能保证跨PyTorch版本、个人提交或不同平台。特别是,CPU和GPU的结果可能不同,即使对于位级相同的输入,在控制随机源后也是如此。

批量计算或切片计算

PyTorch中的许多操作支持批量计算,即对输入批次的元素执行相同的操作。一个例子是torch.mm()torch.bmm()。可以将批量计算实现为对批量元素的循环, 并根据需要对各个批量元素应用数学运算,但由于效率原因我们没有这样做,并且通常对整个批量进行计算。我们调用的数学库以及PyTorch内部的操作实现,在这种情况下可能会产生与非批量计算略有不同的结果。特别是, 令AB为适合批量矩阵乘法的三维张量。 那么(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

线性代数 (torch.linalg)

非有限值

外部库(后端)对 torch.linalg 的使用在输入包含非有限值(如 infNaN)时不做任何行为保证。因此,PyTorch 也不做保证。 操作可能会返回包含非有限值的张量,或者引发异常,甚至导致段错误。

在调用这些函数之前,请考虑使用 torch.isfinite() 来检测这种情况。

线性代数中的极值

函数在torch.linalg中有比其他PyTorch函数更多的极值

求解器逆矩阵 假设输入矩阵 A 是可逆的。如果它接近不可逆(例如,如果它有一个非常小的奇异值),那么这些算法可能会静默地返回不正确的结果。这些矩阵被称为 病态矩阵。 如果提供了病态矩阵作为输入,使用相同输入在不同设备上或使用通过关键字 driver 的不同后端时,这些函数的结果可能会有所不同。

光谱操作如svdeigeigh在输入值接近奇异值时也可能返回错误结果(并且它们的梯度可能是无穷大)。这是因为用于计算这些分解的算法在处理这些输入时难以收敛。

float64 中运行计算(如NumPy默认情况下所做的)通常会有帮助,但这并不能解决所有情况下的问题。 通过 torch.linalg.svdvals() 分析输入的频谱或通过 torch.linalg.cond() 分析它们的条件数 可能有助于检测这些问题。

Nvidia Ampere(及后续)设备上的TensorFloat-32(TF32)

在Ampere(及后续)Nvidia GPU上,PyTorch可以使用TensorFloat32(TF32)来加速数学密集型操作,特别是矩阵乘法和卷积。 当使用TF32张量核心执行操作时,仅读取输入尾数的前10位。 这可能会降低精度并产生意外结果(例如,将矩阵与单位矩阵相乘可能会产生与输入不同的结果)。 默认情况下,TF32张量核心对于矩阵乘法是禁用的,而对于卷积是启用的,尽管大多数神经网络工作负载在使用TF32时具有与使用fp32相同的收敛行为。 我们建议如果你的网络不需要完整的float32精度,则可以启用TF32张量核心进行矩阵乘法torch.backends.cuda.matmul.allow_tf32 = True。 如果你的网络需要对矩阵乘法和卷积都具有完整的float32精度,那么也可以通过torch.backends.cudnn.allow_tf32 = False禁用卷积的TF32张量核心。

有关更多信息,请参见 TensorFloat32

降低精度减少对FP16和BF16 GEMMs的影响

半精度GEMM操作通常使用单精度进行中间累积(归约)以提高数值精度和防止溢出。为了性能,某些GPU架构,尤其是较新的架构,允许对中间累积结果进行一些截断到较低精度(例如,半精度)。从模型收敛的角度来看,这种变化通常是无害的,但它可能会导致意外的结果(例如,当最终结果应该可以用半精度表示时出现inf值)。 如果降低精度的归约存在问题,可以通过 torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False关闭它们。

类似的标志也存在于BF16 GEMM操作中,并且默认是开启的。如果BF16 降低精度的归约存在问题,可以通过 torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = False 将其关闭。

有关更多信息,请参阅 允许fp16降低精度缩减允许bf16降低精度缩减

在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

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源