使用PEFT库进行ChatGLM3-6B模型的LORA高效微调

news2024/10/7 10:18:12

PEFT库进行ChatGLM3-6B模型LORA高效微调

  • LORA微调ChatGLM3-6B模型
    • 安装相关库
    • 使用ChatGLM3-6B
    • 模型GPU显存占用
    • 准备数据集
    • 加载模型
    • 加载数据集
    • 数据处理
    • 数据集处理
    • 配置LoRA
    • 配置训练超参数
    • 开始训练
    • 保存LoRA模型
    • 模型推理
    • 从新加载
    • 合并模型
    • 使用微调后的模型

LORA微调ChatGLM3-6B模型

本文基于transformers、peft等框架,对ChatGLM3-6B模型进行Lora微调。

LORA(Low-Rank Adaptation)是一种高效的模型微调技术,它可以通过在预训练模型上添加额外的低秩权重矩阵来微调模型,从而仅需更新很少的参数即可获得良好的微调性能。这相比于全量微调大幅减少了训练时间和计算资源的消耗。

安装相关库

pip install ransformers==4.37.2 peft==0.8.0 accelerate==0.27.0 bitsandbytes

使用ChatGLM3-6B

直接调用ChatGLM3-6B模型来生成对话

from transformers import AutoTokenizer, AutoModel

model_id = "/root/work/chatglm3-6b"
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
#model = AutoModel.from_pretrained(model_id, trust_remote_code=True).half().cuda()
model = AutoModel.from_pretrained(model_id, trust_remote_code=True, device='cuda')

model = model.eval()
response, history = model.chat(tokenizer, "你好", history=history)
print(response)

在这里插入图片描述

模型GPU显存占用

默认情况下,模型以半精度(float16)加载,模型权重需要大概 13GB显存。

获取当前模型占用的GPU显存

memory_bytes = model.get_memory_footprint()
# 转换为GB
memory_gb = memory_footprint_bytes / (1024 ** 3)  
print(f"{memory_gb :.2f}GB")

注意:与实际进程占用有差异,差值为预留给PyTorch的显存

准备数据集

准备数据集其实就是指令集构建,LLM的微调一般指指令微调过程。所谓指令微调,就是使用的微调数据格式、形式。

训练目标是让模型具有理解并遵循用户指令的能力。因此在指令集构建时,应该针对目标任务,针对性的构建任务指令集。

这里使用alpaca格式的数据集,格式形式如下:

[
	{
	  "instruction": "用户指令(必填)",
	  "input": "用户输入(选填)",
	  "output": "模型回答(必填)",
	},
    "system": "系统提示词(选填)",
    "history": [
      ["第一轮指令(选填)", "第一轮回答(选填)"],
      ["第二轮指令(选填)", "第二轮回答(选填)"]
    ]
]
instruction:用户指令,要求AI执行的任务或问题

input:用户输入,是完成用户指令所必须的输入内容,就是执行指令所需的具体信息或上下文

output:模型回答,根据给定的指令和输入生成答案

这里根据企业私有文档数据,生成相关格式的训练数据集,大概格式如下:

[
  {
    "instruction": "内退条件是什么?",
    "input": "",
    "output": "内退条件包括与公司签订正式劳动合同并连续工作满20年及以上,以及距离法定退休年龄不足5年。特殊工种符合国家相关规定可提前退休的也可在退休前5年内提出内退申请。"
  },
]

加载模型

from transformers import AutoModel, AutoTokenizer

model_id = "/root/work/chatglm3-6b"
model = AutoModel.from_pretrained(model_id, trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)

加载数据集

from datasets import load_dataset

data_id="/root/work/jupyterlab/zd.json"
dataset = load_dataset("json", data_files=data_id)
print(dataset["train"])

在这里插入图片描述

数据处理

Lora训练数据是需要经过tokenize编码处理,然后后再输入模型进行训练。一般需要将输入文本编码为input_ids,将输出文本编码为labels,编码之后的结果都是多维的向量。

需要定义一个预处理函数,这个函数用于对每一个样本,编码其输入、输出文本并返回一个编码后的字典。

