深度学习:基于MindSpore的极简风大模型微调

news2024/12/15 9:46:29

什么是PEFT?What is PEFT?

PEFT(Parameter Efficient Fine-Tuning)是一系列让大规模预训练模型高效适应于新任务或新数据集的技术。

PEFT在保持大部分模型权重冻结,只修改或添加一小部份参数。这种方法极大得减少了计算量和存储开销,但保证了大模型在多个任务上的复用性。

为什么需要PEFT?Why do we need PEFT?

扩展性挑战

大规模预训练模型如GPT、BERT或ViT拥有大量参数。为每个具体任务全参微调这些模型不仅耗费大量计算量,同时需要巨大的存储资源,这些资源往往难以承担。

提升迁移学习效率

PEFT很好地利用了预训练模型在通用任务上的能力,同时提升了模型在具体任务上的表现。同时PEFT能减少过拟合并提供更好的通用型。

PEFT如何工作?How does PEFT work?

1. 冻结大部人预训练模型的参数

2. 修改或添加小部份参数

3. 模型训练时,只修改小部份参数即可

PEFT方法分类

Additive PEFT(加性微调):在模型特定位置添加可学习的模块或参数。如:Adapters、Prompt-Tuning 

Selective PEFT(选择性微调):在微调过程只更新模型中的一部份参数,保持其余参数固定。如:BitFit、HyperNetworks

Reparameterization PEFT(重参数化微调):构建原始模型参数的低秩表示,在训练过程中增加可学习参数以实现高效微调。如:LoRA (Low-Rank Adaptation)、Prefix-Tuning

Prefix Tuning

Prefix Tuning在每个Transformer Block层加入Prefix Learnable Parameter(Embedding层),这些前缀作为特定任务的上下文,预训练模型的参数保持冻结。相当于在seq_len维度中,加上特定个数的token。

class LoRA(nn.Module):
    def __init__(self, original_dim, low_rank):
        super().__init__()
        self.low_rank_A = nn.Parameter(torch.randn(original_dim, low_rank))  # Low-rank matrix A
        self.low_rank_B = nn.Parameter(torch.randn(low_rank, original_dim))  # Low-rank matrix B

    def forward(self, x, original_weight):
        # x: Input tensor [batch_size, seq_len, original_dim]
        # original_weight: The frozen weight matrix [original_dim, original_dim]
        
        # LoRA weight update
        lora_update = torch.matmul(self.low_rank_A, self.low_rank_B)  # [original_dim, original_dim]
        
        # Combined weight: frozen + LoRA update
        adapted_weight = original_weight + lora_update

        # Forward pass
        output = torch.matmul(x, adapted_weight)  # [batch_size, seq_len, original_dim]
        return output

但Prefix Tuning在需要更深层次模型调整的任务上表现较差。

Adapters 

Adapters是较小的,可训练的,插入在预训练模型层之间的模块。每个Adapter由一个下采样模块,一个非线性激活和一个上采样模块组层。预训练模型参数保持冻结,adapters用于捕捉具体任务的知识。 

基于MindSpore的模型微调

环境需求:2.3.0-cann 8.0.rc1-py 3.9-euler 2.10.7-aarch64-snt9b-20240525100222-259922e

Prefix-Tuning 

 安装mindNLP

pip install mindnlp

加载依赖

# 模块导入 and 参数初始化
import os
import mindspore
from mindnlp.transformers import AutoModelForSeq2SeqLM
# peft相关依赖
from mindnlp.peft import get_peft_config, get_peft_model, get_peft_model_state_dict, PrefixTuningConfig, TaskType

from mindnlp.dataset import load_dataset
from mindnlp.core import ops

from mindnlp.transformers import AutoTokenizer
from mindnlp.common.optimization import get_linear_schedule_with_warmup
from tqdm import tqdm

