如何微調(Fine-tuning)大語言模型?



本文介紹了微調的基本概念,以及如何對語言模型進行微調。

從 GPT3 到 ChatGPT、從 GPT4 到 GitHub copilot 的過程,微調在其中扮演了重要角色。什麼是微調(fine-tuning)?微調能解決什麼問題?什麼是 LoRA?如何進行微調?

本文將解答以上問題,並通過代碼實例展示如何使用 LoRA 進行微調。微調的技術門檻並不高,如果微調的模型規模不大 10B 及 10B 以下所需硬件成本也不高(10B 模型並不是玩具,不少生產中會使用 10B 的模型),即使非專業算法同學,也可動手嘗試微調自己的模型。

除了上面提到的 ChatGPT、GitHub copilot 產品,微調可以做的事情還非常多。如針對特定任務讓模型編排 API(論文:GPT4Tools: Teaching Large Language Model to Use Tools via Self-instruction)、模擬特定人的說話方式(character.ai 產品,目前估值 10 億美元)、讓模型支持特定語言,還有 B 站上各種 stable diffusion 煉丹教程,都用到了微調技術。

微調是利用已經訓練好的模型(通常是大型的預訓練模型)作爲起點,在新的數據集進一步訓練模型,從而使其更適合特定的應用場景。本文介紹 fine-tuning 的概念與過程,並對一個微調的過程代碼進行分析。

一、什麼是 fine-tuning

GPT-3 使用大量互聯網上的語料,訓練完成後,並不適合對話這個場景。如給到 GPT3 輸入 “中國的首都是哪裏?” GPT3 基於訓練後的模型的參數進行推理,結果可能是 “美國的首都是哪裏?”。

訓練數據中,這兩句話一起出現的概率非常高,在 GPT3 的訓練預料裏面可能也會出現多次。但這種輸出明顯不滿足 ChatGPT 的場景。還需要多階段的優化過程使 ChatGPT 更擅長處理對話,並且能夠更好地理解和迴應用戶的需求。

CPT3 模型的微調過程包括幾個關鍵步驟:

  1. 在大規模文本數據集上進行預訓練,形成基礎的語言能力(GPT3)。

  2. 通過監督微調,讓模型適應對話任務,使其生成的文本更符合人類對話習慣。

  3. 使用基於人類反饋的強化學習(使用用戶反饋數據,如贊踩、評分),進一步優化模型的輸出質量,使其在多輪對話中表現得更連貫和有效。

  4. 通過持續的微調和更新,適應新需求並確保輸出的安全性和倫理性。

後續會對上述步驟中的一些概念如監督微調、強化學習做介紹,在開始之前,先分析微調能起到什麼作用。

1.1. 爲什麼要 fine-tuning

1.1.1. 微調可以強化預訓練模型在特定任務上的能力

  1. 特定領域能力增強:微調把處理通用任務的能力,在特定領域上加強。比如情感分類任務,本質上預訓練模型是有此能力的,但可以通過微調方式對這一能力進行增強。

  2. 增加新的信息:通過微調可以讓預訓練模型學習到新的信息,比如常見的自我認知類的問題:“你是誰?”“你是誰創造的?”,這類問題可通過微調讓模型有預期內回答。

1.1.2. 微調可以提高模型性能

  1. 減少幻覺:通過微調,可以減少或消除模型生成虛假或不相關信息的情況。

  2. 提高一致性:模型的輸出一致性、穩定性更好。給模型一個適度的 temperature ,往往會得出質量高更有創造性的結果,但結果是每次輸出內容都不一樣。這裏的一致性和穩定性,是指雖每次生成內容不同,但質量維持在一個較高的水平,而不是一次很好,一次很差。

  3. 避免輸出不必要的信息:比如讓模型對宗教作出評價,模型可以委婉拒絕回覆此類問題。在一些安全測試、監管審查測試時,非常有用。

  4. 降低延遲:可通過優化和微調,使用較小參數的模型達到預期效果,減少模型響應的延遲時間。

1.1.3. 微調自有模型可避免數據泄漏

  1. 本地或虛擬私有云部署:可以選擇在本地服務器或虛擬私有云中運行模型,自主控制性強。

  2. 防止數據泄漏:這點對於一些公司來說非常重要,不少公司的核心競爭優勢是長年積累的領域數據。

  3. 安全風險自主可控:如果微調使用特別機密的數據,可自定義高級別的安全微調、運行環境。而不是把安全問題都委託給提供模型推理服務的公司。

