前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Qwen7b微调保姆级教程

Qwen7b微调保姆级教程

作者头像
lyhue1991
发布于 2023-09-17 06:16:53
发布于 2023-09-17 06:16:53
2.1K00
代码可运行
举报
运行总次数:0
代码可运行

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

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

公众号算法美食屋后台回复关键词: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) 时采用)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
inputs = <user1> <assistant1> <user2> <assistant2> <user3> <assistant3>
labels = <-100> <-100> <-100> <-100> <-100> <assistant3>

〇,预训练模型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import warnings
warnings.filterwarnings('ignore')
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)

微调前输出如下:

一,准备数据

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

第一轮问题是 who are you?

第二轮问题是 where are you from?

第三轮问题是 what can you do?

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

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

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

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

1,导入样本

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 

2,做数据集

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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}
    
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ds_train = ds_val = MyDataset(conversation)

3,创建管道

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#如果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,
    }

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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模型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from peft import prepare_model_for_kbit_training 
model = prepare_model_for_kbit_training(model)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
lora_modules = find_all_linear_names(model)
print(lora_modules) 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

三,训练模型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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'


代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
keras_model.fit(train_data = dl_train,
                val_data = dl_val,
                epochs=100,patience=15,
                monitor='val_loss',mode='min',
                ckpt_path = ckpt_path
               )

四,保存模型

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import warnings 
warnings.filterwarnings('ignore')
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from peft import PeftModel

