大模型如何提升訓練效率

Aurelius84發表於2024-07-08

一、問題背景

隨著AIGC領域的興起,各大廠商都在訓練和推出自研的大模型結構,並結合業務進行落地和推廣。在大模型分散式訓練場景中,主流的主要是基於英偉達GPU進行訓練(如A100),如何有效地壓榨GPU的計算能力,提升訓練效率,降低訓練成本,是一個非常重要的實踐最佳化問題。

1.1 直接目標

最直接地目標就是提升GPU使用率,充分發揮GPU的計算潛力,以加快模型訓練。包括調整 Batch 大小以提供更多並行任務給GPU,最佳化DataLoader以減少GPU的等待時間,以及選擇或設計與GPU並行處理能力相匹配的模型架構。

1.2 額外收益

從正向思維來看,透過合理分配CPU、GPU、記憶體和儲存資源,可以提高資源利用率,加速訓練過程,以提高整體的成本效益;同時可減少因資源限制導致訓練中斷或者失敗,保證訓練流程的順利進行,提高模型訓練有效率;
從反向思維來看,透過高效的硬體利用,降低能源消耗(如電力),降低長期的訓練費用,實現經濟高效的模型訓練,並減少訓練成本。

二、衡量指標

2.1 GPU利用率(GPU-Util)

大模型如何提升訓練效率
  • 定義:GPU利用率是指GPU在訓練過程中的繁忙程度,通常以百分比表示。這個指標直接反映了GPU資源的使用情況。
  • 重要性:高利用率表示GPU在大部分時間都在進行計算操作,資源被充分利用;而低利用率則表示GPU有空閒時間未被充分利用,可能存在效能瓶頸或資源分配不均的問題。

2.2 視訊記憶體使用(Memory-Usage)

  • 定義 :視訊記憶體使用是指GPU視訊記憶體的佔用情況,也是衡量GPU資源利用的重要指標之一。
  • 重要性 :視訊記憶體的充分利用對於高效訓練至關重要。如果視訊記憶體使用率很高,接近GPU視訊記憶體的上限,可能會導致視訊記憶體溢位錯誤,影響訓練過程的穩定性。相反,如果視訊記憶體使用率很低,則可能意味著模型沒有充分利用GPU資源。

2.3 MFU(Model FLOPs Utilization)

  • 定義:即模型算力利用率,是指模型一次前反向計算消耗的矩陣算力與機器(如GPU)算力的比值。
  • 重要性 :它直接反映了模型在訓練過程中對計算資源的有效利用程度。在大模型訓練中,提高MFU是最佳化訓練效率的關鍵手段之一。
$\text{MFU} = \frac{\text{model FLOPs per iteration}}{(\text{GPU單卡算力} \times \text{卡數}) \times \text{一次迭代時間}}$

MFU的值受到多種因素的影響,包括但不限於:

  1. 模型架構 :不同架構的模型在分散式訓練中的算力利用率可能存在顯著差異。例如,Transformer架構的模型由於其並行性和矩陣運算密集的特點,通常能夠在分散式訓練中實現較高的MFU。
  2. 分散式訓練策略 :包括資料並行、模型並行、流水線並行等不同的並行策略,以及這些策略的具體實現方式(如梯度累積、張量並行等),都會對MFU產生重要影響。
  3. 硬體環境 :GPU型號、數量、網路連線速度、視訊記憶體大小等硬體因素都會限制分散式訓練的效能和算力利用率。
  4. 軟體最佳化 :包括編譯器最佳化、庫函式最佳化、自動混合精度訓練等軟體層面的最佳化措施,也能在一定程度上提升MFU。
  5. 資料集和批次大小 :資料集的大小和複雜性,以及訓練時使用的批次大小,都會影響每次迭代中的計算量和算力利用率。

由於上述因素的多樣性和複雜性,各大開源模型在分散式訓練上的MFU指標很難一概而論。不過,一般來說,經過良好最佳化和適配的開源模型,在高階GPU叢集上進行分散式訓練時,MFU值通常能夠達到較高的水平(例如,接近或超過50%)。但請注意,這只是一個大致的估計,具體數值可能因模型、硬體和訓練策略的不同而有所差異。

位元組跳動的MegaScale透過演算法-系統協同設計,最佳化了訓練效率。在12288個GPU上訓練一個175B LLM模型時,實現了55.2%的MFU,比Megatron-LM提高了1.34倍。

