參數高效微調-Prefix Tuning、Adapter Tuning、LoRA 原創
回顧一下三種參數高效微調方法-Prefix Tuning、Adapter Tuning、LoRA
Prefix Tuning
Prefix Tuning
在prefix-tuning之前的工作主要是人工設計離散的template或者自動化搜索離散template,問題在于最終的性能對人工設計的template的特別敏感:加一個詞或者少一個詞,或者變動位置,都會造成很大的變化,所以這種離散化的token的搜索出來的結果可能并不是最優的。Prefix Tuning方法使用連續的virtual token embedding來代替離散的token,且與Full-finetuning更新所有參數的方式不同。簡而言之就是Prefix Tuning在原始文本進行詞嵌入之后,在前面拼接上一個前綴矩陣,或者將前綴矩陣拼在模型每一層的輸入前。
Prefix Tuning的兩種示例
Prefix Tuning相關設置:
- 前綴初始化時,[前綴長度, 嵌入維度],其中嵌入維度與模型詞嵌入的維度相同。前綴長度可以根據任務需求進行調整。
- 更長的前綴意味著更多的可微調參數,效果也變好,不過長度還是有閾值限制的(table-to-text是10,summarization是200)
(上):針對表格描述(Table-to-text)、文章總結(Summarization)、翻譯(Translation)三種任務,Fine-Tuning需微調三個LM,且需保存每個特定任務的LM參數,臃腫和低效;(下):然而,Prefix Tuning要清爽得多,針對三類任務,只需訓練三個Prefix生成器,原LM參數可直接復用。
推理階段,只需要將任務相關的輸入序列與訓練好的前綴嵌入進行拼接,然后輸入到模型中即可得到預測結果。
代碼過程,下面這個類旨在將輸入的前綴有效地編碼為適合后續處理的向量形式。
參考:https://github.com/THUDM/P-tuning-v2/blob/main/model/prefix_encoder.py
import torch
class PrefixEncoder(torch.nn.Module):
r'''
The torch.nn model to encode the prefix
Input shape: (batch-size, prefix-length)
Output shape: (batch-size, prefix-length, 2*layers*hidden)
'''
def __init__(self, config):
super().__init__()
self.prefix_projection = config.prefix_projection
if self.prefix_projection:
# Use a two-layer MLP to encode the prefix
self.embedding = torch.nn.Embedding(config.pre_seq_len, config.hidden_size)
self.trans = torch.nn.Sequential(
torch.nn.Linear(config.hidden_size, config.prefix_hidden_size),
torch.nn.Tanh(),
torch.nn.Linear(config.prefix_hidden_size, config.num_hidden_layers * 2 * config.hidden_size)
)
else:
self.embedding = torch.nn.Embedding(config.pre_seq_len, config.num_hidden_layers * 2 * config.hidden_size)
def forward(self, prefix: torch.Tensor):
if self.prefix_projection:
prefix_tokens = self.embedding(prefix)
past_key_values = self.trans(prefix_tokens)
else:
past_key_values = self.embedding(prefix)
return past_key_values
Adapter Tuning
通過引入少量可訓練參數(適配器模塊)來進行特定任務的優化。適配器模塊是一組輕量級的參數,被添加到模型的中間層,以保護原有預訓練模型的參數。這種方法的目標是在不改變整體模型結構的情況下,通過調整適配器模塊的參數來適應新任務。
Adapter Tuning針對Transformer的添加方式。左:針對每個Transformer層,Adapter參數在兩個殘差前插入。在Tuning中,圖中的綠色模塊是可訓練的,其他模塊的參數固定。
Adapter Tuning的核心思想是在預訓練模型的中間層中插入小的可訓練層或“適配器”。這些適配器通常包括一些全連接層、非線性激活函數等,它們被設計用來捕獲特定任務的知識,而不需要對整個預訓練模型進行大規模的微調。
下面舉個例子看下Adapter Tuning過程:
Adapters還可以和HuggingFace的Transformer包無縫整合,可以直接加載HuggingFace上的模型進行Adapter微調。
以文本分類為例,BERT預訓練模型加載:
from transformers import AutoTokenizer, AutoConfig
from adapters import AutoAdapterModel
model_path = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_path)
config = AutoConfig.from_pretrained(model_path, num_labels=3)
model = AutoAdapterModel.from_pretrained(model_path, cnotallow=config)
然后為預訓練模型設置適配器。這里需要注意,在Adapters包里,本節所介紹的適配器結構被稱為瓶頸適配器(Bottleneck adapters)(如上圖1),使用BnConfig類來配置。這里需要為適配器取一個名字,之后可以通過這個名字來激活或者禁用這個適配器。
from adapters import BnConfig
adapter_name = "trouble_shooting"
# 添加一個新的adapter,類型為Bn adapter,即bottleneck adapter
config = BnConfig(mh_adapter=True, output_adapter=True, reduction_factor=16, non_linearity="relu")
model.add_adapter(adapter_name, cnotallow=config)
# 添加一個分類頭
model.add_classification_head(adapter_name,num_labels=3, activation_functinotallow="relu")
# 激活這個adapter
model.train_adapter(adapter_name)
主要參數:
- mh_adapter:設置是否要在多頭注意力模塊之后添加適配器。
- output_adapter:設置是否要在Transformer模塊的輸出層添加適配器。
- reduction_factor:模型參數量與需調整的適配器參數量的比值。
- non_linearity:設置非線性部分使用的激活函數。
trainer訓練模型:
from transformers import TrainingArguments
from adapters import AdapterTrainer
training_args = TrainingArguments(
num_train_epochs=5,
per_device_train_batch_size = 16,
logging_steps=2,
save_steps = 10,
gradient_accumulation_steps = 4,
output_dir="bert-adapter",
)
trainer = AdapterTrainer (
model=model, tokenizer=tokenizer
args=training_args, train_dataset=train_dataset,
optimizers=(optimizer, None)
)
trainer.train() # 開始訓練
trainer.save_model() # 保存訓練好的模型
LoRA
矩陣的秩(Rank):衡量了矩陣中行或列向量的線性無關性。
低秩:秩遠小于矩陣的行數或列數。
LoRA(Low-Rank Adaptation)假設模型在任務適配過程中權重的改變量可以是低秩的。 LoRA通過在預訓練模型中引入一個額外的線性層(由低秩矩陣A和B組成),并使用特定任務的訓練數據來微調這個線性層,從而實現對模型的高效微調。
假設預訓練參數為,那么全量微調時的更新量自然也 是一個矩陣,LoRA將更新量約束為低秩矩陣來降低訓練時的參數量,即設,其中以及,用新的替換模型原參數,并固定不變,只訓練,如下圖所示:
為了使得LoRA的初始狀態跟預訓練模型一致,通常會將之一全零初始化,這樣可以得到,那么初始的就是。但這并不是必須的,如果都是非全零初始化,那么我們只需要將設置為
也就是說將固定不變的權重從換為,同樣可以滿足初始等于這一條件。
影響LoRA微調的相關參數如下:
- 秩(Rank)
參數:lora_rank
描述:秩是LoRA中最重要的參數之一,它決定了低秩矩陣的維度。秩的大小直接影響模型的性能和訓練時間。
常用值:對于小型數據集或簡單任務,秩可以設置為1或2;對于更復雜的任務,秩可能需要設置為4、8或更高。 - 縮放系數(Alpha)
參數:lora_alpha
描述:縮放系數用于在訓練開始時對低秩矩陣的更新進行縮放,以確保訓練過程的穩定性。
常用值:縮放系數的具體值取決于秩的大小和任務的復雜度。 - Dropout系數
參數:lora_dropout
描述:Dropout是一種正則化技術,用于防止模型過擬合。在LoRA Fine-tuning中,Dropout系數決定了在訓練過程中隨機丟棄低秩矩陣中元素的概率。
常用值:Dropout系數的常用值范圍在0到1之間,具體值取決于模型的復雜度和數據的規模。 - 學習率
參數:learning_rate
描述:學習率決定了模型在訓練過程中權重更新的步長。適當的學習率可以幫助模型在訓練過程中更快地收斂到最優解。
常用值:學習率的具體值取決于多個因素,包括模型的復雜度、數據的規模以及訓練過程中的其他超參數設置。
LoRA微調如今是高效微調LLM的重要手段,PEFT庫也集成了相關方法: PEFT庫:https://github.com/huggingface/peft
參考文獻
- Prefix-Tuning: Optimizing Continuous Prompts for Generation
- Parameter-Efficient Transfer Learning for NLP
- LoRA: Low-Rank Adaption of Large Language Models
本文轉載自公眾號大模型自然語言處理 作者:余俊暉
