当前位置: 首页 > news >正文

显存不够?节约显存高效微调语言模型的五种方法及实验

显存不够?节约显存高效微调语言模型的五种方法及实验

随着大语言模型(LLMs)规模的飞速增长,传统全参数微调(Fine-tuning)对计算资源和存储的需求成为许多研究者和开发者的挑战。参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)方法应运而生,通过仅调整模型的部分参数,在保持性能的同时大幅降低资源消耗。本文将深入介绍五种热门的 PEFT 技术:部分参数微调(如网络中最顶部的n层)Adapter TuningPrompt TuningPrefix TuningLoRA,并展示它们的显存节省效果。


1. 部分参数微调(如仅微调顶层):简单而高效的起点

什么是部分参数微调?

部分参数微调仅微调模型的某些层(如最后一层),冻结其他层,基于高层次特征更贴近下游任务的假设。

优势

  • 实现简单,冻结部分层即可。
  • 显存节省显著,仅微调约 5%-10% 的参数。
  • 适合资源受限场景。

局限性

  • 性能可能不如全参数微调,尤其任务复杂时。
  • 冻结层可能限制适应性。

2. Adapter Tuning:模块化适配的强力工具

什么是 Adapter Tuning?

Adapter Tuning(Parameter-Efficient Transfer Learning for NLP)在 Transformer 每层后插入小型全连接网络(Adapter),仅微调这些模块,冻结原始参数。

工作原理

  • Adapter 包含降维-激活-升维的全连接层,插入 FFN 和注意力层后。结构如下:

在这里插入图片描述

优势

  • 模块化设计,支持多任务(每个任务一个 Adapter)。
  • 性能接近全参数微调,显存节省高。
  • 实现复杂度适中。

局限性

  • 推理时增加少量计算开销。
  • 需要调整超参数(如降维因子)。

实验效果

作者在论文中首先对比了全量微调,结果表明Adapter Tuning仅用2%,3%左右参数量微调,但效果却和全量微调类似。

在这里插入图片描述

作者还对比了部分参数微调(如最顶部的n层),结果表明Adapter Tuning使用更少参数便可比顶层微调效果更好更稳定。

在这里插入图片描述


3. Prompt Tuning:轻量化提示词的魔法

什么是 Prompt Tuning?

Prompt Tuning(The Power of Scale for Parameter-Efficient Prompt Tuning)通过在输入层添加可学习的提示词(Prompts)适配任务,仅优化这些提示词嵌入,冻结模型参数。

工作原理

  • 在输入序列前添加虚拟 token(如 [Prompt_1] [Prompt_2]),其嵌入可学习。
  • 冻结模型主体,仅优化 <0.1% 的参数。

优势

  • 显存需求极低。更新参数极少。
  • 实现简单,无需修改架构。
  • 模型参数量越大,效果越好

局限性

  • 性能可能低于全参数微调,模型小时更明显。
  • 提示词长度和初始化影响性能。

实验效果

作者利用不同容量的t5模型做了实验,当模型参数量比较大(10^9以上,1B容量的模型以上)时,Prompt Tuning和全量微调(图中的model tuning曲线)差距不大。
在这里插入图片描述

4. Prefix Tuning:深层语义的层级优化

什么是 Prefix Tuning?

Prefix Tuning(Prefix-Tuning: Optimizing Continuous Prompts for Generation)为 Transformer 每层注入可学习前缀参数,在自注意力中与输入交互。该方法可以看作Prompt Tuning的复杂版本。

工作原理

  • 为每层的键(Key)和值(Value)矩阵添加 10-20 个 token 的前缀。
  • 冻结模型主体,优化约 0.1%-1% 的参数。

优势

  • 适合生成任务,捕捉深层语义。
  • 显存节省显著。

局限性

  • 实现复杂度稍高,需为每层维护前缀。
  • 优化较为困难

5. LoRA:低秩适配的性能之选

什么是 LoRA?

LoRA(LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS)在原始transformer的架构基础上增加了两个可学习的小矩阵,通过微调这两个小矩阵近似transformer的权重更新。

