多機多卡(Distributed Training)微調大模型是一項復雜但非常高效的任務。它允許你利用多臺機器的計算資源來訓練一個模型,從而顯著縮短訓練時間。
多機多卡微調核心流程
整個流程可以概括為以下幾個核心步驟:
- 環境準備與硬件配置
? 硬件:確保擁有多臺(例如2-4臺)服務器/工作站,每臺機器配備多張GPU(通常是4卡或8卡)。機器之間需要通過高速網絡互聯(如InfiniBand或高速萬兆以太網),低延遲和高帶寬對通信效率至關重要。
? 軟件與環境:
? 操作系統:通常使用Linux(如Ubuntu)。? 驅動:在所有機器上安裝相同版本的NVIDIA GPU驅動。? CUDA & cuDNN:在所有機器上安裝相同版本的CUDA工具包和cuDNN。? Python環境:使用conda或venv創建一致的Python環境,確保所有機器上的Python版本、PyTorch/TensorFlow版本、以及其他依賴庫版本完全一致。這是避免莫名錯誤的關鍵。? 通信庫:安裝并配置好NCCL(NVIDIA Collective Communication Library),它是多機多卡通信的基礎,通常隨PyTorch等框架一起安裝。
- 代碼修改與分布式策略選擇
這是最核心的技術部分。你需要修改你的訓練代碼,使其支持分布式訓練。
主流方案選擇:
? A. 基于數據并行(Data Parallelism)
? 核心思想:將一份完整的模型復制到每臺機器的每個GPU上,然后將訓練數據分成多個小塊(batch),分配到各個GPU上并行計算。每個GPU計算完梯度后,再進行梯度同步(所有GPU的梯度求平均),最后每個GPU用同步后的梯度更新自己的模型參數。這樣,所有GPU上的模型始終保持一致。? 實現方式:? DP (DataParallel):PyTorch早期的單機多卡方案,不適用于多機,且效率較低(主卡負載重),不推薦。? DDP (DistributedDataParallel):當前PyTorch的多機多卡首選方案。每個GPU都有一個獨立的進程,通過NCCL在后臺進行高效的All-Reduce通信來完成梯度同步,效率遠高于DP。? 適用場景:模型本身可以完整地放入單個GPU的顯存中。這是最常見和最簡單的場景。
? B. 基于模型并行(Model Parallelism)或混合并行
? 核心思想:當模型非常大,單個GPU的顯存放不下整個模型時,就需要將模型的不同層拆分到不同的GPU甚至不同的機器上。? 實現方式:? 流水線并行 (Pipeline Parallelism):將模型按層切分到多個GPU上。一個batch的數據會像在流水線上一樣,依次經過這些GPU進行計算。需要解決“流水線氣泡”問題。? 張量并行 (Tensor Parallelism):將模型內部的單個大權重矩陣運算(如GEMM)拆分到多個GPU上并行計算。例如,Megatron-LM和ColossalAI主要采用此方式。? FSDP (Fully Sharded Data Parallel):強烈推薦的現代方案。它結合了數據并行和模型并行。它會將模型參數、梯度、優化器狀態分片到所有GPU上,從而極大地節省了每張GPU的顯存占用。在計算時,需要哪個分片的參數就臨時通過通信獲取,計算完后再丟棄。這使得我們可以用更少的GPU來微調極大的模型(如LLaMA2-70B)。? 適用場景:微調超大規模模型(通常 > 10B 參數)。
一個簡單的比喻:??
?
??DDP?? 就像是一個??交響樂團??。每個樂手(GPU)面前都有一份完整的樂譜(完整模型),大家各自演奏自己的部分(處理數據分片),但會聽著指揮(梯度同步)來保持節奏一致。
?
??FSDP?? 就像是一個??劇團在排練一部厚厚的劇本??。劇本(完整模型)被拆成很多頁,分發給不同的演員(GPU)保管。當排練到某一場戲時,需要這場戲臺詞的那些演員就把自己保管的那幾頁紙拿出來念,念完就收回去。這樣不需要每個演員都拿著整本厚厚的劇本
代碼修改關鍵點(以PyTorch DDP為例):
-
初始化進程組:使用 torch.distributed.init_process_group 初始化分布式環境,指定后端(通常是nccl)、當前進程的rank、世界大小(總進程數=機器數*每臺機器的GPU數)等。
-
包裝模型:使用 torch.nn.parallel.DistributedDataParallel 包裝你的模型。
-
分配數據:使用 torch.utils.data.distributed.DistributedSampler 確保每個進程/GPU只加載和數據整體的一部分,避免數據重復。
-
調整日志與評估:通常只在rank 0進程(主進程)上打印日志、保存模型、做驗證評估,避免重復輸出。
-
啟動訓練任務
使用PyTorch內置的 torch.distributed.launch 或更推薦的 torchrun 來啟動訓練腳本。
示例命令:
假設有2臺機器(main_node: 192.168.1.1, node2: 192.168.1.2),每臺機器有4張GPU。在每臺機器上執行(或在主節點上通過ssh觸發):
# 在主節點 (rank 0) 上執行
torchrun \--nnodes=2 \ # 總機器數--nproc_per_node=4 \ # 每臺機器的GPU數--rdzv_id=456 \ # 任務唯一ID--rdzv_backend=c10d \ # 后端--rdzv_endpoint=192.168.1.1:1234 \ # 主節點地址和端口--node_rank=0 \ # 當前機器的rank(主節點為0)your_training_script.py \--your_script_args# 在第二臺機器 (node2, rank 1) 上執行
torchrun \--nnodes=2 \--nproc_per_node=4 \--rdzv_id=456 \--rdzv_backend=c10d \--rdzv_endpoint=192.168.1.1:1234 \ # 注意這里依然是主節點地址--node_rank=1 \ # 當前機器的rank(第二臺為1)your_training_script.py \--your_script_args
- 監控與調試
? 監控:使用nvidia-smi監控GPU利用率、顯存占用。使用htop監控CPU和內存。使用NCCL調試日志(export NCCL_DEBUG=INFO)來觀察通信是否正常。
? 日志:確保只有主進程(rank 0)記錄日志和保存TensorBoard文件,避免混亂。
? 保存與加載 checkpoint:保存 checkpoint 時,通常也只在 rank 0 進程上進行。加載時,需要先將 checkpoint 廣播到所有進程,或者使用 model.module.load_state_dict() 來加載。
關鍵注意事項
-
環境一致性:這是最大的坑!所有機器上的軟件環境(Python, PyTorch, CUDA, 庫版本)必須高度一致,否則會出現難以排查的錯誤。
-
網絡配置:
? 防火墻:確保所有機器之間在指定端口(如上面的1234)上可以互相通信。關閉防火墻或配置規則允許分布式訓練使用的端口范圍。? SSH互信(可選但推薦):方便在主節點上一鍵啟動所有機器上的任務。
-
數據準備:
? 數據集需要放在所有機器都能訪問的位置(如共享文件系統NFS,或每臺機器本地都有一份完整的數據副本)。? DistributedSampler 會自動為每個進程分配不重復的數據,確保每個batch的數據只被計算一次。
-
性能調優:
? 通信瓶頸:如果網絡慢,梯度同步會成為瓶頸。確保使用高速網絡(InfiniBand > 10GbE > 1GbE)。? Batch Size:全局batch size = 每卡batch_size * GPU總數。增大全局batch size時,可能需要調整學習率(線性縮放或使用LARS/LAMB等自適應優化器)。
? 梯度累積:如果每張GPU能承受的batch size很小,可以通過梯度累積來模擬更大的全局batch size,即多次前向后累積梯度再同步和更新。
-
容錯性:
? 多機任務運行時間長,一臺機器出錯可能導致整個任務失敗。需要思考如何做斷點續訓。? 使用 torchrun 時,它具備一定的彈性功能,可以處理部分節點失敗的情況。
-
選擇正確的并行策略:
? 如果顯存夠用,優先選擇 DDP,最簡單高效。? 如果顯存不足,嘗試使用 FSDP,這是目前社區微調大模型的主流和推薦方式。
? 除非模型極大(如萬億參數),否則一般不需要手動配置流水線并行或張量并行。
總結
多機多卡微調的核心流程是:環境配置 -> 選擇并行策略并修改代碼 -> 使用 torchrun 啟動任務 -> 監控和調試。
對于初學者,建議從 單機多卡(DDP) 開始,徹底掌握其原理和流程后,再擴展到多機。對于微調超大規模模型(如70B),直接學習并使用 FSDP。
工具選擇上,可以優先使用PyTorch原生的DDP/FSDP,如果追求更高級的功能和優化,也可以考慮基于PyTorch的第三方框架如 Deepspeed 或 ColossalAI。