2.4 TensorCore利用率

  • 定義 :TensorCore利用率是指Tensor Core在處理深度學習任務時,其計算資源的實際使用效率。
  • 重要性 :Tensor Core是NVIDIA GPU上用於加速矩陣乘法和卷積運算的特殊處理單元,對於提升深度學習模型的訓練和推理速度至關重要。
大模型如何提升訓練效率

使用如torch profiler等工具記錄訓練過程中各個函式的時間消耗,並在tensorboard等視覺化工具上展示,從而可以直接檢視到Tensor Core的利用率。

  • 優點:這種方法能夠提供更詳細和準確的資訊,包括Tensor Core的具體使用情況和潛在的效能瓶頸。

三、問題分析

3.1 GPU利用率低

透過 nvidia-smi 檢視GPU利用率低,則可能意味著GPU等待CPU處理資料,或者IO速度成為瓶頸。這種情況下,可能需要最佳化資料載入過程,使用更快的資料預處理方法,或者提高資料批次大小。

3.1.1 單卡GPU利用率低

  • CPU瓶頸 :CPU處理資料速度跟不上GPU,導致GPU等待。
  • IO限制 :資料從硬碟或網路到GPU的傳輸速度慢。
  • 小批次大小 :批次大小設定過小,GPU未充分利用。
  • 模型複雜度 :簡單模型可能無法充分利用GPU能力。
  • 記憶體頻寬限制 :資料從記憶體到GPU的傳輸速度成為限制因素。

3.1.2 多卡GPU利用率都低

  • 全域性資料處理瓶頸 :所有GPU都在等待資料處理或載入。
  • 網路通訊瓶頸 :分散式訓練中的網路頻寬不足。
  • 不適當的並行策略 :資料並行或模型並行策略設定不當。
  • 軟體框架和驅動問題 :深度學習框架或GPU驅動配置不佳。

3.1.2 多卡GPU利用率間歇性低

  • 非同步資料處理問題 :資料載入和預處理的不連續性。
  • 模型同步問題 :模型並行中的GPU等待其他GPU同步。
  • 負載不均衡 :工作分配給各GPU不均勻。

3.2 CPU負載過高

可以使用 htop 工具觀察CPU核心的使用率。如果大部分或所有核心的使用率接近100%,表明CPU正在高強度工作,很可能成了來訓練的瓶頸。這可能意味著模型訓練正處於密集的計算階段,

最佳化思路:

  1. 最佳化模型程式碼:最佳化資料預處理和模型程式碼,提高計算效率。
  2. 多執行緒或多程序 :在資料載入和預處理階段使用多執行緒或多程序,以更好地利用多核CPU。
  3. 減輕CPU負擔 :如果可能,將一些計算任務轉移到GPU上,特別是對於可以並行化的任務。

另外還需要注意CPU記憶體的使用情況。如果記憶體使用率很高,接近或達到系統的最大容量,可能導致交換使用(swap usage),這會顯著減慢訓練速度。

3.3 高 I/O 等待

如果透過分析確認CPU和記憶體並不是訓練瓶頸,那麼IO(輸入/輸出)瓶頸可能是導致模型訓練效率低下的原因之一。IO瓶頸通常發生在資料從儲存裝置載入到CPU或GPU的過程中。

大模型如何提升訓練效率

我們可以使用iotop工具檢視總的IO讀寫效能現狀資料,如上圖,從分析角度而言:

  • 理解模型需求 :分析模型訓練過程中對資料的需求。某些型別的訓練,如大規模影像或影片處理,自然需要更高的I/O吞吐量。
  • 考慮資料預處理 :評估資料預處理步驟是否在I/O階段造成瓶頸,如複雜的資料增強或轉換操作。

最佳化思路:

1. 使用更快的儲存介質 :如使用SSD代替傳統HDD。
2. 預載入資料 :在訓練開始前預載入資料到記憶體中,或者實現資料快取機制,以減少對磁碟的依賴。
3. 改進資料格式 :使用如HDF5或TFRecord這類支援高效讀寫的檔案格式,這些格式最佳化了大規模資料集的儲存和訪問
4. 合併小檔案 :將多個小資料檔案合併成較大的單個檔案,以減少磁碟尋道時間和提高讀取效率。
5. 並行讀取 :使用多執行緒或非同步I/O來並行讀取資料,特別是從大檔案中讀取時,這可以顯著提高I/O效率。

