大型语言模型(LLM,Large Language Model)的微调(Fine-tuning)是指在一个预训练模型的基础上,使用特定领域或任务的数据对模型进行进一步训练,以使其在该领域或任务上表现更好。微调是迁移学习的一种常见方法,能够显著提升模型在特定任务上的性能。在大型语言模型(LLM)的微调中,有几种常见的方法,包括 SFT(监督微调)、LoRA(低秩适应)、P-tuning v2 和 **Freeze
微调(Fine-tuning)是指在预训练模型的基础上,使用特定任务的标注数据对模型进行进一步训练,使其在该任务上表现更好。微调的核心思想是利用预训练模型已经学习到的通用语言表示,通过少量任务数据调整模型参数,使其适应特定任务。
输入 (X):
目标 (Y):
损失函数:
在语言模型的微调中,交叉熵损失(Cross-Entropy Loss) 是最常用的损失函数。它用于衡量模型预测的概率分布与真实目标分布之间的差异。
对于语言模型,交叉熵损失的公式为:
Cross-Entropy Loss = − ∑ i = 1 N y i log ( p i ) \text{Cross-Entropy Loss} = -\sum_{i=1}^{N} y_i \log(p_i) Cross-Entropy Loss=−i=1∑Nyilog(pi)
其中:
假设模型生成一个句子,每个词的概率分布如下:
["I", "love", "AI"]
I
: 0.9love
: 0.8AI
: 0.7交叉熵损失计算如下:
t e x t L o s s = − ( log ( 0.9 ) + log ( 0.8 ) + log ( 0.7 ) ) text{Loss} = -(\log(0.9) + \log(0.8) + \log(0.7)) textLoss=−(log(0.9)+log(0.8)+log(0.7))
通过最小化损失,模型逐渐学会生成更准确的文本。
SFT 是最常见的微调方法,通过在特定任务的标注数据上对预训练模型进行全参数微调。
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments
from datasets import load_dataset
# 加载预训练模型和分词器
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
# 加载数据集
dataset = load_dataset("imdb")
# 数据预处理
def preprocess_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)
tokenized_datasets = dataset.map(preprocess_function, batched=True)
# 设置训练参数
training_args = TrainingArguments(
output_dir="./sft_results",
evaluation_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
weight_decay=0.01,
save_strategy="epoch",
logging_dir="./logs",
)
# 定义 Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["test"],
tokenizer=tokenizer,
)
# 开始微调
trainer.train()
# 保存模型
model.save_pretrained("./sft-fine-tuned-model")
tokenizer.save_pretrained("./sft-fine-tuned-model")
LoRA 是一种高效的微调方法,通过低秩分解的方式微调模型参数,减少计算量和显存占用。
使用 peft
库实现 LoRA:
pip install peft
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from peft import get_peft_model, LoraConfig, TaskType
from datasets import load_dataset
# 加载预训练模型和分词器
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
# 配置 LoRA
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS, # 任务类型
r=8, # 低秩矩阵的秩
lora_alpha=32, # 缩放因子
lora_dropout=0.1, # Dropout 概率
)
# 应用 LoRA
model = get_peft_model(model, lora_config)
# 加载数据集
dataset = load_dataset("imdb")
# 数据预处理
def preprocess_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)
tokenized_datasets = dataset.map(preprocess_function, batched=True)
# 设置训练参数
training_args = TrainingArguments(
output_dir="./lora_results",
evaluation_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
weight_decay=0.01,
save_strategy="epoch",
logging_dir="./logs",
)
# 定义 Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["test"],
tokenizer=tokenizer,
)
# 开始微调
trainer.train()
# 保存模型
model.save_pretrained("./lora-fine-tuned-model")
P-tuning v2 是一种提示微调方法,通过优化提示(Prompt)来引导模型完成任务,而不改变模型参数。
使用 openprompt
库实现 P-tuning v2:
pip install openprompt
from openprompt import PromptDataLoader, PromptForClassification
from openprompt.plms import load_plm
from openprompt.prompts import ManualTemplate
from openprompt.prompts import ManualVerbalizer
from datasets import load_dataset
# 加载预训练模型
plm, tokenizer, model_config, WrapperClass = load_plm("bert", "bert-base-uncased")
# 加载数据集
dataset = load_dataset("imdb")
# 定义提示模板
template = ManualTemplate(
text='{"placeholder":"text_a"} It was {"mask"}',
tokenizer=tokenizer,
)
# 定义标签词映射
verbalizer = ManualVerbalizer(
classes=["negative", "positive"],
label_words={
"negative": ["bad"],
"positive": ["good"],
},
tokenizer=tokenizer,
)
# 定义 Prompt 模型
prompt_model = PromptForClassification(
plm=plm,
template=template,
verbalizer=verbalizer,
)
# 数据加载器
dataloader = PromptDataLoader(
dataset=dataset["train"],
tokenizer=tokenizer,
template=template,
max_seq_length=512,
)
# 训练(伪代码,需补充优化器和训练循环)
for batch in dataloader:
logits = prompt_model(batch)
# 计算损失并更新模型
Freeze 微调是指冻结模型的大部分参数,只微调部分层(如分类头)。
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments
from datasets import load_dataset
# 加载预训练模型和分词器
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
# 冻结模型的大部分参数
for param in model.bert.parameters():
param.requires_grad = False
# 加载数据集
dataset = load_dataset("imdb")
# 数据预处理
def preprocess_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)
tokenized_datasets = dataset.map(preprocess_function, batched=True)
# 设置训练参数
training_args = TrainingArguments(
output_dir="./freeze_results",
evaluation_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=3,
weight_decay=0.01,
save_strategy="epoch",
logging_dir="./logs",
)
# 定义 Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["test"],
tokenizer=tokenizer,
)
# 开始微调
trainer.train()
# 保存模型
model.save_pretrained("./freeze-fine-tuned-model")
tokenizer.save_pretrained("./freeze-fine-tuned-model")
方法 | 特点 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|
SFT | 全参数微调 | 数据量大,资源充足 | 完全适应任务 | 计算成本高 |
LoRA | 低秩分解,部分参数微调 | 资源有限 | 参数效率高,显存占用低 | 需要额外实现 |
P-tuning | 提示优化,不修改模型参数 | 少样本学习 | 显存占用低 | 需要设计提示模板 |
Freeze | 冻结大部分参数,微调部分层 | 资源有限,数据量小 | 计算成本低 | 模型适应能力有限 |
DeepSeek LLM 是一个强大的开源语言模型,但为了最大限度地发挥其在特定应用中的潜力,微调是必不可少的。
在资源有限的硬件上微调像 DeepSeek LLM 这样的大型语言模型时,在完整数据集(例如,具有 25,000 个样本的 IMDB)上进行训练可能会导致过多的训练时间和 GPU 内存问题。
使用较小的数据集可以加快实验速度,同时有效地展示微调概念。对于生产级微调,应在更强大的基础设施上使用更大的数据集。
首先,安装必要的依赖项:
pip install -U torch transformers datasets accelerate peft bitsandbytes
使用 4 位量化使大型模型与有限的 GPU 内存兼容:
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
model_name = "deepseek-ai/deepseek-llm-7b-base"
# Configure 4-bit quantization
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16 # Use float16 for faster computation
)
# Load tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto"
)
# Apply LoRA for memory-efficient fine-tuning
lora_config = LoraConfig(
r=8, # Low-rank adaptation size
lora_alpha=32,
target_modules=["q_proj", "v_proj"], # Apply LoRA to attention layers
lora_dropout=0.05,
bias="none"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
print(" DeepSeek LLM Loaded with LoRA and 4-bit Precision!")
为了进行微调,需要一个高质量的数据集。 Hugging Face 提供对各种数据集的访问:
对于此示例,让我们使用 IMDB 数据集对 DeepSeek LLM 进行情绪分类微调:
from datasets import load_dataset
# Load dataset
dataset = load_dataset("imdb")
将文本转换为模型的标记化输入:
def tokenize_function(examples):
inputs = tokenizer(
examples["text"],
truncation=True,
padding="max_length",
max_length=512
)
inputs["labels"] = inputs["input_ids"].copy()
return inputs
tokenized_datasets = dataset.map(tokenize_function, batched=True)
# Subset the dataset for faster experimentation
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(500))
small_test_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(100))
# Print a sample tokenized entry
print("Tokenized Sample:")
print(small_train_dataset[0])
LoRA(低秩自适应)是一种旨在通过以下方式使 DeepSeek LLM 等大型模型的微调更加节省内存的技术:
这大大减少了可训练参数的数量,同时保持了模型的性能。LoRA 可以在资源受限的硬件(例如 Colab GPU)上微调大型语言模型。
#4.代码演练:微调 DeepSeek LLM
首先设置训练参数:
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./results",
evaluation_strategy="epoch",
learning_rate=3e-4, # Lower learning rate for LoRA fine-tuning
per_device_train_batch_size=1, # Reduce batch size for memory efficiency
gradient_accumulation_steps=8, # Simulate larger batch size
num_train_epochs=0.5,
weight_decay=0.01,
save_strategy="epoch",
logging_dir="./logs",
logging_steps=50,
fp16=True, # Mixed precision training
)
初始化训练器:
trainer = Trainer(
model=model,
args=training_args,
train_dataset=small_train_dataset,
eval_dataset=small_test_dataset,
)
print(" Trainer Initialized!")
开始微调:
print(" Starting Fine-Tuning...")
trainer.train()
保存微调模型:
trainer.save_model("./fine_tuned_deepseek")
tokenizer.save_pretrained("./fine_tuned_deepseek")
print("Fine-Tuned Model Saved Successfully!")