工作原理

  • 在神经网络隐藏层,输出计算公式为 h = W x + b h = Wx + b h=Wx+b ,其中 W W W 是权重矩阵, x 是输入,b 是偏置。
  • 微调时,权重更新为 W ′ = W + Δ W W' = W + \Delta W W=W+ΔW ,传统方法需优化整个 Δ W \Delta W ΔW (维度 d o u t × d i n d_{out} \times d_{in} dout×din )。
  • LoRA 假设 Δ W \Delta W ΔW 是低秩的,用 Δ W = A ⋅ B \Delta W = A \cdot B ΔW=AB 近似,其中:
    • A 是维度 ( d o u t , r ) (d_{out}, r) (dout,r) 的矩阵;
    • B 是维度 ( r , d i n ) (r, d_{in}) (r,din) 的矩阵;
    • r 是秩,远小于 d o u t d_{out} dout d i n d_{in} din
  • 于是更新后的输出变为 h = ( W + A ⋅ B ) x + b h = (W + A \cdot B)x + b h=(W+AB)x+b 。冻结 W W W ,仅优化 A A A B B B ,参数量从 d o u t × d i n d_{out} \times d_{in} dout×din 减少到 ( d o u t × r ) + ( r × d i n ) (d_{out} \times r) + (r \times d_{in}) (dout×r)+(r×din)

优势

  • 性能接近全参数微调,显存节省高。
  • 模块化,支持多任务(每任务一套 A 和 B )。
  • 实现复杂度适中。

实验效果

作者利用gpt2,gpt3,RoBERTa 在多个数据集上进行了实验。下面给出gpt3的实验结果,可以很明显的看出比起其他对比方法(前面介绍过的几种方法),LoRA所用参数量较小,结果最好。

在这里插入图片描述


对比与应用场景

方法参数量比例适用任务理论显存节省复杂度
部分参数微调5%-10%分类、简单任务
Adapter Tuning1%-3%分类、生成
Prompt Tuning<0.1%分类、生成极高
Prefix Tuning0.1%-1%生成、复杂理解
LoRA0.01%-1%分类、生成、对话
  • 部分参数微调:适合快速原型开发。
  • Adapter Tuning 和 LoRA:通用性强,性能接近全参数微调。
  • Prompt Tuning 和 Prefix Tuning:显存节省最多,适合资源受限或生成任务。

实验部分

原始论文大多是在gpt2基础上开发的微调方法,我们以gpt2-small为基础模型进行实验,在文本分类任务上(使用yelp_review_full数据集,任务根据客户的评论预测其对商家的评分。输入是客户评论,输出是1分到5分,5个类别标签)对比全量微调,部分参数微调,Adapter Tuning,Prompt Tuning,LoRA这五种方法,看看他们的实际表现如何。

实验参数与代码中保持一致,可在colab/kaggle在线运行。

