Hugging Face Transformers + PEFT 的 LoRA 微调
让大模型微调变得轻量灵活,LoRA 让每个人都能拥有自己的 AI。
Hugging Face 的 Transformers 库结合 PEFT(Parameter-Efficient Fine-Tuning,参数高效微调)提供了便捷的接口来对大语言模型进行参数高效微调(PEFT)。其中,LoRA(Low-Rank Adaptation,低秩适配)是一种流行的 PEFT 方法,通过在预训练模型的部分权重上添加低秩矩阵实现微调,而非调整原模型的大量参数。这样可以大幅减少需要训练的参数量,从而降低显存和算力需求,使得在消费级硬件上微调大模型成为可能。
背景原理与核心概念
在特定业务场景下,企业往往希望微调(Fine-tuning)大模型以注入自有数据或偏好。LoRA(Low-Rank Adaptation)是一种流行的参数高效微调方法,它通过为预训练模型的权重添加低秩矩阵实现微调,而非调整原模型的大量参数。
具体来说,LoRA 会在模型的某些权重矩阵上引入两个小型可训练矩阵(通常秩 r 很小,如 4 或 8),只训练这部分新增参数,而冻结原模型的权重不变。经过训练后,在推理阶段这些低秩“适配器”的权重将与原权重相加,产生一个定制化的模型输出。
LoRA 的核心优势是大幅减少需要训练的参数量。例如,对一个 12 亿参数的模型应用 LoRA 微调,实际训练的参数可能不到原来的 0.2%。Hugging Face 的 PEFT 库提供的统计显示:在一个 12.3 亿参数的 mt0 模型上,LoRA(r=8)仅引入大约 236 万可训练参数,占比 0.19%。
如此小的训练开销意味着:
- 资源需求低:微调时显著降低了显存和算力占用,使得在消费级硬件乃至 CPU 上微调小模型成为可能。
- 训练速度快:需要更新的参数少,单步更新计算开销小,一定数据量下收敛更快。
- 多任务适配:可以为同一个基础模型训练多个不同任务的 LoRA 适配器,在推理时按需加载对应适配器实现定制化。这样一来,一个基座模型 + 多套 LoRA 权重即可服务于多种场景,内存开销远低于为每个任务保存一个完整模型。
需要强调,LoRA 在推理时对速度几乎无影响——因为只是增加了对少量权重的线性组合计算。同时它不改变模型结构,所以不会影响上下文长度等模型原有特性。
安装与环境准备
在本节中,我们将介绍如何准备 Hugging Face Transformers 与 PEFT 库的环境,并说明硬件要求。
请确保已安装以下包:
pip install transformers peft datasets
accelerate 配合多线程加速 CPU 运算。本示例选择 Meta 的 OPT-125M 作为基底模型(约 1.2 亿参数),体量小便于 CPU 微调。此外还需要准备一小份示例数据(例如对话或问答数据)。这里为了演示,我们构造一个极简的合成数据集。
微调与运行步骤
下面详细介绍 LoRA 微调的完整流程,包括模型加载、配置、数据准备、训练与推理。
首先,加载预训练模型和分词器:
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "facebook/opt-125m"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
由于 OPT 是 Decoder 模型,我们使用 AutoModelForCausalLM。加载后 model 默认在 CPU。
接下来,准备 LoRA 配置并应用 PEFT:
from peft import LoraConfig, get_peft_model, TaskType
peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # 因为是自回归语言模型
inference_mode=False, # 训练模式
r=8, lora_alpha=16, lora_dropout=0.05,
target_modules=["q_proj", "v_proj"] # 指定只在注意力层的投影矩阵应用 LoRA
)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
以上配置创建了一个 LoRA 设置:秩 r=8,LoRA 内部尺度 alpha=16,dropout 5%。我们限定了在 OPT 结构中相应的 Q,K 投影层添加 LoRA 参数。get_peft_model 会返回一个 PEFT 封装的模型,其中原始模型参数被冻结,新加的 LoRA 参数将参与训练。调用 print_trainable_parameters() 可以看到当前可训练参数占比,例如输出类似:“trainable params: 104,000 || all params: 125,M || trainable%: 0.08%”。
然后,准备训练数据。这里我们构造一个简单的训练样本列表:
train_data = [
{"prompt": "Q: 你好,你是谁?\nA:", "response": " 我是一个 AI 语言模型。"},
{"prompt": "Q: 今天天气怎么样?\nA:", "response": " 很抱歉,我无法获取实时天气信息。"}
]
# 转换为 Dataset
from datasets import Dataset
dataset = Dataset.from_list(train_data)
每条数据包含一个 prompt 和期望的 response。实际应用中应替换为真实任务数据。
接下来,定义训练流程,使用 Transformers 自带的 Trainer 进行微调:
from transformers import Trainer, TrainingArguments
# 定义生成训练样本的函数(将 prompt 和 response 拼接)
def preprocess(example):
text = example["prompt"] + example["response"]
tokens = tokenizer(text, truncation=True, padding="max_length", max_length=128)
# 构造 labels,只计算答案部分的 loss
user_len = len(tokenizer(example["prompt"])["input_ids"])
tokens["labels"] = [-100]*user_len + tokens["input_ids"][user_len:]
return tokens
train_dataset = dataset.map(preprocess, remove_columns=["prompt", "response"])
training_args = TrainingArguments(
output_dir="lora-opt125m",
per_device_train_batch_size=1,
num_train_epochs=5,
learning_rate=1e-4,
logging_steps=1,
save_steps=50,
save_total_limit=1
)
trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset)
trainer.train()
model.save_pretrained("lora-opt125m") # 保存 LoRA 微调后的模型适配器
上述步骤中,我们对每条输入做了预处理,将用户问和回答拼接,并设置了 labels 使得模型仅在回答部分计算 loss(防止模型改变提示部分)。我们使用小批量和较低学习率训练若干轮。由于示例数据极简且训练轮次少,几秒即可完成(在 CPU 上可能需要几分钟)。保存后的模型权重文件夹 lora-opt125m/ 中主要包含 LoRA 的适配器权重,而非整个模型。
最后,加载和推理。微调完成后,我们可以在 Mac 上加载基础模型+LoRA 适配器进行推理:
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_name)
model = PeftModel.from_pretrained(base_model, "lora-opt125m")
model.eval() # 切换到推理模式
prompt = "Q: 你好,你是谁?\nA:"
input_ids = tokenizer(prompt, return_tensors="pt").input_ids
output_ids = model.generate(input_ids, max_new_tokens=50, do_sample=False)
answer = tokenizer.decode(output_ids[0][input_ids.size(1):], skip_special_tokens=True)
print(answer)
这里,我们用基础模型加载 LoRA 权重(PeftModel.from_pretrained 会自动将 LoRA 权重合并到模型中仅在推理时生效)。然后对示例问题生成回答。理想情况下,应输出类似训练中给定的回答:“我是一个 AI 语言模型。”。您也可以尝试其他提示,模型会在微调知识的影响下产出新的结果。
推理方式与参数量对比
通过上述流程,我们完成了 LoRA 微调示例。在这个例子里,LoRA 的参数量和推理方式有如下特点。
- 参数量对比:OPT-125M 模型有约 1.25 亿参数,而我们 LoRA 引入的参数大约只有十万级别,占比不到 0.1%。实际打印结果也验证了这一点。在更大型的模型上这种差异更为明显:LoRA 秩固定时,模型越大,训练参数占比越小。例如对 13B 模型用 LoRA,训练参数可能仅为原模型的万分之几。这体现了 LoRA 作为参数高效微调(PEFT)方法的强大之处。
- 推理方式:LoRA 微调后的模型推理时,需要将 LoRA 适配器与基础模型合并。上面的
PeftModel.from_pretrained实际上在内部将 LoRA 权重加到了 base 模型对应层上(仅在 forward 时暂时叠加,并不改动原权重)。也可以选择直接将 LoRA 权重合并到模型得到一个新的模型(这在 PEFT 库中也有支持),但通常保留分离可以灵活启停不同任务的 LoRA 模块。推理调用与原模型相同,例如使用model.generate()或pipeline等皆可。由于 LoRA 不改变模型结构和大部分权重,推理速度与原模型几乎无差别。
完整示例
为了方便复现,以下是完整的 LoRA 微调示例代码,您可以保存为 lora_finetune_opt125m.py 并运行:
| |
下面对 lora_finetune_opt125m.py 程序的主要步骤进行简要说明:
- 导入依赖库:加载 Hugging Face Transformers、PEFT 和 Datasets 库,为模型微调和数据处理做准备。
- 加载预训练模型和分词器:使用
AutoModelForCausalLM和AutoTokenizer加载 OPT-125M 基础模型及其分词器。 - 配置 LoRA 并应用 PEFT:通过
LoraConfig设置 LoRA 参数(如秩 r、alpha、dropout),指定目标模块,并用get_peft_model封装模型,使 LoRA 参数可训练,原模型参数冻结。 - 构造训练数据集:创建包含 prompt 和 response 的训练样本,并用 Datasets 库转换为标准数据集格式。
- 预处理数据:定义
preprocess函数,将 prompt 和 response 拼接,设置 labels 只在回答部分计算 loss,保证模型学习目标明确。 - 设置训练参数并微调:通过
TrainingArguments配置训练参数(如 batch size、epoch、学习率等),使用Trainer进行模型微调,并保存 LoRA 适配器权重。 - 加载微调后的模型进行推理:用
PeftModel.from_pretrained加载基础模型和 LoRA 适配器,输入 prompt 进行文本生成,输出微调后的模型回答。
通过上述流程,你可以在 Hugging Face 生态下高效完成小模型的 LoRA 微调和推理,适用于个性化定制和低资源场景。
要点总结
- LoRA 是什么:一种参数高效微调技术,通过增加小规模的低秩矩阵来调整模型,对原模型权重几乎零改动。直观理解:只学习“权重的差分”,并用极低的秩近似,达到几乎同等效果的同时,大幅减少训练开销。
- 参数占比与资源优势:LoRA 通常只需训练不到 1% 的参数甚至更少。这意味着微调大型模型不再需要数亿的可训练参数和对应显存。较小的显存/内存即可运行微调,例如 7B 模型用 LoRA 可在单张消费级 GPU 甚至 CPU 上以较短时间收敛。这降低了定制大模型的门槛。
- 效果与全微调对比:研究和实践表明,只要 LoRA 超参数选择得当(如秩、Alpha 等),LoRA 微调的效果能够接近全参数微调。同时,它保存下来的仅是适配器权重,小巧易部署;一个基础模型可以对应多个 LoRA 插件,根据不同任务动态加载,一模多用。
- 推理整合:LoRA 微调后的模型在推理时需要与原模型配合使用——这可以通过 PEFT 库的
PeftModel方便地实现。推理开销几乎不变。对于生产环境,这种方法允许我们部署一个基底模型,再根据用户需求选择加载特定 LoRA 权重,大大节省内存。很多开源大模型社区实践(如对 LLaMA 的各种微调版)都是用 LoRA 发布的,这种方式也使得分享和复现他人微调成果更加轻量(只需几个 MB 的适配器文件)。 - 应用场景:当客户有自己的数据(FAQ 语料、行业文档等)希望在大模型基础上微调,LoRA 是首选方案之一。它能以最小成本将预训练模型个性化,例如定制客服机器人语气、特定领域问答等。而 Hugging Face 的 Transformers 和 PEFT 库提供了完整的工具链支持,从训练到部署非常便利。这也体现出在阿里云上,借助 PAI 平台或 Notebook,我们可以很快地 fine-tune 一个开源大模型并应用于业务。
总结
本文详细介绍了如何利用 Hugging Face Transformers 和 PEFT 库,通过 LoRA 技术实现大语言模型的高效微调。我们从原理、环境准备、完整代码流程到推理与参数量对比,系统梳理了 LoRA 的优势和应用场景。LoRA 让大模型微调变得轻量、灵活且低门槛,为个性化 AI 应用和行业定制提供了强大工具。未来,随着 PEFT 技术的不断发展,更多企业和开发者将能以更低成本享受大模型带来的智能红利。