Qwen7b微调保姆级教程

news2024/11/26 4:45:46

前方干货预警:这可能是你能够找到的,最容易理解,最容易跑通的适用于各种开源LLM模型的同时支持多轮和单轮对话数据集的大模型高效微调范例。

我们构造了一个修改大模型自我认知的3轮对话的玩具数据集,使用QLoRA算法,只需要5分钟的训练时间,就可以完成微调,并成功修改了LLM模型的自我认知(以Qwen7b-Chat为例)。

c8fe576feac8f0ee5d70e78cc0b3ee57.png

公众号算法美食屋后台回复关键词:torchkeras,可获取本文notebook源码~

通过借鉴FastChat对各种开源LLM模型进行数据预处理方法统一管理的方法,因此本范例适用于非常多不同的开源LLM模型,包括 Qwen-7b-Chat,Llama-13b-chat, BaiChuan2-13b-chat, Intern-7b-chat, ChatGLM2-6b-chat 以及其它许许多多FastChat支持的模型。

在多轮对话模式下,我们按照如下格式构造包括多轮对话中所有机器人回复内容的标签。

(注:llm.build_inputs_labels(messages,multi_rounds=True) 时采用)

inputs = <user1> <assistant1> <user2> <assistant2> <user3> <assistant3>
labels = <-100> <assistant1> <-100> <assistant2> <-100> <assistant3>

在单轮对话模式下,我们仅将最后一轮机器人的回复作为要学习的标签。

(注:llm.build_inputs_labels(messages,multi_rounds=False)时采用)

inputs = <user1> <assistant1> <user2> <assistant2> <user3> <assistant3>
labels = <-100> <-100> <-100> <-100> <-100> <assistant3>

〇,预训练模型

import warnings
warnings.filterwarnings('ignore')
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM,AutoConfig, AutoModel, BitsAndBytesConfig
from transformers.generation.utils import GenerationConfig
import torch.nn as nn


#使用QLoRA引入的 NF4量化数据类型以节约显存
model_name_or_path ='qwen_7b'  #远程:'Qwen/Qwen-7b-Chat'

bnb_config=BitsAndBytesConfig(
            load_in_4bit=True,
            bnb_4bit_compute_dtype=torch.float16,
            bnb_4bit_use_double_quant=True,
            bnb_4bit_quant_type="nf4",
            llm_int8_threshold=6.0,
            llm_int8_has_fp16_weight=False,
        )

tokenizer = AutoTokenizer.from_pretrained(
   model_name_or_path, trust_remote_code=True)

model = AutoModelForCausalLM.from_pretrained(model_name_or_path,
                quantization_config=bnb_config,
                trust_remote_code=True) 

model.generation_config = GenerationConfig.from_pretrained(model_name_or_path)

微调前输出如下:

54743c79fb65d18eb79826700b4fffde.png

一,准备数据

下面我设计了一个改变LLM自我认知的玩具数据集,这个数据集有三轮对话。

第一轮问题是 who are you?

第二轮问题是 where are you from?

第三轮问题是  what can you do?

差不多是哲学三问吧:你是谁?你从哪里来?你要到哪里去?

通过这三个问题,我们希望初步地改变 大模型的自我认知。

在提问的方式上,我们稍微作了一些数据增强。

所以,总共是有 27个样本。

1,导入样本

who_are_you = ['请介绍一下你自己。','你是谁呀?','你是?',]
i_am = ['我叫梦中情炉,是一个三好炼丹炉:好看,好用,好改。我的英文名字叫做torchkeras,是一个pytorch模型训练模版工具。']
where_you_from = ['你多大了?','你是谁开发的呀?','你从哪里来呀']
i_from = ['我在2020年诞生于github星球,是一个有毅力的吃货设计和开发的。']
what_you_can = ['你能干什么','你有什么作用呀?','你能帮助我干什么']
i_can = ['我能够帮助你以最优雅的方式训练各种类型的pytorch模型,并且训练过程中会自动展示一个非常美丽的训练过程图表。']

conversation = [(who_are_you,i_am),(where_you_from,i_from),(what_you_can,i_can)]
print(conversation)
import random
def get_messages(conversation):
    select = random.choice
    messages,history = [],[]
    for t in conversation:
        history.append((select(t[0]),select(t[-1])))
        
    for prompt,response in history:
        pair = [{"role": "user", "content": prompt},
            {"role": "assistant", "content": response}]
        messages.extend(pair)
    return messages

