在 Hugging Face 的 PEFT(Parameter-Efficient Fine-Tuning)库中,VeraConfig
是用于配置 VeRA(Vector-based Random Matrix Adaptation)方法的类。VeRA 是一种高效的参数微调技术,通过使用共享的低秩矩阵对和少量可训练的缩放向量(scaling vectors)来减少微调大型语言模型(LLM)所需的参数量。相比于 LoRA,VeRA 显著减少了可训练参数,同时保持相似的性能,特别适合存储受限或多任务适配的场景。以下是对 VeraConfig
的详细讲解,包括其参数、用法、代码示例和注意事项。
1. VeraConfig 概述
VeRA 的核心思想是使用一组共享的低秩矩阵(A 和 B)作为所有层的适配器基底,并为每个层学习少量的缩放向量(scaling vectors)。这些共享矩阵是固定的(基于随机种子生成),不参与训练,从而大幅减少参数量。VeRA 在生成任务(如因果语言建模)中表现良好,尤其适用于需要高效存储或快速切换任务的场景。
VeRA 的优势:
- 极低参数量:仅训练缩放向量,参数量远少于 LoRA(通常只有 LoRA 的 10%-20%)。
- 存储效率:可选不保存共享矩阵(通过随机种子重现),进一步减少模型检查点大小。
- 性能:在 GLUE、E2E 基准测试和指令微调任务中与 LoRA 性能相当。
- 兼容性:与 Hugging Face 的 Transformers 模型无缝集成。
VeRA 的局限性:
- 依赖随机种子生成共享矩阵,跨设备或 PyTorch 版本的再现性可能受限。
- 初始化和训练复杂度略高于 LoRA,可能需要更多计算资源。
2. VeraConfig 的主要参数
以下是 VeraConfig
中常用的参数及其说明(基于 PEFT 文档和代码):
-
peft_type
(默认:"VERA"
)- 指定 PEFT 方法类型,固定为
"VERA"
。 - 通常无需手动设置,由类自动处理。
- 指定 PEFT 方法类型,固定为
-
task_type
(可选,字符串或peft.utils.peft_types.TaskType
)- 指定任务类型,帮助 PEFT 确定模型结构和用途。常见选项包括:
"SEQ_CLS"
:序列分类(如情感分析)。"TOKEN_CLS"
:token 分类(如 NER)。"CAUSAL_LM"
:因果语言模型(如 GPT)。"SEQ_2_SEQ_LM"
:序列到序列模型(如 T5)。
- 推荐明确指定,例如生成任务使用
"CAUSAL_LM"
。
- 指定任务类型,帮助 PEFT 确定模型结构和用途。常见选项包括:
-
r
(整数,默认:256)- 指定低秩矩阵的秩(rank),控制适配器的表达能力。
- 典型值:128 到 512。较高的秩增加表达能力,但也增加缩放向量的参数量。
- 参数量计算:
r * num_layers * 2
(每个层有两个缩放向量:一个用于 A,一个用于 B)。
-
target_modules
(列表或字符串,可选)- 指定应用 VeRA 的模型模块(通常是 Transformer 的注意力层或前馈层)。
- 常见值:
["q_proj", "v_proj"]
:对注意力层的 query 和 value 投影应用 VeRA。["q_proj", "k_proj", "v_proj", "o_proj"]
:更广泛的注意力模块。
- 必须根据模型结构明确指定(例如,LLaMA 使用
["q_proj", "v_proj"]
)。
-
projection_prng_key
(整数,默认:0)- 指定生成共享低秩矩阵 A 和 B 的随机种子。
- 确保一致的矩阵初始化,但跨设备或 PyTorch 版本的再现性可能不完全保证。
-
save_projection
(布尔值,默认:True)- 是否在保存模型时存储共享低秩矩阵 A 和 B。
True
:保存矩阵,确保跨设备完全再现性(检查点稍大)。False
:不保存矩阵,依赖projection_prng_key
重现,显著减少检查点大小(例如,从 MB 级降到 KB 级),但再现性可能受限。
-
vera_dropout
(浮点数,默认:0.0)- 缩放向量的 Dropout 概率,用于正则化。
- 典型值:0.0 到 0.1。推荐从 0.0 开始,若过拟合再增加。
-
d_initial
(浮点数,默认:0.1)- 缩放向量的初始值,用于初始化缩放参数。
- 典型值:0.01 到 0.1。较小的值确保初始行为接近原始模型。
-
fan_in_fan_out
(布尔值,默认:False)- 是否适配特定模型的权重矩阵布局(fan-in/fan-out)。通常针对非标准 Transformer 架构。
-
bias
(字符串,默认:"none"
)- 指定是否为适配器添加偏置(bias)。选项:
"none"
:无偏置。"all"
:为所有适配器层添加偏置。"vera_only"
:仅为 VeRA 层添加偏置。
- 推荐保持默认值
"none"
,除非任务需要额外的表达能力。
- 指定是否为适配器添加偏置(bias)。选项:
-
modules_to_save
(列表,可选)- 指定需要全量微调的模块(不使用 VeRA)。例如,分类头或语言模型头。
- 示例:
["lm_head"]
或["classifier"]
。
-
init_weights
(布尔值或字符串,默认:True)- 指定缩放向量的初始化方式:
True
:使用默认初始化(基于d_initial
)。"gaussian"
:使用高斯分布初始化(不推荐,可能不稳定)。
- 推荐使用默认值
True
。
- 指定缩放向量的初始化方式:
-
layers_to_transform
(列表或整数,可选)- 指定应用 VeRA 的 Transformer 层索引。
- 示例:
[0, 1, 2]
或10
(仅前 10 层)。 - 默认:所有层。
-
layers_pattern
(字符串或列表,可选)- 指定匹配层名称的模式,用于非标准模型。
- 示例:
"blocks"
或["layer", "h"]
。
3. 使用 VeraConfig 的基本流程
以下是一个使用 VeraConfig
微调 GPT-2 模型(用于因果语言建模任务)的完整示例:
步骤 1:安装和导入库
pip install peft transformers torch datasets
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
from peft import VeraConfig, get_peft_model
from datasets import load_dataset
步骤 2:加载模型和数据集
# 加载预训练模型和分词器
model_name = "gpt2"
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token # 为 GPT-2 设置 pad token
# 加载数据集(以 wikitext 为例)
dataset = load_dataset("wikitext", "wikitext-2-raw-v1")
# 数据预处理
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=128)
tokenized_dataset = dataset.map(tokenize_function, batched=True)
步骤 3:配置 VeraConfig
# 配置 VeRA
vera_config = VeraConfig(
task_type="CAUSAL_LM", # 因果语言模型任务
r=128, # 低秩矩阵的秩
target_modules=["c_attn"], # 应用 VeRA 的模块(GPT-2 的注意力层)
projection_prng_key=0, # 随机种子
save_projection=False, # 不保存共享矩阵,减少检查点大小
vera_dropout=0.0, # Dropout 概率
d_initial=0.1, # 缩放向量初始值
modules_to_save=["lm_head"] # 全量微调语言模型头
)
# 将 VeRA 应用到模型
peft_model = get_peft_model(model, vera_config)
# 查看可训练参数
peft_model.print_trainable_parameters()
输出示例:
trainable params: 49,152 || all params: 124,747,008 || trainable%: 0.039
这表明只有约 0.039% 的参数需要训练(缩放向量 + 语言模型头参数),远少于 LoRA。
步骤 4:训练模型
# 配置训练参数
training_args = TrainingArguments(
output_dir="./results",
evaluation_strategy="epoch",
learning_rate=1e-3, # VeRA 通常需要较高学习率
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
weight_decay=0.01,
)
# 初始化 Trainer
trainer = Trainer(
model=peft_model,
args=training_args,
train_dataset=tokenized_dataset["train"].select(range(1000)), # 选取子集以加速示例
eval_dataset=tokenized_dataset["validation"].select(range(200)),
)
# 开始训练
trainer.train()
步骤 5:保存和加载 VeRA 模型
# 保存 VeRA 参数
peft_model.save_pretrained("./vera_model")
# 加载 VeRA 模型
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_name)
loaded_model = PeftModel.from_pretrained(base_model, "./vera_model")
步骤 6:推理
# 准备输入
inputs = tokenizer("Once upon a time", return_tensors="pt").to("cuda")
# 推理
loaded_model.eval()
outputs = loaded_model.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
4. VeraConfig 的优化建议
-
选择
r
:- 从
r=128
或r=256
开始实验。较高的秩(如 512)增加表达能力,但参数量略增。 - 参数量与
r
和层数成正比:r * num_layers * 2
。
- 从
-
配置
target_modules
:- 推荐从
["q_proj", "v_proj"]
开始,仅调整注意力层的 query 和 value,参数量最小。 - 如果性能不足,扩展到
["q_proj", "k_proj", "v_proj", "o_proj"]
。 - 使用
model.named_modules()
检查模型结构,确认模块名称。
- 推荐从
-
设置
save_projection
:- 设置
save_projection=False
以最小化检查点大小(例如,几十 KB),适合多任务存储。 - 如果需要跨设备完全再现性,设置为
True
(检查点大小增加到 MB 级)。
- 设置
-
调整
projection_prng_key
:- 默认值 0 通常足够。更改种子可能影响初始化矩阵,需实验验证。
- 记录种子值以确保实验可重复。
-
学习率:
- VeRA 通常需要较高的学习率(如 1e-3 或 5e-4),因为只优化少量缩放向量。
- 使用学习率调度器(如线性衰减)以提高稳定性。
-
任务类型:
- 确保
task_type
匹配模型类型,例如 GPT 使用"CAUSAL_LM"
,T5 使用"SEQ_2_SEQ_LM"
。
- 确保
-
内存优化:
- VeRA 参数量极小,但基模型可能需要量化以进一步节省内存:
from transformers import BitsAndBytesConfig quantization_config = BitsAndBytesConfig(load_in_4bit=True) model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=quantization_config)
- VeRA 参数量极小,但基模型可能需要量化以进一步节省内存:
-
Dropout:
- 保持
vera_dropout=0.0
除非观察到过拟合。增加到 0.05 或 0.1 以增强正则化。
- 保持
5. VeRA vs. 其他 PEFT 方法
-
VeRA vs. LoRA:
- 参数量:VeRA 参数量远少于 LoRA(仅缩放向量 vs. 低秩矩阵)。
- 存储:VeRA 可不保存共享矩阵,检查点更小(KB 级 vs. MB 级)。
- 性能:VeRA 在生成任务中与 LoRA 性能相当,但 LoRA 更通用。
- 适用场景:VeRA 适合存储受限或多任务场景;LoRA 适合需要高表达能力的任务。
-
VeRA vs. Prompt Tuning:
- 结构:VeRA 调整模型内部权重,Prompt Tuning 添加输入提示。
- 参数量:VeRA 参数量通常更少(缩放向量 vs. 嵌入矩阵)。
- 适用场景:VeRA 更适合生成任务,Prompt Tuning 更适合快速实验。
-
VeRA vs. IA3:
- 结构:VeRA 使用共享低秩矩阵和缩放向量,IAPent Tuning**:
- 结构:VeRA 使用共享低秩矩阵和缩放向量,IA3 使用一维缩放向量。
- 参数量:VeRA 参数量略多于 IA3(因共享矩阵的秩),但表达能力更强。
- 适用场景:VeRA 更适合生成任务,IA3 更高效但表达能力有限。
-
VeRA vs. LoftQ:
- 结构:VeRA 使用共享矩阵,LoftQ 结合量化初始化 LoRA 权重。
- 参数量:VeRA 参数量少于 LoftQ(缩放向量 vs. 低秩矩阵)。
- 适用场景:VeRA 更适合存储受限场景,LoftQ 更适合量化微调。
6. 常见问题与解答
-
Q1:如何确定
target_modules
?- 使用
model.named_modules()
查看模型层结构,找到注意力(q_proj
,v_proj
)或前馈层的名称。 - 参考模型文档或 PEFT 默认值(例如,LLaMA 的
q_proj
,v_proj
)。
- 使用
-
Q2:
save_projection=False
是否安全?- 如果目标设备和 PyTorch 版本一致,
save_projection=False
是安全的,可显著减少检查点大小。 - 如果需要跨设备部署,推荐
save_projection=True
以确保再现性。
- 如果目标设备和 PyTorch 版本一致,
-
Q3:VeRA 是否支持所有模型?
- 支持所有 Hugging Face Transformers 模型,但最适合生成模型(如 GPT、LLaMA、Falcon)。
- 对于分类模型(如 BERT),效果可能不如 LoRA。
-
Q4:性能不佳如何优化?
- 增加
r
(如从 128 到 256 或 512)。 - 扩展
target_modules
(如添加k_proj
,o_proj
)。 - 调整学习率(尝试 5e-4 到 2e-3)。
- 检查数据集质量或增加训练轮次。
- 尝试 LoRA 或 LoftQ 以比较性能。
- 增加
-
Q5:VeRA 的参数量如何计算?
- 每个层的参数量为
r * 2
(两个缩放向量)。 - 总参数量 =
r * num_layers * 2
。 - 示例:GPT-2(12 层,
r=128
):128 * 12 * 2 = 3,072
参数。
- 每个层的参数量为
7. 进阶用法
-
多任务 VeRA:
- 为不同任务创建多个 VeRA 适配器:
vera_config_task1 = VeraConfig( task_type="CAUSAL_LM", r=128, target_modules=["c_attn"], save_projection=False ) vera_config_task2 = VeraConfig( task_type="CAUSAL_LM", r=256, target_modules=["c_attn"], save_projection=True ) peft_model = get_peft_model(model, vera_config_task1, adapter_name="task1") peft_model.add_adapter("task2", vera_config_task2) peft_model.set_adapter("task1") # 切换适配器
- 为不同任务创建多个 VeRA 适配器:
-
合并 VeRA 权重:
- 消除推理时的适配器延迟:
peft_model.merge_and_unload()
- 消除推理时的适配器延迟:
-
保存到 Hugging Face Hub:
peft_model.push_to_hub("your-username/vera-model")
-
结合量化:
- 为大型模型启用 4-bit 量化以节省内存:
from transformers import BitsAndBytesConfig quantization_config = BitsAndBytesConfig(load_in_4bit=True) model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=quantization_config) peft_model = get_peft_model(model, vera_config)
- 为大型模型启用 4-bit 量化以节省内存: