LLMs訓練避坑帖——如何高效 LLMs pretrain?
共 7971字,需瀏覽 16分鐘
·
2024-10-14 08:00
LLM訓練-pretrain
作者:ybq
原文地址:https://zhuanlan.zhihu.com/p/718354385
這篇文章介紹下如何從零到一進行 pretrain 工作。
類似的文章應該有很多,不同的地方可能在于,我并不會去分析 pretrain 階段的核心技術,而是用比較樸素的語言來描述這個大工程的每一塊磚瓦。我的介紹偏方法論一些,主要目的是普及每個環(huán)節(jié)有哪些必須要做的瑣碎工作、有哪些坑、以及有哪些避坑技巧。為了避免老板開了我,文中有一些內容的具體做法不會展開細說,請大家見諒。作為替代,我會推薦一些比較好的開源做法。
一、背景篇
時至今日,dense 模型有 qwen,MOE 模型有 deepseek,小尺寸模型有 minicpm。無論是個人還是大廠,都很難訓出同 size 下更優(yōu)秀的模型,大模型 pretrain 階段全面擁抱開源的日子感覺不太遠了。那么,在這個時代大背景下,自研 pretrain 模型的意義又有哪些呢?
正經(jīng)答案:
-
各公司僅僅是開源了模型參數(shù),但并沒有開源訓練框架、訓練數(shù)據(jù)等更核心的內容,其實本質上還是閉源。在這種情況下,每一個 qwen 模型的使用者都無法為下一版 qwen 模型的迭代做出貢獻,qwen 團隊也僅僅是收獲了口碑,甚至因為自己的模型已經(jīng)開源可以自行部署,買他們服務的客戶可能都會變少。因此,在 llm 真正走向全面開源之前(大廠公開訓練代碼、配比數(shù)據(jù),任何人都可以通過提 CR 來幫助大廠優(yōu)化訓練效率、煉丹技巧),掌握 pretrain 的技術能力依然是有意義的; -
通用模型的變現(xiàn)能力遠不如 domain 模型,continue-pretrain 的需求在日益增長,而 continue-pretrain 的技術棧和 pretrain 的技術棧并沒有本質區(qū)別; -
不是自己做的 pretrain,必然無法得知這個模型在 pretrain 階段到底喂了什么數(shù)據(jù)。各種數(shù)據(jù)的精確配比、各種 knowledge 的掌握程度,并不是靠評估能準確衡量的。而如果不知道這些數(shù)據(jù)細節(jié),那么 alignment 階段就無法對癥下藥,就無法最大限度的開發(fā)模型潛力。舉個簡單的例子,你在 sft 階段,讓一個沒訓過唐詩宋詞的通用模型學習作詩,它不出幻覺誰出幻覺? -
使用開源模型的話,tokenizer 不可控,進而導致解碼速度不可控。這里也舉個例子,如果我們用 llama 模型來做意圖識別任務,有個意圖叫 ai.listen.music,會被映射成 5 個 token,但如果使用自己訓練的大模型,便會在一開始就設置成 1 個 token,極大節(jié)省了生成速度。雖然擴詞表已經(jīng)是一個比較成熟的技術了,但不僅需要花費算力來恢復效果,而且不管訓多少新語料,也很難做到模型效果完全不掉點。
不正經(jīng)答案:
-
有一個自己的模型很 cool,公司可以拿來做宣傳,證明自己的科研能力,個人也會很有成就感; -
可以埋彩蛋,在 pretrain 階段給模型悄悄塞一點自己喜歡的知識或價值觀。比如,每隔 100B token 就讓模型學一次“在 XXX 眼里,YYY 是最好看的女孩子”, 等模型上線了,拿去向 YYY 表白(被老板開了別說是我這篇文章慫恿的)。
二、數(shù)據(jù)篇
2.1 數(shù)據(jù)爬取
pretrain 大模型的第一件事:先找個 10T 左右的訓練數(shù)據(jù)吧。也可以少找一些,等模型開始訓了,在訓練的同時,不斷去收集更多的新數(shù)據(jù)。
至于怎么獲取數(shù)據(jù),爬網(wǎng)頁、逛淘寶、聯(lián)系數(shù)據(jù)販子,等等等等。算法同學往往搞不定這個事情,你敢爬他就敢封你 IP,你爬得起勁他甚至還可以起訴你,所以這個工作最好還是讓專業(yè)的數(shù)據(jù)團隊同學來做。
有些高質量的數(shù)據(jù),比如論文書籍,往往還都是 pdf 格式,這時候還需要去調用效果較好的 pdf 服務。不要指望著靠 python 庫來解析,稍微涉及一點公式、表格的 pdf,解析效果都一塌糊涂。用 GPT4 等大模型進行解析,大概率價格會遠高于 pdf 解析服務。當然,自己訓一個 OCR 模型也是可用的候選方案,前提是你有足夠高質量的 pdf - text 對齊數(shù)據(jù)。
好在,世上還是好人多!今年再做 pretrain 工作,網(wǎng)上的開源數(shù)據(jù)集已經(jīng)很多了。FineWeb、pile、Skypile、RedPajama,湊合著差不多能當啟動資金來用。但從另一個角度講,世界上沒有免費的午餐,所有開源出來的中文大模型數(shù)據(jù)集,我不認為是他們最干凈的數(shù)據(jù),質量多少都有點問題。
(即使是下載 huggingface 數(shù)據(jù),也不是動動嘴皮子這么簡單的。如果你實操了,就會發(fā)現(xiàn):服務器沒連外網(wǎng),只能換成 hf_mirror 的鏈接;下載速度太慢,下完 1T 開源數(shù)據(jù)得好幾天,得手動 split 要下載的數(shù)據(jù)集合,起多個進程在多臺服務器上下載;下載完之后,文件量多的你執(zhí)行 ls 都會卡死,你得用大數(shù)據(jù)集群技術來處理)
準備數(shù)據(jù)還要懂得一個基礎概念:數(shù)據(jù)的知識密度是有差異的。“唐詩三百首”的知識量要遠遠大于“中國新聞網(wǎng)的三百篇新聞”。而這種高知識密度的訓練數(shù)據(jù),往往都是需要花錢的。最近,一種新的數(shù)據(jù)趨勢是“合成高知識密度數(shù)據(jù)”,把幾千字的新聞概括成幾百字喂給模型,四舍五入也等于訓練速度提高了十倍。
總之,如果要認真做 pretrain 工作,我建議要組建數(shù)據(jù)團隊,爬蟲或購買是必須的,否則網(wǎng)上那幾個翻來覆去的數(shù)據(jù)集在清洗之后根本不夠用。
2.2 數(shù)據(jù)清洗
“清洗”是數(shù)據(jù)環(huán)節(jié)最最核心的工作,沒有之一!
目前,利用模型對 pretrain 數(shù)據(jù)的質量進行打分,已經(jīng)成了數(shù)據(jù)清洗工作的標配,llama3、qwen2 的技術報告都有提及。需要注意的是,基本上大家都認同:同等 size 下,BERT 結構的模型的表征能力是強于 transformer-decoder 模型的,因此打分模型最好還是從 BERT 家族中選一個來訓,效果好、速度還快。至于訓練數(shù)據(jù)怎么搞,還是老一套,讓 GPT4 標注一下,或者是利用“某個源的數(shù)據(jù)是高質量數(shù)據(jù),某個源的數(shù)據(jù)是低質量數(shù)據(jù)”這種規(guī)則生產(chǎn)一些。特別強調,任何打分器,都會給 code、markdown、latex 等格式數(shù)據(jù)打很低的分數(shù),我們必須把這些數(shù)據(jù)摘出來,免得被直接洗沒了。(fineWeb 這個數(shù)據(jù)集,就洗的基本沒有 code 數(shù)據(jù)了)
訓打分器這個工作的難點是要學會放低心態(tài),別那么執(zhí)拗,不要執(zhí)著于打分器 100% 的準確率,湊合能用就行了,有打分器總比沒打分器強,但你要花一個月來訓打分器,那就還不如沒打分器。此外要學會變通,你有 32K 的語料不代表你要訓 32K 的打分器,訓個 4K 就差不多了,你非要糾結存在“前 4K 低質量,后 28K 高質量”這種特殊情況,我只能說算你牛逼。
打分器結果只是眾多數(shù)據(jù)特征中的一個特征,并不一定要完全依賴它來洗數(shù)據(jù),可以和其他特征結合使用。這也引出了數(shù)據(jù)清洗的另一個大殺器:規(guī)則。
不要瞧不起規(guī)則!不要瞧不起規(guī)則!不要瞧不起規(guī)則!
數(shù)據(jù)長度是否少于某個值,數(shù)據(jù)中某個 token 的比例超過某個閾值,數(shù)據(jù)的 zh 占比、en 占比、數(shù)字占比,數(shù)據(jù)是否有“http”字段,數(shù)據(jù)是否包含了“新冠”、“疫情”等低質量關鍵詞,數(shù)據(jù)是否包含某些反動詞匯,數(shù)據(jù)是否包含某些黃色字眼,等等等等。用啟發(fā)式的規(guī)則過濾數(shù)據(jù)并不丟人,洗不干凈數(shù)據(jù)才丟人。
但同時,必須注意到,用規(guī)則清洗或者過濾數(shù)據(jù)的時候,一定不要把數(shù)據(jù)搞成分布有偏的數(shù)據(jù)。比如、你覺著:“包含網(wǎng)址的數(shù)據(jù)質量低,而網(wǎng)址的英文占比高”,所以你把英文占比高的數(shù)據(jù)都去掉了。整挺好,模型成了單語模型。因此,用規(guī)則的時候,一定要多 check 下被濾出去的數(shù)據(jù)長什么樣子,勤 vim 一下!
另外,數(shù)據(jù)脫敏也是數(shù)據(jù)清洗環(huán)節(jié)必須要做的一個工作。我們要盡可能的把訓練數(shù)據(jù)中涉及到的人名、電話號碼、郵箱等剔除出去,一旦被模型說出來,就構成了隱私侵犯,公司被罰的錢足夠雇人把數(shù)據(jù)脫敏 N 遍了。更廣義的,把數(shù)據(jù)的“轉載自……”刪掉,黃色信息、反動信息,references 等剔除出去,都可以視作數(shù)據(jù)脫敏工作的一部分。這個工作好像沒任何奇淫巧技,老老實實的寫正則匹配吧。
2.3 數(shù)據(jù)去重
數(shù)據(jù)環(huán)節(jié)最考研工程能力的環(huán)節(jié)到了:對 T 級別的數(shù)據(jù)進行去重。
不要心存任何幻想:能不能不做數(shù)據(jù)去重。答案肯定是不行的!網(wǎng)上基本所有的開源數(shù)據(jù),都是來自 common crawl,你不去重如何混合使用呢。就算你只使用單一數(shù)據(jù)源或者自己爬取數(shù)據(jù),也應該注意到:網(wǎng)頁 A 引用了 網(wǎng)頁 B,網(wǎng)頁 B 引用了 網(wǎng)頁 C……,網(wǎng)頁 Z 又引用了網(wǎng)頁 A。這種 url 循環(huán)調用的現(xiàn)象,在互聯(lián)網(wǎng)屢見不鮮,你的訓練數(shù)據(jù)集大概率會把一個網(wǎng)頁翻來覆去的使用。即使能確保是不同的網(wǎng)頁,一篇文章也會被知乎、CSDN、博客、微信公眾號、小紅書等不同軟件反復轉載。
去重工作唯一可以讓步的地方是:是做 sentence 去重還是做 document 去重,這個我也不好斷定,我的建議是量力而為。能做 sentence 去重,誰不愿意呢?可是數(shù)據(jù)量和工作難度也會陡增。
那么如何去重呢?首先,你一定要有一個大數(shù)據(jù)處理集群,hadoop 也好、spark 也罷,只要是一個 map / reduce 的框架就都可以。這個屬于汽車的輪子,想要靠 python 寫 for 循環(huán)完成這個工作,確實是勇氣可嘉。
然后,就去實現(xiàn)一個簡單的 minhash 代碼,沒啥難度,ChatGPT 一定會寫。
數(shù)據(jù)去重工作有一個比較重要的意識:要先確定需要多少訓練數(shù)據(jù),再確定去重的粒度。去重工作是沒有盡頭的,任何時候你都能把數(shù)據(jù)繼續(xù)洗下去,所以必須明確自己需要多少訓練數(shù)據(jù)。需要 10T 訓練數(shù)據(jù),就卡相似度在 80% 的閾值進行去重;需要 5T 的訓練數(shù)據(jù),就卡相似度在 90% 的閾值進行去重;以此類推。
目前沒有任工作能證明,一條數(shù)據(jù)在 pretrain 階段訓多少遍對模型是最友好的。因此,大膽的按需去重,即使去重粒度小,導致一篇文檔出現(xiàn)多次,也可以通過讓兩篇相似文檔之間隔盡量多的 token 來降低影響。
2.4 數(shù)據(jù)配比
前面提到,我們要在數(shù)據(jù)清洗的時候把 code 等格式化數(shù)據(jù)摘出來,怎么實現(xiàn)呢?訓練一個數(shù)據(jù)分類器!對每一個 document 進行類別判斷,不用特別精準,把數(shù)據(jù)劃分成新聞、百科、代碼、markdown、等類目即可,分類器模型依然可以選擇使用 BERT 家族。
不同的數(shù)據(jù)源,在上文中介紹清洗和去重的時候也要有不同的閾值:
-
清洗的時候,“代碼”和“知識類文本”當然要使用不同的閾值來決定是否是高質量; -
去重的時候,“新聞”類可能 70% 的重復度就不要,“知識”類則可以 85% 的相似度才丟棄,在丟去重復文檔的時候,優(yōu)先保留數(shù)據(jù)打分器比較高的數(shù)據(jù)。
好了,引子環(huán)節(jié)說完了,默認大家已經(jīng)都給自己的數(shù)據(jù)打好了類別,我們繼續(xù)往下講配比工作。
大部分的技術報告里,應該都提及了自己的數(shù)據(jù)是如何配比的,基本上都是“知識 + 代碼 + 邏輯”三個大類目,其中知識數(shù)據(jù)分文中文知識和英文知識,邏輯數(shù)據(jù)則可以認為是 math 數(shù)據(jù)和 cot 數(shù)據(jù)的混合體。整體上,大部分中文模型的配比都在這個區(qū)間左右:中:英:code = 4:4:2(邏輯數(shù)據(jù)的比例我沒有寫進去,加入多少取決于你能收集多少,其他三類數(shù)據(jù)應該是要多少有多少的存在)。
我們可以根據(jù)自己的實際情況調整配比,但英文的比例一定不能太低。目前中文數(shù)據(jù)的質量不如英文數(shù)據(jù)質量基本已經(jīng)成功共識,導致這個現(xiàn)象可能有兩個原因:
-
中文確實比英文難學,語言空間的復雜度更高; -
中文語料無論是干凈程度還是數(shù)量級,都無法與英文語料相比較。
2.4 數(shù)據(jù)順序
pretrain 的本質是一個教模型學知識的過程,既然是學習,那么知識的順序就顯得很重要,總不能先學微積分,再學數(shù)字加減法吧。這也就是“課程學習”的核心思想。
課程學習的內容很寬泛,無論是先學難知識、再學臟知識,還是先學好數(shù)據(jù)、再學臟數(shù)據(jù),都可以視為是課程學習。其本質就是在闡述一件事情:“同樣 1個T的訓練數(shù)據(jù),通過調整訓練順序得到的不同模型,能力是不同的。”這個觀點基本已經(jīng)被很多團隊論證多次了,因此課程學習目前也可以認為是 pretrain 的標配。
雖然 next_token 的訓練方法,基本不存在模型學不會某條數(shù)據(jù)的情況。但從另外一個角度來分析,災難性遺忘可能始終在發(fā)生,A + B 的學習順序可能導致 A 知識遺忘了 30%,B + A 的學習順序可能導致 B 知識遺忘了 20%,那后者忘得少自然能力更強啊。而且,如果 B 是一個簡單的知識,那就代表 B 在訓練語料中會出現(xiàn)非常多的次數(shù),即使遺忘了后續(xù)也會被重新?lián)炱饋恚щy知識在全部訓練數(shù)據(jù)中出現(xiàn)的次數(shù)自然也會小很多。(全局訓練語料中,蜀道難全文出現(xiàn)的次數(shù)一定比靜夜思全文出現(xiàn)的次數(shù)少)。
說了這么多,只是為了強調一件事:數(shù)據(jù)順序真的很重要,那么如何敲定呢?
這里我推薦的 llama 的 In context pretrain 工作:利用語義相似度,優(yōu)先將最相似的 document 進行拼接,從而構成語義更加連貫流暢的上下文,詳見論文 https://arxiv.org/pdf/2310.10638。
需要強調的一個地方是,llama 堅定的認為:在同一條 pretrain 語料中,無關文檔之間不能相互看見。具體來說,sentenceA + "
但在實操中,除了 llama,我沒聽說過還有哪個團隊在 pretrain 階段做 attention_mask,大家的實驗結論基本都是做不做 mask 沒什么區(qū)別。而且,我個人認為,pretrain 階段應該要培養(yǎng)模型切換 topic 的能力,在 llm 的實際應用場景中,我們也不會每切換一個新話題,就起一個新的聊天窗口,模型需要有判斷上文信息和當前信息是否相關的能力。因此,如果使用了 In context pretrain 這篇工作的論文,要不要做 attention_mask 還是要做實驗去斟酌的。
2.5 數(shù)據(jù)流水線
首先要明確一個概念,pretrain 模型一定是動態(tài)加載數(shù)據(jù)的,讀 1B 、訓 1B、再讀 1B 、再訓 1B…… 原因很簡單,你不知道你要訓多少數(shù)據(jù),即使知道你也沒那么大的內存空間一下子讀取好幾 T 的數(shù)據(jù)。
再明確一個概念,pretrain 階段模型獲取的是 token_id,而不是 token 本身,我們的 tokenization、concatenation 操作肯定是要提前做好的。當機器讀取了一個新數(shù)據(jù)塊之后,如果不能直接去訓練,而是還要花時間去轉 token,去 concat、去 pad,這簡直是對 GPU 的一種侮辱。
明確這兩個概念之后,我們就應該知道,pretrain 的兩個進程是獨立的:“數(shù)據(jù)處理進程”和“模型訓練進程”。前者要保證后者始終有最新的數(shù)據(jù)可用,除了 save_checkpoint 的時候,GPU 的空閑是一種極大的浪費。
pretrain 階段的數(shù)據(jù)是可以復用的,高質量數(shù)據(jù)訓多遍對模型并沒有壞處。因此,數(shù)據(jù)處理進程在生產(chǎn) part-00000.jsonl 的同時,它也應該標記清楚每一條原始的 document 數(shù)據(jù)被使用了多少次,被標記次數(shù)多的數(shù)據(jù),后續(xù)要降低它再被選中的概率。
每個數(shù)據(jù)塊不要太大,因為我們訓練的時候,經(jīng)常有燒卡、loss 炸、數(shù)據(jù)配錯了,等不可控的天災人禍,所以回退到上個數(shù)據(jù)塊進行續(xù)訓是一個很頻繁的操作。較大的數(shù)據(jù)塊自然會導致模型版本回退時損失的算力也較多。這里,我推薦每個數(shù)據(jù)塊都以 B 為單位,正好是 1B、2B、4B 等。
每個數(shù)據(jù)塊在訓練代碼中,自然會對應著一個 save_checkpoint 的操作,原因也是為了便于訓練回退。這里可以分享一個以前的小技巧,曾經(jīng)因為 warmup 階段,數(shù)據(jù)塊大小是動態(tài)增長的,陰差陽錯地導致模型的保存邏輯始終為 False。我們眼睜睜看著 tensorboard 美麗的符合預期的 loss 曲線,但就是沒辦法讓它 save,浪費了好一通算力。汲取教訓之后,組里大佬就發(fā)明了一個機制,在訓練代碼加了個邏輯:如果檢測到某個文件夾下存在一個叫“save”的文件,則立刻 save_checkpoint,我們將其稱為模型動態(tài)保存機制(一臉驕傲)。
2.6 數(shù)據(jù)實驗
當把以上的所有環(huán)節(jié)都串起來后,不要盲目的去開始訓練,一定要先在小模型上做好實驗,把 sacaling_law 的這個理念先搞懂。具體的實驗內容,可以根據(jù)自己的時間、人力來三個階段走:
-
粗糙一點的工作:在小模型上起多個數(shù)據(jù)配比、數(shù)據(jù)順序,訓練 500B 左右的數(shù)據(jù)量,然后選擇 loss 曲線最完美,或者 loss 下降最低的那個模型(這個階段刷 benchmark 意義不大,模型小,訓得少,大概率都是瞎蒙); -
專業(yè)一點的工作:額外起多個 size 的小模型,跑出 loss 結果,結合 scaling_law 公式,去推算大模型最適合的數(shù)據(jù)配比、學習率、訓練 token 量等參數(shù); -
創(chuàng)新一點的工作:像 llama 和 deepseek 技術報告里提到的一樣,去繪制出 loss 到 benchmark 的 scaling_law,提前預知模型訓多少 token 量能在某個 benchmark 達到什么樣的能力。
這個地方展開說的話,能說巨多的東西,但不方便細說,感興趣的同學還是多讀讀各公司的技術報告吧。scaling_law 只是放緩了,不是死了,在沒有新的技術指引的情況下,scaling_law 你不信也得信,它畢竟是用某種規(guī)則在做訓練,按照自己的直覺來做訓練基本等于“random”。
開弓沒有回頭箭,pretrain 的實驗階段一定要做的魯棒一些。
