作者 | 崔皓
審核 | 重樓
隨著大模型在人工智能領(lǐng)域的興起,如何將其應(yīng)用于垂直領(lǐng)域成為關(guān)鍵問(wèn)題。本文描述了模型微調(diào)技術(shù),通過(guò)調(diào)整預(yù)訓(xùn)練模型的參數(shù),使其適應(yīng)特定任務(wù),從而提升模型在特定領(lǐng)域的表現(xiàn)。文中以 Yelp 數(shù)據(jù)集為例,詳細(xì)介紹了如何使用 Hugging Face Transformers 框架對(duì) BERT 模型進(jìn)行微調(diào),實(shí)現(xiàn)評(píng)論星級(jí)分類(lèi)任務(wù)。文檔涵蓋了微調(diào)的背景、主流手段、架構(gòu)和工具,并深入講解了微調(diào)的思路和實(shí)踐步驟,包括數(shù)據(jù)預(yù)處理、模型選擇、超參數(shù)設(shè)置、訓(xùn)練評(píng)估和結(jié)果驗(yàn)證等。
大模型風(fēng)頭正盛,微調(diào)展現(xiàn)威力
近年來(lái),大型預(yù)訓(xùn)練模型如GPT系列、Llama系列、通義千問(wèn)在各大科技巨頭的推動(dòng)下,成為了人工智能領(lǐng)域的明星。然而,除了這些通用模型外,越來(lái)越多的垂直領(lǐng)域企業(yè)也在利用這些大模型,通過(guò)模型微調(diào),打造符合行業(yè)需求的定制化解決方案。
以一家科技公司為例,公司需要頻繁參與技術(shù)類(lèi)項(xiàng)目的投標(biāo),每次都要根據(jù)具體項(xiàng)目的要求編寫(xiě)詳細(xì)的技術(shù)方案。如果直接使用預(yù)訓(xùn)練好的通用語(yǔ)言模型來(lái)生成技術(shù)方案,雖然模型在理解語(yǔ)言和生成文本方面表現(xiàn)不錯(cuò),但對(duì)于科技行業(yè)特有的技術(shù)術(shù)語(yǔ)、項(xiàng)目需求以及公司的業(yè)務(wù)特點(diǎn),它的輸出質(zhì)量可能并不理想。通過(guò)模型微調(diào),公司可以使用少量歷史投標(biāo)文件和行業(yè)相關(guān)的標(biāo)記數(shù)據(jù),對(duì)模型進(jìn)行再訓(xùn)練,使其生成的技術(shù)方案更加貼合公司的技術(shù)風(fēng)格和項(xiàng)目要求。這樣,微調(diào)后的模型不僅能顯著提高方案生成的準(zhǔn)確性,還能幫助公司減少手工編寫(xiě)時(shí)間,提高效率和投標(biāo)成功率。
關(guān)于模型微調(diào),我們還可以舉一個(gè)例子:假設(shè)你已經(jīng)學(xué)會(huì)了畫(huà)畫(huà),能畫(huà)出非常漂亮的風(fēng)景畫(huà)?,F(xiàn)在,你想畫(huà)一只特定品種的小鳥(niǎo),雖然你已經(jīng)有了基礎(chǔ)的畫(huà)畫(huà)技巧,但為了畫(huà)好這只小鳥(niǎo),你還需要學(xué)習(xí)一些額外的技巧,比如觀(guān)察它的羽毛顏色和翅膀形狀。模型微調(diào)就像是這樣一個(gè)過(guò)程:AI模型已經(jīng)掌握了很多基礎(chǔ)技能,但要完成特定任務(wù),還需要通過(guò)微調(diào)來(lái)“特訓(xùn)”,以提高它在某個(gè)領(lǐng)域的表現(xiàn)。
從理論上看,模型微調(diào)是針對(duì)已經(jīng)經(jīng)過(guò)大規(guī)模數(shù)據(jù)預(yù)訓(xùn)練的通用模型進(jìn)行小規(guī)模的再次訓(xùn)練,使其在特定任務(wù)中表現(xiàn)更好。這種方法不僅節(jié)省了從零開(kāi)始訓(xùn)練的資源和時(shí)間,還通過(guò)遷移學(xué)習(xí)的方式,充分利用了預(yù)訓(xùn)練模型中的豐富知識(shí)。
在論文BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding (Devlin et al., 2018)中就指出通過(guò)預(yù)訓(xùn)練和微調(diào)的結(jié)合,顯著提升了模型在多項(xiàng)任務(wù)中的表現(xiàn)。在Transfer Learning for NLP: A Comprehensive Survey (Ruder, 2019) 中也提到遷移學(xué)習(xí)與微調(diào)技術(shù)的演變,以及它們?cè)谧匀徽Z(yǔ)言處理中的關(guān)鍵作用。這些研究表明,微調(diào)不僅能讓通用模型更好地適應(yīng)具體任務(wù),還能為各行業(yè)提供高效且專(zhuān)業(yè)化的解決方案。這就是為什么在現(xiàn)代AI領(lǐng)域,微調(diào)成為了一項(xiàng)不可或缺的技術(shù)。
主流的微調(diào)手段
既然大模型微調(diào)解決了企業(yè)在垂直領(lǐng)域的大模型使用問(wèn)題,那么市面上有哪些微調(diào)的手段呢?微調(diào)(Fine-tuning)作為將大型預(yù)訓(xùn)練模型應(yīng)用于特定任務(wù)的重要手段,依賴(lài)于對(duì)模型參數(shù)的靈活調(diào)整。不同任務(wù)的需求、數(shù)據(jù)量的大小以及計(jì)算資源的限制,決定了選擇哪種微調(diào)方式。以下是幾種主流的微調(diào)策略:
- 基于特征的調(diào)優(yōu)(Feature-Base Approach):在不修改預(yù)訓(xùn)練模型參數(shù)的前提下,通過(guò)提取特征向量并應(yīng)用于下游任務(wù)。例如在圖像識(shí)別任務(wù)中,利用預(yù)訓(xùn)練模型的輸出作為輸入特征來(lái)訓(xùn)練一個(gè)簡(jiǎn)單的分類(lèi)器。
- 輸出層更新的微調(diào)(Finetuning I):凍結(jié)模型的前幾層,僅對(duì)輸出層或最后幾層進(jìn)行更新。這種方法適合在數(shù)據(jù)量有限的情況下,保持模型的高效性,并避免過(guò)擬合。
- 全層更新的微調(diào)(Finetuning II):模型的所有層都會(huì)參與更新,適合數(shù)據(jù)量充足且任務(wù)復(fù)雜的場(chǎng)景。它可以讓模型更深層次地適應(yīng)特定領(lǐng)域的特性,但計(jì)算開(kāi)銷(xiāo)較大。
- 參數(shù)高效微調(diào)(Parameter-Efficient Fine-tuning, PEFT):PEFT 通過(guò)適配器訓(xùn)練(Adapter Training)或低秩分解等方法,只增加少量新的參數(shù)進(jìn)行訓(xùn)練,保持原模型參數(shù)的結(jié)構(gòu)不變。這種方法能夠極大減少計(jì)算資源需求,尤其適用于資源有限或需要快速迭代的任務(wù)。
- 蒸餾(Distillation):將大型模型(教師模型)的知識(shí)提取并應(yīng)用到一個(gè)較小的模型(學(xué)生模型)中,從而實(shí)現(xiàn)性能與計(jì)算效率的平衡。學(xué)生模型保留了教師模型中的關(guān)鍵知識(shí),同時(shí)大幅減少了計(jì)算資源的消耗。
- 適配器訓(xùn)練(Adapter Training):適配器是一種將少量新增參數(shù)插入到預(yù)訓(xùn)練模型中的技術(shù),能夠讓模型在不完全重訓(xùn)練的情況下適應(yīng)新任務(wù),從而降低計(jì)算開(kāi)銷(xiāo)并提高效率。
- 提示調(diào)優(yōu)(Prompt-tuning):在這種方式中,通過(guò)設(shè)計(jì)特定的提示詞來(lái)引導(dǎo)模型輸出所需的結(jié)果,而不需要調(diào)整模型參數(shù)。這種方法適合數(shù)據(jù)少且需要快速部署的場(chǎng)景。
微調(diào)的架構(gòu)和工具
前面我們提到了,在不同的應(yīng)用場(chǎng)景需要使用不同的微調(diào)手段,那么有哪些大模型微調(diào)的工具和框架可以幫助開(kāi)發(fā)者快速上手微調(diào)呢?在業(yè)內(nèi), Hugging Face Transformers 和 DeepSpeed 是兩個(gè)備受矚目的工具和架構(gòu),它們的出現(xiàn)使得微調(diào)變得更加高效和可擴(kuò)展。
1. Hugging Face Transformers
Hugging Face 的 Transformers 框架是目前自然語(yǔ)言處理(NLP)領(lǐng)域中最受歡迎的工具之一。它的優(yōu)勢(shì)在于簡(jiǎn)化了從預(yù)訓(xùn)練模型加載到微調(diào)整個(gè)過(guò)程的復(fù)雜性:
- 豐富的模型庫(kù):Hugging Face 提供了一個(gè)龐大的模型庫(kù),包括 BERT、GPT、T5 等預(yù)訓(xùn)練模型。
- 簡(jiǎn)化的微調(diào)流程:借助其高層 API 和 Trainer 模塊,開(kāi)發(fā)者可以輕松地在自己的數(shù)據(jù)集上微調(diào)模型。Trainer 支持自動(dòng)混合精度訓(xùn)練、分布式訓(xùn)練和動(dòng)態(tài)批量大小調(diào)整等功能。
- 集成的社區(qū)和數(shù)據(jù)集:Hugging Face 平臺(tái)上的模型和數(shù)據(jù)集共享機(jī)制讓開(kāi)發(fā)者能夠輕松訪(fǎng)問(wèn)他人的工作成果。
2. DeepSpeed
DeepSpeed 是微軟推出的一款針對(duì)大規(guī)模深度學(xué)習(xí)模型的優(yōu)化工具,它的主要優(yōu)勢(shì)在于極大地提高了訓(xùn)練效率,尤其適合處理像 GPT-3 這樣的超大規(guī)模模型。
- 混合精度訓(xùn)練(FP16/BF16):DeepSpeed 通過(guò)混合精度技術(shù),在不犧牲性能的前提下顯著減少模型的內(nèi)存占用,并加快計(jì)算速度。
- 零冗余優(yōu)化(ZeRO Optimizer):DeepSpeed 采用了零冗余優(yōu)化策略,可以在多 GPU 環(huán)境下大幅度減少內(nèi)存開(kāi)銷(xiāo),使得在相對(duì)有限的硬件資源下也能訓(xùn)練大型模型。
- 支持大規(guī)模并行化:DeepSpeed 提供了靈活且高效的分布式訓(xùn)練方案,能夠高效擴(kuò)展至成千上萬(wàn)的 GPU,適用于大規(guī)模的微調(diào)任務(wù)。
參數(shù)高效的訓(xùn)練:通過(guò)對(duì)訓(xùn)練過(guò)程的優(yōu)化,如梯度累積、張量并行等技術(shù),DeepSpeed 可以在降低內(nèi)存需求的同時(shí)提高訓(xùn)練速度。
微調(diào)思路
雖然兩個(gè)調(diào)優(yōu)工具都非常優(yōu)秀,但由于本文篇幅有限,所以我們選取Hugging face Transformers 框架來(lái)為大家進(jìn)行大模型調(diào)優(yōu)的實(shí)踐。
這里通過(guò)一張圖來(lái)介紹大模型微調(diào)的思路,如下圖所示,展示了從文本預(yù)處理到模型訓(xùn)練、評(píng)估、驗(yàn)證和保存等全過(guò)程。對(duì)于本次微調(diào),我們希望解決“分類(lèi)問(wèn)題”,讓模型理解“評(píng)論內(nèi)容”與“評(píng)分星級(jí)”之間的關(guān)系,這里我們會(huì)提供大量的評(píng)論數(shù)據(jù),每條數(shù)據(jù)會(huì)打上評(píng)分星級(jí)。在微調(diào)之后,給模型輸入任何的評(píng)論內(nèi)容,它都會(huì)返回該評(píng)論對(duì)應(yīng)的星級(jí)。接下來(lái)的過(guò)程將分為幾個(gè)主要步驟:
1. 確認(rèn)問(wèn)題
首先,要確認(rèn)本次微調(diào)要解決的問(wèn)題:理解“評(píng)論內(nèi)容”與“評(píng)分星級(jí)”之間的對(duì)應(yīng)關(guān)系。通過(guò)微調(diào)模型,我們希望模型能夠根據(jù)評(píng)論的文本內(nèi)容自動(dòng)預(yù)測(cè)其星級(jí)評(píng)分(1-5星)。微調(diào)后的模型能夠在新的評(píng)論輸入時(shí)自動(dòng)給出星級(jí)評(píng)定。
2. 裝載數(shù)據(jù)集
本次微調(diào)會(huì)使用Yelp 數(shù)據(jù)集,它是一個(gè)公開(kāi)的評(píng)論數(shù)據(jù)集,包含了大量用戶(hù)對(duì)商家的評(píng)價(jià)和評(píng)分,廣泛應(yīng)用于自然語(yǔ)言處理任務(wù)。數(shù)據(jù)集的特點(diǎn)包括:
- 文本多樣性:包含多種不同類(lèi)型的評(píng)論,覆蓋不同語(yǔ)言風(fēng)格、評(píng)論長(zhǎng)度。
- 結(jié)構(gòu)化評(píng)分:每條評(píng)論都帶有一個(gè)結(jié)構(gòu)化的星級(jí)評(píng)分(1到5星),適合分類(lèi)問(wèn)題。
- 數(shù)據(jù)量龐大:原始數(shù)據(jù)集包含數(shù)條評(píng)論,適用于深度學(xué)習(xí)模型的訓(xùn)練。
3. 分詞器處理文本
對(duì)評(píng)論文本進(jìn)行分詞,將每條評(píng)論分解為詞或詞組。這里我們使用 BERT 自帶的分詞器(Tokenizer),它能夠?qū)⑽谋巨D(zhuǎn)化為模型所需的輸入格式(如 token IDs、attention masks 等)。通過(guò)分詞,我們能夠?qū)⒚總€(gè)詞或詞組轉(zhuǎn)化為模型理解的向量表示,并保留文本的結(jié)構(gòu)信息。
4. 生成小數(shù)據(jù)集
為了加快實(shí)驗(yàn)和測(cè)試過(guò)程,我們從原始的 Yelp 數(shù)據(jù)集中提取一個(gè)小樣本數(shù)據(jù)集。小數(shù)據(jù)集可以幫助我們快速迭代模型的訓(xùn)練、調(diào)參和驗(yàn)證。經(jīng)過(guò)分詞器處理后,我們可以將這些小樣本輸入到模型中進(jìn)行訓(xùn)練和評(píng)估。
5. 選擇微調(diào)模型
我們將使用預(yù)訓(xùn)練的 BERT 模型,并根據(jù) Yelp 數(shù)據(jù)集的需求調(diào)整模型的輸出層。BERT 本身經(jīng)過(guò)大規(guī)模語(yǔ)料庫(kù)的預(yù)訓(xùn)練,具備強(qiáng)大的語(yǔ)言理解能力,通過(guò)微調(diào),可以將其適應(yīng)特定的分類(lèi)任務(wù)。
6. 設(shè)置超參數(shù)
超參數(shù)包括學(xué)習(xí)率、訓(xùn)練輪次、batch size 等。合理的超參數(shù)設(shè)置能夠確保模型的有效訓(xùn)練,避免過(guò)擬合或欠擬合問(wèn)題。在訓(xùn)練過(guò)程中,我們將定期監(jiān)控模型的性能,確保其學(xué)習(xí)到評(píng)論文本中的有效信息。
7. 訓(xùn)練評(píng)估
在訓(xùn)練階段,我們會(huì)定期對(duì)模型進(jìn)行評(píng)估。這通常在每個(gè)訓(xùn)練輪次結(jié)束后進(jìn)行,通過(guò)驗(yàn)證集上的數(shù)據(jù)來(lái)評(píng)估模型的性能。通過(guò)這樣的方式,能夠?qū)崟r(shí)了解模型的學(xué)習(xí)效果,并根據(jù)評(píng)估結(jié)果適時(shí)調(diào)整,避免過(guò)擬合等問(wèn)題。
8. 計(jì)算準(zhǔn)確性計(jì)算
通過(guò)計(jì)算模型的預(yù)測(cè)準(zhǔn)確性來(lái)衡量其表現(xiàn)。在每次評(píng)估時(shí),模型會(huì)對(duì)驗(yàn)證集中的數(shù)據(jù)進(jìn)行預(yù)測(cè),然后將這些預(yù)測(cè)結(jié)果與實(shí)際標(biāo)簽進(jìn)行比較。通過(guò)計(jì)算準(zhǔn)確性,能夠判斷模型在該分類(lèi)任務(wù)中的成功率,即模型在多大程度上能夠正確預(yù)測(cè)評(píng)論的星級(jí)。
9. 執(zhí)行訓(xùn)練
開(kāi)始對(duì)模型進(jìn)行微調(diào)訓(xùn)練。
10.保存模型
在訓(xùn)練和驗(yàn)證完成后,將微調(diào)后的模型進(jìn)行保存。保存的模型可以用于后續(xù)的預(yù)測(cè)和推理任務(wù)。通過(guò)調(diào)用保存的模型,我們可以在給定新評(píng)論的情況下快速獲得預(yù)測(cè)的星級(jí)評(píng)分。
11. 驗(yàn)證結(jié)果
將設(shè)計(jì)對(duì)比實(shí)驗(yàn):讓未經(jīng)過(guò)微調(diào)的模型和經(jīng)過(guò)微調(diào)的模型分別對(duì) Yelp 數(shù)據(jù)集中的評(píng)論進(jìn)行星級(jí)預(yù)測(cè),查看評(píng)分結(jié)果的差異。這樣可以展示微調(diào)后的模型是否能夠更好地理解評(píng)論文本與評(píng)分之間的關(guān)系,并給出更加準(zhǔn)確的預(yù)測(cè)。
通過(guò)這些步驟,我們將詳細(xì)探討如何使用 Hugging Face 的工具對(duì) BERT 模型進(jìn)行微調(diào),并結(jié)合 Yelp 數(shù)據(jù)集,逐步完成從數(shù)據(jù)預(yù)處理、模型訓(xùn)練到結(jié)果驗(yàn)證的整個(gè)流程。
實(shí)現(xiàn)微調(diào)
介紹完整個(gè)微調(diào)思路之后,我們會(huì)通過(guò)Hugging Face的Transformers 模型框架編寫(xiě)微調(diào)的代碼。
由于微調(diào)模型需要算力資源,一般而言都需要多張高顯存的顯卡支持。通過(guò)上面的微調(diào)思路大家可以知道,為了演示微調(diào)過(guò)程,我們將數(shù)據(jù)集進(jìn)行了縮減的操作,利用更小的數(shù)據(jù)集來(lái)完成微調(diào)。在實(shí)際應(yīng)用場(chǎng)景如果需要微調(diào)更大的數(shù)據(jù)集就需要多張高階顯卡。如下圖所示,我們租用AutoDL上的虛擬服務(wù)器,使用3080ti顯卡進(jìn)行微調(diào),實(shí)際測(cè)試的效果還不錯(cuò),在小數(shù)據(jù)微調(diào)的情況下10-20分鐘可以完成。
接著,需要安裝必要的組件庫(kù),如下:
!pip install transformers datasets evaluate accelerate scikit-learn
上述庫(kù)支持自然語(yǔ)言處理任務(wù),尤其是涉及到模型微調(diào)、評(píng)估和數(shù)據(jù)處理的任務(wù)。下面是對(duì)每個(gè)庫(kù)的簡(jiǎn)要解釋?zhuān)?/p>
1. transformers
這個(gè)庫(kù)是由 Hugging Face 提供的,它包含了許多預(yù)訓(xùn)練的自然語(yǔ)言處理模型,支持包括文本分類(lèi)、生成、翻譯等任務(wù)。
2. datasets
該庫(kù)也是由 Hugging Face 提供的,專(zhuān)門(mén)用于處理和管理各種機(jī)器學(xué)習(xí)數(shù)據(jù)集。它可以加載、預(yù)處理和操作不同格式的數(shù)據(jù)集,支持直接加載許多公開(kāi)數(shù)據(jù)集如 Yelp、IMDB 等。
3. evaluate
用于評(píng)估模型的性能。它提供了各種常用的評(píng)估指標(biāo)(如準(zhǔn)確率、精確率、召回率等),方便在模型訓(xùn)練和驗(yàn)證時(shí)計(jì)算這些指標(biāo),幫助衡量模型的表現(xiàn)。
4. accelerate
用于加速模型訓(xùn)練的庫(kù),支持分布式訓(xùn)練和加速設(shè)備的無(wú)縫切換,比如在CPU和GPU之間轉(zhuǎn)換,或者使用多個(gè)GPU進(jìn)行訓(xùn)練。
5. scikit-learn
非常流行的機(jī)器學(xué)習(xí)庫(kù),提供了各種統(tǒng)計(jì)學(xué)習(xí)工具、數(shù)據(jù)預(yù)處理和模型評(píng)估方法。在自然語(yǔ)言處理項(xiàng)目中,它常用于進(jìn)行數(shù)據(jù)處理和分類(lèi)算法的基礎(chǔ)操作,如分詞、向量化、模型評(píng)估等。
裝載數(shù)據(jù)集
這里我們使用了Yelp數(shù)據(jù)集,它是從 2015 年的 Yelp 數(shù)據(jù)集挑戰(zhàn)賽中提取的,包含大量的用戶(hù)評(píng)論,主要用于情感分類(lèi)和文本分類(lèi)任務(wù)。通過(guò)分析這些評(píng)論文本,可以預(yù)測(cè)用戶(hù)的情感傾向,也就是評(píng)分的星級(jí)。數(shù)據(jù)集中的評(píng)論主要以英文撰寫(xiě),每條評(píng)論包含兩個(gè)核心元素:評(píng)論的文本內(nèi)容和對(duì)應(yīng)的評(píng)分標(biāo)簽。評(píng)論內(nèi)容中涉及的文本經(jīng)過(guò)特殊處理,雙引號(hào)和換行符都進(jìn)行了轉(zhuǎn)義。評(píng)分標(biāo)簽表示評(píng)論的星級(jí),范圍從 1 到 5 星不等。整個(gè)數(shù)據(jù)集被隨機(jī)劃分為訓(xùn)練集和測(cè)試集,每個(gè)星級(jí)類(lèi)別各包含 130,000 條訓(xùn)練樣本和 10,000 條測(cè)試樣本,總共包含 650,000 條訓(xùn)練數(shù)據(jù)和 50,000 條測(cè)試數(shù)據(jù)。
數(shù)據(jù)集的格式大致如下:
{
'label': 0,
'text': 'I got \'new\' tires from them and within two weeks got a flat...'
}
接著通過(guò)如下代碼加載數(shù)據(jù)。
from datasets import load_dataset
dataset = load_dataset("yelp_review_full")
dataset["train"][100]
通過(guò)代碼可以看出,load_dataset 函數(shù)可以直接加載 yelp_review_full 數(shù)據(jù)集,它來(lái)自 Hugging Face 的 datasets 庫(kù)。這個(gè)庫(kù)內(nèi)置了許多公開(kāi)可用的數(shù)據(jù)集,Yelp Review Full 就是其中之一。說(shuō)白了Hugging Face 數(shù)據(jù)庫(kù)包含了大量的預(yù)先注冊(cè)的數(shù)據(jù)集,并且通過(guò)統(tǒng)一的接口 load_dataset 來(lái)訪(fǎng)問(wèn),需要什么數(shù)據(jù)你告訴Hugging Face 名字就可以了,不用指明數(shù)據(jù)的文件地址。
當(dāng)使用 load_dataset("yelp_review_full") 時(shí),函數(shù)會(huì)通過(guò)數(shù)據(jù)集名稱(chēng)自動(dòng)從 Hugging Face 的數(shù)據(jù)集庫(kù)中找到對(duì)應(yīng)的資源,并加載相應(yīng)的數(shù)據(jù)集。這意味著你不需要手動(dòng)下載或準(zhǔn)備數(shù)據(jù)集,Hugging Face 已經(jīng)在其平臺(tái)上托管了這個(gè)數(shù)據(jù)集,并定義了它的結(jié)構(gòu)和格式。因此,load_dataset 能夠通過(guò)提供的名稱(chēng)直接訪(fǎng)問(wèn)并加載。
在加載完畢數(shù)據(jù)以后,可以通過(guò)dataset["train"][100],查看Yelp Review 數(shù)據(jù)集中提取第 100 條訓(xùn)練數(shù)據(jù),確認(rèn)數(shù)據(jù)已經(jīng)加載完畢。
分詞器文本處理
分詞器的工作是將我們看得懂的語(yǔ)言文字轉(zhuǎn)化為模型能理解的數(shù)字。無(wú)論是機(jī)器學(xué)習(xí)模型還是深度學(xué)習(xí)模型,它們都只能處理數(shù)字,而不是直接處理文本。我們需要一個(gè)方法來(lái)把文本“翻譯”成模型能夠理解的形式。分詞器正是負(fù)責(zé)這一“翻譯”工作的工具。通過(guò)將句子分解成“詞”或“字符”,然后給每個(gè)詞或字符分配一個(gè)數(shù)字(通常叫做輸入ID),模型就可以開(kāi)始處理這些數(shù)字了。
如下圖所示,比如,“我喜歡蘋(píng)果”這句話(huà),分詞器就會(huì)把“我”翻譯成1,“喜歡”翻譯成2,“蘋(píng)果”翻譯成3。而這些數(shù)字是根據(jù)詞匯表(vocabulary)中的子詞來(lái)索引的,可以理解為在模型內(nèi)部有一個(gè)很大的詞匯表,詞匯表中維護(hù)每個(gè)詞對(duì)應(yīng)的ID,你輸入ID模型就知道你要表達(dá)什么詞(類(lèi)似身份證與人之間的關(guān)系)。這個(gè)詞匯表可以看作是模型用來(lái)理解語(yǔ)言的“字典”,其中包含了大量常見(jiàn)的詞和子詞(subwords)。每個(gè)子詞在詞匯表中都有一個(gè)對(duì)應(yīng)的索引 ID,當(dāng)我們輸入一段文本時(shí),分詞器會(huì)將它切分成多個(gè)子詞,并查找每個(gè)子詞在詞匯表中的索引。
為什么需要詞匯表?因?yàn)檎Z(yǔ)言的多樣性非常大,而單詞的數(shù)量是無(wú)限的。直接給每個(gè)完整的單詞一個(gè)唯一的 ID 會(huì)導(dǎo)致詞匯表非常龐大,這不僅會(huì)增加模型的復(fù)雜性,還容易出現(xiàn)很多未知的詞(也就是詞匯表中沒(méi)有的詞)。使用子詞來(lái)代替整個(gè)單詞能夠減少詞匯表的大小,同時(shí)確保即便遇到生僻詞也能通過(guò)已知的子詞來(lái)表達(dá)。例如,英文"unhappiness" 可以被拆分為 "un-" 、"happi-" 和 “ness”三個(gè)子詞,即便模型沒(méi)有見(jiàn)過(guò)完整的“unhappiness”這個(gè)詞,它仍然可以通過(guò)上述三個(gè)子詞來(lái)理解。
如下圖所示,簡(jiǎn)單理解,分詞器的工作就是講輸入的文字參照詞匯表,將其轉(zhuǎn)化為子詞ID的形式,然后輸入到大模型中進(jìn)行處理。
接著來(lái)看代碼:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
從 Hugging Face 預(yù)訓(xùn)練模型庫(kù)中加載了google-bert/bert-base-cased版本分詞器。
def tokenize_function(examples)用于處理數(shù)據(jù)集中的每一個(gè)數(shù)據(jù)點(diǎn)。
tokenizer(examples["text"], padding="max_length", truncation=True):這里使用分詞器將輸入的文本數(shù)據(jù)進(jìn)行分詞。分詞器會(huì)將文本轉(zhuǎn)換為標(biāo)記(tokens),并將其轉(zhuǎn)換為適合模型輸入的數(shù)字化形式。padding="max_length":表示對(duì)文本進(jìn)行填充,使所有文本的長(zhǎng)度相同,達(dá)到預(yù)定義的最大長(zhǎng)度。truncation=True:如果文本長(zhǎng)度超過(guò)最大長(zhǎng)度,自動(dòng)截?cái)喽嘤嗖糠帧?/p>
tokenized_datasets = dataset.map(tokenize_function, batched=True):使用 map 函數(shù),將定義的分詞函數(shù)應(yīng)用到數(shù)據(jù)集中。通過(guò) batched=True,它批量處理數(shù)據(jù),加快處理速度。最終得到的是一個(gè)包含經(jīng)過(guò)分詞和預(yù)處理的文本數(shù)據(jù)集,格式化后可直接輸入模型。
生成小數(shù)據(jù)集
在處理大型數(shù)據(jù)集時(shí),訓(xùn)練模型和進(jìn)行驗(yàn)證會(huì)耗費(fèi)大量時(shí)間和計(jì)算資源。因此,我們通常會(huì)創(chuàng)建一個(gè)小數(shù)據(jù)集來(lái)進(jìn)行快速的實(shí)驗(yàn)和調(diào)試。
加入如下代碼:
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
tokenized_datasets["train"] 和 tokenized_datasets["test"]: 分別從已分詞的數(shù)據(jù)集中獲取訓(xùn)練集和測(cè)試集。
- shuffle(seed=42): 將數(shù)據(jù)集隨機(jī)打亂,seed=42 確保打亂的順序是可重復(fù)的,即每次執(zhí)行代碼時(shí)得到相同的隨機(jī)順序。
- select(range(1000)): 選擇前 1000 條樣本,生成一個(gè)小型的數(shù)據(jù)集,限制訓(xùn)練和測(cè)試的規(guī)模為 1000 條數(shù)據(jù),適用于快速測(cè)試。
指定微調(diào)模型
微調(diào)數(shù)據(jù)集是基于一個(gè)預(yù)訓(xùn)練模型展開(kāi)的,這里選擇bert-base-cased 模型,用于一個(gè)有 5 個(gè)分類(lèi)標(biāo)簽(1-5星的評(píng)價(jià))的序列分類(lèi)任務(wù)。
代碼如下:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5)
AutoModelForSequenceClassification:是 Hugging Face 的 transformers 庫(kù)中提供的模型,用于文本分類(lèi)任務(wù)。它基于預(yù)訓(xùn)練的 Transformer 模型,并添加一個(gè)分類(lèi)頭(線(xiàn)性層)來(lái)處理分類(lèi)任務(wù)。通過(guò)這個(gè)類(lèi)中的from_pretrained("google-bert/bert-base-cased")方法加載預(yù)訓(xùn)練模型。
num_labels=5:定義了分類(lèi)任務(wù)的標(biāo)簽數(shù)為 5,表示我們要對(duì)文本進(jìn)行 5 類(lèi)分類(lèi)任務(wù)。
設(shè)置超參數(shù)
設(shè)置超參數(shù)幫助配置模型訓(xùn)練時(shí)的各種參數(shù),比如訓(xùn)練輸出的目錄、批量大小、學(xué)習(xí)率、是否使用 GPU 等。
如下代碼用于設(shè)置訓(xùn)練模型時(shí)的超參數(shù),使用 Hugging Face transformers 庫(kù)中的 TrainingArguments 類(lèi)。
from transformers import TrainingArguments
training_args = TrainingArguments(output_dir="test_trainer")
為了簡(jiǎn)化演示過(guò)程這里只配置了output_dir="test_trainer"作為模型輸出的路徑。在訓(xùn)練過(guò)程中,模型的檢查點(diǎn)(checkpoints)、日志文件、以及最終模型都會(huì)被保存在這個(gè)目錄下。
此外,TrainingArguments 提供了許多其他參數(shù),你可以根據(jù)具體的訓(xùn)練需求來(lái)設(shè)置,比如:
- per_device_train_batch_size:每個(gè)設(shè)備上的訓(xùn)練批次大小。
- learning_rate:設(shè)置學(xué)習(xí)率。
- num_train_epochs:訓(xùn)練的迭代次數(shù)。
- evaluation_strategy:定義何時(shí)進(jìn)行評(píng)估(例如 "steps", "epoch" 等)。
- save_steps:在訓(xùn)練過(guò)程中保存模型的頻率。
- logging_dir:用于存放 TensorBoard 日志文件的目錄。
訓(xùn)練評(píng)估與計(jì)算預(yù)測(cè)的準(zhǔn)確性
在模型的訓(xùn)練和評(píng)估過(guò)程中,特別是在分類(lèi)任務(wù)中,衡量模型性能的一個(gè)關(guān)鍵指標(biāo)就是準(zhǔn)確性。訓(xùn)練好的模型可以在驗(yàn)證集上進(jìn)行評(píng)估,查看模型在實(shí)際數(shù)據(jù)上的表現(xiàn),以此來(lái)衡量其分類(lèi)能力。我們會(huì)創(chuàng)建一個(gè)compute_metrics 函數(shù),它用來(lái)計(jì)算預(yù)測(cè)結(jié)果和實(shí)際標(biāo)簽的差異來(lái)評(píng)估模型的準(zhǔn)確性。同時(shí)會(huì)使用evaluate 庫(kù)的 accuracy 指標(biāo)得到模型在驗(yàn)證集上的表現(xiàn)。
代碼如下:
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
metric = evaluate.load("accuracy")加載了 Hugging Face 庫(kù)中預(yù)定義的“準(zhǔn)確率”評(píng)估指標(biāo)。 compute_metrics(eval_pred) 函數(shù)是為訓(xùn)練/驗(yàn)證流程提供的評(píng)估函數(shù),傳入的參數(shù) eval_pred 包含了兩個(gè)部分:logits 和 labels。
logits:模型輸出的原始預(yù)測(cè)結(jié)果,通常是未經(jīng)過(guò)激活函數(shù)的值(在分類(lèi)任務(wù)中通常是未經(jīng) softmax 的分?jǐn)?shù))。
為什么突然跳出一個(gè)logits來(lái)了?這里需要稍微解釋一下,在深度學(xué)習(xí)的分類(lèi)模型中,模型在最后一層輸出的是各個(gè)類(lèi)別的分?jǐn)?shù)(logits)。這些分?jǐn)?shù)用來(lái)表示模型對(duì)各個(gè)類(lèi)別的預(yù)測(cè)概率。在很多情況下,模型會(huì)直接輸出 logits,而不是最終的類(lèi)別,因?yàn)?logits 還可以進(jìn)一步處理(比如通過(guò) softmax 轉(zhuǎn)換為概率)。
大家還記得嗎?在最開(kāi)始處理文本的時(shí)候,我們需要針對(duì)輸入文本進(jìn)行分詞(tokenization),接著每個(gè)詞會(huì)被映射成嵌入(embedding)。嵌入作為輸入經(jīng)過(guò)模型的層層處理,最后在輸出層產(chǎn)生 logits,用于預(yù)測(cè)文本的分類(lèi)結(jié)果。因此,分詞是處理輸入數(shù)據(jù)的第一步,而 logits 是最終的輸出分?jǐn)?shù),用來(lái)確定分類(lèi)的結(jié)果。
來(lái)舉個(gè)例子說(shuō)說(shuō)logits方便大家理解,假設(shè)我們有一個(gè)分類(lèi)任務(wù),模型需要把一句話(huà)分成 5 個(gè)類(lèi)別之一(比如情感分類(lèi):0=負(fù)面, 1=中性, 2=正面, 3=疑問(wèn), 4=驚訝)。在模型處理輸入之后,它會(huì)輸出一個(gè)包含 5 個(gè)數(shù)值的向量(logits),表示模型對(duì)每個(gè)類(lèi)別的信心。
示例:
輸入句子:"I love this product!"
假設(shè)模型的輸出 logits 是:
logits = [2.5, -1.3, 5.2, 0.8, -0.5]
這個(gè)向量表示模型對(duì)每個(gè)類(lèi)別的信心。這里的 5.2 是 logits 中的最大值,表示模型認(rèn)為這個(gè)句子最可能屬于第 2 類(lèi)(“正面”情感),盡管它對(duì)每個(gè)類(lèi)別都有一定的信心值。
所以,這里的logits就是在對(duì)預(yù)測(cè)結(jié)果進(jìn)行“打分”,哪個(gè)分類(lèi)上分?jǐn)?shù)越高,模型就會(huì)認(rèn)為這個(gè)文字描述屬于哪類(lèi)。
而labels表示的是實(shí)際標(biāo)簽,它用于與模型的預(yù)測(cè)結(jié)果對(duì)比。
logits, labels = eval_pred:將 eval_pred 拆分為 logits 和 labels。
predictions = np.argmax(logits, axis=-1):使用 np.argmax() 函數(shù)從 logits 中找到最大值的索引,代表模型的最終預(yù)測(cè)類(lèi)別。axis=-1 表示沿最后一個(gè)維度進(jìn)行操作,對(duì)于每個(gè)輸入,輸出模型認(rèn)為最可能的類(lèi)別。
metric.compute(predictions=predictions, references=labels):計(jì)算模型預(yù)測(cè)的準(zhǔn)確性。predictions 是模型的預(yù)測(cè)值,references 是真實(shí)標(biāo)簽。這個(gè)函數(shù)返回一個(gè)包含準(zhǔn)確性(accuracy)指標(biāo)的字典。
創(chuàng)建Trainer對(duì)象執(zhí)行微調(diào)任務(wù)
微調(diào)前的基本工作已經(jīng)完成了, 接下來(lái)我們需要?jiǎng)?chuàng)建一個(gè)Trainer類(lèi),用于簡(jiǎn)化訓(xùn)練和評(píng)估過(guò)程,該類(lèi)包含了模型、訓(xùn)練參數(shù)、訓(xùn)練和驗(yàn)證數(shù)據(jù)集,以及評(píng)估指標(biāo)計(jì)算方法。然后,執(zhí)行trainer.train() 用于微調(diào)訓(xùn)練,模型的權(quán)重會(huì)根據(jù)訓(xùn)練數(shù)據(jù)逐步優(yōu)化。最后,trainer.save_model() 會(huì)將訓(xùn)練好的模型保存,方便后續(xù)使用。代碼如下:
trainer = Trainer(
model=model, # 定義的模型( BERT 分類(lèi)模型)
args=training_args, # 訓(xùn)練的參數(shù),如學(xué)習(xí)率、批次大小、訓(xùn)練周期等
train_dataset=small_train_dataset, # 用于訓(xùn)練的小數(shù)據(jù)集
eval_dataset=small_eval_dataset, # 用于評(píng)估的小數(shù)據(jù)集
compute_metrics=compute_metrics, # 之前定義的,驗(yàn)證的準(zhǔn)確性等指標(biāo)的函數(shù)
)
trainer.train() # 開(kāi)始訓(xùn)練模型
trainer.save_model() # 保存訓(xùn)練后的模型
執(zhí)行上述代碼,得到如下運(yùn)行截圖:
從圖中可以看出,驗(yàn)證集上的準(zhǔn)確性(Accuracy)分別為:
- 第一輪:57.3%(0.573000)
- 第二輪:58.0%(0.580000)
- 第三輪:59.4%(0.594000)
盡管第三輪的驗(yàn)證損失增加了,準(zhǔn)確率卻依然提升了一點(diǎn)。這可能表明模型在某些類(lèi)別上的預(yù)測(cè)更準(zhǔn)確,但整體的損失仍然較大,表明模型存在過(guò)擬合或不平衡的表現(xiàn)。
同時(shí)得到如下運(yùn)行信息:
TrainOutput(global_step=375, training_loss=0.49261214192708336, metrics={'train_runtime': 127.2273, 'train_samples_per_second': 23.58, 'train_steps_per_second': 2.947, 'total_flos': 789354427392000.0, 'train_loss': 0.49261214192708336, 'epoch': 3.0})
從信息可以看出,模型微調(diào)訓(xùn)練共進(jìn)行了 3 個(gè) Epoch,執(zhí)行了 375 個(gè)訓(xùn)練步數(shù),總運(yùn)行時(shí)間為 127 秒。在訓(xùn)練過(guò)程中,模型每秒能夠處理約 23.58 個(gè)樣本,每秒執(zhí)行 2.95 個(gè)訓(xùn)練步。最終,模型的平均訓(xùn)練損失為 0.49,表明模型在訓(xùn)練數(shù)據(jù)上的表現(xiàn)較為理想。此外,訓(xùn)練的總浮點(diǎn)運(yùn)算量(FLOPs)為 789,354,427,392,000,展示了訓(xùn)練過(guò)程中涉及的大量計(jì)算量。
驗(yàn)證微調(diào)
前面我們通過(guò)獲取數(shù)據(jù)集針對(duì)BERT模型進(jìn)行微調(diào),執(zhí)行微調(diào)命令之后完成了微調(diào)的過(guò)程,并且通過(guò)trainer.save_model()語(yǔ)句將微調(diào)之后的模型保存到本地。接下來(lái),通過(guò)對(duì)比微調(diào)前后模型對(duì)評(píng)論信息的星級(jí)反饋,來(lái)查看微調(diào)之后模型的能力是否提升。
先調(diào)用測(cè)試之前的模型輸入評(píng)論內(nèi)容,看模型對(duì)評(píng)論給出什么星級(jí)的評(píng)定。
如下圖所示,測(cè)試的評(píng)論是2 star(2星的星級(jí)評(píng)定)。
執(zhí)行如下代碼:
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import numpy as np
# Yelp review 示例
input_text = "My first time going to Barb's Country Junction and I really wanted to like this place, I really did...however,……."
# 加載預(yù)訓(xùn)練模型和 tokenizer
tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")
model_pretrained = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5)
# 標(biāo)記化輸入文本
inputs = tokenizer(input_text, padding=True, truncation=True, return_tensors="pt")
# 使用預(yù)訓(xùn)練模型進(jìn)行預(yù)測(cè)(沒(méi)有經(jīng)過(guò)微調(diào))
with torch.no_grad():
outputs_pretrained = model_pretrained(**inputs)
# 獲取未微調(diào)模型的預(yù)測(cè)標(biāo)簽
predicted_label_pretrained = np.argmax(outputs_pretrained.logits, axis=-1).item()
print(f"Prediction before fine-tuning (without knowledge of Yelp reviews): {predicted_label_pretrained}")
得到如下結(jié)果。
Prediction before fine-tuning (without knowledge of Yelp reviews): 3
很明顯,從結(jié)果上看模型預(yù)測(cè)的星級(jí)為“3”,與數(shù)據(jù)集標(biāo)注的“2”存在差異。
接著利用微調(diào)之后模型進(jìn)行推理,代碼如下:
model = AutoModelForSequenceClassification.from_pretrained("test_trainer")
tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")
# 輸入文本
input_text = "My first time going to Barb's Country Junction and I really wanted to like this place, I really did...however, ……"
# 標(biāo)記化輸入文本
inputs = tokenizer(input_text, padding=True, truncation=True, return_tensors="pt")
# 獲取模型預(yù)測(cè)
with torch.no_grad(): # 關(guān)閉梯度計(jì)算
outputs = model(**inputs)
# 獲取預(yù)測(cè)標(biāo)簽
predicted_label = np.argmax(outputs.logits, axis=-1).item()
# 輸出結(jié)果
print(f"Predicted label: {predicted_label}")
上述代碼通過(guò)model = AutoModelForSequenceClassification.from_pretrained("test_trainer"),獲取微調(diào)之后test_trainer目錄下的模型進(jìn)行推理。 執(zhí)行結(jié)果如下:
Predicted label: 2
與預(yù)期的結(jié)果相同,說(shuō)明微調(diào)之后的模型能夠勝任評(píng)論星級(jí)分類(lèi)的工作。
總結(jié)
大模型微調(diào)技術(shù)為將預(yù)訓(xùn)練模型應(yīng)用于垂直領(lǐng)域提供了有效途徑。通過(guò)微調(diào),我們可以利用預(yù)訓(xùn)練模型的知識(shí),并結(jié)合特定領(lǐng)域的任務(wù)需求,打造定制化解決方案。本文以 Yelp 數(shù)據(jù)集為例,演示了如何使用 Hugging Face Transformers 框架對(duì) BERT 模型進(jìn)行微調(diào),實(shí)現(xiàn)評(píng)論星級(jí)分類(lèi)任務(wù)。文檔涵蓋了微調(diào)的背景、主流手段、架構(gòu)和工具,并深入講解了微調(diào)的思路和實(shí)踐步驟,包括數(shù)據(jù)預(yù)處理、模型選擇、超參數(shù)設(shè)置、訓(xùn)練評(píng)估和結(jié)果驗(yàn)證等。相信通過(guò)本文的學(xué)習(xí),讀者能夠更好地理解大模型微調(diào)技術(shù),并將其應(yīng)用于實(shí)際項(xiàng)目中。
作者介紹
崔皓,51CTO社區(qū)編輯,資深架構(gòu)師,擁有18年的軟件開(kāi)發(fā)和架構(gòu)經(jīng)驗(yàn),10年分布式架構(gòu)經(jīng)驗(yàn)。