#!pip install --upgrade datasets
#!pip install --upgrade transformersimport torch
import torch.nn as nn
import gc
from tqdm import tqdm
from transformers import GPT2Model, GPT2Tokenizer, DataCollatorWithPadding
from datasets import load_dataset, DatasetDict
from torch.utils.data import DataLoader
from peft import LoraConfig, get_peft_model, TaskType# 打印显存占用
def print_memory_usage(step_name=""):allocated = torch.cuda.memory_allocated() / (1024 ** 2)reserved = torch.cuda.memory_reserved() / (1024 ** 2)print(f"{step_name} - 已分配: {allocated:.2f} MB, 已预留: {reserved:.2f} MB")# 释放显存
def release_memory():for obj in list(globals().items()):if isinstance(obj[1], (torch.Tensor, torch.nn.Module)):del globals()[obj[0]]gc.collect()torch.cuda.empty_cache()print_memory_usage("释放后")# 加载 yelp_review_full 数据集并取子集
dataset = load_dataset("yelp_review_full")
dataset = DatasetDict({"train": dataset["train"].select(range(1000)),  # 取前 5000 个训练样本"validation": dataset["test"].select(range(1000))  # 取前 1000 个验证样本
})train_dataset = dataset["train"]
val_dataset = dataset["validation"]# 加载分词器
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
tokenizer.pad_token = tokenizer.eos_tokendef tokenize_function(examples):return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=256)train_dataset = train_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
val_dataset = val_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
train_dataset.set_format(type="torch", columns=["input_ids", "label"])
val_dataset.set_format(type="torch", columns=["input_ids", "label"])# 数据加载器
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
train_dataloader = DataLoader(train_dataset, batch_size=32, collate_fn=data_collator, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=32, collate_fn=data_collator)# 基类:GPT-2 模型(用于分类)
class BaseGPT2(nn.Module):def __init__(self, model_name="gpt2"):super().__init__()self.gpt2 = GPT2Model.from_pretrained(model_name)self.tokenizer = tokenizerself.classifier = nn.Linear(self.gpt2.config.n_embd, 5)  # 5 分类头(Yelp)self.pad_token_id = self.tokenizer.pad_token_id  # 填充 token 的 IDself.freeze_model()def freeze_model(self):for param in self.gpt2.parameters():param.requires_grad = Falsedef forward(self, input_ids, labels=None):outputs = self.gpt2(input_ids=input_ids)last_hidden_state = outputs.last_hidden_state# 使用 input_ids 判断填充 tokennon_pad_mask = (input_ids != self.pad_token_id).long()  # 1 表示非填充,0 表示填充seq_lengths = torch.sum(non_pad_mask, dim=1) - 1  # 最后一个非填充 token 的索引batch_size = input_ids.size(0)cls_hidden = last_hidden_state[torch.arange(batch_size), seq_lengths, :]logits = self.classifier(cls_hidden)if labels is not None:loss_fn = nn.CrossEntropyLoss()loss = loss_fn(logits, labels)return type('Output', (), {'loss': loss, 'logits': logits})()return type('Output', (), {'logits': logits})()# 1. 全微调 (Full Fine-Tuning)
class FullFineTuning(BaseGPT2):def __init__(self, model_name="gpt2"):super().__init__(model_name)for param in self.gpt2.parameters():param.requires_grad = Truefor param in self.classifier.parameters():param.requires_grad = True# 2. 部分微调 (Partial Tuning)
class PartialTuning(BaseGPT2):def __init__(self, model_name="gpt2"):super().__init__(model_name)for i, layer in enumerate(self.gpt2.h):if i >= len(self.gpt2.h) - 2:for param in layer.parameters():param.requires_grad = Truefor param in self.classifier.parameters():param.requires_grad = Truedef forward(self, input_ids, labels=None):outputs = self.gpt2(input_ids=input_ids)last_hidden_state = outputs.last_hidden_statenon_pad_mask = (input_ids != self.pad_token_id).long()seq_lengths = torch.sum(non_pad_mask, dim=1) - 1batch_size = input_ids.size(0)cls_hidden = last_hidden_state[torch.arange(batch_size), seq_lengths, :]logits = self.classifier(cls_hidden)if labels is not None:loss_fn = nn.CrossEntropyLoss()loss = loss_fn(logits, labels)return type('Output', (), {'loss': loss, 'logits': logits})()return type('Output', (), {'logits': logits})()# 3. 适配器调优 (Adapter Tuning)
class AdapterTuning(BaseGPT2):def __init__(self, model_name="gpt2", adapter_dim=16):super().__init__(model_name)self.adapters = nn.ModuleList([nn.Sequential(nn.Linear(768, adapter_dim),nn.ReLU(),nn.Linear(adapter_dim, 768)) for _ in range(len(self.gpt2.h))])for adapter in self.adapters:for param in adapter.parameters():param.requires_grad = Truefor param in self.classifier.parameters():param.requires_grad = Truedef forward(self, input_ids, labels=None):outputs = self.gpt2(input_ids=input_ids, output_hidden_states=True)hidden_states = outputs.hidden_statesnew_hidden_states = hidden_states[0]for i, (layer_output, adapter) in enumerate(zip(hidden_states[1:], self.adapters)):new_hidden_states = layer_output + adapter(new_hidden_states)non_pad_mask = (input_ids != self.pad_token_id).long()seq_lengths = torch.sum(non_pad_mask, dim=1) - 1batch_size = input_ids.size(0)cls_hidden = new_hidden_states[torch.arange(batch_size), seq_lengths, :]logits = self.classifier(cls_hidden)if labels is not None:loss_fn = nn.CrossEntropyLoss()loss = loss_fn(logits, labels)return type('Output', (), {'loss': loss, 'logits': logits})()return type('Output', (), {'logits': logits})()# 4. 前缀调优 (Prompt Tuning)
class PromptTuning(BaseGPT2):def __init__(self, model_name="gpt2", num_prompts=5):super().__init__(model_name)self.prompt_embeds = nn.Parameter(torch.randn(num_prompts, 1, 768))self.prompt_embeds.requires_grad = Truefor param in self.classifier.parameters():param.requires_grad = Truedef forward(self, input_ids, labels=None):batch_size = input_ids.size(0)prompt_embeds = self.prompt_embeds.repeat(1, batch_size, 1).transpose(0, 1)input_embeds = self.gpt2.wte(input_ids) + self.gpt2.wpe(torch.arange(input_ids.size(1)).unsqueeze(0).expand(batch_size, -1).to(input_ids.device))input_embeds = torch.cat([prompt_embeds, input_embeds], dim=1)outputs = self.gpt2(inputs_embeds=input_embeds)last_hidden_state = outputs.last_hidden_statenon_pad_mask = (input_ids != self.pad_token_id).long()seq_lengths = torch.sum(non_pad_mask, dim=1) - 1 + self.prompt_embeds.size(0)  # 考虑提示嵌入的偏移cls_hidden = last_hidden_state[torch.arange(batch_size), seq_lengths, :]logits = self.classifier(cls_hidden)if labels is not None:loss_fn = nn.CrossEntropyLoss()loss = loss_fn(logits, labels)return type('Output', (), {'loss': loss, 'logits': logits})()return type('Output', (), {'logits': logits})()# 5. LoRA 调优 (使用 PEFT 框架)
class LoRATuning(BaseGPT2):def __init__(self, model_name="gpt2", r=4, lora_alpha=16):super().__init__(model_name)lora_config = LoraConfig(task_type=TaskType.SEQ_CLS,  # 序列分类任务r=r,lora_alpha=lora_alpha,target_modules=["c_attn"],lora_dropout=0.1)self.gpt2 = get_peft_model(self.gpt2, lora_config)for param in self.classifier.parameters():param.requires_grad = Truedef forward(self, input_ids, labels=None):outputs = self.gpt2(input_ids=input_ids)last_hidden_state = outputs.last_hidden_statenon_pad_mask = (input_ids != self.pad_token_id).long()seq_lengths = torch.sum(non_pad_mask, dim=1) - 1batch_size = input_ids.size(0)cls_hidden = last_hidden_state[torch.arange(batch_size), seq_lengths, :]logits = self.classifier(cls_hidden)if labels is not None:loss_fn = nn.CrossEntropyLoss()loss = loss_fn(logits, labels)return type('Output', (), {'loss': loss, 'logits': logits})()return type('Output', (), {'logits': logits})()# 训练和评估函数
def train_and_evaluate(model, num_epochs=2, lr=0.001):optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=lr)print_memory_usage("训练前")for epoch in range(num_epochs):model.train()total_loss = 0progress_bar = tqdm(train_dataloader, desc=f"Epoch {epoch+1}/{num_epochs}")for batch in progress_bar:input_ids = batch["input_ids"].cuda()labels = batch["labels"].cuda()outputs = model(input_ids, labels=labels)loss = outputs.lossloss.backward()optimizer.step()optimizer.zero_grad()total_loss += loss.item()progress_bar.set_postfix({'Batch Loss': f'{loss.item():.4f}'})avg_loss = total_loss / len(train_dataloader)print(f"{model.__class__.__name__} - Epoch {epoch}, Avg Train Loss: {avg_loss:.4f}")# print_memory_usage(f"Epoch {epoch} 训练后")print_memory_usage("训练后")# 评估model.eval()total_correct = 0total_samples = 0with torch.no_grad():for batch in val_dataloader:input_ids = batch["input_ids"].cuda()labels = batch["labels"].cuda()outputs = model(input_ids)logits = outputs.logitspreds = torch.argmax(logits, dim=1)total_correct += (preds == labels).sum().item()total_samples += labels.size(0)accuracy = total_correct / total_samplesprint(f"{model.__class__.__name__} - Validation Accuracy: {accuracy:.4f}")# 参数分析total_params = sum(p.numel() for p in model.parameters())trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)print(f"{model.__class__.__name__} - 总参数: {total_params}, 可训练: {trainable_params}, 节省: {(1 - trainable_params / total_params):.2%}")# 执行训练和评估
models = [FullFineTuning(),PartialTuning(),AdapterTuning(),PromptTuning(),LoRATuning()]for model in models:model = model.cuda()train_and_evaluate(model, num_epochs=10, lr=1e-4)release_memory()print_memory_usage("释放后")print('-'*50)

