## 一、引言
在自然语言处理领域,大型预训练语言模型展现出了强大的能力,但直接在特定任务上应用这些模型往往效果不佳。监督微调(SFT)是一种有效的方法,它通过在特定的标注数据集上对预训练模型进行微调,使模型能够更好地适应具体任务。而 LoRA 作为一种轻量级的微调技术,通过低秩分解的方式,显著减少了需要训练的参数数量,降低了计算资源的需求,同时保持了与全量微调相近的性能。Hugging Face 提供了丰富的工具和模型库,方便我们进行 LoRA 微调。然而,在国内网络环境下,需要解决一些网络访问和数据下载的问题,下面将详细介绍整个 SFT 过程。
## 二、环境准备
### 2.1 网络设置
由于 Hugging Face 的部分资源在国内访问受限,我们可以使用国内的镜像源来加速模型和数据集的下载。例如,使用清华大学的镜像源:
```
export HF_ENDPOINT=hf-mirror
```
### 2.2 安装依赖库
我们需要安装一些必要的 Python 库,包括 `transformers`、`peft`(用于 LoRA 实现)、`datasets` 等。可以使用以下命令进行安装:
```
pip install transformers peft datasets evaluate accelerate
```
## 三、数据处理
### 3.1 数据集选择
这里我们选择一个简单的文本分类数据集,例如 `imdb` 影评数据集。使用 `datasets` 库进行加载:
```
from datasets import load_dataset
# 加载数据集
dataset = load_dataset("imdb")
```
### 3.2 数据预处理
对数据集进行预处理,包括分词等操作。这里我们使用 `AutoTokenizer` 进行分词:
```
from transformers import AutoTokenizer
# 选择合适的分词器
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
def preprocess_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=128)
# 对数据集进行预处理
tokenized_dataset = dataset.map(preprocess_function, batched=True)
```
## 四、模型选择
我们选择 `bert-base-uncased` 作为基础预训练模型:
```
from transformers import AutoModelForSequenceClassification
# 加载预训练模型
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)
```
## 五、LoRA 配置
### 5.1 引入 LoRA
使用 `peft` 库来配置 LoRA:
```
from peft import LoraConfig, get_peft_model
# 配置 LoRA
config = LoraConfig(
r=8, # 低秩矩阵的秩
lora_alpha=16,
target_modules=["query", "key", "value"], # 要应用 LoRA 的模块
lora_dropout=0.1,
bias="none",
task_type="SEQ_CLS"
)
# 将 LoRA 配置应用到模型上
model = get_peft_model(model, config)
```
### 5.2 查看可训练参数
查看经过 LoRA 配置后,模型中可训练的参数数量:
```
model.print_trainable_parameters()
```
## 六、微调训练
### 6.1 训练参数配置
使用 `transformers` 库中的 `TrainingArguments` 来配置训练参数:
```
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
warmup_steps=500,
weight_decay=0.01,
logging_dir="./logs",
logging_steps=10,
evaluation_strategy="steps",
eval_steps=50,
save_strategy="steps",
save_steps=100,
load_best_model_at_end=True
)
```
### 6.2 定义评估指标
使用 `evaluate` 库定义评估指标,这里我们使用准确率:
```
import evaluate
import numpy as np
metric = evaluate.load("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
```
### 6.3 创建训练器并开始训练
```
from transformers import Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset["train"],
eval_dataset=tokenized_dataset["test"],
compute_metrics=compute_metrics
)
# 开始训练
trainer.train()
```
## 七、模型评估与保存
### 7.1 模型评估
训练完成后,对模型进行评估:
```
eval_results = trainer.evaluate()
print(f"Evaluation results: {eval_results}")
```
### 7.2 模型保存
将微调后的模型保存到本地:
```
model.save_pretrained("./fine_tuned_model")
```
## 八、RoberTa
然而,BERT 模型并非完美无缺,随着研究的深入,其一些局限性逐渐显现,例如在预训练过程中,掩码标记(\[MASK])只在预训练时出现,在微调阶段并不存在,这可能导致预训练 - 微调之间的不匹配问题。此外,NSP 任务的有效性也受到了质疑,一些研究表明该任务对模型性能的提升贡献有限。
。为了进一步提升模型性能,Facebook AI 研究团队提出了 RoBERTa(A Robustly Optimized BERT Pretraining Approach)。RoBERTa 在 BERT 的基础上进行了一系列优化和改进。
* **训练策略调整**
* **动态掩码**:BERT 在预训练时对输入序列进行静态掩码,即在整个预训练过程中,每个序列的掩码位置是固定的。而 RoBERTa 采用动态掩码策略,在每次输入时都随机生成掩码位置,增加了训练数据的多样性,使模型能够学习到更鲁棒的语言表示。
* **去除 NSP 任务**:由于 NSP 任务的有效性受到质疑,RoBERTa 去除了该任务,仅使用 MLM 任务进行预训练。这样可以让模型更专注于学习文本的语义信息,避免了 NSP 任务可能带来的干扰。
* **数据处理优化**
* **更多的数据**:RoBERTa 使用了比 BERT 更多的训练数据,包括 CommonCrawl、Wikipedia 等大规模语料库。更多的数据有助于模型学习到更丰富的语言知识和模式,提升模型的泛化能力。
* **改进的 BPE 算法**:RoBERTa 在字节对编码(Byte Pair Encoding, BPE)算法上进行了改进,能够更好地处理未登录词,提高了模型对不同文本的适应性。
```
model = RobertaForSequenceClassification.from_pretrained('roberta-base', num_labels=len(set(labels)))
# 自定义数据集类
class TextDataset(Dataset):
def __init__(self, texts, labels, tokenizer, max_length):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = self.texts[idx]
label = self.labels[idx]
encoding = self.tokenizer(text, return_tensors='pt', padding='max_length', truncation=True, max_length=self.max_length)
return {
'input_ids': encoding['input_ids'].flatten(),
'attention_mask': encoding['attention_mask'].flatten(),
'labels': torch.tensor(label, dtype=torch.long)
}
# 创建数据集和数据加载器
train_dataset = TextDataset(train_texts, train_labels, tokenizer, max_length=128)
test_dataset = TextDataset(test_texts, test_labels, tokenizer, max_length=128)
train_dataloader = DataLoader(train_dataset, batch_size=16, shuffle=True)
test_dataloader = DataLoader(test_dataset, batch_size=16, shuffle=False)
# 定义训练参数
optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
# 训练模型
num_epochs = 3
for epoch in range(num_epochs):
model.train()
total_loss = 0
for batch in train_dataloader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
optimizer.zero_grad()
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f'Epoch {epoch + 1}, Loss: {total_loss / len(train_dataloader)}')
# 评估模型
model.eval()
correct_predictions = 0
total_predictions = 0
with torch.no_grad():
for batch in test_dataloader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
outputs = model(input_ids, attention_mask=attention_mask)
logits = outputs.logits
predictions = torch.argmax(logits, dim=1)
correct_predictions += (predictions == labels).sum().item()
total_predictions += labels.size(0)
accuracy = correct_predictions / total_predictions
print(f'Test Accuracy: {accuracy}')
```
RoBERTa 在训练策略和数据处理上的优化。动态掩码策略使得模型在训练过程中能够接触到更多样化的输入,从而学习到更鲁棒的特征表示;去除 NSP 任务让模型更专注于文本语义的理解;使用更多的数据和改进的 BPE 算法进一步提升了模型的泛化能力和对不同文本的处理能力。
## 九、展望&结论
通过本文的介绍,我们利用 Hugging Face 平台和 LoRA 技术成功完成了对预训练模型的监督微调(SFT)过程。LoRA 技术的应用大大减少了计算资源的需求,使得在普通设备上也能进行有效的模型微调。同时,Hugging Face 提供的丰富工具和库,让整个微调过程更加便捷和高效。未来,我们可以在此基础上进一步探索不同的数据集和模型,以适应更多的自然语言处理任务。