天天看點

Pytorch中的自動混合精度

最近在訓練yolo v5的模型時,出現了這樣一個bug:

這個子產品之前從來沒有見過,是以就去了解了一下,發現是pytorch中的自動混合精度子產品。這是yolov5新使用的技術,v4,v3都沒有出現過。

1.什麼是自動混合精度

自動混合精度(automatic mixed precision (AMP))是在pytorch1.6版本中釋出的。

神經網絡計算架構的核心就是Tensor, 在深度學習中,Tensor實際上就是一個多元數組(multidimensional array),其目的是能夠創造更高次元的矩陣、向量。

Tensor有不同的資料類型,在pytorch中,Tensor有十種類型:

torch.FloatTensor (32-bit floating point)

torch.DoubleTensor (64-bit floating point)

torch.HalfTensor (16-bit floating point 1)

torch.BFloat16Tensor (16-bit floating point 2)

torch.ByteTensor (8-bit integer (unsigned))

torch.CharTensor (8-bit integer (signed))

torch.ShortTensor (16-bit integer (signed))

torch.IntTensor (32-bit integer (signed))

torch.LongTensor (64-bit integer (signed))

torch.BoolTensor (Boolean)

我們建立的Tensor預設的類型為32-bit floating point,這就是32位浮點型精度的Tensor。

在自動混合精度中,我們主要關注兩種類型的Tensor,他們分别是torch.FloatTensor和torch.HalfTensor,即混合精度。

自動混合精度中的“自動”表示Tensor的類型,即dtype會自動變化,架構會按照需要自己調整tensor的dtype。當然我們也可以手動調整。

導入amp子產品:

from torch.cuda import amp

torch.cuda.amp 的名字意味着這個功能隻能在cuda上使用,事實上,這個功能正是NVIDIA的開發人員貢獻到PyTorch項目中的。而隻有支援Tensor core的CUDA硬體才能享受到AMP的好處(比如2080ti顯示卡)。Tensor Core是一種矩陣乘累加的計算單元,每個Tensor Core每個時鐘執行64個浮點混合精度操作(FP16矩陣相乘和FP32累加),英偉達宣稱使用Tensor Core進行矩陣運算可以輕易的提速,同時降低一半的顯存通路和存儲。

是以,在PyTorch中,當我們提到自動混合精度訓練,我們說的就是在NVIDIA的支援Tensor core的CUDA裝置上使用torch.cuda.amp.autocast (以及torch.cuda.amp.GradScaler)來進行訓練。

2.自動混合精度出現的意義

在訓練的時候為什麼要在torch.FloatTensor類型的基礎上,混合另一種資料類型(torch.HalfTensor)呢?

那就是在某些情況下,我們用torch.HalfTensor會比torch.FloatTensor有優勢。

torch.HalfTensor的優勢就是存儲小、計算快、更好的利用CUDA裝置的Tensor Core。是以訓練的時候可以減少顯存的占用(可以增加batchsize了),同時訓練速度更快;

torch.HalfTensor的劣勢就是:數值範圍小(更容易Overflow / Underflow)、舍入誤差(Rounding Error,導緻一些微小的梯度資訊達不到16bit精度的最低分辨率,進而丢失)。

為了消除torch.HalfTensor的劣勢,又引入了一個新torch.cuda.amp.GradScaler,它是通過放大loss的值來防止梯度的underflow(這隻是BP的時候傳遞梯度資訊使用,真正更新權重的時候還是要把放大的梯度再unscale回去)

3.自動混合精度訓練的使用

我們看看自動混合精度訓練是在yolov5中怎麼使用的:

Pytorch中的自動混合精度

在訓練最開始之前執行個體化一個GradScaler對象。

Pytorch中的自動混合精度

這是一個autocast的上下文管理器,是為了在前向傳播時,使用自動混合精度,當進入autocast的上下文後,上面列出來的那些CUDA ops 會把tensor的dtype轉換為半精度浮點型,進而在不損失訓練精度的情況下加快運算。剛進入autocast的上下文時,tensor可以是任何類型,你不要在model或者input上手工調用.half() ,架構會自動做,這也是自動混合精度中“自動”一詞的由來。

autocast上下文隻用包含前向傳播的過程,因為在反向傳播時,會使用和前向傳播時一樣的資料類型。

Pytorch中的自動混合精度

在反向傳播時,就會用到之前我們執行個體化的GradScaler對象。

#Scales loss. 為了梯度放大.
        scaler.scale(loss).backward()

        # scaler.step() 首先把梯度的值unscale回來.
        # 如果梯度的值不是 infs 或者 NaNs, 那麼調用optimizer.step()來更新權重,
        # 否則,忽略step調用,進而保證權重不更新(不被破壞)
        scaler.step(optimizer)

        # 準備着,看是否要增大scaler
        scaler.update()

           

4.補充

如下操作中tensor會被自動轉化為半精度浮點型的torch.HalfTensor:

matmul

addbmm

addmm

addmv

addr

baddbmm

bmm

chain_matmul

conv1d

conv2d

conv3d

conv_transpose1d

conv_transpose2d

conv_transpose3d

linear

matmul

mm

mv

prelu

其他操作需要手動轉換為自動混合精度。

繼續閱讀