【Datawhale AI 夏令营第四期 Task4 :源大模型微调实战】

Datawhale AI 夏令营 Task4 :源大模型微调实战

前言

本篇文章主要介绍大模型微调技术——LoRA技术

一、什么是大模型微调技术?

1.1 大模型微调技术简介

模型微调也被称为指令微调(Instruction Tuning)或者有监督微调(Supervised Fine-tuning, SFT),该方法利用成对的任务输入与预期输出数据,训练模型学会以问答的形式解答问题,从而解锁其任务解决潜能。

1.2 轻量化微调技术简介

通过训练极少的模型参数,同时保证微调后的模型表现可以与全量微调相媲美。常用的轻量化微调技术有LoRA、Adapter 和 Prompt Tuning。

1.3 LoRA技术简介

LoRA 是通过低秩矩阵分解,在原始矩阵的基础上增加一个旁路矩阵,然后只更新旁路矩阵的参数。

二、源2.0-2B 微调实战

先创建实例并打开。
运行以下命令来安装 tf-keras:

pip install tf-keras

1.跟着教程训练模型

在实例的终端中运行以下代码:

git lfs install
git clone https://www.modelscope.cn/datasets/Datawhale/AICamp_yuan_baseline.git
cp AICamp_yuan_baseline/Task\ 4:源大模型微调实战/* .

双击打开Task 4:源大模型微调实战.ipynb,运行所有单元格。
这样模型就开始进行数据的处理和模型训练(时间会有点长),如下图:
模型训练
如果出现了报错: CUDA out of memory,可能是GPU的显卡不够用,考虑新建一个实例从头开始(笔者遇到这个问题也是这样解决的)。

2.尝试使用训练好的模型,搭建Demo

首先,点击重启内核,清空显存。(因为Demo也需要占用显存,不先清空会因为显存不足报错。)
然后,我们将在终端输入下面的命令,启动streamlit服务:

streamlit run Task\ 4\ 案例:AI简历助手.py --server.address 127.0.0.1 --server.port 6006

在这里插入图片描述
然后点击链接,跳转到浏览器,进入Demo:
输入文本:
Demo运行效果
可以看到,Demo完成了信息抽取,并进行了结构化展示。这样我们完成了一个AI简历助手的构建。


提醒:使用完毕后关闭PAI实例!

3.课后作业

针对一个下游任务,构建数据集,进行Yuan-2B模型微调实战,完成微调后模型的效果测试和调优。
我选择的下游任务是——命名体识别,即NER。
这里我使用的是来自于魔搭平台上面的数据集:

  • 魔搭数据集:https://modelscope.cn/datasets
    我选择的数据集名为——”MultiCoNER2023命名实体识别数据集_检索增强128“
    数据集加载方式如下:
from modelscope.msdatasets import MsDataset

train_datasets = MsDataset.load('multico_ner_2023_wiki128', subset_name='zh', namespace='pangda', split='train')
dev_datasets = MsDataset.load('multico_ner_2023_wiki128', subset_name='zh', namespace='pangda', split='dev')
test_datasets = MsDataset.load('multico_ner_2023_wiki128', subset_name='zh', namespace='pangda', split='test')

但是为了与我们的模型相契合,需要修改代码逻辑。
下面我来具体讲解我是怎么改写模型的:
2.3 数据处理

# 导入环境
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer
from modelscope.msdatasets import MsDataset
from datasets import Dataset
import pandas as pd

# 读取数据
# df = pd.read_json('./data.json')
# ds = Dataset.from_pandas(df)
# 加载 MsDataset 数据集
train_datasets = MsDataset.load('multico_ner_2023_wiki128', subset_name='zh', namespace='pangda', split='train')
dev_datasets = MsDataset.load('multico_ner_2023_wiki128', subset_name='zh', namespace='pangda', split='dev')
test_datasets = MsDataset.load('multico_ner_2023_wiki128', subset_name='zh', namespace='pangda', split='test')

# 提取 MsDataset 中的数据
def extract_data(msdataset):
    data = []
    for sample in msdataset:
        data.append(sample)
    return data

# 将提取的数据转换为 pandas DataFrame
train_data = extract_data(train_datasets)
dev_data = extract_data(dev_datasets)
test_data = extract_data(test_datasets)

train_df = pd.DataFrame(train_data)
dev_df = pd.DataFrame(dev_data)
test_df = pd.DataFrame(test_data)

# 将 pandas DataFrame 转换为 datasets.Dataset
train_ds = Dataset.from_pandas(train_df)
dev_ds = Dataset.from_pandas(dev_df)
test_ds = Dataset.from_pandas(test_df)

# 选择一个数据集用于示例(例如 train_ds)
ds = dev_ds

通过查看数据可以发现数据集确实变了:
新的数据集
定义数据处理函数:

# # 定义数据处理函数
# def process_func(example):
#     MAX_LENGTH = 384    # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性

#     instruction = tokenizer(f"{example['input']}<sep>")
#     response = tokenizer(f"{example['output']}<eod>")
#     input_ids = instruction["input_ids"] + response["input_ids"]
#     attention_mask = [1] * len(input_ids) 
#     labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] # instruction 不计算loss

#     if len(input_ids) > MAX_LENGTH:  # 做一个截断
#         input_ids = input_ids[:MAX_LENGTH]
#         attention_mask = attention_mask[:MAX_LENGTH]
#         labels = labels[:MAX_LENGTH]

#     return {
#         "input_ids": input_ids,
#         "attention_mask": attention_mask,
#         "labels": labels
#     }

def process_func(example):
    MAX_LENGTH = 384    # 设定最大长度

    # 将 tokens 拼接为字符串
    tokens = example['tokens']
    spans = example['spans']
    
    # 将 tokens 转换为模型输入 ID
    encodings = tokenizer(tokens, is_split_into_words=True, truncation=True, padding='max_length', max_length=MAX_LENGTH)
    input_ids = encodings["input_ids"]
    attention_mask = encodings["attention_mask"]
    
    # 创建标签数组
    labels = [-100] * len(input_ids)  # 初始化所有标签为 -100
    
    # 将 spans 中的实体标签对齐到 token ID 上
    for span in spans:
        start, end = span['start'], span['end']
        label = span['type']
        # 在这里处理对齐标签的逻辑
        # 请根据实际情况对齐标签,下面的示例是简化版本
        for idx in range(start, end + 1):
            if idx < len(labels):  # 防止索引超出范围
                labels[idx] = label_to_id(label)  # 将实体标签转换为 ID
    
    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }

def label_to_id(label):
    # 假设有一个标签到 ID 的映射字典
    label2id = {"Politician": 1, "Location": 2, "Organization": 3, "Date": 4}
    return label2id.get(label, -100)  # 默认标签 -100

可以看到模型被训练好了,虽然效果不是很好QAQ
在这里插入图片描述
2.5 效果验证
在这里插入图片描述
到这一步我们的模型就训练并微调完成了。
倒数第二步:修改python文件。
可以改应用的标题,要修改template:
在这里插入图片描述

最后一步,打开终端:
输入以下命令:

streamlit run Task\ 4\ 案例:AI命名体识别助手.py --server.address 127.0.0.1 --server.port 6008

然后点击链接,跳转到浏览器,进入Demo。
在这里插入图片描述

总结

本节内容以Lora为例,介绍了进行大模型微调的基本方法。
同时在课后作业中,完成了更换新的数据集并建立标签,较为成功的训练好了模型。搭建Demo实现了命名体识别的任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值