1.1.4. 使用微調模型,可降低成本

  1. 從零創造大模型,成本高:對大部分公司而言,也很難負擔從零開始訓練一個大模型的成本。meta 最近開源的 llama3.1 405B 模型,24000 張 H100 集羣,訓練 54 天。但在開源模型之上進行微調,使用一些量化(減少精度)微調方式,可以大大降低門檻,還可以得到不錯的效果。

  2. 降低每次請求的成本:一般而言,相同的性能表現,使用微調的模型與通用模型比,模型的參數量會更少,成本也就更低。

  3. 更大的控制權:可以通過模型參數量、使用的資源,自主平衡模型性能、耗時、吞吐量等,爲成本優化提供了空間。

1.2. 一些相關概念區分

1.2.1. 基於人類反饋的強化學習(RLHF)與監督微調(SFT)

目前 OpenAI 的公開信息,ChatGPT 的主要改進是通過微調和 RLHF 來實現的。從 GPT3 到 ChatGPT,大概過程如下:預訓練 → 微調(SFT) → 強化學習(RLHF) → 模型修剪與優化。強化學習與微調有什麼區別?

簡單來說,開發 ChatGPT 過程中,微調使模型能夠生成更自然、更相關的對話,而強化學習強化學習幫助模型通過人類反饋來提升對話質量。

基於人類反饋的強化學習(Reinforcement Learning from Human Feedback, RLHF)是一種強化學習(Reinforcement Learning)的具體方式。

強化學習(Reinforcement Learning, RL)是一種機器學習方法,模型通過與環境的交互來學習決策策略。模型在每一步的選擇中會得到獎勵或懲罰,目標是最大化長期的累積獎勵。在自然語言處理(NLP)中,強化學習可以用於優化模型的輸出,使其更符合期望的目標。

SFT(Supervised Fine-Tuning,監督微調)是一種微調的類型。如果按照是否有監督,還有無監督微調(Unsupervised Fine-Tuning,在沒有明確標籤的情況下,對預訓練模型進行微調)、自監督微調(Self-Supervised Fine-Tuning,模型通過從輸入數據中生成僞標籤(如通過數據的部分遮掩、上下文預測等方式),然後利用這些僞標籤進行微調。)



圖片來自 OpenAI 論文:Training language models to follow instructions with human feedback

在 ChatGPT 的訓練中,OpenAI 使用了一種稱爲通過人類反饋的強化學習(Reinforcement Learning from Human Feedback, RLHF)的方法。RLHF 流程通常包括以下幾個步驟:

  1. 初始模型生成:使用監督學習訓練初始語言模型(Step1 的過程),它已經能夠生成合理的對話內容。

  2. 人類反饋:人類評審者與模型進行互動,對模型的回答進行評價,標註出哪些回答更好。Step2 中的 A labeler ranks the outputs 的過程爲標註員反饋的過程。

  3. 獎勵模型訓練:基於人類反饋的數據,訓練一個獎勵模型(Step2 中的 reward model),該模型能夠根據輸入的內容對模型輸出進行評分。

  4. 策略優化:使用強化學習技術,讓模型生成更高評分的輸出,Step3 的過程。

強化學習與微調相比,不論技術門檻、構造數據的成本、訓練成本、訓練時間、最終效果的不確定性,強化學習與微調都要高很多。強化學習需要使用大量人工標註的數據先訓練一個獎勵模型,然後需要通過大量嘗試與迭代在優化語言模型。

在生產實踐中,雖然強化學習也可提升具體任務表現,但對特定任務採用 SFT 的方式,往往能取得不錯的效果。而強化學習成本高,非常依賴標註的數據,相對於 SFT 使用不多。

1.2.2. 繼續預訓練與微調

ChatGPT 的定位是一個通用場景的對話產品,在具體行業或領域內,類似 ChatGPT 的產品定位會更加細分。比如經常聽到的醫療大模型、法律大模型、資金安全大模型。這種 “行業大模型” 不少是通過對基座繼續預訓練方式得到的。

繼續預訓練是在已經預訓練的模型基礎上,進一步在特定領域的數據上進行訓練,以提高模型對該領域的理解和適應能力。數據集通常是未標註的,並且規模較大。

