随着大语言模型规模的不断增长,全量微调( Full
Fine-tuning)的成本变得越来越高。一个拥有数十亿参数的模型,全量微调需要更新所有参数,这不仅需要巨大的计算资源,还可能导致灾难性遗忘(
Catastrophic Forgetting)。为了解决这些问题,参数高效微调(
Parameter-Efficient Fine-Tuning, PEFT)技术应运而生。
PEFT 技术通过只更新模型的一小部分参数,就能达到接近全量微调的效果。
LoRA( Low-Rank Adaptation)、 QLoRA 、 Adapter 、 Prefix-Tuning
等方法是其中的代表。这些方法不仅大幅降低了计算成本,还使得在消费级硬件上微调大模型成为可能。
本文将深入探讨全量微调与冻结微调的区别,详细解析 LoRA 、 QLoRA 、
Adapter 、 Prefix-Tuning 、 P-Tuning v2 等 PEFT
技术的原理,介绍指令微调( Instruction Tuning)和 RLHF( Reinforcement
Learning from Human Feedback)等对齐技术,并通过实战案例展示如何使用
HuggingFace PEFT 库微调大模型。
全量微调 vs 冻结微调
全量微调( Full Fine-tuning)
全量微调是指更新预训练模型的所有参数。这是最直接的方法,但成本也最高。
过程 :
加载预训练模型权重
在目标任务数据上训练
使用反向传播更新所有参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import torchimport torch.nn as nnfrom transformers import AutoModelForCausalLM, AutoTokenizermodel = AutoModelForCausalLM.from_pretrained("gpt2" ) tokenizer = AutoTokenizer.from_pretrained("gpt2" ) for param in model.parameters(): param.requires_grad = True optimizer = torch.optim.AdamW(model.parameters(), lr=1e-5 ) for epoch in range (num_epochs): for batch in dataloader: outputs = model(**batch) loss = outputs.loss loss.backward() optimizer.step() optimizer.zero_grad()
优势 : - 理论上能达到最佳性能 -
模型可以完全适应目标任务
劣势 : - 需要大量计算资源( GPU 内存、训练时间) -
容易过拟合 - 可能导致灾难性遗忘 - 每个任务需要保存完整模型副本
冻结微调( Frozen
Fine-tuning)
冻结微调是指冻结预训练模型的大部分参数,只训练部分层(通常是顶层)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 model = AutoModelForCausalLM.from_pretrained("gpt2" ) for param in model.parameters(): param.requires_grad = False for param in model.transformer.h[-2 :].parameters(): param.requires_grad = True classifier = nn.Linear(model.config.n_embd, num_labels) optimizer = torch.optim.AdamW(classifier.parameters(), lr=1e-4 )
优势 : - 大幅减少可训练参数 - 降低计算成本 -
保留预训练知识
劣势 : - 性能可能不如全量微调 - 需要仔细选择解冻的层
- 灵活性较低
参数效率对比
假设模型有 个参数:
全量微调 :需要更新 个参数
冻结微调 :只更新 个参数(例如最后几层)
PEFT 方法 :通常只更新 个参数(例如 LoRA 只更新 0.1%-1% 的参数)
LoRA:低秩适应
LoRA( Low-Rank Adaptation)是当前最流行的 PEFT
方法之一。基本思路:不直接更新原始权重矩阵,而是学习一个低秩分解的增量更新 。
LoRA 原理
对于预训练权重矩阵 , LoRA 不直接更新 ,而是学习两个低秩矩阵 和 ,其中 。
前向传播时,实际使用的权重为:
其中
是低秩更新。
参数效率 :
原始矩阵参数量:
LoRA 参数量:
参数减少比例: 当 时,参数量大幅减少。例如, 时:
原始参数量: 1,048,576
LoRA 参数量: 16,384
减少比例:约 98.4%
LoRA 实现
问题背景 :全量微调需要更新所有参数,成本高昂。 LoRA
通过低秩分解,只学习权重的增量更新,大幅减少可训练参数。
解决思路 :不直接更新原始权重矩阵 ,而是学习两个低秩矩阵 和 ,使得 。前向传播时使用 ,其中 是缩放因子。
设计考虑 : - 矩阵 初始化为随机小值, 初始化为零,确保初始时 LoRA 更新为零 -
Rank 控制低秩的维度,通常选择 4-32
- Alpha 控制 LoRA 更新的强度,通常设置为 rank 的倍数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 import torchimport torch.nn as nnimport torch.nn.functional as Fclass LoRALayer (nn.Module): """ LoRA 层实现:低秩适应 问题:如何高效地微调大模型? 解决:学习低秩分解的权重增量,而不是直接更新原始权重 原理: W = W_0 + Δ W = W_0 + BA 其中 B ∈ R^(out_features × r), A ∈ R^(r × in_features) 参数量: r × (out_features + in_features) << out_features × in_features """ def __init__ (self, in_features, out_features, rank=8 , alpha=16 ): """ Args: in_features: 输入特征维度 out_features: 输出特征维度 rank: 低秩的秩 r,控制 LoRA 的容量(通常 4-32) alpha: 缩放因子,控制 LoRA 更新的强度(通常为 rank 的倍数) """ super ().__init__() self.rank = rank self.alpha = alpha self.lora_A = nn.Parameter(torch.randn(rank, in_features) * 0.02 ) self.lora_B = nn.Parameter(torch.zeros(out_features, rank)) self.scaling = alpha / rank def forward (self, x, original_weight ): """ 前向传播:计算 W_0 * x + (alpha/r) * B * A * x Args: x: 输入张量, shape: [batch_size, ..., in_features] original_weight: 原始权重矩阵 W_0, shape: [out_features, in_features] Returns: 输出张量, shape: [batch_size, ..., out_features] """ lora_output = F.linear(x, self.lora_A.t()) lora_output = F.linear(lora_output, self.lora_B) lora_output = lora_output * self.scaling original_output = F.linear(x, original_weight) return original_output + lora_output class LoRALinear (nn.Module): """ 带 LoRA 的线性层包装器 问题:如何将 LoRA 应用到现有的线性层? 解决:包装原始线性层,在前向传播时应用 LoRA 更新 """ def __init__ (self, linear_layer, rank=8 , alpha=16 ): """ Args: linear_layer: 原始的 nn.Linear 层 rank: LoRA 的秩 alpha: LoRA 的缩放因子 """ super ().__init__() self.linear = linear_layer self.lora = LoRALayer( linear_layer.in_features, linear_layer.out_features, rank=rank, alpha=alpha ) def forward (self, x ): """ 前向传播:应用 LoRA 更新 Args: x: 输入张量 Returns: 输出张量 = W_0 * x + LoRA_update """ return self.lora(x, self.linear.weight)
关键点解读 : - 低秩分解 :将 的权重矩阵分解为 和 两个矩阵,参数量从 减少到 -
初始化策略 : 随机初始化, 零初始化,确保初始时 LoRA 不影响模型 -
缩放因子 :
控制 LoRA 更新的强度, 越大,
LoRA 的影响越大
设计权衡 : - ✅
优点:参数量大幅减少(通常<1%),可以合并到原始权重,推理速度不变 -
⚠️ 注意: rank
的选择很重要,太小可能表达能力不足,太大失去参数效率优势
常见问题 : - Q: 为什么 B 初始化为零? A: 确保初始时
LoRA 输出为零,不影响预训练模型的行为 - Q: Rank 如何选择? A: 通常从 8
或 16 开始,根据任务复杂度调整。简单任务可以用 4,复杂任务可能需要 32 或
64 - Q: Alpha 如何设置? A: 通常设置为 rank 的倍数,如 。经验法则: 通常效果较好
使用示例 : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class AttentionWithLoRA (nn.Module): def __init__ (self, attention_layer, rank=8 , alpha=16 ): super ().__init__() self.attention = attention_layer self.q_proj_lora = LoRALinear(attention_layer.q_proj, rank, alpha) self.k_proj_lora = LoRALinear(attention_layer.k_proj, rank, alpha) self.v_proj_lora = LoRALinear(attention_layer.v_proj, rank, alpha) def forward (self, x ): q = self.q_proj_lora(x) k = self.k_proj_lora(x) v = self.v_proj_lora(x)
LoRA 的优势
参数高效 :只更新少量参数(通常 <1%)
模块化 :可以轻松添加或移除 LoRA 适配器
多任务 :可以为不同任务训练不同的 LoRA 适配器
性能接近全量微调 :在大多数任务上能达到 90%+
的性能
LoRA 的超参数选择
rank (r) :低秩的秩,通常选择 4 、 8 、 16 、 32 。
rank 越大,表达能力越强,但参数也越多。
alpha :缩放因子,通常设置为 rank 的倍数(如 rank=8,
alpha=16)。 alpha 越大, LoRA 更新的影响越大。
经验法则:alpha = 2 * rank 通常效果较好。
QLoRA:量化 LoRA
QLoRA( Quantized LoRA)结合了量化和 LoRA,进一步降低了内存需求。
QLoRA 原理
QLoRA 的核心创新:
4-bit 量化 :将模型权重量化为 4-bit
NF4 量化 :使用 NormalFloat4 量化格式
双量化 :对量化常数再次量化
分页优化器 :使用分页 AdamW 优化器
内存节省 :
FP16 全量微调:每个参数 2 bytes
QLoRA:每个参数约 0.5 bytes( 4-bit)+ LoRA 参数
对于 7B 模型: - FP16 全量微调:约 14 GB - QLoRA:约 3-4 GB
QLoRA 实现(使用 PEFT)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from transformers import BitsAndBytesConfigfrom peft import LoraConfig, get_peft_model, prepare_model_for_kbit_trainingfrom transformers import AutoModelForCausalLM, AutoTokenizerbnb_config = BitsAndBytesConfig( load_in_4bit=True , bnb_4bit_quant_type="nf4" , bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True , ) model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-2-7b-hf" , quantization_config=bnb_config, device_map="auto" ) model = prepare_model_for_kbit_training(model) lora_config = LoraConfig( r=16 , lora_alpha=32 , target_modules=["q_proj" , "v_proj" ], lora_dropout=0.1 , bias="none" , task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config)
Adapter 技术
Adapter 是在 Transformer 层中插入小型可训练模块的方法。
Adapter 架构
在每个 Transformer 层中添加两个小的前馈网络( Adapter):
Down-projection :将隐藏维度降到较小的维度
Up-projection :将维度恢复到原始隐藏维度
其中: -Double exponent: use braces to clarify : ^d ^{d_{adapter}} -
Adapter 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Adapter (nn.Module): """Adapter 模块""" def __init__ (self, hidden_size, adapter_size=64 ): super ().__init__() self.adapter_size = adapter_size self.down_proj = nn.Linear(hidden_size, adapter_size) self.up_proj = nn.Linear(adapter_size, hidden_size) nn.init.zeros_(self.up_proj.weight) nn.init.zeros_(self.up_proj.bias) def forward (self, x ): return x + self.up_proj(F.relu(self.down_proj(x))) class TransformerLayerWithAdapter (nn.Module): """带 Adapter 的 Transformer 层""" def __init__ (self, transformer_layer, adapter_size=64 ): super ().__init__() self.transformer_layer = transformer_layer self.adapter = Adapter( transformer_layer.self_attn.embed_dim, adapter_size ) def forward (self, x, **kwargs ): x = self.transformer_layer(x, **kwargs)[0 ] x = self.adapter(x) return (x,)
Adapter vs LoRA
特性
Adapter
LoRA
插入位置
Transformer 层内部
权重矩阵旁
参数量
中等(每个 Adapter ~0.5%)
较少(通常 <1%)
推理速度
略慢(额外前向计算)
较快(可合并到权重)
灵活性
中等
高(易于组合)
Prefix-Tuning
Prefix-Tuning 通过在输入序列前添加可学习的连续前缀(
prefix)来适应任务。
Prefix-Tuning 原理
对于输入序列 , Prefix-Tuning 添加可学习的前缀 ,形成:
这些前缀向量
是可训练的参数,而原始模型参数保持冻结。
注意力计算 :
在注意力机制中,前缀参与 key 和 value 的计算:
其中 和 包含前缀部分:
Prefix-Tuning 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class PrefixTuning (nn.Module): """Prefix-Tuning 实现""" def __init__ (self, config, num_prefix_tokens=10 ): super ().__init__() self.num_prefix_tokens = num_prefix_tokens self.hidden_size = config.hidden_size self.num_heads = config.num_attention_heads self.head_dim = self.hidden_size // self.num_heads self.prefix_embeddings = nn.Parameter( torch.randn(num_prefix_tokens, self.hidden_size) ) self.prefix_key = nn.Linear(self.hidden_size, self.hidden_size) self.prefix_value = nn.Linear(self.hidden_size, self.hidden_size) def get_prefix_kv (self ): """获取前缀的 key 和 value""" prefix_k = self.prefix_key(self.prefix_embeddings) prefix_v = self.prefix_value(self.prefix_embeddings) batch_size = 1 prefix_k = prefix_k.view( batch_size, self.num_prefix_tokens, self.num_heads, self.head_dim ).transpose(1 , 2 ) prefix_v = prefix_v.view( batch_size, self.num_prefix_tokens, self.num_heads, self.head_dim ).transpose(1 , 2 ) return prefix_k, prefix_v
P-Tuning v2
P-Tuning v2 是 Prefix-Tuning 的改进版本,主要改进:
应用到所有层 :不仅在输入层,在所有 Transformer
层都添加前缀
移除重参数化 :直接优化前缀参数,不使用 MLP
重参数化
多任务学习 :支持多任务前缀
P-Tuning v2 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class PTuningV2 (nn.Module): """P-Tuning v2 实现""" def __init__ (self, config, num_layers, num_prefix_tokens=20 ): super ().__init__() self.num_layers = num_layers self.num_prefix_tokens = num_prefix_tokens self.hidden_size = config.hidden_size self.prefix_embeddings = nn.ModuleList([ nn.Parameter(torch.randn(num_prefix_tokens, self.hidden_size)) for _ in range (num_layers) ]) def get_layer_prefix (self, layer_idx ): """获取指定层的前缀""" return self.prefix_embeddings[layer_idx]
指令微调( Instruction
Tuning)
指令微调是让模型遵循指令的关键技术。通过在指令-响应对上微调,模型学会理解和执行各种指令。
指令数据格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 instruction_data = [ { "instruction" : "解释什么是机器学习" , "input" : "" , "output" : "机器学习是人工智能的一个分支,它使计算机能够从数据中学习..." }, { "instruction" : "将以下英文翻译成中文" , "input" : "Hello, how are you?" , "output" : "你好,你好吗?" }, { "instruction" : "总结以下文章" , "input" : "[文章内容]" , "output" : "[摘要]" } ]
指令微调实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from transformers import Trainer, TrainingArgumentsdef format_instruction (example ): """格式化指令数据""" if example['input' ]: prompt = f"指令:{example['instruction' ]} \n 输入:{example['input' ]} \n 输出:" else : prompt = f"指令:{example['instruction' ]} \n 输出:" return { "text" : prompt + example['output' ] } dataset = dataset.map (format_instruction) training_args = TrainingArguments( output_dir="./results" , num_train_epochs=3 , per_device_train_batch_size=4 , gradient_accumulation_steps=4 , learning_rate=2e-4 , fp16=True , logging_steps=10 , save_steps=500 , ) from peft import LoraConfig, get_peft_modellora_config = LoraConfig( r=16 , lora_alpha=32 , target_modules=["q_proj" , "v_proj" , "k_proj" , "o_proj" ], lora_dropout=0.1 , task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) trainer = Trainer( model=model, args=training_args, train_dataset=dataset, ) trainer.train()
RLHF 与对齐技术
RLHF( Reinforcement Learning from Human
Feedback)是让模型与人类价值观对齐的重要技术。
RLHF 流程
RLHF 通常包含三个阶段:
监督微调( SFT) :在指令数据上微调基础模型
奖励模型训练 :训练一个奖励模型来评估输出质量
强化学习优化 :使用 PPO 等算法优化策略模型
奖励模型训练
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class RewardModel (nn.Module): """奖励模型""" def __init__ (self, base_model ): super ().__init__() self.base_model = base_model self.reward_head = nn.Linear(base_model.config.hidden_size, 1 ) def forward (self, input_ids, attention_mask=None ): outputs = self.base_model(input_ids=input_ids, attention_mask=attention_mask) last_hidden_state = outputs.last_hidden_state[:, -1 , :] reward = self.reward_head(last_hidden_state) return reward def train_reward_model (model, chosen_data, rejected_data ): """训练奖励模型""" optimizer = torch.optim.AdamW(model.parameters(), lr=1e-5 ) for chosen, rejected in zip (chosen_data, rejected_data): reward_chosen = model(chosen['input_ids' ], chosen['attention_mask' ]) reward_rejected = model(rejected['input_ids' ], rejected['attention_mask' ]) loss = -torch.log(torch.sigmoid(reward_chosen - reward_rejected)) loss.backward() optimizer.step() optimizer.zero_grad()
PPO 优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from trl import PPOTrainer, PPOConfigppo_config = PPOConfig( model_name="gpt2" , learning_rate=1e-5 , batch_size=4 , mini_batch_size=2 , gradient_accumulation_steps=4 , ) ppo_trainer = PPOTrainer( config=ppo_config, model=model, ref_model=ref_model, tokenizer=tokenizer, reward_model=reward_model, ) for epoch in range (num_epochs): for batch in dataloader: response = model.generate(**batch) rewards = reward_model(response) ppo_trainer.step( queries=batch['input_ids' ], responses=response, rewards=rewards )
实战:使用 PEFT 微调大模型
完整示例:使用 LoRA 微调
LLaMA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForLanguageModeling ) from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_trainingfrom datasets import load_datasetimport torchmodel_name = "meta-llama/Llama-2-7b-hf" tokenizer = AutoTokenizer.from_pretrained(model_name) tokenizer.pad_token = tokenizer.eos_token model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map="auto" ) lora_config = LoraConfig( r=16 , lora_alpha=32 , target_modules=["q_proj" , "v_proj" , "k_proj" , "o_proj" ], lora_dropout=0.1 , bias="none" , task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) model.print_trainable_parameters() def tokenize_function (examples ): return tokenizer( examples["text" ], truncation=True , max_length=512 , padding="max_length" ) dataset = load_dataset("wikitext" , "wikitext-2-raw-v1" ) tokenized_dataset = dataset.map (tokenize_function, batched=True ) training_args = TrainingArguments( output_dir="./llama-lora" , num_train_epochs=3 , per_device_train_batch_size=4 , gradient_accumulation_steps=4 , learning_rate=2e-4 , fp16=True , logging_steps=10 , save_steps=500 , evaluation_strategy="steps" , eval_steps=500 , ) data_collator = DataCollatorForLanguageModeling( tokenizer=tokenizer, mlm=False ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_dataset["train" ], eval_dataset=tokenized_dataset["validation" ], data_collator=data_collator, ) trainer.train() model.save_pretrained("./llama-lora-final" )
使用 QLoRA 微调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from transformers import BitsAndBytesConfigfrom peft import LoraConfig, get_peft_model, prepare_model_for_kbit_trainingbnb_config = BitsAndBytesConfig( load_in_4bit=True , bnb_4bit_quant_type="nf4" , bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True , ) model = AutoModelForCausalLM.from_pretrained( "meta-llama/Llama-2-7b-hf" , quantization_config=bnb_config, device_map="auto" ) model = prepare_model_for_kbit_training(model) lora_config = LoraConfig( r=16 , lora_alpha=32 , target_modules=["q_proj" , "v_proj" ], lora_dropout=0.1 , bias="none" , task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config)
多任务 LoRA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 task1_lora = LoraConfig( r=16 , lora_alpha=32 , target_modules=["q_proj" , "v_proj" ], task_type="CAUSAL_LM" ) task2_lora = LoraConfig( r=8 , lora_alpha=16 , target_modules=["q_proj" , "v_proj" , "k_proj" ], task_type="CAUSAL_LM" ) model_task1 = get_peft_model(base_model, task1_lora) model_task2 = get_peft_model(base_model, task2_lora) model.set_adapter("task1_adapter" ) output1 = model.generate(...) model.set_adapter("task2_adapter" ) output2 = model.generate(...)
❓ Q&A: 模型微调与 PEFT
常见问题
Q1: 什么时候应该使用全量微调,什么时候使用
PEFT?
全量微调 :当你有充足的计算资源、大量高质量数据、且需要最佳性能时
PEFT :当计算资源有限、数据量中等、需要快速迭代或多任务适配时
Q2: LoRA 的 rank 如何选择?
小任务/简单任务 : rank=4 或 8
中等任务 : rank=16 或 32
复杂任务 : rank=32 或 64
建议:从 rank=16 开始,根据效果调整 。
Q3: QLoRA 和 LoRA 有什么区别?
LoRA :在 FP16/BF16 模型上应用
QLoRA :在 4-bit 量化模型上应用,内存需求更低
QLoRA 适合内存受限 的场景。
Q4: 应该对哪些模块应用 LoRA?
通常选择注意力层的投影矩阵: - q_proj,
k_proj, v_proj, o_proj( QKV
注意力) - gate_proj, up_proj,
down_proj( MLP,可选)
建议:至少包含 q_proj 和 v_proj 。
Q5: PEFT 会影响推理速度吗?
LoRA :可以合并到原始权重,推理速度不变
Adapter :需要额外计算,推理略慢
Prefix-Tuning :需要处理额外 token,推理略慢
Q6: 如何选择 PEFT 方法?
方法
适用场景
LoRA
通用场景,平衡性能和效率
QLoRA
内存受限,大模型
Adapter
需要模块化设计
Prefix-Tuning
生成任务,需要控制生成
Q7: 指令微调需要多少数据?
最少 : 100-1000 条高质量指令
推荐 : 1000-10000 条
最佳 : 10000+ 条多样化指令
质量比数量更重要。
Q8: RLHF 是必需的吗?
不是。 RLHF 主要用于: - 需要与人类价值观对齐 - 需要控制输出风格 -
需要减少有害内容
对于大多数任务,指令微调已经足够。
Q9: 如何评估微调效果?
任务指标 :准确率、 F1 、 BLEU 等
生成质量 :人工评估、 GPT-4 评估
对齐度 :遵循指令的能力
效率 :参数量、推理速度
Q10: PEFT 的未来发展方向?
更高效的参数利用 :用更少参数达到更好效果
自动化方法选择 :自动选择最优 PEFT 配置
多模态扩展 :应用到视觉、语音等模态
组合方法 :结合多种 PEFT 技术