四、分層最佳化總結

4.1 模型層面

4.1.1 避免D2H的同步複製

當頻繁地使用 tensor.cpu() 將張量從 GPU 轉到 CPU(或使用 tensor.cuda() 將張量從 CPU 轉到 GPU)時,代價是非常昂貴的。item() 和 .numpy() 也是一樣可以使用. detach() 代替。如果你需要傳輸資料,可以使用 . to(non_blocking=True) ,只要在傳輸之後沒有同步點。

4.1.2 開啟運算元融合策略

運算元融合是將原本需要多次記憶體訪問和計算的操作合併為一個操作,減少中間結果的儲存和傳輸開銷。例如,在深度學習模型中,常見的運算元融合包括將多個連續的線性變換(如卷積、全連線層等)合併為一個大的線性變換,或者將矩陣乘法與偏置相加等操作融合。

1. 高頻運算元最佳化 :針對大模型中頻繁出現的運算元進行最佳化。例如,矩陣乘法(MatMul)是Transformer等大模型中的高頻運算元,透過運算元tiling最佳化合理切分資料,透過運算元流水最佳化使得資料搬運與計算流水並行,可以顯著提升計算效率。
2. 大顆粒運算元融合 :將多個計算步驟融合為一個大的計算步驟。例如,CANN(昇騰計算架構)透過Flash/Sparse Attention大顆粒運算元融合,減少視訊記憶體耗用,提升計算效能。這種融合方式支援多種資料型別和長序列模型,具有廣泛的應用前景。
3. Transformer加速庫 :針對Transformer等大模型結構,構築專門的加速庫來最佳化核心Kernel效能。這些加速庫包含了一系列針對Transformer最佳化的運算元,如FlashAttention、MOE FFN等,能夠顯著提升模型的訓練速度。

4.1.3 使用AMP加速訓練

PyTorch 1.6 版本包括對 PyTorch 的自動混合精度訓練的本地實現。這裡想說的是,與單精度 (FP32) 相比,某些運算在半精度 (FP16) 下執行更快,而不會損失準確率。AMP 會自動決定應該以哪種精度執行哪種運算。這樣既可以加快訓練速度,又可以減少記憶體佔用。

4.1.4 其他API層面技巧

torch.tensor() 總是會複製資料。如果你要轉換一個 numpy 陣列,使用 torch.as_tensor() 或 torch.from_numpy() 來避免複製資料。
在開始 BatchNormalization 層之前關閉 bias 層。對於一個 2-D 卷積層,可以將 bias 關鍵字設定為 False:torch.nn.Conv2d(..., bias=False, ...)。

4.2 資料處理

4.2.1 合理設定num_worker

當使用 torch.utils.data.DataLoader 時,設定 num_workers > 0,而不是預設值 0,同時設定 pin_memory=True ,而不是預設值 False。
選擇 worker 數量的經驗法則是將其設定為可用 GPU 數量的四倍,大於或小於這個數都會降低訓練速度。請注意,增加 num_workers 將增加 CPU 記憶體消耗。

4.2.2 指定 pinn_memory=True

在使用 PyTorch 的 DataLoader 時,設定pin_memory=True主要是為了加速資料的載入過程。這個選項的作用是將載入的資料張量(Tensor)預先儲存在鎖頁記憶體(pinned memory)中,而不是常規的記憶體。鎖頁記憶體的一個重要特性是,它可以被GPU直接訪問,而不需要透過CPU進行中轉。這樣做的好處是可以減少資料在CPU和GPU之間的傳輸次數,從而降低資料傳輸的開銷,加快訓練過程。

什麼是鎖頁記憶體?

鎖頁記憶體(Pinned Memory或Page Locked Memory)是一種特殊的記憶體分配方式,在CUDA程式設計和深度學習中尤為重要。以下是關於鎖頁記憶體的詳細解釋:

