深度學習最吃機器,耗資源,在本文,我將來科普一下在深度學習中:
何爲 “資源”
不同操作都耗費什麼資源
如何充分的利用有限的資源
如何合理選擇顯卡
並糾正幾個誤區:
顯存和 GPU 等價,使用 GPU 主要看顯存的使用?
Batch Size 越大,程序越快,而且近似成正比?
顯存佔用越多,程序越快?
顯存佔用大小和 batch size 大小成正比?
0 預備知識
nvidia-smi是 Nvidia 顯卡命令行管理套件,基於 NVML 庫,旨在管理和監控 Nvidia GPU 設備。
nvidia-smi 的輸出
這是 nvidia-smi 命令的輸出,其中最重要的兩個指標:
-
顯存佔用
-
GPU 利用率
顯存佔用和 GPU 利用率是兩個不一樣的東西,顯卡是由 GPU 計算單元和顯存等組成的,顯存和 GPU 的關係有點類似於內存和 CPU 的關係。
這裏推薦一個好用的小工具:gpustat, 直接pip install gpustat即可安裝,gpustat 基於nvidia-smi,可以提供更美觀簡潔的展示,結合 watch 命令,可以動態實時監控GPU 的使用情況。
watch --color -n1 gpustat -cpu
gpustat 輸出
顯存可以看成是空間,類似於內存。
-
顯存用於存放模型,數據
-
顯存越大,所能運行的網絡也就越大
GPU 計算單元類似於 CPU 中的核,用來進行數值計算。衡量計算量的單位是 flop: the number of floating-point multiplication-adds,浮點數先乘後加算一個 flop。計算能力越強大,速度越快。衡量計算能力的單位是 flops: 每秒能執行的 flop 數量
1*2+3 1個flop
1*2+3*4+4*5 3個flop
1. 顯存分析1.1 存儲指標
1Byte=8bit
1K=1024Byte
1M=1024K
1G=1024M
1T=1024G
10K=10*1024Byte
除了K、M,G,T等之外,我們常用的還有KB、MB,GB,TB。二者有細微的差別。
1Byte=8bit
1KB=1000Byte
1MB=1000KB
1GB=1000MB
1TB=1000GB
10KB=10000Byte
K、M,G,T是以 1024 爲底,而KB、MB,GB,TB以 1000 爲底。不過一般來說,在估算顯存大小的時候,我們不需要嚴格的區分這二者。
在深度學習中會用到各種各樣的數值類型,數值類型命名規範一般爲TypeNum,比如 Int64、Float32、Double64。
-
Type:有 Int,Float,Double 等
-
Num: 一般是 8,16,32,64,128,表示該類型所佔據的比特數目
常用的數值類型如下圖所示:
常用的數值類型
其中 Float32 是在深度學習中最常用的數值類型,稱爲單精度浮點數,每一個單精度浮點數佔用 4Byte 的顯存。
舉例來說:有一個 1000x1000 的 矩陣,float32,那麼佔用的顯存差不多就是
1000x1000x4 Byte = 4MB
32x3x256x256 的四維數組(BxCxHxW)佔用顯存爲:24M
1.2 神經網絡顯存佔用
神經網絡模型佔用的顯存包括:
-
模型自身的參數
-
模型的輸出
舉例來說,對於如下圖所示的一個全連接網絡 (不考慮偏置項 b)
模型的輸入輸出和參數
模型的顯存佔用包括:
-
參數:二維數組 W
-
模型的輸出: 二維數組 Y
輸入 X 可以看成是上一層的輸出,因此把它的顯存佔用歸於上一層。
這麼看來顯存佔用就是 W 和 Y 兩個數組?
並非如此!!!
下面細細分析。
1.2.1 參數的顯存佔用
只有有參數的層,纔會有顯存佔用。這部份的顯存佔用和輸入無關,模型加載完成之後就會佔用。
有參數的層主要包括:
-
卷積
-
全連接
-
BatchNorm
-
Embedding 層
-
... ...
無參數的層:
-
多數的激活層 (Sigmoid/ReLU)
-
池化層
-
Dropout
-
... ...
更具體的來說,模型的參數數目 (這裏均不考慮偏置項 b) 爲:
-
Linear(M->N): 參數數目:M×N
-
Conv2d(Cin, Cout, K): 參數數目:Cin × Cout × K × K
-
BatchNorm(N): 參數數目: 2N
-
Embedding(N,W): 參數數目: N × W
參數佔用顯存 = 參數數目 ×n
n = 4 :float32
n = 2 : float16
n = 8 : double64
在 PyTorch 中,當你執行完model=MyGreatModel().cuda()之後就會佔用相應的顯存,佔用的顯存大小基本與上述分析的顯存差不多(會稍大一些,因爲其它開銷)。
1.2.2 梯度與動量的顯存佔用
舉例來說, 優化器如果是 SGD:
可以看出來,除了保存 W 之外還要保存對應的梯度
,因此顯存佔用等於參數佔用的顯存 x2,
如果是帶 Momentum-SGD
這時候還需要保存動量, 因此顯存 x3
如果是 Adam 優化器,動量佔用的顯存更多,顯存 x4
總結一下,模型中與輸入無關的顯存佔用包括:
-
參數W
-
梯度 dW(一般與參數一樣)
-
優化器的動量(普通 SGD 沒有動量,momentum-SGD 動量與梯度一樣,Adam 優化器動量的數量是梯度的兩倍)
1.2.3 輸入輸出的顯存佔用
這部份的顯存主要看輸出的 feature map 的形狀。
feature map
比如卷積的輸入輸出滿足以下關係:
據此可以計算出每一層輸出的 Tensor 的形狀,然後就能計算出相應的顯存佔用。
模型輸出的顯存佔用,總結如下:
-
需要計算每一層的 feature map 的形狀(多維數組的形狀)
-
模型輸出的顯存佔用與 batch size 成正比
-
需要保存輸出對應的梯度用以反向傳播(鏈式法則)
-
模型輸出不需要存儲相應的動量信息(因爲不需要執行優化)
深度學習中神經網絡的顯存佔用,我們可以得到如下公式:
顯存佔用 = 模型顯存佔用 + batch_size × 每個樣本的顯存佔用
可以看出顯存不是和 batch-size 簡單的成正比,尤其是模型自身比較複雜的情況下:比如全連接很大,Embedding 層很大
另外需要注意:
-
輸入(數據,圖片)一般不需要計算梯度
-
神經網絡的每一層輸入輸出都需要保存下來,用來反向傳播,但是在某些特殊的情況下,我們可以不要保存輸入。比如 ReLU,在 PyTorch 中,使用nn.ReLU(inplace = True)能將激活函數 ReLU 的輸出直接覆蓋保存於模型的輸入之中,節省不少顯存。感興趣的讀者可以思考一下,這時候是如何反向傳播的(提示:y=relu(x) -> dx = dy.copy();dx[y<=0]=0)
1.3 節省顯存的方法
在深度學習中,一般佔用顯存最多的是卷積等層的輸出,模型參數佔用的顯存相對較少,而且不太好優化。
節省顯存一般有如下方法:
-
降低 batch-size
-
下采樣 (NCHW -> (1/4)*NCHW)
-
減少全連接層(一般只留最後一層分類用的全連接層)
2 計算量分析
計算量的定義,之前已經講過了,計算量越大,操作越費時,運行神經網絡花費的時間越多。
2.1 常用操作的計算量
常用的操作計算量如下:
-
全連接層:BxMxN , B 是 batch size,M 是輸入形狀,N 是輸出形狀。
-
卷積的計算量:
卷積的計算量分析
-
BatchNorm 計算量我個人估算大概是
,歡迎指正
-
池化的計算量:
-
ReLU 的計算量: BHWC
2.2 AlexNet 分析
AlexNet 的分析如下圖,左邊是每一層的參數數目(不是顯存佔用),右邊是消耗的計算資源
AlexNet 分析
可以看出:
-
全連接層佔據了絕大多數的參數
-
卷積層的計算量最大
2.3 減少卷積層的計算量
今年穀歌提出的 MobileNet,利用了一種被稱爲 DepthWise Convolution 的技術,將神經網絡運行速度提升許多,它的核心思想就是把一個卷積操作拆分成兩個相對簡單的操作的組合。如圖所示, 左邊是原始卷積操作,右邊是兩個特殊而又簡單的卷積操作的組合(上面類似於池化的操作,但是有權重,下面類似於全連接操作)。
Depthwise Convolution
這種操作使得:
-
顯存佔用變多 (每一步的輸出都要保存)
-
計算量變少了許多,變成原來的(
)(一般爲原來的 10-15%)
2.4 常用模型 顯存 / 計算複雜度 / 準確率
去年一篇論文 (arxiv.org/abs/1605.07678https://http://link.zhihu.com/?target=https%3A//arxiv.org/abs/1605.07678http://link.zhihu.com/?target=https%3A//arxiv.org/abs/1605.07678https://arxiv.org/abs/1605.07678) 總結了當時常用模型的各項指標,橫座標是計算複雜度(越往右越慢,越耗時),縱座標是準確率(越高越好),圓的面積是參數數量(不是顯存佔用)。左上角我畫了一個紅色小圓,那是最理想的模型的的特點:快,效果好,佔用顯存小。
常見模型計算量 / 顯存 / 準確率
3 總結3.1 建議
-
時間更寶貴,儘可能使模型變快(減少 flop)
-
顯存佔用不是和 batch size 簡單成正比,模型自身的參數及其延伸出來的數據也要佔據顯存
-
batch size 越大,速度未必越快。在你充分利用計算資源的時候,加大 batch size 在速度上的提升很有限
尤其是 batch-size,假定 GPU 處理單元已經充分利用的情況下:
-
增大 batch size 能增大速度,但是很有限(主要是並行計算的優化)
-
增大 batch size 能減緩梯度震盪,需要更少的迭代優化次數,收斂的更快,但是每次迭代耗時更長。
-
增大 batch size 使得一個 epoch 所能進行的優化次數變少,收斂可能變慢,從而需要更多時間才能收斂(比如 batch_size 變成全部樣本數目)。
3.2 關於顯卡選購
當前市面上常用的顯卡指標如下:
常見顯卡指標
更多顯卡的更多指標請參閱 http://t.cn/RLZfju0https://ist_of_Nvidia_graphics_processing_unit
顯然GTX 1080TI性價比最高,速度超越新 Titan X,價格卻便宜很多,顯存也只少了 1 個 G(據說故意閹割掉一個 G,不然全面超越了 Titan X 怕激起買 Titan X 人的民憤~)。
-
K80 性價比很低(速度慢,而且賊貴)
-
注意 GTX TITAN 和 Nvidia TITAN 的區別,別被騙
另外,針對本文,我做了一個 Google 幻燈片:神經網絡性能分析(http://t.cn/RTla8BQ),國內用戶可以在此鏈接(http://t.cn/RTla1vB)下載 ppt。Google 幻燈片格式更好,後者格式可能不太正常。
本文都是針對單機單卡的分析,分佈式的情況會和這個有所區別。在分析計算量的時候,只分析了前向傳播,反向傳播計算量一般會與前向傳播有細微的差別。