训练前 - 已分配: 487.48 MB, 已预留: 542.00 MB
Epoch 1/10: 100%|██████████| 157/157 [02:14<00:00,  1.16it/s, Batch Loss=1.1420]
FullFineTuning - Epoch 0, Avg Train Loss: 1.3543
Epoch 2/10: 100%|██████████| 157/157 [02:14<00:00,  1.16it/s, Batch Loss=0.9553]
FullFineTuning - Epoch 1, Avg Train Loss: 0.9291
Epoch 3/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.4834]
FullFineTuning - Epoch 2, Avg Train Loss: 0.7328
Epoch 4/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.1393]
FullFineTuning - Epoch 3, Avg Train Loss: 0.5441
Epoch 5/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.2724]
FullFineTuning - Epoch 4, Avg Train Loss: 0.3521
Epoch 6/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.1061]
FullFineTuning - Epoch 5, Avg Train Loss: 0.2362
Epoch 7/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.0142]
FullFineTuning - Epoch 6, Avg Train Loss: 0.1297
Epoch 8/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.1506]
FullFineTuning - Epoch 7, Avg Train Loss: 0.1315
Epoch 9/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.0248]
FullFineTuning - Epoch 8, Avg Train Loss: 0.1103
Epoch 10/10: 100%|██████████| 157/157 [02:15<00:00,  1.16it/s, Batch Loss=0.0099]
FullFineTuning - Epoch 9, Avg Train Loss: 0.0729
训练后 - 已分配: 1454.73 MB, 已预留: 11238.00 MB
FullFineTuning - Validation Accuracy: 0.5520
FullFineTuning - 总参数: 124443653, 可训练: 124443653, 节省: 0.00%
释放后 - 已分配: 503.73 MB, 已预留: 566.00 MB
--------------------------------------------------
训练前 - 已分配: 991.97 MB, 已预留: 1066.00 MB
Epoch 1/10: 100%|██████████| 157/157 [00:58<00:00,  2.69it/s, Batch Loss=1.5662]
PartialTuning - Epoch 0, Avg Train Loss: 1.6686
Epoch 2/10: 100%|██████████| 157/157 [00:58<00:00,  2.70it/s, Batch Loss=1.0963]
PartialTuning - Epoch 1, Avg Train Loss: 1.1661
Epoch 3/10: 100%|██████████| 157/157 [00:58<00:00,  2.70it/s, Batch Loss=1.1493]
PartialTuning - Epoch 2, Avg Train Loss: 1.0263
Epoch 4/10: 100%|██████████| 157/157 [00:58<00:00,  2.70it/s, Batch Loss=0.7306]
PartialTuning - Epoch 3, Avg Train Loss: 0.9494
Epoch 5/10: 100%|██████████| 157/157 [00:58<00:00,  2.70it/s, Batch Loss=1.4429]
PartialTuning - Epoch 4, Avg Train Loss: 0.8899
Epoch 6/10: 100%|██████████| 157/157 [00:58<00:00,  2.70it/s, Batch Loss=1.0357]
PartialTuning - Epoch 5, Avg Train Loss: 0.8131
Epoch 7/10: 100%|██████████| 157/157 [00:58<00:00,  2.69it/s, Batch Loss=1.0957]
PartialTuning - Epoch 6, Avg Train Loss: 0.7295
Epoch 8/10: 100%|██████████| 157/157 [00:58<00:00,  2.69it/s, Batch Loss=0.4231]
PartialTuning - Epoch 7, Avg Train Loss: 0.6223
Epoch 9/10: 100%|██████████| 157/157 [00:58<00:00,  2.69it/s, Batch Loss=0.4047]
PartialTuning - Epoch 8, Avg Train Loss: 0.5195
Epoch 10/10: 100%|██████████| 157/157 [00:58<00:00,  2.69it/s, Batch Loss=0.1439]
PartialTuning - Epoch 9, Avg Train Loss: 0.4187
训练后 - 已分配: 1100.95 MB, 已预留: 3702.00 MB
PartialTuning - Validation Accuracy: 0.5180
PartialTuning - 总参数: 124443653, 可训练: 14179589, 节省: 88.61%
释放后 - 已分配: 991.97 MB, 已预留: 1066.00 MB
--------------------------------------------------
训练前 - 已分配: 1480.62 MB, 已预留: 1588.00 MB
Epoch 1/10: 100%|██████████| 157/157 [00:45<00:00,  3.47it/s, Batch Loss=2.4384]
AdapterTuning - Epoch 0, Avg Train Loss: 1.7955
Epoch 2/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.4789]
AdapterTuning - Epoch 1, Avg Train Loss: 1.4427
Epoch 3/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.1268]
AdapterTuning - Epoch 2, Avg Train Loss: 1.3128
Epoch 4/10: 100%|██████████| 157/157 [00:45<00:00,  3.49it/s, Batch Loss=1.2316]
AdapterTuning - Epoch 3, Avg Train Loss: 1.2546
Epoch 5/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.5112]
AdapterTuning - Epoch 4, Avg Train Loss: 1.1966
Epoch 6/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=0.8671]
AdapterTuning - Epoch 5, Avg Train Loss: 1.1501
Epoch 7/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.3616]
AdapterTuning - Epoch 6, Avg Train Loss: 1.1308
Epoch 8/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.2581]
AdapterTuning - Epoch 7, Avg Train Loss: 1.0910
Epoch 9/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.7641]
AdapterTuning - Epoch 8, Avg Train Loss: 1.0813
Epoch 10/10: 100%|██████████| 157/157 [00:45<00:00,  3.48it/s, Batch Loss=1.5006]
AdapterTuning - Epoch 9, Avg Train Loss: 1.0672
训练后 - 已分配: 1483.02 MB, 已预留: 3298.00 MB
AdapterTuning - Validation Accuracy: 0.4990
AdapterTuning - 总参数: 124747973, 可训练: 308165, 节省: 99.75%
释放后 - 已分配: 1480.62 MB, 已预留: 1588.00 MB
--------------------------------------------------
训练前 - 已分配: 1968.12 MB, 已预留: 2108.00 MB
Epoch 1/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.7829]
PromptTuning - Epoch 0, Avg Train Loss: 3.3533
Epoch 2/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.8602]
PromptTuning - Epoch 1, Avg Train Loss: 1.7629
Epoch 3/10: 100%|██████████| 157/157 [01:46<00:00,  1.48it/s, Batch Loss=1.9476]
PromptTuning - Epoch 2, Avg Train Loss: 1.7062
Epoch 4/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.7505]
PromptTuning - Epoch 3, Avg Train Loss: 1.6368
Epoch 5/10: 100%|██████████| 157/157 [01:46<00:00,  1.48it/s, Batch Loss=1.6407]
PromptTuning - Epoch 4, Avg Train Loss: 1.5913
Epoch 6/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.5653]
PromptTuning - Epoch 5, Avg Train Loss: 1.5536
Epoch 7/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.6262]
PromptTuning - Epoch 6, Avg Train Loss: 1.5194
Epoch 8/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.5458]
PromptTuning - Epoch 7, Avg Train Loss: 1.4945
Epoch 9/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.4250]
PromptTuning - Epoch 8, Avg Train Loss: 1.4765
Epoch 10/10: 100%|██████████| 157/157 [01:45<00:00,  1.48it/s, Batch Loss=1.6108]
PromptTuning - Epoch 9, Avg Train Loss: 1.4535
训练后 - 已分配: 1968.25 MB, 已预留: 10128.00 MB
PromptTuning - Validation Accuracy: 0.3650
PromptTuning - 总参数: 124447493, 可训练: 7685, 节省: 99.99%
释放后 - 已分配: 1968.12 MB, 已预留: 2108.00 MB
--------------------------------------------------
训练前 - 已分配: 2456.16 MB, 已预留: 2650.00 MB
Epoch 1/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=1.5113]
LoRATuning - Epoch 0, Avg Train Loss: 2.3991
Epoch 2/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=1.2222]
LoRATuning - Epoch 1, Avg Train Loss: 1.4949
Epoch 3/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=0.9314]
LoRATuning - Epoch 2, Avg Train Loss: 1.1755
Epoch 4/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=0.9765]
LoRATuning - Epoch 3, Avg Train Loss: 1.0757
Epoch 5/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=0.8280]
LoRATuning - Epoch 4, Avg Train Loss: 1.0215
Epoch 6/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=1.0337]
LoRATuning - Epoch 5, Avg Train Loss: 0.9930
Epoch 7/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=1.0447]
LoRATuning - Epoch 6, Avg Train Loss: 0.9707
Epoch 8/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=1.2437]
LoRATuning - Epoch 7, Avg Train Loss: 0.9594
Epoch 9/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=1.0753]
LoRATuning - Epoch 8, Avg Train Loss: 0.9434
Epoch 10/10: 100%|██████████| 157/157 [01:42<00:00,  1.53it/s, Batch Loss=0.8284]
LoRATuning - Epoch 9, Avg Train Loss: 0.9332
训练后 - 已分配: 2457.40 MB, 已预留: 10700.00 MB
LoRATuning - Validation Accuracy: 0.5930
LoRATuning - 总参数: 124591109, 可训练: 151301, 节省: 99.88%
释放后 - 已分配: 2456.16 MB, 已预留: 2650.00 MB
--------------------------------------------------