# 演示模型 t5-small
model_name_or_path = "t5-small"
tokenizer_name_or_path = "t5-small"
checkpoint_name = "financial_sentiment_analysis_prefix_tuning_v1.ckpt"

max_length = 128
lr = 1e-2
num_epochs = 5
batch_size = 8

 通过mindnlp.peft库加载模型并进行prefix配置

# Prefix-Tuning参数设置以及配置模型
peft_config = PrefixTuningConfig(task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, num_virtual_tokens=20)
# 加载预训练模型
model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
# 加载加入prefix后的模型
model = get_peft_model(model, peft_config)

model.print_trainable_parameters()

加载、预处理数据集

# 微调 t5 for 金融情感分析
# input: 金融短句
# output: 情感类别
# 由于华为云无法连接huggingface,因此需要先本地下载,再上传至华为云
mindspore.dataset.config.set_seed(123)
# loading dataset
dataset = load_dataset("financial_phrasebank", cache_dir='/home/ma-user/work/financial_phrasebank/')

train_dataset, validation_dataset = dataset.shuffle(64).split([0.9, 0.1])

classes = dataset.source.ds.features["label"].names
# 将标签号映射为文本
def add_text_label(sentence, label):
    return sentence, label, classes[label.item()]
# 输入为两列,输出为三列
train_dataset = train_dataset.map(add_text_label, ['sentence', 'label'], ['sentence', 'label', 'text_label'])
validation_dataset = validation_dataset.map(add_text_label, ['sentence', 'label'], ['sentence', 'label', 'text_label'])

# 加载t5模型的分词器
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)

# tokenize 输入和text_label
import numpy as np
from mindnlp.dataset import BaseMapFunction
from threading import Lock
# 线程锁?
lock = Lock()

class MapFunc(BaseMapFunction):
    def __call__(self, sentence, label, text_label):
        lock.acquire()
        model_inputs = tokenizer(sentence, max_length=max_length, padding="max_length", truncation=True)
        labels = tokenizer(text_label, max_length=2, padding="max_length", truncation=True)
        lock.release()
        # 提取 labels 中的 input_ids
        # 这些 ID 实际上是模型词汇表中相应单词或子词单元的位置索引。
        # 因此,input_ids 是一个整数列表,代表了输入文本序列经过分词和编码后的结果,它可以直接作为模型的输入。
        labels = labels['input_ids']
        # 将 labels 中的填充标记替换为 -100,这是常见的做法,用于告诉损失函数忽略这些位置。
        labels = np.where(np.equal(labels, tokenizer.pad_token_id), -100, lables)
        return model_inputs['input_ids'], model_inputs['attention_mask'], labels

    
def get_dataset(dataset, tokenizer, shuffle=True):
    input_colums=['sentence', 'label', 'text_label']
    output_columns=['input_ids', 'attention_mask', 'labels']
    dataset = dataset.map(MapFunc(input_colums, output_columns),
                          input_colums, output_columns)
    if shuffle:
        dataset = dataset.shuffle(64)
    dataset = dataset.batch(batch_size)
    return dataset

train_dataset = get_dataset(train_dataset, tokenizer)
eval_dataset = get_dataset(validation_dataset, tokenizer, shuffle=False)

进行微调训练 

# 初始化优化器和学习策略
from mindnlp.core import optim

optimizer = optim.AdamW(model.trainable_params(), lr=lr)

# 动态学习率
lr_scheduler = get_linear_schedule_with_warmup(
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=(len(train_dataset) * num_epochs),
)

from mindnlp.core import value_and_grad

def forward_fn(**batch):
    outputs = model(**batch)
    loss = outputs.loss
    return loss

grad_fn = value_and_grad(forward_fn, model.trainable_params())