微調一般的目的在於優化模型在特定任務上的表現。微調通常是在一個小規模的任務數據集上進行的,目的是讓模型在該特定任務上達到最佳表現。

兩者可以結合使用,比如在安全領域內,一個特定的任務如對欺詐手法打一些具體的標籤,模型使用的方式大概如下:

通用預訓練(例如在大規模互聯網數據上,公司級別進行訓練) → 繼續預訓練(在特定領域數據上,公司內不同的行業 / 部門) → 微調(基於特定任務數據,部門 / 行業負責具體業務的小組各自微調)。

1.3. 小結

通過微調可以提升模型在特定任務上的表現。相對於預訓練、強化學習,在生產過程中,使用到微調技術的場景更多,瞭解基本概念後,非技術人員也可進行微調,下一章節主要圍繞如何微調進行展開。

二、如何 Fine-tuning

2.1. 微調的基本原理

微調是基於一個已經訓練好的神經網絡模型,通過對其參數進行細微調整,使其更好地適應特定的任務或數據。通過在新的小規模數據集上繼續訓練模型的部分或全部層,模型能夠在保留原有知識的基礎上,針對新任務進行優化,從而提升在特定領域的表現。

根據微調的範圍,可以分爲全模型微調和部分微調。

全模型微調(Full Model Fine-Tuning)更新模型的所有參數,適用於目標任務與預訓練任務差異較大或需要最大化模型性能的場景。雖然這種方法能獲得最佳性能,但它需要大量計算資源和存儲空間,並且在數據較少的情況下容易導致過擬合。相比之下,部分微調(Partial Fine-Tuning)僅更新模型的部分參數,其他參數保持凍結。這種方法減少了計算和存儲成本,同時降低了過擬合的風險,適合數據較少的任務,但在任務複雜度較高時可能無法充分發揮模型的潛力。

生產中,使用較多的是部分微調的方式,由於大模型的參數量級較大,即使對於部分參數調整也需要非常多的計算資源,目前使用比較多的一種方式的是參數高效微調(Parameter-Efficient Fine-Tuning, PEFT), PEFT 通過引入額外的低秩矩陣(如 LoRA)或適配層(如 Adapters),減少計算資源的需求。

LoRA 是一種高效的微調技術,能顯著降低了微調的參數量和計算資源需求。它在保持模型原有能力的同時,實現了任務特定的高效適應,是一種特別適合大模型微調的技術。下一小節,注重介紹下 LoRA 這種微調方式。

2.2. 什麼是 LoRA

2.2.1. LoRA 基本概念



LoRA 原理(來源 LoRA 論文:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS)

LoRA(Low-Rank Adaptation)通過引入低秩矩陣來減少微調過程中需要更新的參數數量(矩陣 A 和矩陣 B),從而顯著降低計算資源需求(降低爲之前 1/3,論文中數據)。

LoRA 另外一個非常重要的特性是:可重用性。由於 LoRA 不改變原模型的參數,它在多任務或多場景的應用中具有很高的可重用性。不同任務的低秩矩陣可以分別存儲和加載,靈活應用於不同任務中。

比如在手機終端上,要跑應用的終端大模型。一個應用的模型會處理不同的任務,可以針對不同的任務,訓練不同的 LoRA 參數,運行時基於不同任務,使用相同的基座模型,動態加載需要的 LoRA 參數。相對於一個任務一個模型,可以大大降低存儲、運行需要的空間。

2.2.2. LoRA 原理分析

在機器學習中,通常會使用非常複雜的矩陣來讓模型處理數據。這些結構通常都很 “全能”,它們可以處理非常多種類的信息。但研究表明,讓模型去適應特定任務時,模型其實並不需要用到所有這些複雜的能力。相反,模型只需要利用其中一部分就能很好地完成任務。

打個比方,這就像你有一把瑞士軍刀,裏面有很多工具(像剪刀、螺絲刀等等),但是在解決特定任務時,通常只需要用到其中的幾個工具就可以完成大多數工作。在這個例子中,模型的矩陣就像瑞士軍刀,雖然它很複雜(全秩),但實際上你只需要用到一些簡單的工具(低秩)就足夠了。