鎖頁記憶體是指分配在主機(CPU)記憶體中的一塊區域,該區域被作業系統鎖定,以防止其在實體記憶體中移動或換頁到虛擬記憶體(即硬碟空間)中。這樣做的主要目的是為了提高資料在主機與裝置(如GPU)之間的傳輸效率,主要特性如下:

  1. 固定位置 :鎖頁記憶體被固定在實體記憶體中的特定位置,不會因記憶體壓力而被作業系統移動到虛擬記憶體中。這保證了記憶體地址的穩定性,對於需要直接記憶體訪問(DMA)的硬體外設(如GPU)來說至關重要。
  2. 提高傳輸效率 :由於鎖頁記憶體不會移動,GPU上的DMA控制器可以直接訪問這些記憶體區域,而無需CPU的介入。這減少了CPU的負擔,並提高了資料傳輸的速度和效率。
  3. 分配與釋放:在CUDA中,鎖頁記憶體可以透過CUDA驅動API(如cuMemAllocHost())或執行時API(如cudaMallocHost())來分配。釋放時,應使用相應的函式(如cudaFreeHost())。此外,還可以使用主機上的Malloc()函式分配記憶體,然後透過cudaHostRegister()函式將其註冊為鎖頁記憶體。

4.2.3 使用DALI庫

DALI庫的核心優勢在於其GPU加速能力。傳統的資料預處理過程大多在CPU上執行,包括資料的載入、解碼、裁剪、調整大小等操作,這些操作往往是計算密集型的,且受限於CPU的計算能力。而DALI庫透過將部分或全部資料預處理任務解除安裝到GPU上執行,充分利用了GPU強大的平行計算能力,從而顯著提升了資料預處理的速度。這種GPU加速能力使得DALI在處理大規模資料集時能夠保持高效,減少了資料預處理對整體訓練過程的瓶頸效應。

DALI允許開發人員建立自定義的資料處理pipeline,透過靈活的計算圖來表示不同的資料處理流程。開發人員可以根據需要選擇適當的運算子,並指定它們在CPU或GPU上執行,從而構建出最適合自己應用場景的資料預處理方案。此外,DALI還支援透過自定義運算子來擴充套件其功能,以滿足特定的資料處理需求。DALI的非同步執行和預取機制有助於隱藏預處理延遲,使得整體訓練流程更加流暢。

4.2.4 藉助資料預取

在模型訓練過程中,資料預取技術主要應用於以下幾個方面:

  1. 大規模資料集處理 :對於包含數百萬甚至數十億樣本的大規模資料集,資料預取技術可以顯著減少資料處理的時間,提高訓練效率。
  2. 深度學習模型訓練 :在訓練深度學習模型時,資料預取可以幫助模型在GPU或TPU等加速器上持續獲得訓練資料,減少因資料載入不足而導致的計算資源閒置。
  3. 分散式訓練 :在分散式訓練環境中,資料預取技術可以確保各個計算節點能夠及時獲得所需的資料,保持訓練過程的同步性和高效性。

4.3 框架層面

如果是自研的框架,可以考慮結合前沿深度學習框架發展趨勢,研發更高效的功能,開發給使用者訓推加速。比如開啟編譯器,或者針對模型特點,自定義實現更大的融合運算元;

如果是成熟的開源框架,可以參考官網的最佳實踐,或者探索、組合、定製化各種加速策略,包括分散式混合並行的編排方式等,實現訓練加速;

4.4 硬體層面

  1. 記憶體頻寬限制:資料從記憶體到GPU的傳輸速度成為限制因素。
  2. 軟體框架和驅動問題:深度學習框架或GPU驅動配置不佳。

五、業務導向

如果所在的部門是負責管理公司的GPU資源的,GPU利用率的治理和提升是部門重要的OKR,從這個角度來來闡述下如何達成OKR?

5.1 指標監控體系(What)

5.1.1 GPU 利用率

可以透過 nvidia-smi 工具和自動化指令碼,監控模型訓練期間的GPU 利用率、視訊記憶體佔用、功率、顯示卡溫度、風扇轉速等資訊

5.1.2 CPU 負載

透過 htop 等系統或三方工具,監控模型訓練程序的CPU負載、判斷是否為CPU瓶頸;監控記憶體使用情況,是否處於接近打滿記憶體的狀態,此時可能會出現頻繁的記憶體交換;如果關心磁碟IO的狀態,可以使用iotop等工具來監控模型訓練期間磁碟讀寫的情況,用於判斷是否為IO瓶頸;

5.1.3 TensorCore利用率

