<address id="v7711"></address><p id="v7711"><pre id="v7711"></pre></p>
<p id="v7711"><ruby id="v7711"></ruby></p>
<pre id="v7711"><ruby id="v7711"></ruby></pre>

      <track id="v7711"></track>

          <pre id="v7711"></pre>

              <pre id="v7711"><ruby id="v7711"></ruby></pre>

              • 自動秒收錄
              • 軟件:1974
              • 資訊:5199|
              • 收錄網站:302286|

              IT精英團

               基于Apex的混合精度加速,【PyTorch】加速不香嗎?

              基于Apex的混合精度加速,【PyTorch】加速不香嗎?

              作者/小帥


              解決問題的辦法:混合精度訓練+動態損失放大

              混合精度訓練(Mixed Precision)

              混合精度訓練的精髓在于“在內存中用FP16做儲存和乘法從而加速計算,用FP32做累加避免舍入誤差”?;旌暇扔柧毜牟呗杂行У鼐徑饬松崛胝`差的問題。

              • 損失放大(Loss Scaling)

              即使用了混合精度訓練,還是會存在無法收斂的情況,原因是激活梯度的值太小,造成了下溢出(Underflow)。損失放大的思路是:

              1. 反向傳播前,將損失變化(dLoss)手動增大$2^k$倍,因此反向傳播時得到的中間變量(激活函數梯度)則不會溢出;

              2. 反向傳播后,將權重梯度縮$2^k$倍,恢復正常值。

               Apex的新API:Automatic Mixed Precision (AMP)

              曾經的Apex混合精度訓練的api仍然需要手動half模型已經輸入的數據,比較麻煩,現在新的api只需要三行代碼即可無痛使用:

              <table class=" style="outline: none; border-collapse: collapse; width: 100%;">

              1
              2
              3
              4
              from apex import amp
              model, optimizer = amp.initialize(model, optimizer, opt_level="O1") # 這里是“歐一”,不是“零一”
              with amp.scale_loss(loss, optimizer) as scaled_loss:
                  scaled_loss.backward()
              1. opt_level
                其中只有一個opt_level需要用戶自行配置:

              • O0:純FP32訓練,可以作為accuracy的baseline;

              • O1:混合精度訓練(推薦使用),根據黑白名單自動決定使用FP16(GEMM, 卷積)還是FP32(Softmax)進行計算。

              • O2:“幾乎FP16”混合精度訓練,不存在黑白名單,除了Batch norm,幾乎都是用FP16計算。

              • O3:純FP16訓練,很不穩定,但是可以作為speed的baseline;

              • 動態損失放大(Dynamic Loss Scaling)
                AMP默認使用動態損失放大,為了充分利用FP16的范圍,緩解舍入誤差,盡量使用最高的放大倍數($2^{24}$),如果產生了上溢出(Overflow),則跳過參數更新,縮小放大倍數使其不溢出,在一定步數后(比如2000步)會再嘗試使用大的scale來充分利用FP16的范圍: 踩過的坑 

              這一部分是整篇最干貨的部分,在最近在apex使用中的踩過的所有的坑,由于apex報錯并不明顯,常常debug得讓人很沮喪,但只要注意到以下的點,95%的情況都可以暢通無阻了:

              1. 判斷你的GPU是否支持FP16:構擁有Tensor Core的GPU(2080Ti、Titan、Tesla等),不支持的(Pascal系列)就不建議折騰了。

              2. 常數的范圍:為了保證計算不溢出,首先要保證人為設定的常數(包括調用的源碼中的)不溢出,如各種epsilon,INF等。

              3. Dimension最好是8的倍數:Nvidia官方的文檔的2.2條表示,維度都是8的倍數的時候,性能最好。

              4. 涉及到sum的操作要小心,很容易溢出,類似Softmax的操作建議用官方API,并定義成layer寫在模型初始化里。

              5. 模型書寫要規范:自定義的Layer寫在模型初始化函數里,graph計算寫在forward里。

              6. 某些不常用的函數,在使用前需要注冊:amp.register_float_function(torch, 'sigmoid')

              7. 某些函數(如einsum)暫不支持FP16加速,建議不要用的太heavy,xlnet的實現改FP16困擾了我很久。

              8. 需要操作模型參數的模塊(類似EMA),要使用AMP封裝后的model。

              9. 需要操作梯度的模塊必須在optimizer的step里,不然AMP不能判斷grad是否為Nan。

               總結 

              這篇從理論到實踐地介紹了混合精度計算以及Apex新API(AMP)的使用方法?,F在在做深度學習模型的時候,幾乎都會第一時間把代碼改成混合精度訓練的了,速度快,精度還不減,確實是調參煉丹必備神器。目前網上還并沒有看到關于AMP以及使用時會遇到的坑的中文博客,所以這一篇也是希望大家在使用的時候可以少花一點時間debug。

              為什么需要FP16?

              在使用FP16之前,我想再贅述一下為什么我們使用FP16。

              • 減少顯存占用

              現在模型越來越大,當你使用Bert這一類的預訓練模型時,往往顯存就被模型及模型計算占去大半,當想要使用更大的Batch Size的時候會顯得捉襟見肘。由于FP16的內存占用只有FP32的一半,自然地就可以幫助訓練過程節省一半的顯存空間。

              • 加快訓練和推斷的計算

              與普通的空間時間Trade-off的加速方法不同,FP16除了能節約內存,還能同時節省模型的訓練時間。在大部分的測試中,基于FP16的加速方法能夠給模型訓練帶來多一倍的加速體驗(爽感類似于兩倍速看肥皂?。?。

              • 張量核心的普及

              硬件的發展同樣也推動著模型計算的加速,隨著Nvidia張量核心(Tensor Core)的普及,16bit計算也一步步走向成熟,低精度計算也是未來深度學習的一個重要趨勢,再不學習就out啦。

              FP16帶來的問題:量化誤差

              這個部分是整個博客最重要的理論核心。講了這么多FP16的好處,那么使用FP16的時候有沒有什么問題呢?當然有。FP16帶來的問題主要有兩個:1. 溢出錯誤;2. 舍入誤差。

              • 溢出錯誤(Grad Overflow / Underflow)


              由于FP16的動態范圍($6 \times 10^{-8} \sim 65504$)比FP32的動態范圍($1.4 \times 10^{-45} \sim 1.7 \times 10^{38}$)要狹窄很多,因此在計算過程中很容易出現上溢出(Overflow,$g>65504$)和下溢出(Underflow,$g<6\times10^{-8}$)的錯誤,溢出之后就會出現“Nan”的問題。

              在深度學習中,由于激活函數的的梯度往往要比權重梯度小,更易出現下溢出的情況。

              image.png

              • 舍入誤差(Rounding Error)

              舍入誤差指的是當梯度過小,小于當前區間內的最小間隔時,該次梯度更新可能會失敗,用一張圖清晰地表示:

              image.png


               背景 

              我們提到圓周率 π 的時候,它有很多種表達方式,既可以用數學常數3.14159表示,也可以用一長串1和0的二進制長串表示。

              圓周率 π 是個無理數,既小數位無限且不循環。因此,在使用圓周率進行計算時,人和計算機都必須根據精度需要將小數點后的數字四舍五入。

              在小學的時候,小學生們可能只會用手算的方式計算數學題目,圓周率的數值也只能計算到小數點后兩位——3.14;而高中生使用圖形計算器可能會使圓周率數值排到小數點后10位,更加精確地表示圓周率。在計算機科學中,這被稱為精度,它通常以二進制數字來衡量,而非小數。

              對于復雜的科學模擬,開發人員長期以來一直都依靠高精度數學來研究諸如宇宙大爆炸,或是預測數百萬個原子之間的相互作用。

              數字位數越高,或是小數點后位數越多,意味著科學家可以在更大范圍內的數值內體現兩個數值的變化。借此,科學家可以對最大的星系,或是最小的粒子進行精確計算。

              但是,計算精度越高,意味著所需的計算資源、數據傳輸和內存存儲就越多。其成本也會更大,同時也會消耗更多的功率。

              由于并非每個工作負載都需要高精度,因此 AI 和 HPC 研究人員可以通過混合或匹配不同級別的精度的方式進行運算,從而使效益最大化。NVIDIA Tensor Core GPU 支持多精度和混合精度技術,能夠讓開發者優化計算資源并加快 AI 應用程序及其推理功能的訓練。

              單精度、雙精度和半精度浮點格式之間的區別

              image.png

              IEEE 浮點算術標準是用來衡量計算機上以二進制所表示數字精度的通用約定。在雙精度格式中,每個數字占用64位,單精度格式占用32位,而半精度僅16位。

              要了解其中工作原理,我們可以拿圓周率舉例。在傳統科學記數法中,圓周率表示為3.14 x100。但是計算機將這些信息以二進制形式存儲為浮點,即一系列的1和0,它們代表一個數字及其對應的指數,在這種情況下圓周率則表示為1.1001001 x 21。

              在單精度32位格式中,1位用于指示數字為正數還是負數。指數保留了8位,這是因為它為二進制,將2進到高位。其余23位用于表示組成該數字的數字,稱為有效數字。

              而在雙精度下,指數保留11位,有效位數為52位,從而極大地擴展了它可以表示的數字范圍和大小。半精度則是表示范圍更小,其指數只有5位,有效位數只有10位。

              圓周率在每個精度級別表現如下:

              多精度和混合精度計算的差異

              多精度計算意味著使用能夠以不同精度進行計算的處理器,在需要使用高精度進行計算的部分使用雙精度,并在應用程序的其他部分使用半精度或單精度算法。

              混合精度(也稱為超精度)計算則是在單個操作中使用不同的精度級別,從而在不犧牲精度的情況下實現計算效率。

              在混合精度中,計算從半精度值開始,以進行快速矩陣數學運算。但是隨著數字的計算,機器會以更高的精度存儲結果。例如,如果將兩個16位矩陣相乘,則結果為32位大小。

              使用這種方法,在應用程序結束計算時,其累積得到結果,在準確度上可與使用雙精度算法運算得到的結果相媲美。

              這項技術可以將傳統的雙精度應用程序加速多達25倍,同時減少了運行所需的內存、時間和功耗。它可用于 AI 和模擬 HPC 工作負載。

              隨著混合精度算法在現代超級計算應用程序中的普及,HPC 專家 Jack Dongarra 提出了一個新的基準,即 HPL-AI,以評估超級計算機在混合精度計算上的性能。當 NVIDIA 在全球最快的超級計算機 Summit 上運行 HPL-AI 計算時,該系統達到了前所未有的性能水平,接近550 petaflop,比其在 TOP500 榜單上的官方性能快了3倍。

               理論 

              PyTorch實現

              from apex import amp
              model, optimizer = amp.initialize(model, optimizer, opt_level="O1") # 這里是“歐一”,不是“零一”
              with amp.scale_loss(loss, optimizer) as scaled_loss:
                  scaled_loss.backward()

              但是如果你希望對FP16和Apex有更深入的了解,或是在使用中遇到了各種不明所以的“Nan”的同學,可以接著讀下去,后面會有一些有趣的理論知識和瓦礫最近一個月使用Apex遇到的各種bug,不過當你深入理解并解決掉這些bug后,你就可以徹底擺脫“慢吞吞”的FP32啦。

              理論部分

              為了充分理解混合精度的原理,以及API的使用,先補充一點基礎的理論知識。

              什么是FP16?

              半精度浮點數是一種計算機使用的二進制浮點數數據類型,使用2字節(16位)存儲。


              image.png

              其中,sign位表示正負,exponent位表示指數($2^{n-15+{1}(n=0)}$),fraction位表示的是分數($\frac{m}{1024}$)。其中當指數為零的時候,下圖加號左邊為0,其他情況為1。

              image.png

              FP16的表示范例

              點擊這里復制本文地址 以上內容由IT精英團整理呈現,請務必在轉載分享時注明本文地址!如對內容有疑問,請聯系我們,謝謝!
              發表評論 共有條評論
              用戶名: 密碼:
              驗證碼: 匿名發表
              退出閱讀|首頁
              亚洲AV色福利天堂网妓女
              <address id="v7711"></address><p id="v7711"><pre id="v7711"></pre></p>
              <p id="v7711"><ruby id="v7711"></ruby></p>
              <pre id="v7711"><ruby id="v7711"></ruby></pre>

                  <track id="v7711"></track>

                      <pre id="v7711"></pre>

                          <pre id="v7711"><ruby id="v7711"></ruby></pre>