也就是說微調的時候,只調整那些對特定任務有影響的參數就可以了。原始矩陣維度較高,假設爲 dk 維矩陣 W0,要想進行矩陣調整,並且保持矩陣的數據(爲了重用),最簡單方式是使用矩陣加法,增加一個 dk 維度的矩陣ΔW。但如果微調的數據,還是一個 d*k 維度的矩陣,參數量就很多。LoRA 通過將後者表示爲低秩分解,來減少參數的量級。

其中:





上圖是矩陣分解後的示意圖,可以直觀的從矩陣的面積感知參數的多少,W0 爲原始權重矩陣,如果需要進行全參數微調,W0 面積對應的參數都需要進行調整,而 LoRA 的方式只調整矩陣 B、和矩陣 A 對應的參數面積的矩陣,比 W0 要少很多。

舉例計算,d 爲 1000,k 爲 1000,本來需要計算ΔW 10001000 = 100w 個參數,但通過矩陣分解,如果 r = 4,那麼只需要計算 1000 * 4(矩陣 B) + 41000(矩陣 A) = 8000 個參數。

這裏的 r = 4 並不是爲了參數量級的減少而特意選的小的值,實際微調時很多情況使用的值就是 4,論文中中實驗數據表明,在調整 Transformer 中的權重矩陣時,在 r = 1 時對特定任務就有非常好的效果。



上面表格爲在 WikiSQL 和 MultiNLI 上使用不同秩 r 的 LoRA 驗證準確率。適配 Wq 和 Wv 時,只有 1 的秩就足夠了,而僅訓練 Wq 則需要更大的 r。Wq, Wk, Wv, Wo 爲 Transformer 架構中自注意力模塊中的權重矩陣。

2.3. 微調過程

微調基本過程,大概如下:

1. 準備數據: 收集與目標任務相關的標註數據,將數據分爲訓練集、驗證集,進行 Tokenization 處理。

2. 微調參數設: 配置 LoRA 參數、微調參數如學習率,確保模型收斂。

3. 微調模型: 在訓練集上訓練模型,並調整超參數以防止過擬合。

4. 評估模型: 在驗證集上評估模型性能。

其中需要特別注意的是微調過程中使用的數據,要求如下:

  1. 高質量:非常重要,再強調也不過分:Garbage in garbage out、Textbooks Are All You Need,都在強調數據質量重要性。

  2. 多樣性:就像寫代碼的測試用例一樣,儘量使用差異較大數據,能覆蓋的場景更多的數據。

  3. 儘量人工生成:語言模型生成的文本,有一種隱含的 “模式”。在看一些文字的時候,經常能識別出來 “這是語言模型生成的”。

  4. 數量不少太少:通過 LoRA 論文看,100 條開始有明顯的改善,1000 條左右,有不錯的效果。

關於微調的數據量,OpenAI 微調至少 10 就可以。一般經驗而言 50 到 100 條數據,有非常明顯的微調效果。建議是從 50 條開始,有明顯效果逐步增加數量。

Example count recommendations

To fine-tune a model, you are required to provide at least 10 examples. We typically see clear improvements from fine-tuning on 50 to 100 training examples with gpt-3.5-turbo but the right number varies greatly based on the exact use case.

We recommend starting with 50 well-crafted demonstrations and seeing if the model shows signs of improvement after fine-tuning. In some cases that may be sufficient, but even if the model is not yet production quality, clear improvements are a good sign that providing more data will continue to improve the model. No improvement suggests that you may need to rethink how to set up the task for the model or restructure the data before scaling beyond a limited example set.

2.4. 使用 LoRA 微調代碼分析

本節使用 LoRA 微調了一個 67M 的 Bert 的蒸餾模型,distilbert/distilbert-base-uncased,實現對電影的評論進行分類的功能,用於是正面還是負面的評論,微調使用的數據爲 stanfordnlp/imdb,相關資源地址:

初始模型:https://huggingface.co/distilbert/distilbert-base-uncased

微調數據:https://huggingface.co/datasets/stanfordnlp/imdb

完整代碼地址:https://github.com/wangzhenyagit/myColab/blob/main/fine-tuning.ipynb

使用的 colab 免費的 T4 GPU(跑代碼一定記得設置,CPU 慢不止 10 倍)進行微調的,1000 條微調數據,10 個 Epoch,大概 6 分鐘跑完,稍大參數量的模型,應該也可以免費微調。如果微調 10B 的模型,估計需要付費買些計算資源,充值 10 美元估計差不多。