#可能需要5分钟左右
peft_model = PeftModel.from_pretrained(model, ckpt_path)
model_new = peft_model.merge_and_unload()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from transformers.generation.utils import GenerationConfig
model_new.generation_config = GenerationConfig.from_pretrained(model_name_or_path)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
save_path = 'qwen_torchkeras'
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
tokenizer.save_pretrained(save_path)
model_new.save_pretrained(save_path)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
!cp qwen_7b/*.py  qwen_torchkeras/

五,使用模型

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

import warnings
warnings.filterwarnings('ignore')

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-09-13 09:21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 算法美食屋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
BaiChuan13B多轮对话微调范例
前方干货预警:这可能是你能够找到的,最容易理解,最容易跑通的,适用于多轮对话数据集的大模型高效微调范例。
lyhue1991
2023/09/05
1.1K0
BaiChuan13B多轮对话微调范例
Baichuan-13B 保姆级微调范例
干货预警:这可能是你能够找到的最容易懂的,最完整的,适用于各种NLP任务的Baichuan-13B-Chat的finetune教程~
lyhue1991
2023/09/05
1.4K0
Baichuan-13B 保姆级微调范例
微调BaiChuan13B来做命名实体识别
传统上,一般把NLP的研究领域大致分为自然语言理解(NLU)和自然语言生成(NLG)两种。
lyhue1991
2023/09/05
6860
微调BaiChuan13B来做命名实体识别
用Kaggle免费GPU微调ChatGLM2
前方干货预警:这篇文章可能是你目前能够找到的可以无痛跑通LLM微调并基本理解整个流程的门槛最低的入门范例。
lyhue1991
2023/09/05
1.1K0
用Kaggle免费GPU微调ChatGLM2
单样本微调给ChatGLM2注入知识~
前方干货预警:这可能也是一篇会改变你对LLM微调范式,以及对LLM原理理解的文章。
lyhue1991
2023/09/05
9400
单样本微调给ChatGLM2注入知识~
60分钟吃掉ChatGLM2-6b微调范例~
干货预警:这可能是你能够找到的最容易懂的,最完整的,适用于各种NLP任务的开源LLM的finetune教程~
lyhue1991
2023/09/05
7920
60分钟吃掉ChatGLM2-6b微调范例~
Qwen2大模型微调入门实战-命名实体识别(NER)任务
以Qwen2作为基座大模型,通过指令微调的方式做高精度的命名实体识别(NER),是学习入门LLM微调、建立大模型认知的非常好的任务。
zenRRan
2024/07/04
2K0
Qwen2大模型微调入门实战-命名实体识别(NER)任务
【LLM训练系列04】手把手教你Qlora微调
IGNORE_TOKEN_ID 是一个常量,通常用于在训练过程中忽略某些特定的标签或输入。它的作用是告诉模型在计算损失时不考虑这些特定的标签或输入。
致Great
2024/12/21
1710
【LLM训练系列04】手把手教你Qlora微调
【机器学习】QLoRA:基于PEFT亲手量化微调Qwen2大模型
之前陆续写了Qwen1.5、Qwen2.0、GLM-4等国产开源大模型的原理、训练及推理相关的文章,每一篇都拿到了热榜第一,但其中训练部分均基于Llama-factory框架,对于工程师而言,最喜欢的就是刨根问底,使用中间层的训练框架,还是少一些“安全感”。今天我们抛开中间框架,深入底层,一步一步带大家微调一个大模型。
LDG_AGI
2024/08/13
1.5K0
【机器学习】QLoRA:基于PEFT亲手量化微调Qwen2大模型
Qwen2大模型微调入门实战-命名实体识别(NER)任务
以Qwen2作为基座大模型,通过指令微调的方式做高精度的命名实体识别(NER),是学习入门LLM微调、建立大模型认知的非常好的任务。
用户9029617
2024/06/20
9810
Qwen2大模型微调入门实战-命名实体识别(NER)任务
Qwen2大模型微调入门实战(完整代码)
Qwen2是一个开源大语言模型。以Qwen2作为基座大模型,通过指令微调的方式实现高准确率的文本分类,是学习大语言模型微调的入门任务。
用户9029617
2024/06/09
3K1
Qwen2大模型微调入门实战(完整代码)
GLM4大模型微调入门实战(完整代码)
以GLM4作为基座大模型,通过指令微调的方式做高精度文本分类,是学习LLM微调的入门任务。
用户9029617
2024/06/12
1.9K0
GLM4大模型微调入门实战(完整代码)
【机器学习】Google开源大模型Gemma2:原理、微调训练及推理部署实战
Gemma 是 Google 推出的轻量级、先进的开放模型系列,采用与 Gemini 模型相同的研究成果和技术构建而成。它们是仅使用解码器的文本到文本大型语言模型(提供英语版本),为预训练变体和指令调整变体具有开放权重。Gemma 模型非常适合各种文本生成任务,包括问题解答、摘要和推理。由于它们相对较小,因此可以将其部署在资源有限的环境(如笔记本电脑、桌面设备或您自己的云基础架构)中,让更多人能够使用先进的 AI 模型,并帮助促进每个人的创新。
LDG_AGI
2024/08/13
7220
【机器学习】Google开源大模型Gemma2:原理、微调训练及推理部署实战
Transformers 4.37 中文文档(二)
除了🤗 Transformers 的 notebooks 之外,还有示例脚本演示如何使用PyTorch、TensorFlow或JAX/Flax训练模型的方法。
ApacheCN_飞龙
2024/06/26
6620
Transformers 4.37 中文文档(二)
微调llama2模型教程:创建自己的Python代码生成器
本文将演示如何使用PEFT、QLoRa和Huggingface对新的lama-2进行微调,生成自己的代码生成器。所以本文将重点展示如何定制自己的llama2,进行快速训练,以完成特定任务。
deephub
2023/08/30
1.3K0
微调llama2模型教程:创建自己的Python代码生成器
使用BERT进行文本分类
准备数据阶段主要需要用到的是datasets.Dataset 和transformers.AutoTokenizer。
lyhue1991
2023/09/05
7070
使用BERT进行文本分类
零基础入门:DeepSeek微调的评测教程来了!
三天前,看到了我们 Datawhale 公众号上发了文章《零基础入门:DeepSeek 微调教程来了!》反响很好,其中的内容写的非常接地气,适合学习者进行学习体验。
Datawhale
2025/02/28
1920
零基础入门:DeepSeek微调的评测教程来了!
【机器学习】Qwen2大模型原理、训练及推理部署实战
刚刚写完【机器学习】Qwen1.5-14B-Chat大模型训练与推理实战 ,阿里Qwen就推出了Qwen2,相较于Qwen1.5中0.5B、1.8B、4B、7B、14B、32B、72B、110B等8个Dense模型以及1个14B(A2.7B)MoE模型共计9个模型,Qwen2包含了0.5B、1.5B、7B、57B-A14B和72B共计5个尺寸模型。从尺寸上来讲,最关键的就是推出了57B-A14B这个更大尺寸的MoE模型,有人问为什么删除了14B这个针对32G显存的常用尺寸,其实对于57B-A14B剪枝一下就可以得到。
LDG_AGI
2024/08/13
2.4K0
【机器学习】Qwen2大模型原理、训练及推理部署实战
使用QLoRa微调Llama 2
上篇文章我们介绍了Llama 2的量化和部署,本篇文章将介绍使用PEFT库和QLoRa方法对Llama 27b预训练模型进行微调。我们将使用自定义数据集来构建情感分析模型。只有可以对数据进行微调我们才可以将这种大模型进行符合我们数据集的定制化。
deephub
2023/08/30
6730
使用QLoRa微调Llama 2
【多模态大模型实战】 搭建DeepSeek Janus-Pro 7B 多模态模型,以及推理微调,推理后的模型融合
Janus-Pro是DeepSeek最新开源的多模态模型,是一种新颖的自回归框架,统一了多模态理解和生成。通过将视觉编码解耦为独立的路径,同时仍然使用单一的、统一的变压器架构进行处理,该框架解决了先前方法的局限性。这种解耦不仅缓解了视觉编码器在理解和生成中的角色冲突,还增强了框架的灵活性。Janus-Pro 超过了以前的统一模型,并且匹配或超过了特定任务模型的性能。
AI浩
2025/02/08
1.8K1
【多模态大模型实战】 搭建DeepSeek Janus-Pro 7B 多模态模型,以及推理微调,推理后的模型融合
推荐阅读
相关推荐
BaiChuan13B多轮对话微调范例
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文