一、LoRA
简单来说,LoRA不直接调整个大模型的全部参数(那样太费资源),而是在模型的某些层(通常是注意力层)加个“旁路”——两个小的矩阵(低秩矩阵)。训练时只更新这俩小矩阵,原模型冻住不动。这样既省内存,又能快速适配新任务。
大模型就像一座豪华大宅,里面房间多、家具全,但装修风格已经固定。LoRA就像给某些关键房间(比如客厅、厨房)加装了智能化的小设备(比如智能灯、智能窗帘)。你不用翻新整个大宅,只调整这些小设备,就能让房子适应新的生活需求,比如自动调节灯光或开窗通风。虽然改动不大,但住起来照样舒服,甚至更适合你的习惯。
举个例子
假设你部署的是LLaMA-7B
,想让它学会用中文写诗:
- 数据:收集1000首中文古诗,格式是纯文本。
- LoRA配置:
r=16
,target_modules=["q_proj", "v_proj"]
。 - 训练:跑3轮,每轮500步,显存8GB够用。
- 结果:输入“春风”,它能生成“春风拂面柳丝轻”之类。
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
import torch
from datasets import load_dataset# 加载 LLaMA-7B 模型和分词器
model_name = "meta-llama/Llama-2-7b-hf" # 假设使用 LLaMA-7B
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16, device_map="auto")# 配置 LoRA
lora_config = LoraConfig(r=16, # 低秩矩阵的秩lora_alpha=32, # 缩放因子target_modules=["q_proj", "v_proj"], # 目标模块lora_dropout=0.05, # Dropoutbias="none",task_type="CAUSAL_LM"
)# 应用 LoRA 到模型
model = get_peft_model(model, lora_config)# 加载中文古诗数据集(假设是纯文本格式)
dataset = load_dataset("text", data_files={"train": "chinese_poetry.txt"})
def tokenize_function(examples):return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=128)tokenized_dataset = dataset.map(tokenize_function, batched=True)# 配置训练参数
from transformers import TrainingArguments, Trainertraining_args = TrainingArguments(output_dir="./lora_poetry",num_train_epochs=3, # 训练 3 轮per_device_train_batch_size=4,save_steps=500,logging_steps=100,learning_rate=2e-4,fp16=True, # 使用混合精度训练
)# 初始化 Trainer
trainer = Trainer(model=model,args=training_args,train_dataset=tokenized_dataset["train"],
)# 开始训练
trainer.train()# 保存微调后的模型
model.save_pretrained("./lora_poetry_model")# 测试生成
input_text = "春风"
inputs = tokenizer(input_text, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
# 输出示例:春风拂面柳丝轻
二、P-Tuning
P-Tuning不是直接改模型的权重,而是在输入里加一些特殊的“虚拟词”(可训练的嵌入向量),这些词的作用就像给模型下个指令,告诉它接下来该怎么处理任务。训练时,只更新这些虚拟词的参数,原模型保持冻结。这样既省资源,又能快速适配新需求。
大模型是个聪明但固执的厨师,P-Tuning就像在菜谱开头加几句特别的“口令”(比如“做中式”、“少放盐”),厨师不用改自己的烹饪习惯,但能按你的要求调整菜的风格。
举个例子
假设你用的是LLaMA-7B,想让它生成更幽默的中文对话:
- 数据:收集1000条幽默对话的文本数据。
- P-Tuning配置:添加10个虚拟词(prompt tokens),嵌入维度与模型一致。
- 训练:跑5轮,每轮300步,显存6GB足以。
- 结果:输入“今天心情如何?”,它可能回复“心情像Wi-Fi信号,满格但偶尔掉线!”
通过这些虚拟词,模型就像被“调教”出幽默风格,效果精准又高效。
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PromptTuningConfig, get_peft_model, PromptTuningInit
import torch
from datasets import load_dataset# 加载模型和分词器
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16, device_map="auto")# 配置 P-Tuning
ptuning_config = PromptTuningConfig(task_type="CAUSAL_LM",prompt_tuning_init=PromptTuningInit.RANDOM,num_virtual_tokens=10, # 10 个虚拟词tokenizer_name_or_path=model_name,
)# 应用 P-Tuning
model = get_peft_model(model, ptuning_config)# 加载幽默对话数据集
dataset = load_dataset("text", data_files={"train": "humor_dialog.txt"})
def tokenize_function(examples):return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=128)tokenized_dataset = dataset.map(tokenize_function, batched=True)# 训练参数
from transformers import TrainingArguments, Trainertraining_args = TrainingArguments(output_dir="./ptuning_humor",num_train_epochs=5,per_device_train_batch_size=4,save_steps=300,logging_steps=100,learning_rate=3e-4,fp16=True,
)# 初始化 Trainer
trainer = Trainer(model=model,args=training_args,train_dataset=tokenized_dataset["train"],
)# 训练
trainer.train()# 保存模型
model.save_pretrained("./ptuning_humor_model")# 测试生成
input_text = "今天心情如何?"
inputs = tokenizer(input_text, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
# 输出示例:心情像Wi-Fi信号,满格但偶尔掉线!