大模型微调:比RAG更专业的优化方案
要理解微调为什么重要,咱们得从ChatGPT的训练过程说起。
整个过程大体分三步。第一步,先在大规模的语料数据上做自监督训练,得到预训练模型。所谓的自监督,说白了就是把一句话后面的内容遮住,让模型来猜;如果是BERT那种,则是遮住中间某些词。第二步,此时的预训练模型已经具备了通用知识,也能用在某些具体领域,但为了让它更好地适应对话模式,还得用监督数据做指令微调。监督数据的格式比较简单,就是问题和答案用特殊分隔符拼在一起——比如输入"what is AI?",模型根据这个续写,在损失函数和优化器的作用下不断调整参数,直到输出尽量和标准答案一致。第三步,光会聊天还不够,还得和人类在同一个频道上。为了进一步对齐,ChatGPT用了一种叫RLHF的方法,也就是基于人类反馈的强化学习,再微调一轮。
这样下来,一个会聊天的ChatGPT才算真正成型。
第二步和第三步确实挺折腾的。但如果你直接把预训练模型扔到一个垂直领域——比如金融、农业、医疗,或者公司内部产品里——效果往往不太好。原因很简单:训练数据里根本没有你们公司的内部资料。
所以,要想效果好,一般都需要对预训练模型做微调。
微调可以从两个方向入手:模型 和 Prompt。
模型
从模型入手,自然是要更新它的参数。不过,更新多少参数、怎么更新,里面的门道不少。
Full fine-tuning,就是让模型的所有参数都参与更新。比如拿着医疗数据在预训练模型上继续训练,让它更熟悉医疗知识。这其实就是迁移学习——好比图像领域先在ImageNet上预训练,学完图像的基本特征,再用医疗数据微调,整个过程全部参数都会更新。至于微调需要多少数据,规律是:和预训练数据越相似,需要的微调数据就越少。
Parameter-efficient fine-tuning,只更新部分参数,其余的冻结;或者加一个Adaptor,预训练模型的参数不动,只更新Adaptor。典型的例子是LORA。这种方式的效率很高,因为只需要一份预训练模型,针对不同任务训练不同的Adaptor就行。
如上图所示,预训练模型的参数矩阵W要比A和B大得多。LORA训练过程只更新A和B,A和B可以看作是W的低秩分解。
Instruction-tuning,就是前面ChatGPT训练过程中说的那一步:预训练完成后,用有监督的方式继续训练。
Prompt
从Prompt入手,就不改模型参数了。
Prompt engineering,什么都不用改,不加任何Adaptor,纯粹靠人的本事把Prompt写好。
Prefix tuning(前缀调整),核心思路是在模型输入前加一个连续的、任务特定的向量序列,这个序列叫Virtual Token,是可以学习的。通过优化这个Virtual Token,而不动预训练语言模型的参数,Prefix Tuning就能适应新任务、提升性能。
P-Tuning,和Prefix Tuning类似,只不过Virtual Tokens是通过一个额外的Prompt Encoder(比如LSTM)生成的。
LORA实现
接下来聊一个NLP和大模型领域非常知名的包:HuggingFace的Transformers,并基于它用LORA方法对预训练模型做微调。
- 安装必要的库
确认已经装好Transformers库和PEFT库。如果没有,用下面的命令安装:
pip install transformers peft
- 选择预训练模型
选一个预训练模型,比如T5、BERT或者GPT-2。去Hugging Face模型库找合适的就行。
- 准备数据集
准备好训练集和测试集,用对应的Tokenizer对数据做分词处理。
- 定义LORA配置
创建LORA配置对象,指定要适配的模块和训练设置。
from peft import LoraConfig
lora_config = LoraConfig(
r=16, # 秩的大小
lora_alpha=32, # 每个自注意力头的参数数量
target_modules=["q", "v"], # 需要适配的模块
lora_dropout=0.05, # dropout率
bias="none", # 是否包含偏置项
task_type=TaskType.SEQ_2_SEQ_LM # 任务类型
)- 加载模型和Tokenizer
用Transformers库加载预训练模型和对应的Tokenizer。
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
model_name = "google/flan-t5-xxl" # 例如使用Flan-T5 XXL模型
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)- 准备模型进行LORA训练
用PEFT库准备模型。
from peft import get_peft_model, prepare_model_for_int8_training
model = prepare_model_for_int8_training(model) # 准备模型进行int-8训练
model = get_peft_model(model, lora_config) # 添加LORA适配器- 定义训练参数和数据收集器
设置训练参数,定义数据收集方式。
from transformers import Seq2SeqTrainingArguments, DataCollatorForSeq2Seq
training_args = Seq2SeqTrainingArguments(
output_dir="lora-flan-t5-xxl",
learning_rate=1e-3,
num_train_epochs=5,
# 其他训练参数...
)
data_collator = DataCollatorForSeq2Seq(
tokenizer=tokenizer,
model=model,
# 其他数据收集参数...
)- 创建Trainer并开始训练
用Trainer类启动训练。
from transformers import Seq2SeqTrainer
trainer = Seq2SeqTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=your_train_dataset, # 你的训练数据集
eval_dataset=your_eval_dataset, # 你的评估数据集
training_args=training_args,
data_collator=data_collator,
)
trainer.train()- 评估和推理
训练完成后,用训练好的模型做评估和推理。
总结
总的来说,除了RAG,微调是让大模型在特定领域变得更专业的另一种关键方法。这篇文章梳理了从模型层面和Prompt层面的不同微调方式,并用HuggingFace的Transformers配合LORA做了一次完整的实现。