了解低秩适应的理论后,下一步是将其应用于实际模型。Hugging Face的PEFT(参数高效微调)库提供了一个高级API,简化了此过程,只需几行代码即可将LoRA适配器注入现有Transformer模型。该库处理修改模型架构和冻结相应权重的繁重工作,以便您专注于配置训练。PEFT库的工作流程使用PEFT的核心思路是:取一个预训练的基础模型,为您的LoRA适配器定义配置,然后将它们结合起来创建一个新的、可训练的模型。这个新模型保持原始权重冻结,只训练注入的小型适配器层。此过程包含三个主要步骤:加载基础模型: 从Hugging Face Hub加载一个标准预训练模型。定义LoRA配置: 创建一个LoraConfig对象,以指定适配器层的超参数,例如它们的秩(r)以及基础模型的哪些部分需要修改。创建PeftModel: 使用get_peft_model函数,根据您的配置将LoRA适配器封装到基础模型中。我们来逐一详细说明每个步骤。使用LoraConfig配置LoRA适配器LoraConfig类是您LoRA实现的核心控制部分。它允许您定义适配器层的所有必要参数。以下是您将使用的最主要参数:r:这个整数表示低秩更新矩阵($A$ 和 $B$)的秩。它直接控制可训练参数的数量。较小的r会导致更少的参数和更快的训练,但可能捕获较少的任务特定信息。较大的r会以更多参数为代价增加模型容量。r的常见取值范围是4到64。lora_alpha:这是LoRA激活的缩放因子,作用类似于适配器的学习率。LoRA更新按lora_alpha / r进行缩放,因此调整此值会影响适配器所做更改的幅度。常见做法是将lora_alpha设置为r值的两倍。target_modules:一个字符串列表,指定基础模型架构中哪些模块应用LoRA。对于Transformer模型,这通常是注意力机制中的线性层,例如query、key和value。例如,["q_proj", "v_proj"]。确定正确的模块名称需要检查基础模型的架构。lora_dropout:一个浮点值,表示应用于LoRA层的Dropout概率。这作为一种正则化技术,以防止适配器权重过拟合。task_type:指定您正在微调的任务类型。例如,对于文本生成模型,您可以将其设置为TaskType.CAUSAL_LM。这有助于PEFT库正确配置模型的正向传播。from peft import LoraConfig, TaskType # 因果语言模型的LoRA配置示例 lora_config = LoraConfig( r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"], lora_dropout=0.1, bias="none", task_type=TaskType.CAUSAL_LM )创建PeftModel一旦您加载了基础模型并定义了LoraConfig,您可以使用get_peft_model函数将它们结合起来。该函数将基础模型和配置作为输入,并返回一个PeftModel对象。返回的模型已准备好进行训练,所有基础模型权重都被冻结,只有新的LoRA适配器权重被标记为可训练。下图说明了此工作流程。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="rounded,filled", fontname="Arial", margin="0.2,0.1"]; edge [fontname="Arial"]; subgraph cluster_input { label="输入"; style=dashed; color="#adb5bd"; base_model [label="基础模型\n(例如:Llama 2 7B)\n(所有权重冻结)", fillcolor="#a5d8ff"]; lora_config [label="LoraConfig\n(r=16, alpha=32, ...)", fillcolor="#b2f2bb", shape=note]; } subgraph cluster_process { label="PEFT 库"; style=dashed; color="#adb5bd"; get_peft_model [label="get_peft_model()", shape=oval, style=filled, fillcolor="#ffd8a8"]; } subgraph cluster_output { label="输出"; style=dashed; color="#adb5bd"; peft_model [label="PeftModel", fillcolor="#d0bfff"]; subgraph cluster_peft { label="目标模块(例如:注意力层)"; style=filled; color="#f8f9fa"; frozen_w0 [label="冻结的 W₀", shape=box, style=filled, fillcolor="#dee2e6"]; subgraph cluster_lora { label="可训练的LoRA适配器"; style=filled; color="#eebefa"; node [style=filled]; lora_b [label="矩阵 B\n(d x r)", fillcolor="#fcc2d7"]; lora_a [label="矩阵 A\n(r x k)", fillcolor="#fcc2d7"]; } } } base_model -> get_peft_model; lora_config -> get_peft_model; get_peft_model -> peft_model [lhead=cluster_output]; peft_model -> frozen_w0 [style=invis]; frozen_w0 -> lora_b [style=invis]; lora_a -> lora_b [label=" ΔW = BA"]; }get_peft_model函数接收一个冻结的基础模型和一个LoraConfig来生成一个PeftModel,其中只有注入的小型LoRA适配器矩阵是可训练的。一个实际的代码示例我们将通过一个完整的示例来演示如何为LoRA微调设置模型。我们将使用meta-llama/Llama-2-7b-chat-hf模型作为基础,但同样的原理适用于Hugging Face Hub上的任何Transformer模型。首先,请确保您已安装必要的库:pip install torch transformers peft accelerate bitsandbytes现在,我们来编写准备模型的Python代码。import torch from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training # 1. 定义模型ID并加载分词器 model_id = "meta-llama/Llama-2-7b-chat-hf" tokenizer = AutoTokenizer.from_pretrained(model_id) # 2. 配置量化以4比特加载模型 # 这是一种节省内存的技术,我们将在QLoRA部分做进一步介绍 quantization_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16, ) # 3. 加载量化的基础模型 base_model = AutoModelForCausalLM.from_pretrained( model_id, quantization_config=quantization_config, device_map="auto" # 自动将层映射到可用硬件 ) # 4. 为k比特训练准备模型(可选但推荐) base_model = prepare_model_for_kbit_training(base_model) # 5. 定义LoRA配置 lora_config = LoraConfig( r=8, lora_alpha=16, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) # 6. 创建PeftModel peft_model = get_peft_model(base_model, lora_config) # 7. 打印可训练参数的百分比 def print_trainable_parameters(model): """ 打印模型中可训练参数的数量。 """ trainable_params = 0 all_param = 0 for _, param in model.named_parameters(): all_param += param.numel() if param.requires_grad: trainable_params += param.numel() print( f"trainable params: {trainable_params} || all params: {all_param} || " f"trainable%: {100 * trainable_params / all_param:.2f}" ) print_trainable_parameters(peft_model)运行此代码时,print_trainable_parameters的输出将体现PEFT的效能。对于一个70亿参数的模型,输出会类似这样:trainable params: 8,388,608 || all params: 3,508,801,536 || trainable%: 0.24这是LoRA的主要优势。我们已经成功地为微调准备了一个70亿参数的模型,而只需训练总参数的不到0.25%。优化器状态和梯度的内存需求大幅减少,使得在单个消费级GPU上运行训练过程成为可能。一旦您有了PeftModel,您就可以像进行完整微调一样,将其与标准的Hugging Face Trainer API一起使用。Trainer会自动处理只针对可训练LoRA参数的梯度更新。这小组适配器权重可以独立于大型基础模型进行保存和加载,从而方便管理同一基础模型的多个专门版本。