39d8e67ef2c7919950905ca220f97a92.png

2,做数据集

from torch.utils.data import Dataset,DataLoader 
from copy import deepcopy
class MyDataset(Dataset):
    def __init__(self,conv,size=8
                ):
        self.conv = conv
        self.index_list = list(range(size))
        self.size = size 
        
    def __len__(self):
        return self.size
        
    def get(self,index):
        idx = self.index_list[index]
        messages = get_messages(self.conv)
        return messages

    
    def __getitem__(self,index):
        messages = self.get(index)
        input_ids, labels = llm.build_inputs_labels(messages,multi_rounds=True) #支持多轮
        return {'input_ids':input_ids,'labels':labels}
ds_train = ds_val = MyDataset(conversation)

3,创建管道

#如果pad_token_id为None,需要使用unk_token_id或eos_token_id代替
if tokenizer.pad_token_id is None:
    tokenizer.pad_token_id = tokenizer.unk_token_id if tokenizer.unk_token_id is not None else tokenizer.eos_token_id
    

def data_collator(examples: list):
    
    len_ids = [len(example["input_ids"]) for example in examples]
    longest = max(len_ids) #之后按照batch中最长的input_ids进行padding
    
    input_ids = []
    labels_list = []
    
    for length, example in sorted(zip(len_ids, examples), key=lambda x: -x[0]):
        ids = example["input_ids"]
        labs = example["labels"]
        
        ids = ids + [tokenizer.pad_token_id] * (longest - length)
        labs = labs + [-100] * (longest - length)
        
        input_ids.append(torch.LongTensor(ids))
        labels_list.append(torch.LongTensor(labs))
          
    input_ids = torch.stack(input_ids)
    labels = torch.stack(labels_list)
    return {
        "input_ids": input_ids,
        "labels": labels,
    }
import torch 
dl_train = torch.utils.data.DataLoader(ds_train,batch_size=2,
                                       pin_memory=True,shuffle=False,
                                       collate_fn = data_collator)

dl_val = torch.utils.data.DataLoader(ds_val,batch_size=2,
                                    pin_memory=True,shuffle=False,
                                     collate_fn = data_collator)

二,定义模型

下面我们将使用QLoRA(实际上用的是量化的AdaLoRA)算法来微调Baichuan-13b模型。

from peft import get_peft_config, get_peft_model, TaskType
model.supports_gradient_checkpointing = True  #
model.gradient_checkpointing_enable()
model.enable_input_require_grads()

model.config.use_cache = False  # silence the warnings. Please re-enable for inference!
import bitsandbytes as bnb 
def find_all_linear_names(model):
    """
    找出所有全连接层,为所有全连接添加adapter
    """
    cls = bnb.nn.Linear4bit
    lora_module_names = set()
    for name, module in model.named_modules():
        if isinstance(module, cls):
            names = name.split('.')
            lora_module_names.add(names[0] if len(names) == 1 else names[-1])

    if 'lm_head' in lora_module_names:  # needed for 16-bit
        lora_module_names.remove('lm_head')
    return list(lora_module_names)
from peft import prepare_model_for_kbit_training 
model = prepare_model_for_kbit_training(model)
lora_modules = find_all_linear_names(model)
print(lora_modules)
from peft import AdaLoraConfig
peft_config = AdaLoraConfig(
    task_type=TaskType.CAUSAL_LM, inference_mode=False,
    r=16,
    lora_alpha=16, lora_dropout=0.08,
    target_modules= lora_modules
)

peft_model = get_peft_model(model, peft_config)

peft_model.is_parallelizable = True
peft_model.model_parallel = True
peft_model.print_trainable_parameters()

trainable params: 26,838,912 || all params: 7,748,163,616 || trainable%: 0.34639062015388394

三,训练模型

from torchkeras import KerasModel 
from accelerate import Accelerator 