TensorCore的統計有多種,對於給定的模型,其一個前反向計算所需要的FLOPS是固定的,只需要在模型訓練日誌裡記錄其每個迭代的訓練時間即可,透過公式即可得到TensorCore的利用率。

5.1.4 通訊延遲和頻寬佔用

5.2 瓶頸分析工具(Why)

5.2.1 Nsight system 工具

NVIDIA Nsight Systems是一個用於監測程式碼執行效率及分析效能的工具,它是NVIDIA Nsight系列的一部分。Nsight Systems可以收集和分析應用程式在CPU和GPU上的執行資料,幫助開發者識別效能瓶頸,最佳化程式效能。
1. 安裝Nsight Systems:

  • 訪問NVIDIA官方網站下載Nsight Systems的安裝包。
  • 按照安裝嚮導完成安裝過程。

2. 配置環境:

  • 確保CUDA環境變數已正確配置,以便Nsight Systems能夠識別CUDA應用程式。

3. 生成Profile檔案:

  • 使用Nsight Systems提供的命令列工具(如nsys)來生成Profile檔案。例如,可以使用nsys profile -o profile_name.qdrep python test.py命令來生成Python指令碼的Profile檔案。

4. 分析Profile檔案:

  • 開啟Nsight Systems GUI,載入生成的Profile檔案(.qdrep格式)。
  • 使用Nsight Systems的圖形介面來檢視和分析應用程式的執行資料,包括CPU和GPU的使用情況、函式呼叫時間、記憶體訪問等。

5.2.2 torch profile 工具

如果你是使用的Pytorch框架,可以使用其profile工具和tensorboard視覺化介面來分析模型的效能瓶頸點,具體可以參考:PyTorch Profiler With TensorBoard。

1. 計算圖概況 :torch profile可以生成模型的計算圖,並突出顯示每個操作的時間消耗,這有助於識別潛在的瓶頸。
2. 記憶體使用統計 :實時監控模型訓練過程中的GPU記憶體佔用情況,便於調整引數以最佳化資源利用。
3. 快速定位效能問題 :透過視覺化每個操作的執行時間,可以迅速找到導致模型訓練速度慢的原因。
4. 最佳化硬體資源利用 :檢視記憶體使用情況,以便確定合適的批次大小和其他記憶體管理策略。
5. 詳細的層析剖析 :不僅僅提供總體效能分析,還可以對模型中的每一個單獨層進行分析,展示每個層在CPU和CUDA上的執行時間。
6. 事件列表 :提供原始的PyTorch事件列表,以便開發者能夠深入瞭解底層操作(如卷積、啟用函式等)的執行時間。
7. 選擇性剖析 :使用者可以指定要分析的具體層,其餘層將被忽略,這樣可以更加聚焦於關鍵部分。
8. 硬體資源管理 :分析模型在不同硬體環境下的效能,比如CPU與GPU之間的切換。

5.3 系統的最佳化方法論(How)

5.3.1 明確GPU 空閒產生的原因

藉助視覺化工具分析profile檔案中,GPU 空閒產生的原因,判斷屬於是哪幾類問題?

  • 資料讀取瓶頸 :一般在timeline裡每個Step的開始前面都會有固定的GPU空閒,資料讀取無法與計算實現Overlap;
  • 模型寫法問題 :比如模型計算中間有Sync操作,可能是存在D2H的同步複製,比如存在Tensor.numpy()等類似操作;
  • 通訊存在瓶頸 :在分散式訓練中,不同的並行策略可能會影響通訊成本;一般張量並行放在同一個機器不同卡上,資料並行是不同機器之間;
大模型如何提升訓練效率

5.3.2 對症下藥

最終目的是最大化GPU利用率,前面章節已經根據各種場景針對性地闡述了對應的解決方案;

5.3.3 指導性建議

類似torch.profile在基於TensorBorad視覺化後,會針對性提供一些最佳化的思路,本質是一個對各種監控指標和原生profile資料的建模問題。可以根據沉澱的經驗,結合不同的指標特徵給業務同學更多的最佳化經驗準則。

從GPU資源管理平臺而言,前期應該是儘可能是給出可置信的核心指標資料,以及輔助的層次化指標(如CPU負載、IO等)。大模型訓練最佳化實施方仍然需要模型負責人接入,由於大模型一般變動的頻率比較低,因為聯動框架同學一同定製化最佳化。

相關文章