for epoch in range(num_epochs):
    model.set_train()
    total_loss = 0
    train_total_size = train_dataset.get_dataset_size()
    
    for step, batch in enumerate(tqdm(train_dataset.create_dict_iterator(), total=train_total_size)):
        optimizer.zero_grad()
        loss = grad_fn(**batch)
        optimizer.step()
        total_loss += loss.float()
        lr_scheduler.step()
    
    model.set_train(False)
    eval_loss = 0
    eval_preds = []
    eval_total_size = eval_dataset.get_dataset_size()
    for step, batch in enumerate(tqdm(eval_dataset.create_dict_iterator(), total=eval_total_size)):
        with mindspore._no_grad():
            outputs = model(**batch)
        loss = outputs.loss
        eval_loss += loss.float()
        eval_preds.extend(
            tokenizer.batch_decode(ops.argmax(outputs.logits, -1).asnumpy(), skip_special_tokens=True)
        )
    # 验证集loss
    eval_epoch_loss = eval_loss / len(eval_dataset)
    eval_ppl = ops.exp(eval_epoch_loss)
    # 测试集loss
    train_epoch_loss = total_loss / len(train_dataset)
    train_ppl = ops.exp(train_epoch_loss)
    print(f"{epoch=}: {train_ppl=} {train_epoch_loss=} {eval_ppl=} {eval_epoch_loss=}")

模型评估

# 模型评估
correct = 0
total = 0

ground_truth = []

correct = 0
total = 0

ground_truth = []

for pred, data in zip(eval_preds, validation_dataset.create_dict_iterator(output_numpy=True)):
    true = str(data['text_label'])
    ground_truth.append(true)
    if pred.strip() == true.strip():
        correct += 1
    total += 1
accuracy = correct / total * 100
print(f"{accuracy=} % on the evaluation dataset")
print(f"{eval_preds[:10]=}")
print(f"{ground_truth[:10]=}")

模型保存

# 模型保存
# saving model
peft_model_id = f"{model_name_or_path}_{peft_config.peft_type}_{peft_config.task_type}"
model.save_pretrained(peft_model_id)

加载模型进行推理

# 加载模型并推理
from mindnlp.peft import PeftModel, PeftConfig

config = PeftConfig.from_pretrained(peft_model_id)
model = AutoModelForSeq2SeqLM.from_pretrained(config.base_model_name_or_path)
model = PeftModel.from_pretrained(model, peft_model_id)

model.set_train(False)

example = next(validation_dataset.create_dict_iterator(output_numpy=True))
print("input", example["sentence"])
print(example["text_label"])
inputs = tokenizer(example['text_label'], return_tensors="ms")

with mindspore._no_grad():
    outputs = model.generate(input_ids=inputs["input_ids"], max_new_tokens=10)
    print(tokenizer.batch_decode(outputs.numpy(), skip_special_tokens=True))

 BitFit

BitFit需要冻结除Bias外的所有参数,只训练Bias参数。

for n, p in model.named_parameters():
    if "bias" not in n:
        p.requires_grad = False
    else:
        p.requires_grad = True

其余数据预处理代码和训练代码与上述相同。 

LoRA

LoRA(Low Rank Adaptation)专注于学习一个低秩矩阵。通过在冻结的预训练权重中添加可学习的低秩矩阵。在前向传递过程中,冻结的权重和新的低秩矩阵参与计算。

低秩矩阵指的是相较于原矩阵,秩更低的矩阵。加入一个矩阵的形状为m x n,矩阵的秩最多为min(m, n),低秩矩阵的秩数远远小于原本的m和n。

LoRA微调不更新原本m x n的权重矩阵,转而更新更小的低秩矩阵A(m, r), B(r, n)。假设W0为512x512,低秩矩阵的r则可以为16,这样需要更新的数据只需要(512x16+16x512)=16384,相较于原来的512x512=262144,少了93.75%。

LoRA实现的基本思路代码