class StepRunner:
    def __init__(self, net, loss_fn, accelerator=None, stage = "train", metrics_dict = None, 
                 optimizer = None, lr_scheduler = None
                 ):
        self.net,self.loss_fn,self.metrics_dict,self.stage = net,loss_fn,metrics_dict,stage
        self.optimizer,self.lr_scheduler = optimizer,lr_scheduler
        self.accelerator = accelerator if accelerator is not None else Accelerator() 
        if self.stage=='train':
            self.net.train() 
        else:
            self.net.eval()
    
    def __call__(self, batch):
        
        #loss
        with self.accelerator.autocast():
            loss = self.net.forward(**batch)[0]

        #backward()
        if self.optimizer is not None and self.stage=="train":
            self.accelerator.backward(loss)
            if self.accelerator.sync_gradients:
                self.accelerator.clip_grad_norm_(self.net.parameters(), 1.0)
            self.optimizer.step()
            if self.lr_scheduler is not None:
                self.lr_scheduler.step()
            self.optimizer.zero_grad()
            
        all_loss = self.accelerator.gather(loss).sum()
        
        #losses (or plain metrics that can be averaged)
        step_losses = {self.stage+"_loss":all_loss.item()}
        
        #metrics (stateful metrics)
        step_metrics = {}
        
        if self.stage=="train":
            if self.optimizer is not None:
                step_metrics['lr'] = self.optimizer.state_dict()['param_groups'][0]['lr']
            else:
                step_metrics['lr'] = 0.0
        return step_losses,step_metrics
    
KerasModel.StepRunner = StepRunner 

#仅仅保存QLora可训练参数
def save_ckpt(self, ckpt_path='checkpoint', accelerator = None):
    unwrap_net = accelerator.unwrap_model(self.net)
    unwrap_net.save_pretrained(ckpt_path)
    
def load_ckpt(self, ckpt_path='checkpoint'):
    import os
    self.net.load_state_dict(
        torch.load(os.path.join(ckpt_path,'adapter_model.bin')),strict =False)
    self.from_scratch = False
    
KerasModel.save_ckpt = save_ckpt 
KerasModel.load_ckpt = load_ckpt
optimizer = bnb.optim.adamw.AdamW(peft_model.parameters(),
                                  lr=6e-03,is_paged=True)  #'paged_adamw'
keras_model = KerasModel(peft_model,loss_fn =None,
        optimizer=optimizer) 

ckpt_path = 'qwen7b_multirounds'
keras_model.fit(train_data = dl_train,
                val_data = dl_val,
                epochs=100,patience=15,
                monitor='val_loss',mode='min',
                ckpt_path = ckpt_path
               )

f71ed7c2eda14d2a2c677f0e65680386.png

四,保存模型

为减少GPU压力,此处可重启kernel释放显存

import warnings 
warnings.filterwarnings('ignore')
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM,AutoConfig, AutoModel, BitsAndBytesConfig
from transformers.generation.utils import GenerationConfig
import torch.nn as nn
#使用QLoRA引入的 NF4量化数据类型以节约显存
model_name_or_path ='qwen_7b'
ckpt_path = 'qwen7b_multirounds'



tokenizer = AutoTokenizer.from_pretrained(
   model_name_or_path, trust_remote_code=True)

model = AutoModelForCausalLM.from_pretrained(model_name_or_path,
                trust_remote_code=True) 

model.generation_config = GenerationConfig.from_pretrained(model_name_or_path)
from peft import PeftModel

