萱仔大模型学习记录3.2-BERT算法微调理论和实践Adapter_Tuning、Prefix_Tuning等

3.Adapter Tuning

原版论文:https://arxiv.org/pdf/1902\.00751\.pdf

原版代码:GitHub - google-research/adapter-bert

Adapter Tuning
是一种在预训练模型的基础上进行高效微调的方法。通过插入adapter modules,只需增加少量可训练参数,就能使模型在新任务上取得优异的性能。(我个人感觉这个在使用上和lora有点类似,lora是引入了两个矩阵,在外面训练微调,较低成本实现训练任务。这个是加入一个适配器的模块。
Adapter Modules
是插入到现有预训练模型中的小型网络层。由少量可训练参数组成,通常包括一个下采样层、一个ReLU之类的非线性激活层,以及一个上采样层。(这一点又和计算机视觉中有一点点像,上采样和下采样各有作用,然后加上激活函数的非线性函数,加入到神经网络中可以让网格拟合非线性映射。
Adapter Modules
插入位置通常在Transformer层之间。)

在适配器微调过程中,预训练模型的参数保持不变。只训练适配器模块的参数,从而实现参数高效的迁移学习。相比于传统的微调,适配器微调只需增加很少的新参数,但能够取得接近或甚至超越全量微调的效果。适配器模块可以独立于任务进行训练,适应多任务学习的需求。

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

import torch
from transformers import BertModel, BertTokenizer, BertForSequenceClassification, AdapterConfig

# 加载预训练BERT模型和Tokenizer
model_name = "bert-base-uncased"
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name)

# 冻结BERT主体模型的所有参数
for param in model.bert.parameters():
param.requires_grad = False

# 添加适配器模块配置
adapter_config = AdapterConfig(
hidden_size=64, # 适配器模块的隐藏层大小
adapter_non_linearity="relu", # 非线性激活函数
reduction_factor=16 # 下采样比例
)

# 为分类任务添加适配器
model.add_adapter("text_classification_adapter", config=adapter_config)
model.train_adapter("text_classification_adapter")

# 示例输入
inputs = tokenizer("This is a test sentence.", return_tensors="pt")

# 模型预测前向传播
outputs = model(**inputs)

# 打印模型输出
print(outputs.logits)

# 准备训练数据(以GLUE任务中的MRPC数据集为例)
from datasets import load_dataset
dataset = load_dataset("glue", "mrpc")
train_dataset = dataset['train']
validation_dataset = dataset['validation']

# 数据处理函数
def preprocess_function(examples):
return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True, padding=True)

# 预处理数据
train_dataset = train_dataset.map(preprocess_function, batched=True)
validation_dataset = validation_dataset.map(preprocess_function, batched=True)

# 数据加载器
from torch.utils.data import DataLoader
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
validation_loader = DataLoader(validation_dataset, batch_size=16)

# 定义优化器和损失函数
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=5e-5)
loss_fn = torch.nn.CrossEntropyLoss()

# 训练适配器
model.train()
for epoch in range(3): # 训练3个epoch
for batch in train_loader:
inputs = {k: v.to(model.device) for k, v in batch.items() if k in tokenizer.model_input_names}
labels = batch['label'].to(model.device)
optimizer.zero_grad()
outputs = model(**inputs)
loss = loss_fn(outputs.logits, labels)
loss.backward()
optimizer.step()
print(f"Epoch {epoch + 1}: Loss {loss.item()}")

# 验证模型
model.eval()
correct_predictions = 0
total_predictions = 0
with torch.no_grad():
for batch in validation_loader:
inputs = {k: v.to(model.device) for k, v in batch.items() if k in tokenizer.model_input_names}
labels = batch['label'].to(model.device)
outputs = model(**inputs)
predictions = torch.argmax(outputs.logits, dim=-1)
correct_predictions += (predictions == labels).sum().item()
total_predictions += labels.size(0)
accuracy = correct_predictions / total_predictions
print(f"Validation Accuracy: {accuracy}")

# 保存适配器
model.save_adapter("text_classification_adapter", "output_adapter_directory")

4.Prefix Tuning输入增加可训练的上下文前缀

原版论文:
https://arxiv.org/pdf/2101.00190.pdf

原版代码:

Fine-tuning是利用大型预训练语言模型来执行下游任务的默认方式。然而,微调会修改所有语言模型的参数,因此需要为每个任务存储一份完整的副本。本文提出了一种轻量级的微调替代方法——前缀调优prefix-tuning,专用于自然语言生成任务。前缀调优保持语言模型参数不变,仅优化一个小的任务特定的连续向量(称为前缀)。前缀调优受提示(prompting)的启发,允许后续的token关注这个前缀,仿佛它们是“虚拟token”。这篇论文将前缀调优应用于GPT-2进行表格到文本生成,以及BART进行摘要生成。结果表明,通过仅学习0.1%的参数,前缀调优在完整数据集环境中获得了与全量微调相当的性能,在低数据环境中表现优于微调,并且在训练时未见过主题的例子上具有更好的外推能力。

Prefix Tuning
是一种在预训练语言模型(如 BERT 或 GPT-2)基础上进行高效微调的方法。与传统的微调方式不同,Prefix Tuning 只调整少量的前缀参数,而不修改原始模型的参数,从而在多个任务之间实现高效迁移。

  1. 前缀参数(Prefix Parameters)

* 前缀参数是在每层 Transformer 网络的输入前增加的一组可训练参数。
* 这些参数在模型进行任务特定的微调时可以独立更新,而无需调整原始模型的参数。
  1. 冻结预训练模型

* 在 Prefix Tuning 过程中,预训练模型的参数保持不变。
* 只训练前缀参数,从而实现高效的参数更新和迁移学习。
  1. 高效微调

* 相比于传统的微调,Prefix Tuning 只需调整少量的前缀参数,但能够取得接近或甚至超越全量微调的效果。
* 前缀参数可以独立于任务进行训练,适应多任务学习的需求。

总结

  1. 传统微调的局限性

    • 修改所有语言模型参数,需要为每个任务存储一个完整的模型副本。
  2. 前缀调优的优势

    • 保持语言模型参数不变,仅优化前缀参数。
    • 通过仅学习0.1%的参数,前缀调优在全数据集设置中获得了可比的性能,在低数据设置中表现优于微调,并在训练中未见的主题上表现更好。
  3. 应用场景

    • 前缀调优适用于自然语言生成任务,如表格到文本生成和摘要生成。

所有P-tuning都是基于优化连续的prefix或者prompt的思想。调整软提示优于先前对离散提示优化的工作。Prompt-tuning的工作表明全量微调和P*-tuning之间的性能差距随着模型的大小增大而消失。

5.Prompt Tuning输入增加可训练的嵌入向量提示