最終實現效果,從微調前 50% 的正確率(基本瞎猜)微調後爲 87%。只微調 Wq 權重矩陣。

r = 4 數據如下:

r = 1 數據如下:

可見和 LoRA 論文中的結論差不多,在微調 Wq 的情況下 r = 1 就已經足夠了 (相差不到 0.01,可忽略)。

代碼分析如下。

2.4.1. 基本庫安裝與包引入

!pip install datasets
!pip install transformers
!pip install evaluate
!pip install torch
!pip install peft
from datasets import load_dataset, DatasetDict, Dataset
from transformers import (
    AutoTokenizer,
    AutoConfig, 
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer)
from peft import PeftModel, PeftConfig, get_peft_model, LoraConfig
import evaluate
import torch
import numpy as np

2.4.2. 微調數據構造

# # load imdb data
imdb_dataset = load_dataset("stanfordnlp/imdb")
# # define subsample size
N = 1000 
# # generate indexes for random subsample
rand_idx = np.random.randint(24999, size=N) 
# # extract train and test data
x_train = imdb_dataset['train'][rand_idx]['text']
y_train = imdb_dataset['train'][rand_idx]['label']
x_test = imdb_dataset['test'][rand_idx]['text']
y_test = imdb_dataset['test'][rand_idx]['label']
# # create new dataset
dataset = DatasetDict({'train':Dataset.from_dict({'label':y_train,'text':x_train}),
                       'validation':Dataset.from_dict({'label':y_test,'text':x_test})})
import numpy as np  # Import the NumPy library
np.array(dataset['train']['label']).sum()/len(dataset['train']['label']) # 0.508

imdb 中數據格式例子如下:

{
  "label": 0,
  "text": "Not a fan, don't recommed."
}

分別使用 1000 條數據作爲微調數據與驗證數據。訓練數據中,正向與負向的評價各自 50%。

2.4.3. 加載初始模型

from transformers import AutoModelForSequenceClassification
model_checkpoint = 'distilbert-base-uncased'
# model_checkpoint = 'roberta-base' # you can alternatively use roberta-base but this model is bigger thus training will take longer
# define label maps
id2label = {0: "Negative", 1: "Positive"}
label2id = {"Negative":0, "Positive":1}
# generate classification model from model_checkpoint
model = AutoModelForSequenceClassification.from_pretrained(
    model_checkpoint, num_labels=2, id2label=id2label, label2id=label2id)
# display architecture
model

模型架構:

