預訓練是研發大語言模型的第一個訓練階段,通過在大規模語料上進行預訓練,大語言模型可以獲得通用的語言理解與生成能力,掌握較為廣泛的世界知識,具備解決眾多下游任務的性能潛力
一、數據預處理
1. 數據的收集
1)通用文本數據(“主食”)
- 來源:網頁(C4 、RefinedWeb、CC-Stories?等);書籍(Books3 、Bookcorpus2等);
- 特點:量大;多樣;需要清洗;注意搭配
2)專用文本數據(“特色”)
- 多語言文本:加入大量非英語的文本數據,加強多語言任務的同時,還能促進不同語言的知識遷移,提升模型泛化能力(BLOOM 和 PaLM 模型使用了百種語言進行訓練)
- 科學文本:加入大量科學文獻、論文(比如 ArXiv 數據集)、教科書等,提升模型在專業領域的問答、推理和信息抽取能力(注意公式等符號需要采用特定的分詞和預處理技術)
- 代碼:加入海量來自Stack Exchange、GitHub 等的代碼數據,提升編程能力
2. 數據預處理
1) 質量過濾--去除低質量
- 基于啟發式規則:以通過精心設計的規則(基于語種、統計指標、關鍵詞)來針對地識別和剔除低質量的文本數據
- 基于分類器:先人工標注一批高質量和低質量數據作為“教學樣本”訓練一個模型,讓它學習判斷文本質量的好壞(目前常用來實現分類器的方法包括輕量級模型(如 FastText 等)、可微調的預訓練語言模型(如 BERT、BART 或者 LLaMA 等)以及閉源大語言模型 API(如 GPT-4)
2) 敏感內容過濾--去除敏感
- 有毒內容:采用基于分類器的過濾方法(Jigsaw 評論數據集提供了用于訓練毒性分類器的數據)通過設置合理(精確度和召回率的平衡)的分類閾值,訓練完成的分類器將能夠有效識別并過濾掉含有有毒內容的信息
- 隱私內容:直接且有效的方法是使用啟發式方法,如關鍵字識別來檢測和刪除私人信息
3)去重
- 基于計算粒度:去重可以在句子級別、文檔級別和數據集級別等多種粒度上進行,現有的數據集往往采用多階段、多粒度的方式來實現高效的去重,針對數據集和文檔級別進行去重,旨在去除那些具高度相似甚至完全一致內容的文檔,隨后,進一步在句子級別實現更為精細的去重
- 基于匹配算法:精確匹配算法(使用后綴數組匹配最小長度的完全相同子串)&近似匹配算法(局部敏感哈希:minhash)--文檔層面可以使用開銷較小的近似匹配,句子層面可以使用精確匹配
4)數據詞元化(Tokenization)
分詞是數據預處理中的一個關鍵步驟,旨在將原始文本分詞成模型可識別和建模的詞元(token)序列,作為大語言模型的輸入數據,代表性的分詞方法:
- BPE:?統計文本里最常相鄰出現的“零件”組合,把它們合并成一個新的、更長的“零件”,不斷重復這個過程,直到達到預設的“零件庫”大小;基于此擴展出字節級 BPE,無論什么語言、什么奇怪的符號,都能被表示(因為任何字符都能分解成字節)
- WordPiece:BPE 是看誰出現次數多就合并誰。WordPiece 更“功利”一點,它看合并哪個組合能讓整個文本的可能性(語言模型的似然性)提升最大。在合并前,WordPiece 分詞算法會首先訓練一個語言模型,并用這個語言模型對所有可能的詞元對進行評分=詞對出現的頻率/第一個詞出現的頻率x第二個詞出現的頻率。然后,在每次合并時,它都會選擇使得訓練數據的似然性增加最多的詞元對
- Unigram:Unigram 相反,從語料庫的一組足夠大的字符串或詞元初始集合開始,迭代地刪除其中的詞元,直到達到預期的詞表大小。它采用期望最大化 EM算法,不斷評估和移除那些“不怎么好用”或者“可以被其他零件組合替代”的零件,逐步縮減零件庫,直到達到目標大小。它會計算每個零件被移除后對整體表達能力的影響,優先移除影響小的
- 分詞器Tokenizer:對于混合了多領域多種格式的語料,制定專門的分詞器會更加有效,而制定高效的分詞器,需要具備無損重構的特性;有高壓縮率;高適應性;
流行的分詞庫SentencePiece:?Google 開源的庫,同時支持 BPE 和 Unigram,很多大模型都在用它來定制分詞器
3. 數據調度Data Scheduling
僅僅有好數據還不夠,如何組合這些數據以及按照什么順序喂給模型,對模型的最終能力和訓練效率有著重要影響
1) 數據混合配比
確定不同來源的數據(網頁、書籍、代碼等)應該各自占多少比例。不同類型的數據對模型學習特定能力有不同貢獻(如代碼數據提升編程能力,書籍數據提升長文本理解能力)。合適的混合比例是模型全面發展或專精某項技能的關鍵。 常見做法有:
- 設定整體比例: 在整個預訓練開始前,就定好各種數據源的總量占比。訓練時按照這個比例隨機采樣數據。
- 分階段調整比例: 可能在訓練的不同階段使用不同的混合比例。
- 上/下采樣: 為了達到目標比例,可能需要對某些數據源進行“上采樣”(重復使用)或“下采樣”(只用一部分)。
怎么定比例?
經驗為主 + 增加多樣性: 目前很多比例是基于經驗設定的。普遍認同的原則是增加數據源的多樣性,讓模型見多識廣,有助于提升綜合能力和下游任務表現。可以通過“消融實驗”(依次去掉某個數據源看效果)來研究不同數據源的重要性。
可學習的優化方法 (如 DoReMi): 試圖讓模型自動學習最優的數據混合比例。先用一個初始比例訓練一個小模型,看它在哪些數據域上學得不好(損失高),然后就增加這些“難學”數據域的采樣權重,再訓練一個小模型,反復迭代。最終得到一個優化的權重比例,用于訓練大模型。
針對特定能力優化: 想讓模型數學好,就多喂數學題和代碼;想讓模型懂長文本,就多喂書。這通常結合多階段訓練(數據課程)來實現。
LLaMA 的配比:網頁數據占大頭 (超 80%) 提供廣泛的世界知識,輔以一定比例的代碼 (6.5%)、書籍 (4.5%) 和科學文獻 (2.5%)。
專業模型如 CodeGen 會大幅增加代碼比例,但通常仍會保留一些通用網頁數據,以維持基礎的語義理解能力,防止模型變得過于“偏科”。
2) 數據安排Data Curriculum
?不僅僅是數據比例,喂給模型的數據順序也很重要。 核心思想 : 先易后難,先廣后專。讓模型先學習基礎、通用的知識,再逐漸接觸更復雜、更專業的內容。 廣泛定義: 也可以指在訓練的不同階段使用不同的數據混合比例。
實踐方法: 監控與調整,通過評測基準持續監控模型在各項關鍵能力上的學習進展,然后動態調整數據混合比例或引入新的數據類型。
主要應用場景:繼續預訓練 (Continual Pre-training):,由于從頭預訓練代價高昂,數據課程的研究更多集中在如何在一個已經訓練好的通用大模型基礎上,通過繼續喂食特定數據來增強特定能力或適應新任務。
應用實例 - 如何通過數據課程提升特定能力?
提升代碼能力 (CodeLLaMA):? 先用海量通用數據 (2T tokens) 訓練一個基礎模型 → 然后用大量代碼密集型數據 (500B tokens) 繼續訓練。 針對特定語言 (Python): 通用數據 → 代碼數據 → 大量 Python 相關代碼數據 (100B tokens)。
提升數學能力 (Llemma):? 基于一個代碼能力已經不錯的模型 (CodeLLaMA) → 再用包含科學論文、數學、代碼的混合數據繼續訓練 (50-200B tokens)。 注意“正則化”: 在這個專業訓練階段,仍然保留少量 (5%) 通用數據,防止模型在提升數學能力的同時,忘記了原有的通用語言能力。
提升長文本能力 (CodeLLaMA / LongLLaMA):? 通常需要修改位置編碼(如 RoPE 的參數)來適應更長的上下文窗口。? 先用短上下文窗口 (如 4K) 訓練 → 然后用長序列數據和更長的上下文窗口 (如 16K, 甚至 100K) 繼續訓練少量數據 (如 20B tokens)。 好處: 這種從短到長的訓練方式,既能讓模型學會處理長文本,又能節省大量訓練時間(因為大部分訓練還是在短序列上完成的)。
二、預訓練過程
1、預訓練任務?
在大規模預訓練時,需要設計合適的自監督預訓練任務,使得模型能夠從海量無標注數據中學習到廣泛的語義知識與世界知識。目前,常用的預訓練任務主要分為三類,包括語言建模(Language Modeling, LM)、去噪自編碼(Denoising Autoencoding, DAE)以及混合去噪器(Mixture-of-Denoisers, MoD)。
1)語言建模--文本預測
任務核心:預測序列中的下一個詞元 (token)
方法:利用自回歸,在預測第?t?個詞元?ut??時,??僅使用前?t?1?個詞元?u<t??作為輸入?,并在訓練中根據似然函數最大化正確猜中每個詞ut的概率的總和:
擴展:
· 前綴語言建模(常用語對話,續寫):專門為采用前綴編碼器架構的模型設計,基于前綴(prefix)信息預測后綴(suffix)的詞元,將輸入序列?u?隨機切分為前綴?uprefix?={u1?,…,uk?}?和后綴?usuffix?={uk+1?,…,uT?},僅計算后綴部分的損失。訓練時的目標函數為:
· 中間任務填充(常用語代碼補全):訓練模型填充序列中間缺失的部分,將輸入序列分為前綴?uprefix?、中間?umiddle?、后綴?usuffix?,并將中間部分移至末尾,形成新序列?unew?=[uprefix?,usuffix?,umiddle?],模型需自回歸預測新序列,同時恢復中間缺失內容
2)去噪自編碼--文本修復
輸入文本經過一系列隨機替換或刪除操作,形成損壞的文本𝒖\ 。模型的目標是根據這些損壞的文本恢復出被替換或刪除的詞元片段:
去噪自編碼的實際實現通常更復雜,因為需要精心設計“損壞策略”(比如遮蓋多少比例的詞、遮蓋多長的片段等),這些策略會直接影響模型的學習效果
3)混合去噪器(UL2)--全能去噪
UL2 框架讓模型接受多種不同難度和類型的“損壞-修復”考驗:
- S-denoiser (序列去噪 / 前綴挑戰): 與之前說的“前綴語言建模”目標相同。給模型一段開頭的文本,讓它把后續的內容補充完整。
- R-denoiser (常規去噪 / 跨度修復):?與去噪自編碼任務的優化目標相似。二者僅在被掩蓋片段的跨度和損壞比例上有所區別。R-去噪器屏蔽序列中約 15% 的詞元,且每個被屏蔽的片段僅包含 3 到 5 個詞元。
- X-denoiser (極限去噪 / 深度修復):?X-去噪器采用更長的片段(12 個詞元以上)或更高的損壞比例(約 50%),進而要求模型能夠精準還原原始信息。這種設置增加了任務難度,迫使模型學習到更全面的文本表示。
2、優化參數設置?
核心目標:?確保訓練過程穩定、高效,并且模型能夠充分學習數據中的知識,最終達到良好的泛化能力(在沒見過的數據上也能表現好),同時避免過擬合(在訓練數據上表現完美,但在新數據上表現糟糕),主要調校手段:
1)批次數據訓練
-
批次大小 (Batch Size) - 一次“喂”多少數據給模型學習?通常設置非常大的批次大小,比如 1M 到 4M 個詞元 (tokens)。像 GPT-3, PaLM, Gopher 這些巨型模型,它們的批次大小都是百萬級別的。即使是相對小一些的 LLaMA-2 (7B),批次大小也達到了 4M。
- 原因:
-
提高訓練穩定性:?大批次數據計算出的梯度更穩定,抖動更小。
-
提高吞吐量 (Throughput):?在并行計算硬件(如 GPU/TPU)上,大批次能更充分地利用計算資源,加快訓練速度。
- 動態調整批次大小 (如 PaLM-540B):?有些模型在訓練初期使用較小的批次,然后逐漸增大。這可能與訓練不同階段對梯度穩定性和計算效率的需求不同有關。
2)學習率
?學習率是訓練中最關鍵的超參數之一,直接影響模型能否收斂以及收斂的速度和質量。固定的學習率往往不是最優的。訓練初期,我們希望模型學得快一些(大學習率);訓練后期,接近最優解時,我們希望模型學得穩一些(小學習率),避免“步子邁大了扯著蛋”,錯過最優解。
-
學習率調度策略 (Learning Rate Scheduling) - “學習步伐”的動態調整:預熱 (Warmup) + 衰減 (Decay)
-
衰減階段 (Decay):?達到峰值學習率后,隨著訓練的進行,學習率會按照某種策略(比如余弦衰減 Cosine Decay,這是非常常用的一種)逐漸降低,直到訓練結束時接近于零。余弦衰減的特點是下降平滑,前期下降慢,后期下降快。
-
預熱階段 (Warmup):?訓練剛開始時,模型參數是隨機初始化的,很不穩定。此時如果學習率太大,容易導致訓練發散。所以,先用一個非常小的學習率開始,然后線性地逐漸增加到一個預設的峰值學習率(通常這個峰值學習率在預訓練階段的整體學習步數中只占很小一部分,比如前幾個百分點的步數)。
-
3)優化器
根據計算出的梯度,決定如何更新模型的參數。常選擇:AdamW 和 Adafactor
-
Adam (Adaptive Moment Estimation):?一種非常流行的自適應學習率優化算法。它會為每個參數維護一個獨立的學習率,并根據梯度的一階矩(均值)和二階矩(未中心化的方差)來動態調整這個學習率。
-
“動量” (Momentum):?Adam 借鑒了動量的思想,使得參數更新方向在一定程度上保持歷史趨勢,有助于加速收斂并越過一些小的局部最優。
-
自適應學習率:?對不同參數使用不同的學習率,對梯度大的參數學習率小一些,對梯度小的參數學習率大一些。
-
常見超參數設置 (β1, β2, ε):?β1 (通常 0.9) 控制一階矩的衰減率,β2 (通常 0.95 或 0.999) 控制二階矩的衰減率,ε (通常 10^-8) 是一個防止除零的小常數。
-
-
AdamW:?是 Adam 的一個改進版本,它將權重衰減 (Weight Decay)?從梯度更新中解耦出來,直接作用于參數本身,被認為在泛化性能上通常優于原始的 Adam。這是目前大模型預訓練中最常用的優化器。
-
Adafactor:?Adam 的另一個變種,主要目的是減少內存占用。它通過一些技巧(如不存儲完整的二階矩,或者對二階矩進行低秩分解)來降低優化器狀態的存儲需求,這對于訓練參數量巨大的模型非常有幫助。T5 和 PaLM 等模型就使用了 Adafactor。
-
Adafactor 的超參數設置:?通常也需要設置 β1 和 β2,但其更新規則和內存管理方式與 Adam/AdamW 不同
-
3、可擴展的高效訓練技術?
如何在有限的計算資源下高效地訓練模型已經成為制約大語言模型研發的關鍵技術挑戰。其中,主要面臨著兩個技術問題:一是如何提高訓練效率;二是如何將龐大的模型有效地加載到不同的處理器中
1)?3D 并行訓練--三維協同作戰
3D 并行并不是指三維空間,而是指三種并行策略的組合,它們從不同維度分解訓練任務,分配給多個計算單元(通常是 GPU)協同完成。這三種策略是:
- 數據并行 (Data Parallelism, DP) - “大家分頭處理不同作業,最后匯總答案”
- 模型復制:?把完整的模型參數和優化器狀態復制到每一個 GPU?上。
- 數據分片:?把一個大的訓練批次 (Batch) 的數據平均分配給這些 GPU。
- 獨立計算梯度:?每個 GPU 只處理自己分到的那部分數據,獨立地進行前向傳播和反向傳播,計算出梯度。
- 梯度同步與更新:?所有 GPU 計算完梯度后,將各自的梯度進行聚合(通常是求平均),然后用這個聚合后的梯度去更新每一個 GPU 上的模型參數。這樣保證了所有 GPU 上的模型始終保持一致。
優點:?實現相對簡單,很多深度學習框架都內置支持。可以有效提高訓練的吞吐量(單位時間處理的數據量)。
缺點:?每個 GPU 都需要存儲完整的模型,當模型非常大時,單個 GPU 可能還是放不下。
- 流水線并行 (Pipeline Parallelism, PP) - “生產線模式,各 GPU 負責不同工序”
將模型的不同層(或者說計算圖的不同階段)分配到不同的 GPU?上。數據像在流水線上一樣,依次流過這些 GPU,每個 GPU 只負責自己那一部分“工序”的計算。
-
流水線氣泡 (Pipeline Bubble):?簡單的流水線會導致很多 GPU 處于空閑等待狀態(比如 GPU2 要等 GPU1 處理完才能開始)。這就像生產線上,如果前一個工序慢了,后面的工序就得等著。
-
GPipe / Micro-batching:?為了減少氣泡,通常會將一個大的批次數據再切分成更小的“微批次 (Micro-batches)”。讓這些微批次盡可能連續地流過流水線,使得不同 GPU 可以更充分地并行工作,提高設備利用率。
-
梯度累積 (Gradient Accumulation):?由于數據被切分成了微批次,每個微批次計算的梯度可能不夠穩定。通常會在處理完一個完整的大批次的所有微批次后,才累積梯度并進行一次模型更新。
優點:?可以將非常大的模型(其層數很多)分散到多個 GPU 上,解決單個 GPU 存不下的問題。
缺點:?實現比數據并行復雜,需要仔細設計層切分和調度策略以減少氣泡。通信開銷可能較大。
- 張量并行 (Tensor Parallelism, TP) / 模型并行的一種 - “一個大矩陣運算,大家分塊計算”
?
對于模型中的單個大操作(比如一個巨大的矩陣乘法,這在 Transformer 的 FFN 層和 Attention 層的權重矩陣中非常常見),將其在張量(多維數組)的維度上進行切分,分配給不同的 GPU 并行計算,最后再將結果合并。流水線并行是按層切分模型(縱向切);張量并行是對層內的單個大運算進行切分(橫向切或更細致的切)。實現方式 (如 Megatron-LM):
-
按列切分 (Column Parallelism):?比如一個權重矩陣?W,可以將其按列切分成?W = [W_A, W_B]。那么?Y = XW?就變成了?Y = X[W_A, W_B] = [XW_A, XW_B]。可以讓 GPU0 計算?XW_A,GPU1 計算?XW_B,然后拼接結果。
-
按行切分 (Row Parallelism):?同樣,也可以按行切分。
-
在 Transformer 中,通常會將 FFN 層的第一個權重矩陣按列切分,第二個權重矩陣按行切分,并通過巧妙的 AllReduce 通信操作來保證計算的正確性。Attention 層的 Q, K, V 投影和輸出投影也可以類似地進行張量并行。
優點:?可以將單個非常大的層(其權重矩陣單個 GPU 放不下)分散到多個 GPU 上,解決了層內參數過大的問題。可以有效減少模型參數在單個 GPU 上的存儲壓力。
缺點:?實現非常復雜,需要對模型的具體運算進行深入分析和改造。通信開銷也比較大,因為需要在不同 GPU 之間傳遞中間計算結果。
2)零冗余優化器ZeRO
在數據并行中,雖然模型參數在每個 GPU 上都有副本,但更占顯存的其實是優化器狀態(比如 Adam 優化器的動量和方差)、梯度以及模型參數本身。ZeRO 的目標就是消除這些狀態在數據并行時的冗余存儲。將優化器狀態、梯度和模型參數也進行分片,每個 GPU 只負責存儲和更新完整狀態的一部分。
-
ZeRO 的不同階段 (Stage 1, 2, 3):
-
Stage 1:?只對優化器狀態進行分片。
-
Stage 2:?對優化器狀態和梯度進行分片。
-
Stage 3:?對優化器狀態、梯度和模型參數本身都進行分片。這是最極致的節省顯存的方式。
-
-
PyTorch FSDP (Fully Sharded Data Parallel):?PyTorch 提供的功能,與 ZeRO Stage 3 的思想類似。
優點:?極大地降低了數據并行時的顯存占用,使得可以在同等硬件條件下訓練更大的模型,或者使用更大的批次大小。
缺點:?增加了通信開銷,因為需要在不同 GPU 之間傳遞分片后的狀態。
3)激活重計算
在前向傳播過程中,為了計算反向傳播時的梯度,需要存儲每一層的激活值 (Activation)。對于層數很深的模型,這些激活值會占用大量的顯存。
- 核心思想:?不存儲所有中間層的激活值,而是在反向傳播需要用到某個激活值時,重新從前一個檢查點 (Checkpoint) 開始進行一次局部的前向計算來得到它。
-
做法:
-
在前向傳播時,只保存少量關鍵節點(檢查點)的激活值。
-
在反向傳播計算到某個需要中間激活值的層時,如果這個激活值沒有被保存,就找到離它最近的前一個檢查點,然后從這個檢查點開始重新執行一小段前向傳播,得到所需的激活值。
-
優點:?顯著減少顯存占用,使得可以在有限的顯存下訓練更深或更大的模型。
缺點:?增加了計算時間,因為需要進行額外的局部前向傳播。這是一種典型的“時間換空間”的策略。
4)混合精度訓練
傳統的神經網絡訓練通常使用 32 位浮點數 (FP32) 進行計算。但現代 GPU 對 16 位浮點數 (FP16 或 BF16) 的計算速度更快,顯存占用也更小。
- 核心思想:?在訓練過程中,同時使用高精度 (FP32) 和低精度 (FP16/BF16) 的浮點數
-
做法:
-
模型權重和激活值用低精度 (FP16/BF16) 存儲和計算:?這可以加快計算速度,減少顯存占用。
-
梯度計算和參數更新用高精度 (FP32) 進行:?為了保持數值的穩定性和精度,在反向傳播計算梯度以及優化器更新模型權重時,通常會將梯度和權重轉換回 FP32 進行。
-
動態損失縮放 (Dynamic Loss Scaling):?由于 FP16 的表示范圍較小,在反向傳播時梯度值可能變得非常小(下溢)而變成零,導致無法更新參數。動態損失縮放通過將損失值乘以一個縮放因子,使得梯度值相應增大,避免下溢。如果梯度值過大(上溢),則減小縮放因子。
-
優點:訓練速度提升:?GPU 對低精度運算有加速;顯存占用減少:?低精度數據占用顯存更少
缺點:?需要仔細處理數值精度問題,防止訓練不穩定。