在這篇文章中,我們將盡可能詳細地梳理一個完整的 LLM 訓練流程。包括模型預訓練(Pretrain)、Tokenizer 訓練、指令微調(Instruction Tuning)等環(huán)節(jié)。
文末進群,作者答疑、不錯過直播
1.預訓練階段(Pretraining Stage)
工欲善其事,必先利其器。
當前,不少工作選擇在一個較強的基座模型上進行微調,且通常效果不錯(如:[alpaca]、[vicuna] 等)。
這種成功的前提在于:預訓練模型和下游任務的差距不大,預訓練模型中通常已經包含微調任務中所需要的知識。
但在實際情況中,我們通常會遇到一些問題,使得我們無法直接使用一些開源 backbone:
語言不匹配:大多數開源基座對中文的支持都不太友好,例如:[Llama]、[mpt]、[falcon] 等,這些模型在英文上效果都很優(yōu)秀,但在中文上卻差強人意。
專業(yè)知識不足:當我們需要一個專業(yè)領域的 LLM 時,預訓練模型中的知識就尤為重要。由于大多數預訓練模型都是在通用訓練語料上進行學習,對于一些特殊領域(金融、法律等)中的概念和名詞無法具備很好的理解。我們通常需要在訓練語料中加入一些領域數據(如:[xuanyuan 2.0]),以幫助模型在指定領域內獲得更好的效果。
軒轅 2.0(金融對話模型)論文中所提及的訓練語料分布,其中 Financial Pretraining 為金融語料
基于上述原因,我們在進行 SFT 步驟之前,先來看看預訓練任務是如何做的。
1.1 Tokenizer Training
在進行預訓練之前,我們需要先選擇一個預訓練的模型基座。
一個較為普遍的問題是:大部分優(yōu)秀的語言模型都沒有進行充分的中文預訓練,
因此,許多工作都嘗試將在英語上表現比較優(yōu)秀的模型用中文語料進行二次預訓練,期望其能夠將英語上的優(yōu)秀能力遷移到中文任務中來。
已經有許多優(yōu)秀的倉庫做過這件事情,比如:[Chinese-LLaMA-Alpaca]。
但在進行正式的訓練之前,我們還有一步很重要的事情去做:詞表擴充。
通俗來講,tokenizer 的目的就是將一句話進行切詞,并將切好詞的列表喂給模型進行訓練。
例如:
輸入句子>>>你好世界 切詞結果>>>['你','好','世','界']
通常,tokenizer 有 2 種常用形式:WordPiece 和 BPE。
WordPiece
WordPiece 很好理解,就是將所有的「常用字」和「常用詞」都存到詞表中,
當需要切詞的時候就從詞表里面查找即可。
bert-base-chinese tokenizer 可視化
上述圖片來自可視化工具 [tokenizer_viewer]。
如上圖所示,大名鼎鼎的 BERT 就使用的這種切詞法。
當我們輸入句子:你好世界,
BERT 就會依次查找詞表中對應的字,并將句子切成詞的組合。
BERT 切詞測試圖
當遇到詞表中不存在的字詞時,tokenizer 會將其標記為特殊的字符 [UNK]:
Out of Vocabulary(OOV)情況
Byte Pair Encoder(BPE)
WordPiece 的方式很有效,但當字詞數目過于龐大時這個方式就有點難以實現了。
對于一些多語言模型來講,要想窮舉所有語言中的常用詞(窮舉不全會造成 OOV),
既費人力又費詞表大小,為此,人們引入另一種方法:BPE。
BPE 不是按照中文字詞為最小單位,而是按照 unicode 編碼 作為最小粒度。
對于中文來講,一個漢字是由 3 個 unicode 編碼組成的,
因為平時我們不會拆開來看(畢竟中文漢字是不可拆分的),所以我一開始對這個概念也不太熟悉。
我們來看看 LLaMA 的 tokenizer(BPE)對中文是如何進行 encode 的:
上述圖片來自可視化工具 [tokenizer_viewer]。
可以看到,「編碼」兩個字能夠被正常切成 2 個字,
但「待」卻被切成了 3 個 token,這里的每個 token 就是 1 個 unicode 編碼。
LLaMA tokenizer 查找結果,「待」不在詞表中,「編」「碼」在詞表中
通過 token 查找功能,我們可以發(fā)現「編」「碼」在詞表中,但「待」不在詞表中。
但任何 1 個漢字都是可以由 unicode 表示(只是組合順序不同),因此「待」就被切成了 3 個 token。
通常在模型訓練不夠充足的時候,模型會輸出一些亂碼(不合法的 unicode 序列):
游泳池是杭州西湖的一個游泳池,???
詞表擴充
為了降低模型的訓練難度,人們通常會考慮在原來的詞表上進行「詞表擴充」,
也就是將一些常見的漢字 token 手動添加到原來的 tokenizer 中,從而降低模型的訓練難度。
我們對比 [Chinese-LLaMA] 和 [LLaMA] 之間的 tokenizer 的區(qū)別:
Chinese LLaMA 和 原始LLaMA 之間 tokenizer 的區(qū)別
我們可以發(fā)現:Chinese LLaMA 在原始 tokenizer 上新增了17953 個 tokens,且加入 token 的大部分為漢字。
而在 [BELLE] 中也有同樣的做法:
在 120w 行中文文本上訓練出一個 5w 規(guī)模的 token 集合,
并將這部分 token 集合與原來的 LLaMA 詞表做合并,
最后再在 3.2B 的中文語料上對這部分新擴展的 token embedding 做二次預訓練。
《Towards Better Instruction Following Language Models for Chinese》 Page-4
1.2 Language Model PreTraining
在擴充完 tokenizer 后,我們就可以開始正式進行模型的預訓練步驟了。
Pretraining 的思路很簡單,就是輸入一堆文本,讓模型做 Next Token Prediction 的任務,這個很好理解。
我們主要來討論幾種預訓練過程中所用到的方法:數據源采樣、數據預處理、模型結構。
數據源采樣
在 [gpt3] 的訓練過程中,存在多個訓練數據源,論文中提到:對不同的數據源會選擇不同采樣比例:
GPT3 Paper Page-9
通過「數據源」采樣的方式,能夠緩解模型在訓練的時候受到「數據集規(guī)模大小」的影響。
從上圖中可以看到,相對較大的數據集(Common Crawl)會使用相對較大的采樣比例(60%),
這個比例遠遠小于該數據集在整體數據集中所占的規(guī)模(410 / 499 = 82.1%),
因此,CC 數據集最終實際上只被訓練了 0.44(0.6 / 0.82 * (300 / 499))個 epoch。
而對于規(guī)模比較小的數據集(Wikipedia),則將多被訓練幾次(3.4 個 epoch)。
這樣一來就能使得模型不會太偏向于規(guī)模較大的數據集,從而失去對規(guī)模小但作用大的數據集上的學習信息。
數據預處理
數據預處理主要指如何將「文檔」進行向量化。
通常來講,在 Finetune 任務中,我們通常會直接使用 truncation 將超過閾值(2048)的文本給截斷,
但在 Pretrain 任務中,這種方式顯得有些浪費。
以書籍數據為例,一本書的內容肯定遠遠多余 2048 個 token,但如果采用頭部截斷的方式,
則每本書永遠只能夠學習到開頭的 2048 tokens 的內容(連序章都不一定能看完)。
因此,最好的方式是將長文章按照 seq_len(2048)作分割,將切割后的向量喂給模型做訓練。
模型結構
為了加快模型的訓練速度,通常會在 decoder 模型中加入一些 tricks 來縮短模型訓練周期。
目前大部分加速 tricks 都集中在 Attention 計算上(如:MQA 和 Flash Attention [falcon] 等);
此外,為了讓模型能夠在不同長度的樣本上都具備較好的推理能力,
通常也會在 Position Embedding 上進行些處理,選用 ALiBi([Bloom])或 RoPE([GLM-130B])等。
具體內容可以參考下面這篇文章[1]
1.3 數據集清理
中文預訓練數據集可以使用 [悟道],數據集分布如下(主要以百科、博客為主):
悟道-數據分布圖
但開源數據集可以用于實驗,如果想突破性能,則需要我們自己進行數據集構建。
在 [falcon paper] 中提到,
僅使用「清洗后的互聯網數據」就能夠讓模型比在「精心構建的數據集」上有更好的效果,
一些已有的數據集和它們的處理方法如下:
各種數據源 & 數據清理方法
有關 Falcon 更多的細節(jié)可以看這里[2]
1.4 模型效果評測
關于 Language Modeling 的量化指標,較為普遍的有 [PPL],[BPC] 等,
可以簡單理解為在生成結果和目標文本之間的 Cross Entropy Loss 上做了一些處理。
這種方式可以用來評估模型對「語言模板」的擬合程度,
即給定一段話,預測后面可能出現哪些合法的、通順的字詞。
但僅僅是「生成通順句子」的能力現在已經很難滿足現在人們的需求,
大部分 LLM 都具備生成流暢和通順語句能力,很難比較哪個好,哪個更好。
為此,我們需要能夠評估另外一個大模型的重要能力 —— 知識蘊含能力。
C-Eval
一個很好的中文知識能力測試數據集是 [C-Eval],涵蓋1.4w 道選擇題,共 52 個學科。
覆蓋學科如下:
c-eval 數據集覆蓋學科圖
由于是選擇題的形式,我們可以通過將題目寫進 prompt 中,
并讓模型續(xù)寫 1 個 token,判斷這個續(xù)寫 token 的答案是不是正確答案即可。
但大部分沒有精調過的預訓練模型可能無法續(xù)寫出「A B C D」這樣的選項答案,
因此,官方推薦使用 5-shot 的方式來讓模型知道如何輸出答案:
以下是中國關于會計考試的單項選擇題,請選出其中的正確答案。
下列關于稅法基本原則的表述中,不正確的是____。
A. 稅收法定原則包括稅收要件法定原則和稅務合法性原則
B. 稅收公平原則源于法律上的平等性原則
C. 稅收效率原則包含經濟效率和行政效率兩個方面
D. 稅務機關按法定程序依法征稅,可以自由做出減征、停征或免征稅款的決定
答案:D
甲公司是國內一家領先的新媒體、通信及移動增值服務公司,由于遭受世界金融危機,甲公司經濟利潤嚴重下滑,經營面臨困境,但為了穩(wěn)定職工隊伍,公司并未進行裁員,而是實行高層管理人員減薪措施。甲公司此舉采用的收縮戰(zhàn)略方式是____。
A. 轉向戰(zhàn)略
B. 放棄戰(zhàn)略
C. 緊縮與集中戰(zhàn)略
D. 穩(wěn)定戰(zhàn)略
答案:C
... # 第 3, 4, 5 道樣例題
下列各項中,不能增加企業(yè)核心競爭力的是____。
A. 產品差異化
B. 購買生產專利權
C. 創(chuàng)新生產技術
D. 聘用生產外包商
答案:
通過前面的樣例后,模型能夠知道在「答案:」后面應該輸出選項字母。
于是,我們獲得模型續(xù)寫后的第一個 token 的概率分布(logits),
并取出「A B C D」這 4 個字母的概率,通過 softmax 進行歸一化:
probs=( torch.nn.functional.softmax( torch.tensor( [ logits[self.tokenizer.encode( "A",bos=False,eos=False)[0]], logits[self.tokenizer.encode( "B",bos=False,eos=False)[0]], logits[self.tokenizer.encode( "C",bos=False,eos=False)[0]], logits[self.tokenizer.encode( "D",bos=False,eos=False)[0]], ] ), dim=0, ).detach().cpu().numpy() ) pred={0:"A",1:"B",2:"C",3:"D"}[np.argmax(probs)]#將概率最大的選項作為模型輸出的答案
C-Eval 通過這種方式測出了許多模型在中文知識上的效果,
由于是 4 選項問題,所以基線(隨機選擇)的正確率是 25%。
C-Eval 也再一次證明了 GPT-4 是個多么強大的知識模型:
各模型在 5-shot 下的得分排名
2. 指令微調階段(Instruction Tuning Stage)
在完成第一階段的預訓練后,就可以開始進到指令微調階段了。
由于預訓練任務的本質在于「續(xù)寫」,而「續(xù)寫」的方式并一定能夠很好的回答用戶的問題。
例如:
因為訓練大多來自互聯網中的數據,我們無法保證數據中只存在存在規(guī)范的「一問一答」格式,
這就會造成預訓練模型通常無法直接給出人們想要的答案。
但是,這并不代表預訓練模型「無知」,只是需要我們用一些巧妙的「技巧」來引導出答案:
不過,這種需要用戶精心設計從而去「套」答案的方式,顯然沒有那么優(yōu)雅。
既然模型知道這些知識,只是不符合我們人類的對話習慣,那么我們只要再去教會模型「如何對話」就好了。
這就是 Instruction Tuning 要做的事情,即指令對齊。
OpenAI 在 [instruction-following] 中展示了 GPT-3 和經過指令微調前后模型的區(qū)別:
GPT-3 只是在做續(xù)寫任務,InstructGPT 則能夠回答正確內容
2.1 Self Instruction
既然我們需要去「教會模型說人話」,
那么我們就需要去精心編寫各式各樣人們在對話中可能詢問的問題,以及問題的答案。
在 [InstructGPT Paper] 中,使用了 1.3w 的數據來對 GPT-3.5 進行監(jiān)督學習(下圖中左 SFT Data):
InstructGPT Paper 訓練數據集預覽
可以觀察到,數據集中人工標注(labeler)占大頭,
這還僅僅只是 InstructGPT,和 ChatGPT 遠遠不是一個量級。
非官方消息:ChatGPT 使用了百萬量級的數據進行指令微調。
可見,使用人工標注是一件成本巨大的事情,只是找到人不夠,需要找到「專業(yè)」且「認知一致」的標注團隊。
如果這件事從頭開始做自然很難(OpenAI 確實厲害),但今天我們已經有了 ChatGPT 了,
我們讓 ChatGPT 來教我們自己的模型不就好了嗎?
這就是 Self Instruction 的思路,即通過 ChatGPT 的輸入輸出來蒸餾自己的模型。
一個非常出名的項目是 [stanford_alpaca]。
如果從 ChatGPT 「套」數據,那么我們至少需要「套」哪些數據。
Instruction Tuning 中的「輸入」(問題)和「輸出」(答案)是訓練模型的關鍵,
答案很好得到,喂給 ChatGPT 問題根據返回結果就能獲得,
但「問題」從哪里獲得呢?
(靠人想太累了,屏幕前的你不妨試試,看看短時間內能想出多少有價值的問題)
Alpaca 則是使用「種子指令(seed)」,使得 ChatGPT 既生成「問題」又生成「答案」。
由于 Alpaca 是英文項目,為了便于理解,我們使用相同思路的中文項目 [BELLE] 作為例子。
通俗來講,就是人為的先給一些「訓練數據樣例」讓 ChatGPT 看,
緊接著利用 ChatGPT 的續(xù)寫功能,讓其不斷地舉一反三出新的訓練數據集:
你被要求提供10個多樣化的任務指令。這些任務指令將被提供給GPT模型,我們將評估GPT模型完成指令的能力。
以下是你提供指令需要滿足的要求:
1.盡量不要在每個指令中重復動詞,要最大化指令的多樣性。
2.使用指令的語氣也應該多樣化。例如,將問題與祈使句結合起來。
3.指令類型應該是多樣化的,包括各種類型的任務,類別種類例如:brainstorming,open QA,closed QA,rewrite,extract,generation,classification,chat,summarization。
4.GPT語言模型應該能夠完成這些指令。例如,不要要求助手創(chuàng)建任何視覺或音頻輸出。例如,不要要求助手在下午5點叫醒你或設置提醒,因為它無法執(zhí)行任何操作。例如,指令不應該和音頻、視頻、圖片、鏈接相關,因為GPT模型無法執(zhí)行這個操作。
5.指令用中文書寫,指令應該是1到2個句子,允許使用祈使句或問句。
6.你應該給指令生成適當的輸入,輸入字段應包含為指令提供的具體示例,它應該涉及現實數據,不應包含簡單的占位符。輸入應提供充實的內容,使指令具有挑戰(zhàn)性。
7.并非所有指令都需要輸入。例如,當指令詢問一些常識信息,比如“世界上最高的山峰是什么”,不需要提供具體的上下文。在這種情況下,我們只需在輸入字段中放置“<無輸入>”。當輸入需要提供一些文本素材(例如文章,文章鏈接)時,就在輸入部分直接提供一些樣例。當輸入需要提供音頻、圖片、視頻或者鏈接時,則不是滿足要求的指令。
8.輸出應該是針對指令和輸入的恰當回答。
下面是10個任務指令的列表:
###
1.指令: 在面試中如何回答這個問題?
1.輸入:當你在車里獨處時,你會想些什么?
1.輸出:如果是在晚上,我通常會考慮我今天所取得的進步,如果是在早上,我會思考如何做到最好。我也會嘗試練習感恩和活在當下的狀態(tài),以避免分心駕駛。
###
2.指令: 按人口對這些國家進行排名。
2.輸入:巴西,中國,美國,日本,加拿大,澳大利亞
2.輸出:中國,美國,巴西,日本,加拿大,澳大利亞
###
3.指令:
如上述例子所示,我們先給出 2 個樣例,并讓 ChatGPT 進行續(xù)寫:
ChatGPT 續(xù)寫結果
關于 BELLE 的更多細節(jié)可以參考這篇文章[3]
2.2 開源數據集整理
在這一章中,我們將梳理一些開源的 Instruction Tuning 的數據集,
除了直接拿來用以外,我們期望通過分析這些已有數據集,從而學習如何構建一個指令數據集。
Alpaca
[stanford_alpaca] 采用上述的 self instruction 的方式采集了 5200 條指令訓練數據集。
數據樣例如下:
{ "instruction":"Arrangethewordsinthegivensentencetoformagrammaticallycorrectsentence.", "input":"quicklythebrownfoxjumped", "output":"Thequickbrownfoxjumpedquickly." }
其中,instruction 代表要求模型做的任務,input 代表用戶輸入, output 代表喂給模型的 label。
Alpaca 覆蓋了多種類型的指令,其數據分布如下:
Alpaca 數據分布圖
包含寫作、描述、分類、摘要等多種類型的任務集合。
BELLE
BELLE 開放了好幾種規(guī)模的數據集,[100萬]、[200萬]、[350萬] 等。
訓練數據集樣例如下:
{ "instruction":"判斷給定的文章是否符合語法規(guī)則。如果不符合,請?zhí)峁┬薷慕ㄗh。下面是一篇文章的開頭:‘為了探討這個主題,本文將提供一系列數據和實例,以證明這一觀點?!?, "input":"", "output":"這個開頭符合語法規(guī)則。" }
由于數據量很大,我們無法統計出訓練數據集中各任務的真實占比,
但從 [1000條評測集] 數據分布可以推測出,訓練數據集中同樣包含:摘要、問答、分類等任務。
BELLE - 評測集分布
我們按照類別對評測數據進行采樣,結果如下:
2.3 模型的評測方法
比起預訓練(Pretrain)環(huán)節(jié)里相對明確的評價指標(如PPL、NLL等),
Instruction 環(huán)節(jié)中的評價指標比較令人頭疼。
鑒于語言生成模型的發(fā)展速度,BLEU 和 ROUGH 這樣的指標已經不再客觀。
一種比較流行的方式是像 [FastChat] 中一樣,利用 GPT-4 為模型的生成結果打分,
我們也嘗試使用同樣的 Prompt 對 3 種開源模型:OpenLlama、ChatGLM、BELLE 進行測試。
注意:下面的測試結果僅源自我們自己的實驗,不具備任何權威性。
對于每一個問題,我們先獲得 ChatGPT 的回復,以及另外 3 種模型的回復,
接著我們將 「ChatGPT 答案 - 候選模型答案」這樣的 pair 喂給 GPT-4 打分(滿分為 10 分)。
得到的結果如下:
測試結果 & 測試 prompt
我們對每個任務單獨進行了統計,并在最后一列求得平均值。
GPT-4 會對每一條測試樣本的 2 個答案分別進行打分,并給出打分理由:
GPT-Review 的結果
但是,我們發(fā)現,GPT-4 打出的分數和給出理由并不一定正確。
如上圖所示,GPT-4 為右邊模型的答案打出了更高的分數,給出的理由是:
將「最長時期」改為了「最長時期之一」會更準確。
但事實上,Instruction 中明確設定就是「最長時期」,
這種「給高分」的理由其實是不正確的。
此外,我們還發(fā)現,僅僅調換句子順序也會對最后打分結果產生影響,
針對這個問題,我們考慮「調換句子順序并求和平均」來緩解。
但不管怎么樣,GPT-4 給出的分數或許并沒有我們想象中的那么靠譜,
為此,我們通過人工的 Review 的方式對每個答案進行了一次回掃,得到的結果和標準如下:
再次重申:我們只是期望指出 GPT-4 打分可能會和實際產生偏差的問題,這里排名不具備任何權威性。
人工 Review 結果 & 打分原則
我們可以看到,
在 GPT-4 打分的結果中,已經有模型的效果甚至超過了 ChatGPT(分數為 1.02),
但再經過人工 Review 后,ChatGPT 的答案是我們認為更合理一些的。
當然,最近陸陸續(xù)續(xù)的推出了許多新的評測方法,如:[PandaLM],
以及許多比較有影響力的評測集,如:[C-Eval]、[open_llm_leaderboard] 等,
-
數據
+關注
關注
8文章
7139瀏覽量
89575 -
編碼
+關注
關注
6文章
957瀏覽量
54951 -
模型
+關注
關注
1文章
3305瀏覽量
49220 -
LLM
+關注
關注
0文章
299瀏覽量
400
原文標題:從零詳細地梳理一個完整的 LLM 訓練流程
文章出處:【微信號:zenRRan,微信公眾號:深度學習自然語言處理】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論