# tokenize_func 函数
def tokenize_func(example, tokenizer, ignore_label_id=-100):
    """
    对单个数据样本进行tokenize处理。

    参数:
    example (dict): 包含'content'和'summary'键的字典,代表训练数据的一个样本。
    tokenizer (transformers.PreTrainedTokenizer): 用于tokenize文本的tokenizer。
    ignore_label_id (int, optional): 在label中用于填充的忽略ID,默认为-100。

    返回:
    dict: 包含'tokenized_input_ids'和'labels'的字典,用于模型训练。
    """
    prompt_text = ''                          # 所有数据前的指令文本
    max_input_length = 512                    # 输入的最大长度
    max_output_length = 1536                  # 输出的最大长度

    # 构建问题文本
    question = prompt_text + example['instruction']
    if example.get('input', None) and example['input'].strip():
        question += f'\n{example["input"]}'

    # 构建答案文本
    answer = example['output']

    # 对问题和答案文本进行tokenize处理
    q_ids = tokenizer.encode(text=question, add_special_tokens=False)
    a_ids = tokenizer.encode(text=answer, add_special_tokens=False)

    # 如果tokenize后的长度超过最大长度限制,则进行截断
    if len(q_ids) > max_input_length - 2:  # 保留空间给gmask和bos标记
        q_ids = q_ids[:max_input_length - 2]
    if len(a_ids) > max_output_length - 1:  # 保留空间给eos标记
        a_ids = a_ids[:max_output_length - 1]

    # 构建模型的输入格式
    input_ids = tokenizer.build_inputs_with_special_tokens(q_ids, a_ids)
    question_length = len(q_ids) + 2  # 加上gmask和bos标记

    # 构建标签,对于问题部分的输入使用ignore_label_id进行填充
    labels = [ignore_label_id] * question_length + input_ids[question_length:]

    return {'input_ids': input_ids, 'labels': labels}

进行数据映射处理,同时删除特定列

# 获取 'train' 部分的列名
column_names = dataset['train'].column_names  

# 使用lambda函数调用tokenize_func函数,并传入example和tokenizer作为参数
tokenized_dataset = dataset['train'].map(
    lambda example: tokenize_func(example, tokenizer),
    batched=False,  # 不按批次处理
    remove_columns=column_names  # 移除特定列(column_names中指定的列)
)

执行print(tokenized_dataset[0]),打印tokenize处理结果
在这里插入图片描述

数据集处理

还需要使用一个数据收集器,可以使用transformers 中的DataCollatorForSeq2Seq数据收集器

from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(
    tokenizer,
    model=model,
    label_pad_token_id=-100,
    pad_to_multiple_of=None,
    padding=True
)

或者自定义实现一个数据收集器

import torch
from typing import List, Dict, Optional

# DataCollatorForChatGLM 类
class DataCollatorForChatGLM:
    """
    用于处理批量数据的DataCollator,尤其是在使用 ChatGLM 模型时。

    该类负责将多个数据样本(tokenized input)合并为一个批量,并在必要时进行填充(padding)。

    属性:
    pad_token_id (int): 用于填充(padding)的token ID。
    max_length (int): 单个批量数据的最大长度限制。
    ignore_label_id (int): 在标签中用于填充的ID。
    """

    def __init__(self, pad_token_id: int, max_length: int = 2048, ignore_label_id: int = -100):
        """
        初始化DataCollator。

        参数:
        pad_token_id (int): 用于填充(padding)的token ID。
        max_length (int): 单个批量数据的最大长度限制。
        ignore_label_id (int): 在标签中用于填充的ID,默认为-100。
        """
        self.pad_token_id = pad_token_id
        self.ignore_label_id = ignore_label_id
        self.max_length = max_length

    def __call__(self, batch_data: List[Dict[str, List]]) -> Dict[str, torch.Tensor]:
        """
        处理批量数据。

        参数:
        batch_data (List[Dict[str, List]]): 包含多个样本的字典列表。

        返回:
        Dict[str, torch.Tensor]: 包含处理后的批量数据的字典。
        """
        # 计算批量中每个样本的长度
        len_list = [len(d['input_ids']) for d in batch_data]
        batch_max_len = max(len_list)  # 找到最长的样本长度

        input_ids, labels = [], []
        for len_of_d, d in sorted(zip(len_list, batch_data), key=lambda x: -x[0]):
            pad_len = batch_max_len - len_of_d  # 计算需要填充的长度
            # 添加填充,并确保数据长度不超过最大长度限制
            ids = d['input_ids'] + [self.pad_token_id] * pad_len
            label = d['labels'] + [self.ignore_label_id] * pad_len
            if batch_max_len > self.max_length:
                ids = ids[:self.max_length]
                label = label[:self.max_length]
            input_ids.append(torch.LongTensor(ids))
            labels.append(torch.LongTensor(label))

        # 将处理后的数据堆叠成一个tensor
        input_ids = torch.stack(input_ids)
        labels = torch.stack(labels)

        return {'input_ids': input_ids, 'labels': labels}