实验分析

微调方法验证准确率训练参数量参数量节省比显存预留(MB)训练时间/epoch最终训练损失
FullFineTuning0.5520124.4M0%11238.00~2m15s0.0729
PartialTuning0.518014.2M88.61%3702.00~58s0.4187
AdapterTuning0.49900.31M99.75%3298.00~45s1.0672
PromptTuning0.36507.7K99.99%10128.00~1m45s1.4535
LoRATuning0.59300.15M99.88%10700.00~1m42s0.9332

尽管在论文中各个微调方法都报告了很好的表现,我们这次的实验结果情况更为复杂:

  • 显存占用显存预留(MB),相当于训练过程中的峰值显存利用情况)

尽管各个微调方法对比全量微调的可训练参数量大大节省了(基本不超过全量的1%),但是却增加了额外的复杂度(修改了原始模型输入/架构,实际使用需要配参数,选择框架等),最终在我们的实验中,PartialTuning和AdapterTuning的显存节约效果明显(峰值用了3g+,全量微调峰值是11g+),但PromptTuning和LoRA的显存节约没有达到理想水平(峰值用了10g,全量微调是11g+)

  • 实验效果(验证准确率)

由于新的微调方法都要进行细致的配置和验证,我们只是简单是实验了一下,并不能保证和全量微调FullFineTuning一样的效果。我们这次实验中,lora的效果高于全量微调,其他方法的准确率都低于全量微调。其中PromptTuning的效果最低(代码实现不够好,优化困难,可以用框架)

  • 参数节省量

