數值精度¶
在現代電腦中,浮點數使用 IEEE 754 標準表示。有關浮點運算和 IEEE 754 標準的更多詳細資訊,請參閱浮點運算 特別要注意的是,浮點數提供的精度有限(單精度浮點數約為 7 位小數,雙精度浮點數約為 16 位小數),並且浮點加法和乘法不具結合性,因此運算順序會影響結果。因此,PyTorch 不能保證對數學上相同的浮點運算產生位元相同的結果。同樣地,PyTorch 版本、個別提交或不同平台之間也不能保證位元相同的結果。特別是,即使輸入的位元相同,並且在控制了隨機性來源之後,CPU 和 GPU 的結果也可能不同。
批次運算或切片運算¶
PyTorch 中的許多運算都支援批次運算,其中對輸入批次的元素執行相同的運算。例如 torch.mm() 和 torch.bmm()。可以將批次運算實現為對批次元素的迴圈,並對個別批次元素應用必要的數學運算,出於效率原因,我們不會這樣做,通常會對整個批次執行運算。與非批次運算相比,我們呼叫的數學函式庫和 PyTorch 運算的內部實現可能會在這種情況下產生略有不同的結果。特別是,令 A 和 B 為適合批次矩陣乘法的 3D 張量。則 (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 使用的外部函式庫(後端)不保證當輸入具有非有限值(如 inf 或 NaN)時的行為。因此,PyTorch 也不保證。運算可能會返回具有非有限值的張量,或引發異常,甚至發生區段錯誤。
請考慮在呼叫這些函數之前使用 torch.isfinite() 來偵測這種情況。
linalg 中的極值¶
torch.linalg 中的函數比其他 PyTorch 函數具有更多 極值。
求解器 和 逆矩陣 假設輸入矩陣 A 是可逆的。如果它接近於不可逆(例如,如果它有一個非常小的奇異值),那麼這些演算法可能會靜默地返回不正確的結果。這些矩陣被稱為 病態矩陣。如果提供了病態輸入,則在不同裝置上使用相同輸入或透過關鍵字 driver 使用不同後端時,這些函數的結果可能會有所不同。
當輸入的奇異值彼此接近時,譜運算(如 svd、eig 和 eigh)也可能返回不正確的結果(並且它們的梯度可能是無限的)。這是因為用於計算這些分解的演算法難以針對這些輸入收斂。
在 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 精度,我們建議您使用 torch.backends.cuda.matmul.allow_tf32 = True 為矩陣乘法啟用 TF32 張量核心。如果您的網路在矩陣乘法和卷積都需要完整的 float32 精度,則也可以使用 torch.backends.cudnn.allow_tf32 = False 為卷積停用 TF32 張量核心。
如需更多資訊,請參閱 TensorFloat32。
FP16 和 BF16 GEMM 的降低精度降低¶
半精度 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 將其關閉
如需更多資訊,請參閱 allow_fp16_reduced_precision_reduction 和 allow_bf16_reduced_precision_reduction
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 來覆寫預設行為。這些環境變數的行為如下:
| 正向 | 反向 | |
|---|---|---|
| 環境變數未設定 | 原始 | 替代 | 
| 環境變數設定為 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