data_collator = DataCollatorForChatGLM(pad_token_id=tokenizer.pad_token_id)

配置LoRA

在peft中使用LoRA非常简单。借助PeftModel抽象,可以快速将低秩适配器(LoRA)应用到任意模型中。

在初始化相应的微调配置类(LoraConfig)时,需要显式指定在哪些层新增适配器(Adapter),并将其设置正确。

ChatGLM3-6B模型通过以下方式获取需要训练的模型层的名字

from peft.utils import TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING

target_modules = TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING['chatglm']

在PEFT库的 constants.py 文件中定义了不同的 PEFT 方法,在各类大模型上的微调适配模块。

在这里插入图片描述
主要是配置LoraConfig类,其中可以设置很多参数,但主要参数只有几个

# 从peft库导入LoraConfig和get_peft_model函数
from peft import LoraConfig, get_peft_model, TaskType

# 创建一个LoraConfig对象,用于设置LoRA(Low-Rank Adaptation)的配置参数
config = LoraConfig(
    r=8,  # LoRA的秩,影响LoRA矩阵的大小
    lora_alpha=32,  # LoRA适应的比例因子
    # 指定需要训练的模型层的名字,不同模型对应层的名字不同
    # target_modules=["query_key_value"],
    target_modules=target_modules,
    lora_dropout=0.05,  # 在LoRA模块中使用的dropout率
    bias="none",  # 设置bias的使用方式,这里没有使用bias
    # task_type="CAUSAL_LM"  # 任务类型,这里设置为因果(自回归)语言模型
    task_type=TaskType.CAUSAL_LM
)

# 使用get_peft_model函数和给定的配置来获取一个PEFT模型
model = get_peft_model(model, config)

# 打印出模型中可训练的参数
model.print_trainable_parameters()

在这里插入图片描述

配置训练超参数

配置训练超参数使用TrainingArguments类,可配置参数同样有很多,但主要参数也是只有几个

from transformers import TrainingArguments, Trainer

training_args = TrainingArguments(
        output_dir=f"{model_id}-lora",  # 指定模型输出和保存的目录
        per_device_train_batch_size=4,  # 每个设备上的训练批量大小
        learning_rate=2e-4,  # 学习率
        fp16=True,  # 启用混合精度训练,可以提高训练速度,同时减少内存使用
        logging_steps=20,  # 指定日志记录的步长,用于跟踪训练进度
        save_strategy="steps",   # 模型保存策略
        save_steps=50,   # 模型保存步数
        # max_steps=50, # 最大训练步长
        num_train_epochs=1  # 训练的总轮数
    )

查看添加LoRA模块后的模型

print(model)

开始训练

配置model、参数、数据集后就可以进行训练了

trainer = Trainer(
    model=model,  # 指定训练时使用的模型
    train_dataset=tokenized_dataset,  # 指定训练数据集
    args=training_args,
    data_collator=data_collator,
)

model.use_cache = False
# trainer.train() 
with torch.autocast("cuda"): 
    trainer.train()

在这里插入图片描述

注意:

执行trainer.train() 时出现异常,参考:bitsandbytes的issues

保存LoRA模型

lora_model_path = "lora/chatglm3-6b-int8"
trainer.model.save_pretrained(lora_model_path )
#model.save_pretrained(lora_model_path )

在这里插入图片描述

模型推理

使用LoRA模型,进行模型推理

lora_model = trainer.model

1.文本补全

text = "人力资源部根据各部门人员"

inputs = tokenizer(text, return_tensors="pt").to(0)

out = lora_model.generate(**inputs, max_new_tokens=500)
print(tokenizer.decode(out[0], skip_special_tokens=True))

在这里插入图片描述
2.问答对话

from peft import PeftModel

input_text = '公司的招聘需求是如何提出的?'
model.eval()
response, history = lora_model.chat(tokenizer=tokenizer, query=input_text)
print(f'ChatGLM3-6B 微调后回答: \n{response}')

在这里插入图片描述

从新加载

加载源model与tokenizer,使用PeftModel合并源model与PEFT微调后的参数,然后进行推理测试。

from peft import PeftModel
from transformers import AutoModel, AutoTokenizer

