数值精度¶
在现代计算机中,浮点数使用 IEEE 754 标准表示。 有关浮点运算和 IEEE 754 标准的更多详细信息,请参阅浮点运算 特别注意,浮点提供的精度有限(大约 7 位十进制数字 对于单精度浮点数,对于双精度,大约为 16 个十进制数字 浮点数),并且浮点加法和乘法不是 associative,因此操作的顺序会影响结果。 因此,PyTorch 无法得到保证 为浮点计算生成按位相同的结果,这些浮点计算是 数学上相同。同样,不能保证 PyTorch 版本、单个提交或不同平台。特别是 CPU 和 GPU 即使对于按位相同的输入,甚至在控制了 随机性的来源。
批处理计算或切片计算¶
PyTorch 中的许多操作都支持批量计算,其中执行相同的操作
对于输入批次的元素。这方面的一个例子是 and
。可以将批处理计算实现为批处理元素的循环,
并将必要的数学运算应用于各个 batch 元素,以提高效率
我们不会这样做,通常会对整个批次执行计算。数学
库,而 PyTorch 内部的操作实现可以生成
与非批处理计算相比,在这种情况下的结果略有不同。特别
let 和 是 3D 张量,其维度适合批量矩阵乘法。
then (批处理结果的第一个元素) 不能保证按位
identical to (输入批次的第一个元素的矩阵乘积)
尽管在数学上它是相同的计算。
A
B
(A@B)[0]
A[0]@B[0]
同样,应用于张量切片的操作不能保证产生以下结果
与应用于完整张量的相同操作的结果的切片相同。例如,设 为 2 维张量。 不保证按位等于 。A
A.sum(-1)[0]
A[:,0].sum()
极值¶
当输入包含较大的值,以至于中间结果可能会溢出 used 数据类型,则最终结果也可能溢出,即使它可以在原始 数据类型。例如:
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
)¶
非有限值¶
使用的外部库(后端)不保证它们的行为
当输入具有非有限值(如 或 )时。因此,PyTorch 也是如此。
这些操作可能会返回具有非有限值的张量,或者引发异常,甚至是段错误。torch.linalg
inf
NaN
linalg 中的极值¶
其中的函数比其他 PyTorch 函数具有更多的极值。torch.linalg
求解器和逆矩阵假定输入矩阵是可逆的。如果接近
是不可逆的(例如,如果它有一个非常小的奇异值),那么这些算法可能会静默返回
不正确的结果。这些矩阵被称为病态。
如果提供不良条件输入,则这些函数的结果可能会有所不同,当对不同的输入使用相同的输入时
devices 或通过关键字 .A
driver
像 , 和 这样的频谱运算也可能返回不正确的结果(并且它们的梯度可能是无限的)
当它们的输入具有彼此接近的奇异值时。这是因为用于计算这些分解的算法
努力为这些输入而收敛。svd
eig
eigh
运行计算(就像 NumPy 默认所做的那样)通常会有所帮助,但它并不能在所有情况下都解决这些问题。
分析输入的频谱 via 或其条件编号 via
可能有助于检测这些问题。
float64
Nvidia Ampere(及更高版本)设备上的 TensorFloat-32(TF32)¶
在 Ampere(及更高版本)Nvidia GPU 上,PyTorch 可以使用 TensorFloat32 (TF32) 来加速数学密集型运算,特别是矩阵乘法和卷积。
使用 TF32 张量核心执行操作时,仅读取输入尾数的前 10 位。
这可能会降低准确性并产生令人惊讶的结果(例如,将矩阵乘以单位矩阵可能会产生与输入不同的结果)。
默认情况下,TF32 张量核心对矩阵乘法处于禁用状态,而对卷积处于启用状态,尽管大多数神经网络工作负载在使用 TF32 时具有与使用 fp32 时相同的收敛行为。
如果您的网络不需要完整的 float32 精度,我们建议为矩阵乘法启用 TF32 张量核心。
如果您的网络需要矩阵乘法和卷积的全 float32 精度,则也可以使用 .torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = False
有关更多信息,请参阅 TensorFloat32。
FP16 和 BF16 GEMM 的精度降低¶
半精度 GEMM 操作通常以单精度进行中间累积(减少)来完成,以提高数值精度和提高对溢出的弹性。为了提高性能,某些 GPU 架构,尤其是较新的 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
有关更多信息,请参阅 allow_fp16_reduced_precision_reduction 和 allow_bf16_reduced_precision_reduction
缩小了 Scaled Dot Product Attention (SDPA) 中 FP16 和 BF16 的精度降低¶
当使用 FP16/BF16 输入时,由于使用低精度的中间缓冲区,天真的 SDPA 数学后端可能会累积大量数值误差。为了缓解此问题,默认行为现在涉及将 FP16/BF16 输入向上转换为 FP32。在 FP32/TF32 中执行计算,然后将最终的 FP32 结果降级回 FP16/BF16。这将提高具有 FP16/BF16 输入的数学后端最终输出的数值精度,但会增加内存使用量,并且当计算从 FP16/BF16 BMM 转移到 FP32/TF32 BMM/Matmul 时,可能会导致数学后端的性能回归。
对于首选降低精度降低以提高速度的方案,可以使用以下设置启用它们:torch.backends.cuda.allow_fp16_bf16_reduction_math_sdp(True)
AMD Instinct MI200 器件上的精度降低的 FP16 和 BF16 GEMM 和卷积¶
在 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 覆盖默认行为。这些环境变量的行为如下:
向前 |
向后 |
|
---|---|---|
环境未设置 |
源语言 |
互生 |
Env 设置为 1 |
互生 |
互生 |
Env 设置为 0 |
源语言 |
源语言 |
以下是可以使用 rocBLAS 的操作列表:
torch.addbmm
torch.addmm
Torch.baddbmm
Torch.bmm
torch.mm
torch.nn.GRUCell
torch.nn.LSTMCell
torch.nn.线性
torch.sparse.addmm
以下 torch._C._ConvBackend 实现:
慢Nd
slowNd_transposed
slowNd_dilated
slowNd_dilated_transposed
以下是可以使用 MIOpen 的操作列表:
torch.nn.Conv[转置]Nd
以下 torch._C._ConvBackend 实现:
ConvBackend::Miopen
ConvBackend::MiopenDepthwise
ConvBackend::MiopenTranspose