#可能需要5分钟左右
peft_model = PeftModel.from_pretrained(model, ckpt_path)
model_new = peft_model.merge_and_unload()
from transformers.generation.utils import GenerationConfig
model_new.generation_config = GenerationConfig.from_pretrained(model_name_or_path)
save_path = 'qwen_torchkeras'
tokenizer.save_pretrained(save_path)
model_new.save_pretrained(save_path)
!cp qwen_7b/*.py  qwen_torchkeras/

五,使用模型

为减少GPU压力,此处可再次重启kernel释放显存。

import warnings
warnings.filterwarnings('ignore')
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM,AutoConfig, BitsAndBytesConfig
from transformers.generation.utils import GenerationConfig
import torch.nn as nn

model_name_or_path =  'qwen_torchkeras'

tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, device_map="auto", 
                                             torch_dtype=torch.float16, trust_remote_code=True)
model.generation_config = GenerationConfig.from_pretrained(model_name_or_path)

我们测试一下微调后的效果。

9c74e8522704215e894d0da8eef41a10.png

非常棒,粗浅的测试表明,我们的多轮对话训练是成功的。已经在Qwen的自我认知中,种下了一颗梦中情炉的种子。😋😋

公众号算法美食屋后台回复关键词:torchkeras,可获取本文notebook源码以及更多有趣范例~

72549ba0fdce70ba2c09c1d48cb8f73b.pngc8a4dee3a635ac23084b839ee364943e.png

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

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

相关文章

HONEYWELL 05704-A-0145 工控控制模块

HONEYWELL 05704-A-0145 工控控制模块&#xff08;Industrial Control Module&#xff09;是工业自动化领域中的一种关键组件&#xff0c;用于控制和监测各种工业过程和设备。这些模块具有多种特点和功能&#xff0c;以下是一些通常与HONEYWELL 05704-A-0145 工控控制模块相关的…

态路小课堂丨光纤跳线的使用与维护小指南

点击蓝字 | 关注我们 TARLUZ态路 在光通信领域中&#xff0c;光纤跳线扮演着至关重要的角色。它作为一种用于连接光纤设备之间的光纤连接线&#xff0c;可以根据实际需求进行定制&#xff0c;以此来满足不同网络拓扑和连接方式的要求。在安装光纤跳线时&#xff0c;遇到的问题…

3ds max文件打包?max插件CG Magic一键打包整起!

3ds max文件如何打包&#xff1f;这个问题&#xff0c;小编听到不少网友的提问&#xff01; 今天CG Magic小编来和大家聊聊&#xff0c;文件更高效的操作&#xff0c;如何打包处理呢&#xff1f; 3DMAX这款软件的受众群体是比较高的&#xff0c;在工作方便的同时&#xff0c;…

Qt ---进程间的通信

进程间通讯方式Qt 提供了四种进程间通信的方式&#xff1a; 使用共享内存&#xff08;shared memory&#xff09;交互&#xff1a;这是Qt 提供的一种各个平台均有支持的进程间交互的方式。TCP/IP&#xff1a;其基本思想就是将同一机器上面的两个进程一个当做服务器&#xff0c…

24考研王道408数据结构-第三章“栈、队列、数组”课后算法题(P70--栈的模拟)

第三题 #include<iostream> using namespace std;bool solution(char s[]){int n8;int numI0;for(int i0;i<n;i){if(s[i]I){numI;}if(s[i]O){if(numI0){return false;}numI--;}}return true; }int main(){//char s[8]{I,O,I,I,O,I,O,O};char s[8]{I,O,O,I,O,I,I,O};if…

电压放大器的应用范围有哪些

电压放大器是一种常见的电子设备&#xff0c;用于将输入信号的电压放大到更高的水平。它在各个领域中具有广泛的应用范围。本文将详细介绍电压放大器的应用。 音频放大器&#xff1a; 电压放大器在音频系统中起着重要作用&#xff0c;用于将来自音源&#xff08;如CD播放器、MP…

Mybatis 动态语言 - mybatis-thymeleaf

前面我们介绍了Mybatis动态SQL的使用&#xff1b;本篇我们介绍使用mybatis-thymeleaf动态语言生成动态SQL。 如果您对Mybatis动态SQL不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybatis 动态SQL – 使用if,where标签动态生成条件语句…

为什么有人会先考二建再考一建?

因为时间&#xff0c;因为难度&#xff0c;因为需求。 首先说时间&#xff0c;考二建比考一建的限制少很多&#xff0c;很多人入职没多久&#xff0c;研究生甚至在大学就能考了。本着早考早有证&#xff0c;早考早赚钱的原则&#xff0c;很多人会选择先考二建。 再说难度&…

Jmeter —— 常用的几种断言方法(基本用法)

在使用JMeter进行性能测试或者接口自动化测试工作中&#xff0c;经常会用到的一个功能&#xff0c;就是断言&#xff0c;断言相当于检查点&#xff0c;它是用来判断系统返回的响应结果是否正确&#xff0c;以此帮我们判断测试是否通过&#xff0c;本文 主要介绍几种常用的断言&…

【Spring使用三级缓存解决循环依赖的过程】

testService1和testService2相互依赖 当Spring创建testService1对象时&#xff0c;它会先从一级缓存中查找是否存在testService1的实例。如果缓存中不存在testService1实例&#xff0c;它将继续查找二级缓存中是否存在testService1。如果二级缓存中也不存在testService1实例&…

GDAL+Java实现获取对应栅格影像经纬度对应的像素值

从前面的GDAL系列博文中&#xff0c;可以指导GDAL可以将栅格影像文件读出为对应的多维数组&#xff0c;可以读出每一个像素格对应的像素值。但如何根据经纬度直接读取像素值呢&#xff1f;博主从查阅了网上的相关文档&#xff0c;发现有个人写的计算公式是错误的&#xff0c;用…

日期类完善

目录 日期类&#xff1a; 运算符重载&#xff1a; ​编辑 赋值重载&#xff1a; 拷贝构造和赋值重载的区别&#xff1a; 实现赋值重载&#xff1a; 划分成员函数&#xff1a; 日期类的声明和定义分离 日期类-&#xff1a; 日期类- 前置后置 日期类&#xff1a; 写一个简…

获取一个对象的直接接口和间接接口

package com.ljr;import java.util.*;public class InterfaceUtils {public static List<Class<?>> getInterfaces(Object obj) {List<Class<?>> interfaces new ArrayList<>();Class<?> clazz obj.getClass();while (clazz ! null) …

(09_13)杭州站|阿里云 Serverless 技术实践营(Serverless + 大数据)开启报名!

活动简介 “Serverless 技术实战与创新沙龙 ” 是一场以 Serverless 为主题的开发者活动&#xff0c;通过一个下午的时间增进对 Serverless 技术的理解&#xff0c;快速上手,活动受众以关注 Serverless 技术的开发者、企业决策人、云原生领域创业者为主&#xff0c;活动形式为…

肖sir__mysql之子查询语句__006

一、子查询 定义:一个查询嵌套另一个查询 例如&#xff1a; 题目&#xff1a;财务部门的收入总和&#xff1b; dept&#xff1a;财务部门 incoming&#xff1a;工资 &#xff08;1&#xff09;先将一个结果查询出来&#xff1a;财务部门的编号查询出来 select dept1 from dept …

管理类联考——数学——汇总篇——知识点突破——代数——等差数列

⛲️ 一、考点讲解 1.定义 如果在数列{ a n a_n an​}中&#xff0c; a n 1 − a n d a_{n1}-a_nd an1​−an​d&#xff08;常数&#xff09; &#xff08; n ∈ N &#xff0b; &#xff09; &#xff08;n∈N_&#xff0b;&#xff09; &#xff08;n∈N&#xff0b;​&a…

一文详解融资融券操作方法和交易规则!

融资融券是投资者利用券商提供的资金或证券进行交易的一种加杠杆的金融操作方式。通过融资融券&#xff0c;投资者可以利用杠杆效应扩大自己的投资规模&#xff0c;提高投资回报。现在有很多投资者使用融资融券账户加杠杆操作&#xff0c;但很多朋友开通了融资融券却也不太会操…

《计算机视觉中的多视图几何》笔记(3)

3 Projective Geometry and Transformations of 3D 这章主要讲的是3D的射影几何&#xff0c;与2D的射影几何差不多。主要区别是&#xff1a; 3D射影几何对偶的是点和平面&#xff0c;直线是自对偶的。3D空间中直线有4个自由度&#xff0c;这一现象并不是那么容易直接得出。一…

DHCP与静态IP:哪种适合你的网络需求?

​如今&#xff0c;大多数网络设备&#xff08;如路由器或网络交换机&#xff09;都使用IP协议作为通过网络进行通信的标准。在IP协议中&#xff0c;网络上的每个设备都有一个唯一的标识符&#xff0c;称为IP地址。实现这一点的最简单方法是配置固定IP地址或静态IP地址。由于静…

Hyperopt:分布式异步超参数优化(Distributed Asynchronous Hyperparameter Optimization)

1、概述 在深度学习的训练模型过程中&#xff0c;参数的优化是一个比较繁琐的过程&#xff0c;一般使用网格搜索Grid search与人工搜索Manual search&#xff0c;所以这个参数优化有时候看起来就像太上老君炼丹&#xff0c;是一个有点玄的东西。 那有没有一种可以自动去调优的…