轉載:來源CSDN https://blog.csdn.net/qq_34405401/article/details/108519823
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時,或者不設置這個參數,會出現這個情況。
下圖中可以看出,加載數據的實際是12.8s,模型GPU運算時間是0.16s,loss反傳和更新時間是0.48s。此時,即使CPU為2349%,但模型的訓練速度還是非常慢,而且,GPU大部分是時間是空閑等待狀態。

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

由此可見,CPU的利用率不一定最大才最好。
對於這個問題,解決辦法是,增加DataLoader這個num_wokers的個數,主要是增加子線程的個數,來分擔主線程的數據處理壓力,多線程協同處理數據和傳輸數據,不用放在一個線程里負責所有的預處理和傳輸任務。
我將num_workers=8,16都能取得不錯的效果。此時用top查看CPU和線程數,如果我設置為num_workers=8,線程數有了8個連續開辟的線程PID,且大家的占用率都在100%左右,這表明模型的CPU端,是較好的分配了任務,提升數據吞吐效率。效果如下圖所示,CPU利用率很平均和高效,每個線程是發揮了最大的性能。
此時,在用nvidia-smi查看GPU的利用率,幾塊GPU都在滿負荷,滿GPU內存,滿GPU利用率的處理模型,速度得到巨大提升。
上圖中可以看見,GPU的內存利用率最大化,此時是將batch size設置的較大,占滿了GPU的內存,然后將num_workers=8,分配多個子線程,且設置pin_memory=True,直接映射數據到GPU的專用內存,減少數據傳輸時間。GPU和CPU的數據瓶頸得到解決。整體性能得到權衡。
4. 總結
對上面的分析總結一下,第一是增加batch size,增加GPU的內存占用率,盡量用完內存,而不要剩一半,空的內存給另外的程序用,兩個任務的效率都會非常低。第二,在數據加載時候,將num_workers線程數設置稍微大一點,推薦是8,16等,且開啟pin_memory=True。不要將整個任務放在主進程里面做,這樣消耗CPU,且速度和性能極為低下。