model_path="/root/work/chatglm3-6b"
peft_model_checkpoint_path="./chatglm3-6b-lora/checkpoint-50"

model = AutoModel.from_pretrained(model_path, trust_remote_code=True, low_cpu_mem_usage=True)
tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False, trust_remote_code=True)

 # 将训练所得的LoRa权重加载起来
p_model = PeftModel.from_pretrained(model, model_id=peft_model_checkpoint_path) 

p_model = p_model.cuda()
response, history = p_model.chat(tokenizer, "内退条件是什么?", history=[])
print(response)

合并模型

将lora权重合并到大模型中,将模型参数加载为16位浮点数

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch 

model_path="/root/work/chatglm3-6b"
peft_model_path="./lora/chatglm3-6b-int8"
save_path = "chatglm3-6b-lora"

tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True, low_cpu_mem_usage=True, torch_dtype=torch.float16, device_map="auto")
model = PeftModel.from_pretrained(model, peft_model_path)
model = model.merge_and_unload()

tokenizer.save_pretrained(save_path)
model.save_pretrained(save_path)

查看合并文件
在这里插入图片描述

使用微调后的模型

from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained("chatglm3-6b-lora", trust_remote_code=True)
model = AutoModel.from_pretrained("chatglm3-6b-lora", trust_remote_code=True, device='cuda')

model = model.eval()
response, history = model.chat(tokenizer, "内退条件是什么?", history=[])
print(response)

在这里插入图片描述

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

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

相关文章

前端:Element UI 与 Vuetify 的选择

vuetify优势 1、多端适配,Vuetify完全按照Material设计规范进行开发,每一个组件都经过精心设计,具有模块化、响应式和优秀的性能。 使用独特和动态的 布局 自定义您的应用程序,并使用 SASS 变量 自定义您的组件的样式。只需要做下…

私有化部署ChatGPT:潜力与挑战

背景 以ChatGPT为代表的大语言模型服务在2023年初开始大规模爆发,AI技术从来没有如此接近普通民众。随着以Microsoft, Google, Meta (Facebook)为代表的科技巨头在AI技术领域相继发布重量级产品和服务,国内…

仓库管理系统12--物资设置供应商设置

1、添加供应商窗体 2、布局控件UI <UserControl x:Class"West.StoreMgr.View.SupplierView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc"http://…

业绩尚可但股价不振,浙商银行陆建强闯“3元大关”

&#xff08;题图&#xff09; 文&#xff5c;新熔财经 作者&#xff5c;宏一 本来做着钱生钱的“美梦”&#xff0c;现在倒好&#xff0c;本金都不一定拿得回来。 因为不想把“鸡蛋都放在一个笼子里”&#xff0c;所以前几年在理财的时候一部分放在银行定存&#xff0c;一…

利用 Swifter 加速 Pandas 操作的详细教程

利用 Swifter 加速 Pandas 操作的详细教程 引言 Pandas 是数据分析中常用的库&#xff0c;但在处理大型数据集时效率可能会较低。Swifter 提供了一种简便的方法&#xff0c;通过并行处理来显著加速 Pandas 操作。 Swifter 简介 Swifter 是一个开源库&#xff0c;旨在自动优…

如何使用代理 IP 防止多个 Facebook 帐户关联 - 最佳实践

在社交媒体被广泛应用的今天&#xff0c;Facebook作为全球最大的社交网络平台之一&#xff0c;面临着很多挑战&#xff0c;其中之一就是用户行为的管理和安全。 为了防止多个账户之间的关联和滥用&#xff0c;Facebook需要采取一系列措施&#xff0c;其中包括使用静态住宅代理…

谷歌上搞下来的,无需付费,可以收藏!

在数字化时代&#xff0c;我们越来越依赖于智能设备来获取信息和知识。中国智谋App正是这样一款应用&#xff0c;它将中国古代的智慧与谋略书籍带入了我们的移动设备&#xff0c;让我们能够随时随地学习和领悟。而且提供文言文的原文和译文。 软件下载方式&#xff1a;谷歌上搞…

Firefox 火狐浏览器现在允许您在其 Nightly 版本中选择您喜欢的 AI 聊天机器人

Firefox Nightly版本是Mozilla推出的一个特殊的频道&#xff0c;用户可以在这里试用最新的功能和更改。这个版本每天都会更新&#xff0c;并且持续发布新的功能和修复。例如&#xff0c;在2023年10月8日发布的版本中&#xff0c;引入了一个新的按钮&#xff0c;用于快速重置隐私…