DistilBertForSequenceClassification(
  (distilbert): DistilBertModel(
    (embeddings): Embeddings(
      (word_embeddings): Embedding(30522, 768, padding_idx=0)
      (position_embeddings): Embedding(512, 768)
      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (transformer): Transformer(
      (layer): ModuleList(
        (0-5): 6 x TransformerBlock(
          (attention): MultiHeadSelfAttention(
            (dropout): Dropout(p=0.1, inplace=False)
            (q_lin): Linear(in_features=768, out_features=768, bias=True)
            (k_lin): Linear(in_features=768, out_features=768, bias=True)
            (v_lin): Linear(in_features=768, out_features=768, bias=True)
            (out_lin): Linear(in_features=768, out_features=768, bias=True)
          )
          (sa_layer_norm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
          (ffn): FFN(
            (dropout): Dropout(p=0.1, inplace=False)
            (lin1): Linear(in_features=768, out_features=3072, bias=True)
            (lin2): Linear(in_features=3072, out_features=768, bias=True)
            (activation): GELUActivation()
          )
          (output_layer_norm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
        )
      )
    )
  )
  (pre_classifier): Linear(in_features=768, out_features=768, bias=True)
  (classifier): Linear(in_features=768, out_features=2, bias=True)
  (dropout): Dropout(p=0.2, inplace=False)
)

一個 6 層的 Transformer 模型,LoRA 影響的是:

(q_lin):Linear(in_features=768, out_features=768, bias=True) 

這一層的權重,是個 768*768 矩陣的權重向量。

2.4.4. tokenize 與 pad 預處理

# create tokenizer
from transformers import AutoTokenizer # Import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, add_prefix_space=True)
# add pad token if none exists
if tokenizer.pad_token is None:
    tokenizer.add_special_tokens({'pad_token': '[PAD]'})
    model.resize_token_embeddings(len(tokenizer))
# create tokenize function
def tokenize_function(examples):
    # extract text
    text = examples["text"]
    #tokenize and truncate text
    tokenizer.truncation_side = "left"
    tokenized_inputs = tokenizer(
        text,
        return_tensors="np",
        truncation=True,
        max_length=512,  # Change max_length to 512 to match model's expected input length
        padding='max_length' # Pad shorter sequences to the maximum length
    )
    return tokenized_inputs
# tokenize training and validation datasets
tokenized_dataset = dataset.map(tokenize_function, batched=True)
from transformers import DataCollatorWithPadding # Import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
tokenized_dataset

對於 Tokenize 和 pad,幾點說明:

  1. 數字化表示與模型對齊: 語言模型無法直接理解原始的文本數據。這些模型處理的對象是數字化的表示形式,Tokenize 的過程將文本轉化爲模型可以處理的整數序列,這些整數對應於詞彙表中的特定單詞或子詞。不同模型使用不同的 Tokenize 方式,這也要求微調的時候,需要與模型中的一致。

  2. 減少詞彙量:Tokenize 過程根據詞彙表將文本切分爲模型可識別的最小單位(如單詞、子詞、字符)。這不僅減少了詞彙量,降低了模型的複雜性,還提高了模型處理罕見詞或新詞的能力。

  3. 並行計算需要: 通過 tokenization,可以將輸入文本統一爲模型預期的固定長度。對於較長的文本,Tokenize 過程可以將其截斷;對於較短的文本,可以通過填充(padding)來補足長度。這樣模型輸入具有一致性,便於並行計算。



上面是文本進行 Tokenize 的過程,其中 Tuning 被拆成了兩個小的 token,這樣就可以用有限的 token 來表示所有的單詞。這也是有些時候大語言模型會 “造詞” 的原因,錯誤的生成,Decode 後的詞可能是沒有的單詞。

2.4.5. 微調配置

微調前數據:

import torch  # Import PyTorch
model_untrained = AutoModelForSequenceClassification.from_pretrained(
    model_checkpoint, num_labels=2, id2label=id2label, label2id=label2id)
# define list of examples
text_list = ["It was good.", "Not a fan, don't recommed.", "Better than the first one.", "This is not worth watching even once.", "This one is a pass."]
print("Untrained model predictions:")
print("----------------------------")
for text in text_list:
    # tokenize text
    inputs = tokenizer.encode(text, return_tensors="pt")
    # compute logits
    logits = model_untrained(inputs).logits
    # convert logits to label
    predictions = torch.argmax(logits)
    print(text + " - " + id2label[predictions.tolist()])

輸出基本是隨機輸出。

Untrained model predictions:


It was good. - Positive

Not a fan, don't recommed. - Positive

Better than the first one. - Positive

This is not worth watching even once. - Positive

This one is a pass. - Positive

import evaluate  # Import the evaluate module
# import accuracy evaluation metric
accuracy = evaluate.load("accuracy")
# define an evaluation function to pass into trainer later
def compute_metrics(p):
    predictions, labels = p
    predictions = np.argmax(predictions, axis=1)
    return {"accuracy": accuracy.compute(predictions=predictions, references=labels)}
from peft import LoraConfig, get_peft_model # Import the missing function
peft_config = LoraConfig(task_type="SEQ_CLS",
                        r=1,
                        lora_alpha=32,
                        lora_dropout=0.01,
                        target_modules = ['q_lin'])

peft_config:

LoraConfig(peft_type=<PeftType.LORA: 'LORA'>, auto_mapping=None, base_model_name_or_path=None, revision=None, task_type='SEQ_CLS', inference_mode=False, r=1, target_modules={'q_lin'}, lora_alpha=32, lora_dropout=0.01, fan_in_fan_out=False, bias='none', use_rslora=False, modules_to_save=None, init_lora_weights=True, layers_to_transform=None, layers_pattern=None, rank_pattern={}, alpha_pattern={}, megatron_config=None, megatron_core='megatron.core', loftq_config={}, use_dora=False, layer_replication=None, runtime_config=LoraRuntimeConfig(ephemeral_gpu_offload=False))

幾點說明:

1.task_type="SEQ_CLS",說明任務的類型爲分類任務。

2.r=1,LoRA 的 rank,在論文 7.2 WHAT IS THE OPTIMAL RANK r FOR LORA? 有分析此參數影響,一般爲 1~8,通常可設置爲 4。

3.lora_alpha=32, lora_alpha 參數是一個縮放因子,BA 的權重係數,h = W0 + lora_alphaB*A。用於控制 LoRA 適應矩陣對原始權重的影響程度。經驗法則是在開始時嘗試一個較大的值(如 32)。

4.lora_dropout=0.01,防止模型過擬合的配置,訓練過程中隨機 “丟棄” 一部分神經元來防止模型過擬合,通常從一個較小的值開始。

5.target_modules = ['q_lin'],前面提到的,影響的權重矩陣,這裏隻影響 model 中 q_lin。

model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

trainable params: 601,346 || all params: 67,556,356 || trainable%: 0.8901

訓練的數量不到參數量的百分之一。參數量越大的模型,這個訓練的參數比越小。

# hyperparameters
lr = 1e-3
batch_size = 4
num_epochs = 10
from transformers import TrainingArguments # Import Trainer
# define training arguments
training_args = TrainingArguments(
    output_dir= model_checkpoint + "-lora-text-classification",
    learning_rate=lr,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    num_train_epochs=num_epochs,
    weight_decay=0.01,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
)
from transformers import Trainer
# creater trainer object
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["validation"],
    tokenizer=tokenizer,
    data_collator=data_collator, # this will dynamically pad examples in each batch to be equal length
    compute_metrics=compute_metrics,
)
# train model
trainer.train()

輸結果爲開頭表格數據。

再次運行開始的的測試數據,輸出:

Trained model predictions:


It was good. - Positive

Not a fan, don't recommed. - Negative

Better than the first one. - Positive

This is not worth watching even once. - Negative

This one is a pass. - Positive

微調後分類正確。

以上爲 LoRA 微調的代碼示例。篇幅原因一些參數沒有具體講解,推薦使用 Colab,目前 Google 也集成了免費的 Gemini,參數意思有些模糊可以直接提問,試用下來和 GPT-4o 效果差不多。而且有報錯一鍵點擊,Gemini 就能提供改進建議,基本一次就能解決問題。

三、結語

本文介紹了微調的基本概念,以及如何對語言模型進行微調。微調雖成本低於大模型的預訓練,但對於大量參數的模型微調成本仍非常之高。好在有摩爾定律,相信隨着算力增長,微調的成本門檻會越來越低,微調技術應用的場景也會越來越多。

“Textbooks Are All You Need” 這篇論文中強調了數據質量對預訓練的重要性,deep learning 的課程中,也強調了訓練數據的 Quality。想起 AngelList 創始人 Naval 的一句話,“Read the Best 100 Books Over and Over Again ” ,微調之於模型,類似於人去學習技能 / 特定領域知識。高質量的輸入非常重要,正確方式可能是:閱讀經典,反覆閱讀。

以上,感謝閱讀。

參考鏈接:

1、LoRA: Low-Rank Adaptation of Large Language Models- LoRA Paper:

https://arxiv.org/abs/2106.09685

2、Training language models to follow instructions with human feedback- OpenAI 指令微調論文:

https://arxiv.org/abs/2203.02155

3、https://ai.meta.com/blog/meta-llama-3-1/- Llama3.1 論文:

https://ai.meta.com/blog/meta-llama-3-1/

4、https://learn.deeplearning.ai/courses/finetuning

-large-language-models/lesson/1/introduction- deeplearn fine-tuning 視頻:

https://learn.deeplearning.ai/courses/finetuning-large-language-models/lesson/1/introduction

5、https://github.com/tatsu-lab/stanford_alpaca#fine-tuning- 斯坦福從 LLaMA 微調到 Alpaca 的過程:

https://github.com/tatsu-lab/stanford_alpaca#fine-tuning

6、https://arxiv.org/abs/2306.11644- Textbooks Are All You Need 論文:

https://arxiv.org/abs/2306.11644

7、https://www.youtube.com/watch?v=yTROqe8T_eA- How To Fine-Tune the Alpaca Model For Any Language:

https://www.youtube.com/watch?v=yTROqe8T_eA

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/gZNtruNT_wQbemMqO7Gncw