这点和论文一致,确实大大节省了。

总结

本文总结多种高效微调语言模型的方法,并通过实验表明了各个方法的显存节约情况,实验中也出现了实际显存占用和理论节约量不一致的情况,本文也提供了实验代码可供进一步实验。

相关文章:

  • c++树状数组模板Fenwick (Binary Indexed) Trees
  • python3GUI--运维系统大屏 By:PyQt5(附下载地址)
  • 11.SPI和W25Q64
  • Gemini 的超长回复
  • CSS相关知识
  • 6个月Python学习计划 Day 4
  • 前端流行框架Vue3教程:26. 异步组件
  • 【25软考网工】第八章 (1)交换机基础
  • springboot 控制层调用业务逻辑层,注入报错,无法自动装配 解决办法
  • 在机器学习中,L2正则化为什么能够缓过拟合?为何正则化等机制能够使一个“过度拟合训练集”的模型展现出更优的泛化性能?正则化
  • c++总结-04-智能指针
  • 奈雪小程序任务脚本
  • Python与C++中浮点数的精度与计算误差(易忽略易错)
  • C++11(2):
  • 历年华东师范大学保研上机真题
  • 计算机病毒的发展历程及其分类
  • 审计报告附注救星!实现Word表格纵向求和+横向计算及其对应的智能校验
  • JavaScript 中的 structuredClone() 如何彻底改变你的对象复制方式
  • 制造业主要管理哪些主数据范围
  • 智能办公系统 — 审批管理模块 · 开发日志
  • 网站建设相关的网站/seo链接优化建议
  • php做旅游网站/java培训
  • 做趣味图形的网站/活动营销案例100例
  • 送网站建设/网络营销策划书3000字
  • 加工厂做网站/重庆森林影评
  • 网站建设图标图片/著名营销策划公司