微调]大型模型微调理论与方法、Pytorch&HuggingFace 微调实践
Fine-Tuning: 大模型微调理论及方法, Pytorch&HuggingFace微调实战
文章目录
- Fine-Tuning: 大模型微调理论及方法, Pytorch&HuggingFace微调实战
- 1. 什么是微调
- (1) 为什么要进行微调
- (2) 经典简单例子:情感分析
- 任务
- 背景
- 微调
- (3) 为什么微调work, 理论解释下
- 2. 详细介绍微调的流程
- (1) 准备数据, 预处理
- (2) 微调策略
- **前三种都差不多的逻辑, 古早**
- 1. 冻结, 逐层微调
- 2. 部分参数微调
- 3. 全参数微调
- 4. LoRA(低秩适应)
- 5. Prompt Tuning
- 6. RLHF(基于人类反馈的强化学习)
- 7. Prefix Tuning
- 8. Adapter微调
- (3) 设置微调超参数
- (4) 训练, 评估
- 3. 具体怎么做
- 常用的微调框架
- HuggingFace版
- Pytorch版
- Pytorch vs HuggingFace
- 易用性:
- 灵活性
- 性能
1. 什么是微调
大模型微调是指在预训练的大型模型基础上,使用特定数据集进行进一步训练,以适应特定任务或领域。
(1) 为什么要进行微调
- 大模型虽然知识丰富(由于其极大批量的预训练任务),但在特定领域可能不够准确。微调能让模型更好地理解特定任务。
- 相比从头开始训练一个新模型,微调节省了大量时间和计算资源(站在前人的肩膀上), 只需少量的数据就能有效提升模型在特定领域的性能。
(2) 经典简单例子:情感分析
任务
训练一个情感分析模型
背景
硬件很烂, 不可能从头训练一个情感分析大模型
但已经有预训练的语言模型比如BERT,已经在大量文本上进行过训练(这叫预训练)。
微调
BERT本身没有直接判断情感的能力, 但由于其在大量文本上进行的预训练任务, 其具有很多自然语言领域的 知识(预训练的权重), 通过少量的情感分析数据, 和合适的微调策略, 就能低成本的(数据, 算力)来微调出一个能进行情感分析的BERT
(3) 为什么微调work, 理论解释下
- 迁移学习: 深度学习模型有分层学习特征的特点, 底层学习通用特征, 高层学习任务相关特征, 将通用特征的知识迁移到相关的特定领域, 合理
- 统计学: 预训练可以看作为参数分布的先验估计, 微调就是在已有先验知识的基础上结合新数据
2. 详细介绍微调的流程
(1) 准备数据, 预处理
首先收集数据, 分成训练验证测试, 老生常谈, 都2024年了就不多说了
预处理: 每种大模型都有特定的输入格式, 要把原始数据转换成预训练大模型认识的数据输入
(2) 微调策略
策略有很多, 也有很多新冒出来的策略, 说一些常见的
前三种都差不多的逻辑, 古早
1. 冻结, 逐层微调
冻结就是权重固定, 不会再反向传播调整了
在这种策略中,模型的一部分参数被冻结,仅对特定层进行微调。逐层解冻的方法允许从顶层开始逐步释放冻结状态,以平衡预训练知识与新任务学习之间的关系.
2. 部分参数微调
和逐层微调本质上类似, 仅选择性地更新模型中的某些权重,通常是顶层或最后几层,而保持底层的大部分权重不变(冻结).
3. 全参数微调
全部参数都会反向传播, 这种方法资源消耗很大, 对数据要求也很高, 而且容易导致灾难性遗忘
灾难性遗忘(Catastrophic Forgetting): 微调模型在学习新任务时,突然或彻底忘记其预训练所学到的知识
4. LoRA(低秩适应)
LoRA通过在模型的每一层引入可训练的低秩矩阵来进行微调, 自适应的调整部分参数.
5. Prompt Tuning
轻量级的微调方法,不改变模型的主参数(全部冻结),通过为特定任务设计可学习的提示(prompt)来引导模型生成期望的输出。
6. RLHF(基于人类反馈的强化学习)
利用人类的反馈来纠正模型, 生成符合期望的结果
7. Prefix Tuning
在输入的前面前拼一些可训练的参数,使得模型在处理任务时能够更好地理解输入意图
8. Adapter微调
模型层之间插入小型可训练模块,这些模块可以适应新任务,而不影响原始模型的参数
(3) 设置微调超参数
设置/调整 学习率, BatchSize等参数, 让模型能收敛和防止拟合不好, 后面介绍
(4) 训练, 评估
用现成的框架训练, 验证, 测试, 后面介绍
3. 具体怎么做
由于深度学习技术的不断成熟, 各种稳定易用的框架逐渐出现, 让微调过程仅需要少许代码就能实现, 下面看看例子
常用的微调框架
-
Hugging Face Transformer
-
Pytorch
HuggingFace版
用HuggingFace对GraphCodeBERT进行微调
import torch
from transformers import RobertaTokenizer, RobertaForSequenceClassification, Trainer, TrainingArguments
# 加载预训练模型和tokenizer
tokenizer = RobertaTokenizer.from_pretrained("microsoft/graphcodebert-base")
model = RobertaForSequenceClassification.from_pretrained("microsoft/graphcodebert-base")
# 准备数据, 数据的预处理一般比较复杂
train_data = [...] # 训练数据
train_encodings = tokenizer(train_data, truncation=True, padding=True)
# 定义训练参数
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=16,
save_steps=10_000,
save_total_limit=2,
)
# 创建Trainer实例并开始训练
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_encodings,
)
trainer.train()
实际肯定不止这么简单, 细节比较多, 比如数据的预处理, 和自定义的训练和评估.
由于各种下游任务的多样性, 不同任务的数据/标签差异非常大,这里没办法根据每种任务详细介绍预处理流程, 故在此略过. 我们一般需要自己写很多数据预处理的代码, 构造数据, 使得预训练模型能够接受数据输入.
再或是训练和评估, 由于使用者对模型的需求不同, 训练和评估过程也不一定相同, 自定义的流程往往需要写一些代码, 但是基本的训练和评估流程是封装好的. 代码中给出来了
一些基本的东西在HuggingFace中都有稳定的接口, 比如微调的策略, 参数定义, 基本的训练评估流程, 都是即插即用的
Pytorch版
用pytorch对BERT 使用RLHF策略 在情感分析任务上进行微调
import torch
import torch.nn as nn
from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from torch.utils.data import DataLoader, Dataset
# 假设我们有一个简单的数据集
class CustomDataset(Dataset):
def __init__(self, texts, labels):
self.texts = texts
self.labels = labels
self.tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
encoding = self.tokenizer(self.texts[idx], padding='max_length', truncation=True, return_tensors='pt', max_length=128)
return {
'input_ids': encoding['input_ids'].flatten(),
'attention_mask': encoding['attention_mask'].flatten(),
'labels': torch.tensor(self.labels[idx], dtype=torch.long)
}
# 1. 监督微调(SFT)
def supervised_fine_tuning(model, dataloader):
model.train()
optimizer = AdamW(model.parameters(), lr=5e-5)
for epoch in range(3): # 假设训练3个epoch
for batch in dataloader:
optimizer.zero_grad()
input_ids = batch['input_ids']
attention_mask = batch['attention_mask']
labels = batch['labels']
outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()
print(f"Loss: {loss.item()}")
# 2. 奖励模型训练
def train_reward_model(model, reward_data):
# 假设reward_data包含文本和对应的奖励分数
model.train()
optimizer = AdamW(model.parameters(), lr=5e-5)
for epoch in range(3): # 假设训练3个epoch
for text, reward in reward_data:
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True)
optimizer.zero_grad()
outputs = model(**inputs)
reward_loss = nn.MSELoss()(outputs.logits.squeeze(), torch.tensor(reward, dtype=torch.float32))
reward_loss.backward()
optimizer.step()
print(f"Reward Loss: {reward_loss.item()}")
# 3. RLHF训练
def rl_training(actor_model, critic_model, dataloader):
actor_model.train()
critic_model.eval() # 奖励模型在评估模式
for epoch in range(3): # 假设训练3个epoch
for batch in dataloader:
input_ids = batch['input_ids']
attention_mask = batch['attention_mask']
# 使用actor模型生成输出
actor_outputs = actor_model(input_ids=input_ids, attention_mask=attention_mask)
# 使用critic模型评估输出的奖励
with torch.no_grad():
critic_outputs = critic_model(input_ids=input_ids, attention_mask=attention_mask)
# 根据奖励调整actor模型的参数(PPO等算法可在此实现)
# 此处省略具体的PPO实现,需根据具体需求添加
# 示例数据集和模型初始化
texts = ["I love this!", "This is terrible."]
labels = [1, 0] # 假设1为正面,0为负面
dataset = CustomDataset(texts, labels)
dataloader = DataLoader(dataset, batch_size=2)
model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)
# 执行微调流程
supervised_fine_tuning(model, dataloader)
# 假设我们有一些奖励数据用于训练奖励模型
reward_data = [("I love this!", 1.0), ("This is terrible.", 0.0)]
train_reward_model(model, reward_data)
# 最后,进行RLHF训练(需实现具体的PPO算法)
rl_training(model, model) # 此处使用同一模型作为示例
Pytorch vs HuggingFace
易用性:
HuggingFace的API非常简洁, 并且有丰富的涵盖多个领域的预训练模型库, 集成了多种常用的微调策略, 比如上面提到的LoRA等, 还有活跃的社区和丰富的文档
Pytorch缺乏高层封装, 在比如数据处理, 模型保存上需要用户手动实现更多的功能, 学习曲线陡峭
灵活性
HuggingFace灵活性不如Pytorch, 在高度自定义场景下, Pytorch表现更佳
性能
在一些情况下, Pytorch在计算上设计了专门的优化, HuggingFace的高层API不如Pytorch的性能优化高效
上一篇: C++ 学习笔记 (55)
推荐阅读
-
微调]大型模型微调理论与方法、Pytorch&HuggingFace 微调实践
-
【摩尔线程+Colossal-AI强强联手】MusaBert登上CLUE榜单TOP10:技术细节揭秘 - 技术实力:摩尔线程凭借"软硬兼备"的技术底蕴,让MusaBert得以从底层优化到顶层。其内置多功能GPU配备AI加速和并行计算模块,提供了全面的AI与科学计算支持,为AI推理和低资源条件下的大模型训练等场景带来了高效、经济且环保的算力。 - 算法层面亮点:依托Colossal-AI AI大模型开发系统,MusaBert在训练过程中展现出了卓越的并行性能与易用性,特别在预处理阶段对DataLoader进行了优化,适应低资源环境高效处理海量数据。同时,通过精细的建模优化、领域内数据增强以及Adan优化器等手段,挖掘和展示了预训练语言模型出色的语义理解潜力。基于MusaBert,摩尔线程自主研发的MusaSim通过对比学习方法微调,结合百万对标注数据,MusaSim在多个任务如语义相似度、意图识别和情绪分析中均表现出色。 - 数据资源丰富:MusaBert除了自家高质量语义相似数据外,还融合了悟道开源200GB数据、CLUE社区80GB数据,以及浪潮公司提供的1TB高质量数据,保证模型即便在较小规模下仍具备良好性能。 当前,MusaBert已成功应用于摩尔线程的智能客服与数字人项目,并广泛服务于语义相似度、情绪识别、阅读理解与声韵识别等领域。为了降低大模型开发和应用难度,MusaBert及其相关高质量模型代码已在Colossal-AI仓库开源,可快速训练优质中文BERT模型。同时,通过摩尔线程与潞晨科技的深度合作,仅需一张多功能GPU单卡便能高效训练MusaBert或更大规模的GPT2模型,显著降低预训练成本,进一步推动双方在低资源大模型训练领域的共享目标。 MusaBert荣登CLUE榜单TOP10,象征着摩尔线程与潞晨科技联合研发团队在中文预训练研究领域的领先地位。展望未来,双方将携手探索更大规模的自然语言模型研究,充分运用上游数据资源,产出更为强大的模型并开源。持续强化在摩尔线程多功能GPU上的大模型训练能力,特别是在消费级显卡等低资源环境下,致力于降低使用大模型训练的门槛与成本,推动人工智能更加普惠。而潞晨科技作为重要合作伙伴,将继续发挥关键作用。
-
微调大型模型的几种常用方法
-
Prompt设计与大语言模型的微调方法