class LoRA(nn.Module):
    def __init__(self, original_dim, low_rank):
        super().__init__()
        self.low_rank_A = nn.Parameter(torch.randn(original_dim, low_rank))  # Low-rank matrix A
        self.low_rank_B = nn.Parameter(torch.randn(low_rank, original_dim))  # Low-rank matrix B

    def forward(self, x, original_weight):
        # x: Input tensor [batch_size, seq_len, original_dim]
        # original_weight: The frozen weight matrix [original_dim, original_dim]
        
        # LoRA weight update
        lora_update = torch.matmul(self.low_rank_A, self.low_rank_B)  # [original_dim, original_dim]
        
        # Combined weight: frozen + LoRA update
        adapted_weight = original_weight + lora_update

        # Forward pass
        output = torch.matmul(x, adapted_weight)  # [batch_size, seq_len, original_dim]
        return output

LoRA的MindSpore实现 

# creating model
# r 控制适应层的秩,lora_alpha 是缩放因子,而 lora_dropout 定义了在训练期间应用于 LoRA 参数的 dropout 率。
# 缩放因子用于控制低秩矩阵对模型参数更新的影响程度。
peft_config = LoraConfig(task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1)

model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

其余数据预处理代码和训练代码与上述相同。 

更多内容可以参考mindspore的官方视频:

【第二课】昇腾+MindSpore+MindSpore NLP:极简风的大模型微调实战_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2259868.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【一本通】最小圈

【一本通】最小圈 💐The Begin💐点点关注,收藏不迷路💐 对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除以k,现要…

ansible自动化运维(四)jinjia2模板

Jinjia2模板 前面说到playbook组成的时候,有介绍到template模块,而template模块对模板文件进行渲染时,使用的就是jinja2模板引擎,jinja2本身就是基于python的模板引擎,所以下面先来了解一下jinjia2模板的一些用法 基…

【USB-HID】“自动化键盘“

这里写目录标题 【USB-HID】"自动化键盘"1. 前言2. 框架3. 实现3.1 模拟键盘按键输入 【USB-HID】“自动化键盘” 1. 前言 最近从朋友那了解了一种"自动化键盘",能够通过上位机录制按键脚本,然后执行脚本,实现物理键盘…

使用ECK 快速部署 Elasticsearch 集群 + Kibana

部署 ECK [2.12] 安装说明 ElasticCloudonKubernetes(ECK)是一个 Elasticsearch Operator,但远不止于此。ECK 使用 Kubernetes Operator 模式构建而成,需要安装在您的 Kubernetes 集群内; 借助 Elastic Cloud on Kubernetes (ECK)&#xff0…

ruoyi Cannot find module ‘@/views/system/user/index‘

Cannot find module /views/system/user/index 删除node_module 后打包成功

从开始实现扩散概率模型 PyTorch 实现

目录 一、说明 二、从头开始实施 三、线性噪声调度器 四、时间嵌入 五、下层DownBlock类块 六、中间midBlock类块 七、UpBlock上层类块 八、UNet 架构 九、训练 十、采样 十一、配置(Default.yaml) 十二、数据集 (MNIST) keyword: Diffusion…

航空航天总线协议分析ARINC429

