深度學習PyTorch,TensorFlow中GPU利用率較低,CPU利用率很低,且模型訓練速度很慢的問題總結與分析

        在深度學習模型訓練過程中,在服務器端或者本地pc端,輸入nvidia-smi來觀察顯卡的GPU內存佔用率Memory-Usage),顯卡的GPU利用率GPU-util),然後採用top來查看CPU的線程數(PID數)和利用率(%CPU)。往往會發現很多問題,比如,GPU內存佔用率低,顯卡利用率低,CPU百分比低等等。接下來仔細分析這些問題和處理辦法。

1. GPU內存佔用率問題

        這往往是由於模型的大小以及batch size的大小,來影響這個指標。當你發下你的GPU佔用率很小的時候,比如40%,70%,等等。此時,如果你的網絡結構已經固定,此時只需要改變batch size的大小,就可以儘量利用完整個GPU的內存。GPU的內存佔用率主要是模型的大小,包括網絡的寬度,深度,參數量,中間每一層的緩存,都會在內存中開闢空間來進行保存,所以模型本身會佔用很大一部分內存。其次是batch size的大小,也會佔用影響內存佔用率。batch size設置爲128,與設置爲256相比,內存佔用率是接近於2倍關係。當你batch  size設置爲128,佔用率爲40%的話,設置爲256時,此時模型的佔用率約等於80%,偏差不大。所以在模型結構固定的情況下,儘量將batch size設置大,充分利用GPU的內存。(GPU會很快的算完你給進去的數據,主要瓶頸在CPU的數據吞吐量上面。)

2. GPU利用率問題

        這個是Volatile GPU-Util表示,當沒有設置好CPU的線程數時,這個參數是在反覆的跳動的,0%,20%,70%,95%,0%。這樣停息1-2 秒然後又重複起來。其實是GPU在等待數據從CPU傳輸過來,當從總線傳輸到GPU之後,GPU逐漸起計算來,利用率會突然升高,但是GPU的算力很強大,0.5秒就基本能處理完數據,所以利用率接下來又會降下去,等待下一個batch的傳入。因此,這個GPU利用率瓶頸在內存帶寬和內存介質上以及CPU的性能上面。最好當然就是換更好的四代或者更強大的內存條,配合更好的CPU。

        另外的一個方法是,在PyTorch這個框架裏面,數據加載Dataloader上做更改和優化,包括num_workers(線程數),pin_memory,會提升速度。解決好數據傳輸的帶寬瓶頸和GPU的運算效率低的問題。在TensorFlow下面,也有這個加載數據的設置。

torch.utils.data.DataLoader(image_datasets[x],
                            batch_size=batch_size, 
                            shuffle=True,
                            num_workers=8,
                            pin_memory=True)

        爲了提高利用率,首先要將num_workers(線程數)設置得體,4,8,16是幾個常選的幾個參數。本人測試過,將num_workers設置的非常大,例如,24,32,等,其效率反而降低,因爲模型需要將數據平均分配到幾個子線程去進行預處理,分發等數據操作,設高了反而影響效率。當然,線程數設置爲1,是單個CPU來進行數據的預處理和傳輸給GPU,效率也會低。其次,當你的服務器或者電腦的內存較大,性能較好的時候,建議打開pin_memory打開,就省掉了將數據從CPU傳入到緩存RAM裏面,再給傳輸到GPU上;爲True時是直接映射到GPU的相關內存塊上,省掉了一點數據傳輸時間。

3. CPU的利用率問題

        很多人在模型訓練過程中,不只是關注GPU的各種性能參數,往往還需要查看CPU處理的怎麼樣,利用的好不好。這一點至關重要。但是對於CPU,不能一味追求超高的佔用率。如圖所示,對於14339這個程序來說,其CPU佔用率爲2349%(我的服務器是32核的,所以最高爲3200%)。這表明用了24核CPU來加載數據和做預處理和後處理等。其實主要的CPU花在加載傳輸數據上。此時,來測量數據加載的時間發現,即使CPU利用率如此之高,其實際數據加載時間是設置恰當的DataLoader的20倍以上,也就是說這種方法來加載數據慢20倍。當DataLoader的num_workers=0時,或者不設置這個參數,會出現這個情況。

CPU利用率查看結果
CPU利用率查看結果

        下圖中可以看出,加載數據的實際是12.8s,模型GPU運算時間是0.16s,loss反傳和更新時間是0.48s。此時,即使CPU爲2349%,但模型的訓練速度還是非常慢,而且,GPU大部分是時間是空閒等待狀態。

num_workers=0,模型每個階段運行時間統計

        當我將num_workers=1時,出現的時間統計如下,load data time爲6.3,數據加載效率提升1倍。且此時的CPU利用率爲170%,用的CPU並不多,性能提升1倍。

num_workers=1時,模型每個階段運行時間統計

        此時,查看GPU的性能狀態(我的模型是放在1,2,3號卡上訓練),發現,雖然GPU(1,2,3)的內存利用率很高,基本上爲98%,但是利用率爲0%左右。表面此時網絡在等待從CPU傳輸數據到GPU,此時CPU瘋狂加載數據,而GPU處於空閒狀態

1,2,3號GPU的內存佔用率和計算效率截圖

        由此可見,CPU的利用率不一定最大才最好。

        對於這個問題,解決辦法是,增加DataLoader這個num_wokers的個數,主要是增加子線程的個數,來分擔主線程的數據處理壓力,多線程協同處理數據和傳輸數據,不用放在一個線程裏負責所有的預處理和傳輸任務。

        我將num_workers=8,16都能取得不錯的效果。此時用top查看CPU和線程數,如果我設置爲num_workers=8,線程數有了8個連續開闢的線程PID,且大家的佔用率都在100%左右,這表明模型的CPU端,是較好的分配了任務,提升數據吞吐效率。效果如下圖所示,CPU利用率很平均和高效,每個線程是發揮了最大的性能。

num_workers=8時,CPU利用率和8個連續PID任務

        此時,在用nvidia-smi查看GPU的利用率,幾塊GPU都在滿負荷,滿GPU內存,滿GPU利用率的處理模型,速度得到巨大提升。

優化數據加載num_workers=8,和設置batch size的結果

        上圖中可以看見,GPU的內存利用率最大化,此時是將batch size設置的較大,佔滿了GPU的內存,然後將num_workers=8,分配多個子線程,且設置pin_memory=True,直接映射數據到GPU的專用內存,減少數據傳輸時間。GPU和CPU的數據瓶頸得到解決。整體性能得到權衡。

        此時的運行時間在表中做了統計:

處理時間統計
處理階段 時間
數據加載 0.25s
模型在GPU計算 0.21s
loss反傳,參數更新 0.43s

4. 總結

        對上面的分析總結一下,第一是增加batch size,增加GPU的內存佔用率,儘量用完內存,而不要剩一半,空的內存給另外的程序用,兩個任務的效率都會非常低。第二,在數據加載時候,將num_workers線程數設置稍微大一點,推薦是8,16等,且開啓pin_memory=True不要將整個任務放在主進程裏面做,這樣消耗CPU,且速度和性能極爲低下。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章