混合精度训练-Pytorch
目錄
- 1、需求解讀
- 2、F16和FP32的區(qū)別與聯(lián)系
- 3、F16優(yōu)點(diǎn)簡(jiǎn)介
- 4、F16缺點(diǎn)簡(jiǎn)介
- 5、混合精度訓(xùn)練代碼實(shí)戰(zhàn)
- 5.1 代碼實(shí)現(xiàn)
- 5.2 代碼解析
- 6、F16訓(xùn)練效果展示
- 7、個(gè)人總結(jié)
- 參考資料
- 注意事項(xiàng)
1、需求解讀
??作為一名算法工程師,我們經(jīng)常會(huì)遇到訓(xùn)練網(wǎng)絡(luò)的事情,當(dāng)前訓(xùn)練網(wǎng)絡(luò)的整個(gè)過程基本上都是在N卡上面執(zhí)行的,當(dāng)我們的數(shù)據(jù)集比較大時(shí),訓(xùn)練網(wǎng)絡(luò)會(huì)耗費(fèi)大量的時(shí)間。由于我們需要使用反向傳播來更新具有細(xì)微變化的權(quán)重,因而我們?cè)谟?xùn)練網(wǎng)絡(luò)的過程中通常會(huì)選用FP32類型的數(shù)據(jù)和權(quán)重。說了這么多,那么混合精度到底是什么呢,有什么用呢?
??簡(jiǎn)而言之,所謂的混合精度訓(xùn)練,即當(dāng)你使用N卡訓(xùn)練你的網(wǎng)絡(luò)時(shí),混合精度會(huì)在內(nèi)存中用FP16做儲(chǔ)存和乘法從而加速計(jì)算,用FP32做累加避免舍入誤差。它的優(yōu)勢(shì)就是可以使你的訓(xùn)練時(shí)間減少一半左右。它的缺陷是只能在支持FP16操作的一些特定類型的顯卡上面使用,而且會(huì)存在溢出誤差和舍入誤差。
2、F16和FP32的區(qū)別與聯(lián)系
聯(lián)系:
- FP32和FP16都是用來表示某一個(gè)數(shù)值;
- FP32和FP16都是由符號(hào)位、指數(shù)和尾數(shù)一起組成;
區(qū)別:
- FP32由1位符號(hào)位、8位指數(shù)和23位尾數(shù)組成,FP16由1位符號(hào)位、5位指數(shù)和10位尾數(shù)組成;
- FP32能夠表示的范圍是1.4×10?45<x<3.4×10381.4 \times 10^{-45}<x<3.4 \times 10^{38}1.4×10?45<x<3.4×1038,FP16能夠表示的范圍是5.96×10?8<x<655045.96 \times 10^{-8}<x<655045.96×10?8<x<65504。即FP16最大能夠表示的數(shù)字是65503;
- FP32能夠更加準(zhǔn)確的表示某一個(gè)數(shù)字;
3、F16優(yōu)點(diǎn)簡(jiǎn)介
優(yōu)點(diǎn)1-FP16計(jì)算速度更快、更加節(jié)約內(nèi)存
??上圖展示了FP16和FP32在內(nèi)存消耗上面的不同之處。通過觀察上圖我們可以得出:
- 計(jì)算同樣的操作,FP16可以獲得8倍的加速;
- FP16能夠獲得2倍左右的內(nèi)存扇出;
- FP16能夠節(jié)省1/2的內(nèi)存資源;
優(yōu)點(diǎn)2-FP16可以使用上特定顯卡中專門為加速所設(shè)計(jì)的Tensor Core
??上圖展示了執(zhí)行卷積的過程(乘操作和加操作)。使用FP16執(zhí)行成操作,然后使用FP16或者FP32執(zhí)行乘操作。與使用FP32計(jì)算相比,在Volta V100(該架構(gòu)中存在Tensor Core,支持FP16操作)上面可以獲得8倍的性能提速,最終達(dá)到125TFlops的扇出。
4、F16缺點(diǎn)簡(jiǎn)介
缺點(diǎn)1-FP16會(huì)帶來梯度溢出錯(cuò)誤
??Grad Overflow / Underflow,即梯度溢出。由于FP16的動(dòng)態(tài)范圍是5.96×10?8<x<655045.96 \times 10^{-8}<x<655045.96×10?8<x<65504,比FP32的動(dòng)態(tài)范圍小了很多,因而在計(jì)算的過程中很容易出現(xiàn)上溢出(超出能夠表示的最大數(shù)值)和下溢出(超出能夠表示的最小數(shù)值)問題,溢出之后就會(huì)出現(xiàn)NAN的問題。在深度學(xué)習(xí)中,由于激活函數(shù)的的梯度往往要比權(quán)重梯度小,更易出現(xiàn)下溢出的情況,具體的細(xì)節(jié)如下圖所示。
缺點(diǎn)2-FP16會(huì)帶來舍入誤差
??舍入誤差,即當(dāng)梯度過小,小于當(dāng)前區(qū)間內(nèi)的最小間隔時(shí),該次梯度更新可能會(huì)失敗,具體的細(xì)節(jié)如下圖所示,由于更新的梯度值超出了FP16能夠表示的最小值的范圍,因此該數(shù)值將會(huì)被舍棄,這個(gè)權(quán)重將不進(jìn)行更新。
解決方案:
- 使用混合精度訓(xùn)練。所謂的混合精度訓(xùn)練,即在內(nèi)存中用FP16做儲(chǔ)存和乘法從而加速計(jì)算,用FP32做累加避免舍入誤差,這樣可以很好的解決舍入誤差的問題。
- 損失放大。有些情況下,即使使用了混合精度訓(xùn)練的方法,由于激活梯度的值太小,會(huì)造成下溢出,從而導(dǎo)致模型無法收斂的問題。所謂的損失放大,即反向傳播前,將損失變化(dLoss)手動(dòng)增大2k2^{k}2k倍,因此反向傳播時(shí)得到的中間變量(激活函數(shù)梯度)則不會(huì)溢出;反向傳播后,將權(quán)重梯度縮2k2^{k}2k倍,恢復(fù)正常值。
5、混合精度訓(xùn)練代碼實(shí)戰(zhàn)
5.1 代碼實(shí)現(xiàn)
使用FP32訓(xùn)練代碼如下所示:
# coding=utf-8 import torchN, D_in, D_out = 64, 1024, 512 x = torch.randn(N, D_in, device=“cuda”) y = torch.randn(N, D_out, device=“cuda”) model = torch.nn.Linear(D_in, D_out).cuda() optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) for t in range(500):y_pred = model(x)loss = torch.nn.functional.mse_loss(y_pred, y)optimizer.zero_grad()loss.backward()optimizer.step()使用FP16訓(xùn)練代碼如下所示,僅僅需要在原始的Pytorch代碼中增加3行代碼,你就可以體驗(yàn)到極致的性能加速啦。
# coding=utf-8 import torchN, D_in, D_out = 64, 1024, 512 x = torch.randn(N, D_in, device=“cuda”) y = torch.randn(N, D_out, device=“cuda”) model = torch.nn.Linear(D_in, D_out).cuda() optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) model, optimizer = amp.initialize(model, optimizer, opt_level=“O1”) for t in range(500):y_pred = model(x)loss = torch.nn.functional.mse_loss(y_pred, y)optimizer.zero_grad()with amp.scale_loss(loss, optimizer) as scaled_loss:scaled_loss.backward()optimizer.step()5.2 代碼解析
1、model, optimizer = amp.initialize(model, optimizer, opt_level=“O1”)
??這行代碼的主要作用是對(duì)模型和優(yōu)化器執(zhí)行初始化操作,方便后續(xù)的混合精度訓(xùn)練。其中opt_level表示優(yōu)化的等級(jí),當(dāng)前支持4個(gè)等級(jí)的優(yōu)化,具體的情況如下圖所示:
- 當(dāng)opt_level='00’時(shí),表示的是當(dāng)前執(zhí)行FP32訓(xùn)練,即正常的訓(xùn)練,當(dāng)前優(yōu)化等級(jí)執(zhí)行的具體操作是cast_model_type=torch.float32、patch_torch_function= False、keep_batchnorm_fp32=None、master_weight=False、loss_scale=1.0。
- 當(dāng)opt_level='01’時(shí),表示的是當(dāng)前使用部分FP16混合訓(xùn)練,當(dāng)前優(yōu)化等級(jí)執(zhí)行的具體操作是cast_model_type=None、patch_torch_function=True、keep_batch norm_fp32=None、master_weight=None、loss_scale=“dynamic”。
- 當(dāng)opt_level='02’時(shí),表示的是除了BN層的權(quán)重外,其他層的權(quán)重都使用FP16執(zhí)行訓(xùn)練,當(dāng)前優(yōu)化等級(jí)執(zhí)行的具體操作是cast_model_type=torch.float16、patch _torch_function=False、keep_batchnorm_fp32=True、master_weight =True 、loss_scale=“dynamic”。
- 當(dāng)opt_level='03’時(shí),表示的是默認(rèn)所有的層都使用FP16執(zhí)行計(jì)算,當(dāng)keep_batch norm_fp32=True,則會(huì)使用cudnn執(zhí)行BN層的計(jì)算,該優(yōu)化等級(jí)能夠獲得最快的速度,但是精度可能會(huì)有一些較大的損失。當(dāng)前優(yōu)化等級(jí)執(zhí)行的具體操作是cast_ model_type=torch.float16、patch _torch_function=False、keep_batchnorm _fp32=False、master_weight =False、loss_scale=1.0
注意事項(xiàng):
1、cast_model_type表示的是模型的輸入類型。當(dāng)前支持的類型包括torch.float32和torch.float16;
2、patch _torch_function表示的是根據(jù)不同函數(shù)的輸入數(shù)據(jù)要求獲得一個(gè)最優(yōu)的輸入類型。GEMM和Convolution等運(yùn)算可以使用FP16快速的獲得最終的結(jié)果,由于softma x/exponentiation/pow等運(yùn)算需要較高的精度,所以選擇使用FP32來計(jì)算。當(dāng)前支持的類型包括False和True。
3、keep_batchnorm _fp32表示的是是否需要對(duì)網(wǎng)絡(luò)中BN層執(zhí)行特殊處理。由于網(wǎng)絡(luò)中的BN層會(huì)影響數(shù)據(jù)的分布情況,從而進(jìn)一步影響網(wǎng)絡(luò)的訓(xùn)練過程,因此需要認(rèn)真的去處理這個(gè)類型的層。當(dāng)該層使用FP32時(shí),網(wǎng)絡(luò)的訓(xùn)練過程會(huì)更加穩(wěn)定。當(dāng)前支持的類型包括False和True。
4、master_weight 表示的是網(wǎng)絡(luò)在訓(xùn)練過程中部分參數(shù)使用FP32來表示,部分參數(shù)使用FP16來表示。上圖中藍(lán)色的框表示FP32類型,綠色的框表示FP16類型,FP32在轉(zhuǎn)化為FP16的過程中會(huì)進(jìn)行備份(master_0),optimizer都是使用FP32來表示,而model部分中部分參數(shù)是FP16類型,部分參數(shù)是FP32類型,梯度更新的過程通常是在master上面執(zhí)行。
5、loss_scale表示是是否需要執(zhí)行損失放大操作。1.0表示不需要執(zhí)行損失放大操作,dynamic表示需要執(zhí)行損失放大操作。
NVIDIA官方給出的使用規(guī)則如下所示:
- 首先,建議將opt_level設(shè)置為00。即使用FP32訓(xùn)練模型,從而建立起一個(gè)準(zhǔn)確的Baseline;
- 然后,嘗試著將opt_level設(shè)置為01。即嘗試著使用混合精度訓(xùn)練方法;
- 接著,如果你對(duì)訓(xùn)練的速度有著較高的要求,建議將opt_level設(shè)置為02或者03;
2、 with amp.scale_loss(loss, optimizer) as scaled_loss:
?? ?? scaled_loss.backward()
??這行代碼的主要作用是在反向傳播前進(jìn)行梯度放大來進(jìn)行更新,在反向傳播后進(jìn)行梯度縮放,返回原來的值,但是可以很好的解決由于梯度值太小模型無法更新的問題。具體的細(xì)節(jié)如下圖所示:
??上圖展示了FP16在計(jì)算的過程中由于梯度值太小,超出了FP16能表示的下限值,因而無法進(jìn)行權(quán)重更新,導(dǎo)致網(wǎng)絡(luò)不收斂。
??上圖展示了使用損失方法(Scaled Loss)的方法來很好的解決這個(gè)問題,即在反向傳播之前,給這些比較少的數(shù)值乘上2k2^{k}2k,即將其擴(kuò)大2k2^{k}2k倍,將其調(diào)整到FP16能夠支持的一個(gè)合理的范圍內(nèi),那么FP16就可以對(duì)這個(gè)比較小的梯度增量值執(zhí)行更新,這樣就可以很好的解決這個(gè)問題。
??上圖展示了對(duì)反向傳播之后的結(jié)果之后后處理的過程,由于我們?yōu)榱私鉀Q方向傳播之前梯度數(shù)值太小而將它擴(kuò)大2k2^{k}2k倍,那么這樣計(jì)算之后就相當(dāng)于我們認(rèn)為的將梯度值增加了2k2^{k}2k倍,為了獲得準(zhǔn)確的權(quán)重值,我們需要在反向傳播之后除以2k2^{k}2k,整個(gè)過程在optimizer.step()執(zhí)行之前。
6、F16訓(xùn)練效果展示
??上圖展示了使用混合訓(xùn)練在多個(gè)經(jīng)典模型上面的加速效果。在BERT模型上,使用混合精度訓(xùn)練可以獲得4倍的提速。換句話說,我們?cè)拘枰?天才能訓(xùn)練好的模型,現(xiàn)在1天就可以訓(xùn)練出來,而且能夠達(dá)到幾乎相同的精度級(jí)別。這在很多情況下還是挺有用的,這個(gè)方法在減少模型訓(xùn)練時(shí)間的同時(shí)可以節(jié)省更多的電費(fèi),除此之外,可以節(jié)約算法工程師們的時(shí)間,從而提高他們的工作效率。
??上圖展示了使用混合精度訓(xùn)練的模型的精度。通過觀察我們可以得出以下的結(jié)論:混合精度訓(xùn)練在提升訓(xùn)練速度的同時(shí)可以達(dá)到和FP32訓(xùn)練同樣的精度。
7、個(gè)人總結(jié)
??通過仔細(xì)理解上面的內(nèi)容,你應(yīng)該會(huì)對(duì)混合精度訓(xùn)練有了一個(gè)全新的認(rèn)識(shí)。所謂的混合精度訓(xùn)練,即當(dāng)你使用N卡訓(xùn)練你的網(wǎng)絡(luò)時(shí),混合精度會(huì)在內(nèi)存中用FP16做儲(chǔ)存和乘法從而加速計(jì)算,用FP32做累加避免舍入誤差。它的優(yōu)勢(shì)就是可以使你的訓(xùn)練時(shí)間減少一半左右。它的缺陷是只能在支持FP16操作的一些特定類型的顯卡上面使用,而且會(huì)存在溢出誤差和舍入誤差??偠灾?#xff0c;混合精度訓(xùn)練可以在保證精度的同時(shí)極大的提升你的訓(xùn)練速度,如果你習(xí)慣使用pytorch來訓(xùn)練網(wǎng)絡(luò),那你就可以獲得極致的訓(xùn)練速度啦。當(dāng)前混合精度訓(xùn)練仍然存在著一些限制條件,首先,你的硬件設(shè)備需要支持FP16計(jì)算;其次,你的硬件設(shè)備需要具有Tensor_Core單元(這僅僅存在于一些新架構(gòu)的N卡上面);接著,當(dāng)前的僅有少量的深度學(xué)習(xí)框架支持混合精度訓(xùn)練(Pytorch);最后,混合精度不僅僅可以用在網(wǎng)絡(luò)訓(xùn)練的過程中,同樣也可以將它應(yīng)用在網(wǎng)絡(luò)推理過程中執(zhí)行加速。隨著越來越多的硬件設(shè)備支持FP16計(jì)算之后,混合精度訓(xùn)練和推理應(yīng)該會(huì)成為一個(gè)首選,我相信越來越多的訓(xùn)練和推理框架都會(huì)在短期內(nèi)逐漸支持混合精度訓(xùn)練。
參考資料
[1] 參考博客
[2] NVIDIA參考資料
[3] GTC_2019
[4] apex
注意事項(xiàng)
[1] 該博客是本人原創(chuàng)博客,如果您對(duì)該博客感興趣,想要轉(zhuǎn)載該博客,請(qǐng)與我聯(lián)系(qq郵箱:1575262785@qq.com),我會(huì)在第一時(shí)間回復(fù)大家,謝謝大家的關(guān)注.
[2] 由于個(gè)人能力有限,該博客可能存在很多的問題,希望大家能夠提出改進(jìn)意見。
[3] 如果您在閱讀本博客時(shí)遇到不理解的地方,希望您可以聯(lián)系我,我會(huì)及時(shí)的回復(fù)您,和您交流想法和意見,謝謝。
[4] 本人業(yè)余時(shí)間承接各種本科畢設(shè)設(shè)計(jì)和各種小項(xiàng)目,包括圖像處理(數(shù)據(jù)挖掘、機(jī)器學(xué)習(xí)、深度學(xué)習(xí)等)、matlab仿真、python算法及仿真等,有需要的請(qǐng)加QQ:1575262785詳聊,備注“項(xiàng)目”!!!
總結(jié)
以上是生活随笔為你收集整理的混合精度训练-Pytorch的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机英语天天学(070720)
- 下一篇: CharNet算法详解