多车自动驾驶编队与协同控制引领智能物流革命

多车自动驾驶编队与协同控制引领智能物流革命 随着科技的不断进步&#xff0c;智能物流正以前所未有的速度和效率改变着我们的生活和工作方式。在这个领域的最前沿&#xff0c;北京渡众机器人科技有限公司的多车自动驾驶编队与协同控制技术正在为物流行业带来革命性的变革。 北…

【Java】Java序列化和反序列化

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 # Java中的序列化和反序列化 在Java中&#xff0c;序列化是将对象的状态写入字节流的机制。它主要用于Hibernate…

【教学类-64-05】20240625彩棒鱼骨图(二)AB排列 6.5*1CM 6选2根 30种

背景需求&#xff1a; 【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种-CSDN博客文章浏览阅读897次&#xff0c;点赞23次&#xff0c;收藏13次。【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种https:…

JAVA每日作业day6.26

ok了家人们&#xff0c;今天我们学习了面向对象-多态&#xff0c;话不多说我们一起来看看吧 一.多态概述 面向对象的第三大特性&#xff1a;封装、继承、多态 我们拿一个生活中的例子来看 生活中&#xff0c;比如跑的动作&#xff0c;小猫、小狗和大象&#xff0c;跑起来是不一…

CAN总线学习之路

闻道有先后&#xff0c;术业有专攻。我们接触新的事物时总会有个学习过程&#xff0c;如今现场总线非常繁多&#xff0c;CAN总线就是其中不可忽视的一种&#xff0c;在此以个人的学习过程与大家共勉&#xff01; 我大学时的专业是电子科学与技术&#xff0c;专业课程是数电、模…

从写下第1个脚本到年薪40W,我的测试开发心路历程!

对于任何职业来说&#xff0c;薪资始终都会是众多追求的重要部分。前几年测试行业还是风口&#xff0c;但是随着不断新鲜血液的加入&#xff0c;再加上就业大环境不好&#xff0c;企业也都在“降本增效”。目前内卷也是越来越激烈。不得不承认当下的现状&#xff0c;已经不仅仅…

视频分享的二维码怎么做?多种视频可用的二维码制作技巧

视频分享的快捷操作技巧可以在二维码生成器上来制作&#xff0c;与传统分享方式相比用二维码的方法能够更快捷&#xff0c;有利于用户能够在不下载视频占用空间的同时&#xff0c;就能够扫描二维码观看视频内容。视频二维码能够应用于很多的场景下&#xff0c;那么制作一个视频…

【Linux】使用ntpdate同步时间

ntpdate 是一个在 Linux 系统中用于同步系统时间的命令行工具&#xff0c;它通过与 NTP 服务器通信来调整本地系统时钟。然而&#xff0c;需要注意的是&#xff0c;ntpdate 已经被许多现代 Linux 发行版弃用。 安装 yum install -y ntpdate 查看时间 date同步时间 ntpdate ntp…

防火墙双机热备

防火墙双机热备 随着移动办公、网上购物、即时通讯、互联网金融、互联网教育等业务蓬勃发展&#xff0c;网络承载的业务越来越多&#xff0c;越来越重要。所以如何保证网络的不间断传输成为网络发展过程中急需解决的一个问题。 防火墙部署在企业网络出口处&#xff0c;内外网之…

想远程控制手机,用哪个软件好?

很多人都想知道安卓系统或iOS系统要如何实现手机远程控制手机、电脑远程控制手机&#xff0c;分别需要用到什么软件&#xff0c;这篇文章一次说清楚。 注意&#xff0c;安卓系统需要是7.0及以上版本&#xff0c;iOS系统需要是11及以上版本。具体使用步骤请点击关注&#xff0c;…

633. 平方数之和(中等)

633. 平方数之和 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java内存溢出溢出代码正确代码与截图 1. 题目描述 题目中转&#xff1a;633. 平方数之和 2.详细题解 本题是167. 两数之和 II - 输入有序数组&#xff08;中等&#xff09;题目的变型&#xff0c;由两数之和变…

数字图像分析(第三部分)

文章目录 第11章 基于概率图模型的图像分析概率有向图模型因子分解生成式模型链式图条件独立性有向图模型的马尔科夫毯概率无向图模型模型定义概率无向图模型的因子分解条件随机场条件随机场的定义条件随机场的预测算法第12章 运动分析运动相机建模光流运动表达方法运动估计准则…