ARINC429是商用飞机和运输机运用最广泛的总线之一,ARINC是美国航空无线电公司(Aeronautical Radio INC.)的缩写,ARINC429总线协议是美国航空电子工程委员会于1977年7月提出发表并获批准使用,它的规范全称是数字式信息传输系统(Digital Inform…

Unity UGUI图片循环列表插件

效果展示: 下载链接:https://gf.bilibili.com/item/detail/1111843026 概述: LoopListView2 是一个与 UGUI ScrollRect 相同的游戏对象的组件。它可以帮助 UGUI ScrollRect 以高效率和节省内存的方式支持任意数量的项目。 对于具有10,000个…

【经验分享】私有云运维的知识点

最近忙于备考没关注,有次点进某小黄鱼发现首页出现了我的笔记还被人收费了 虽然我也卖了一些资源,但我以交流、交换为主,笔记都是免费给别人看的 由于当时刚刚接触写的并不成熟,为了避免更多人花没必要的钱,所以决定公…

解决MAC装win系统投屏失败问题(AMD显卡)

一、问题描述 电脑接上HDMI线后,电脑上能显示有外部显示器接入,但是外接显示器无投屏画面 二、已测试的方法 1 更改电脑分辨,结果无效 2 删除BootCamp,结果无效 3更新电脑系统,结果无效 4 在设备管理器中&#…

huggingface NLP -Transformers库

1 特点 1.1 易于使用:下载、加载和使用最先进的NLP模型进行推理只需两行代码即可完成。 1.2 灵活:所有型号的核心都是简单的PyTorch nn.Module 或者 TensorFlow tf.kears.Model,可以像它们各自的机器学习(ML)框架中的…

1. 机器学习基本知识(2)——机器学习分类

1.4 机器学习分类 1.4.1 训练监督 1. 监督学习:已对训练数据完成标记 分类:根据数据及其分类信息来进行训练,使模型能够对新的数据进行分类 回归:给出一组特征值来预测目标数值 2. 无监督学习:没有对训练数据进行任…

[C#]使用winform部署ddddocr的onnx模型进行验证码识别文字识别文字检测

【算法介绍】 ddddocr是一个强大的Python OCR(光学字符识别)库,特别适用于验证码识别。它利用深度学习技术,如卷积神经网络(CNN)和循环神经网络(RNN),对图像中的文字进行…

day10 电商系统后台API——接口测试(使用postman)

【没有所谓的运气🍬,只有绝对的努力✊】 目录 实战项目简介: 1、用户管理(8个) 1.1 登录 1.2 获取用户数据列表 1.3 创建用户 1.4 修改用户状态 1.5 根据id查询用户 1.6 修改用户信息 1.7 删除单个用户 1.8 …

云服务器搭建lamp的wordpress

Ubuntu系统搭建过程目录 一、检查环境1.1 检查是否安装Apache1.2 检查是否安装Mysql1.3 检查是否安装PHP 二、安装Apache截图 三、安装Mysql3.1 安全安装配置3.2 修改权限和密码3.3 重启MySQL服务 四、安装PHP4.1 测试截图 五、下载并安装wordpress以及配置5.1 下载并解压移动5…

C#速成(GID+图形编程)

常用类 类说明Brush填充图形形状,画刷GraphicsGDI绘图画面,无法继承Pen定义绘制的对象直线等(颜色,粗细)Font定义文本格式(字体,字号) 常用结构 结构说明Color颜色Point在平面中定义点Rectan…

babeltrace与CTF相关学习笔记-4

babeltrace与CTF相关学习笔记-4 写在前面metadata_string 重头开始定位,操作meta的位置bt_ctf_trace_get_metadata_string stream部分内存的问题 写在前面 正在并行做几件事。 在编译过程中,突然想到,还是再详细研究一下之前的例程。 一是详…

多旋翼无人机 :桨叶设计—跷跷板结构

多旋翼无人机 :桨叶设计——跷跷板结构 前言跷跷板结构 前言 2024年11月,大疆发布了最新的农业无人机T70和T100。其中T70不同于以往的机型,在桨夹处采用了翘翘板结构,大疆将其命名为“挥舞桨叶”。 T70 无人机如下 放大其中螺旋…

低通滤波器,高通滤波器,公式

1 低通滤波器 :输出的是电容的电压 1 低通滤波器可以把低频信号上面的高频信号给滤掉 2 100hz正常通过 3 经过低通滤波器后,波形光滑,绿色波形。一致 4 电容充电速度跟不上输入信号的速度(因为加了电阻,限制了电流&…

如何打造个人知识体系?

第一,每个人的基本情况不同。比如我有一个类别跟「设计」相关,这是自己的个人爱好,但不一定适合其他人。再比如我还有一个类别跟「广告文案」相关,因为里面很多表达可以借用到演讲或写作中,这也不适合所有人。 第二&am…