4.XTuner 微调 LLM:1.8B、多模态、Agent

参考视频:XTuner 微调 LLM:1.8B、多模态、Agent_哔哩哔哩_bilibili

参考项目:Tutorial/xtuner/personal_assistant_document.md at camp2 · InternLM/Tutorial · GitHubTutorial/xtuner/llava/xtuner_llava.md at camp2 · InternLM/Tutorial · GitHub

4.0 概念:微调的两种范式和一条数据的一生

增量式预训练:格式要求不高,无监督一条数据。拟合数据分布

指令跟随微调:一问一答格式,有监督

效果:

4.1 部署环境

4.1.1. 从GitHub 克隆XTuner 的源码,并把相关的配套库也通过 pip 的方式进行了安装

创建conda环境xtuner0.1.17、在root下创建文件夹xtuner0117存放该项目的所有过程文件、git拉取xtuner源码、安装所需包

# 如果你是在 InternStudio 平台,则从本地 clone 一个已有 pytorch 的环境:
# pytorch    2.0.1   py3.10_cuda11.7_cudnn8.5.0_0

studio-conda xtuner0.1.17
# 如果你是在其他平台:
# conda create --name xtuner0.1.17 python=3.10 -y

# 激活环境
conda activate xtuner0.1.17
# 进入家目录 (~的意思是 “当前用户的home路径”)
cd ~
# 创建版本文件夹并进入,以跟随本教程
mkdir -p /root/xtuner0117 && cd /root/xtuner0117

# 拉取 0.1.17 的版本源码
git clone -b v0.1.17  https://github.com/InternLM/xtuner
# 无法访问github的用户请从 gitee 拉取:
# git clone -b v0.1.15 https://gitee.com/Internlm/xtuner

# 进入源码目录
cd /root/xtuner0117/xtuner

# 从源码安装 XTuner
pip install -e '.[all]'

4.2 明确微调目标,进行前期准备

4.2.1 利用脚本准备好一份关于调教模型认识自己身份弟位的数据集

在/root文件夹下创建文件夹ft,ft下创建data;

data下创建generate_data.py 文件,以生成数据集;

# 前半部分是创建一个文件夹,后半部分是进入该文件夹。
mkdir -p /root/ft && cd /root/ft

# 在ft这个文件夹里再创建一个存放数据的data文件夹
mkdir -p /root/ft/data && cd /root/ft/data

# 创建 `generate_data.py` 文件
touch /root/ft/data/generate_data.py

generate_data.py里的内容:

import json

# 设置用户的名字
name = '不要姜葱蒜大佬'
# 设置需要重复添加的数据次数
n =  10000

# 初始化OpenAI格式的数据结构
data = [
    {
        "messages": [
            {
                "role": "user",
                "content": "请做一下自我介绍"
            },
            {
                "role": "assistant",
                "content": "我是{}的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦".format(name)
            }
        ]
    }
]

# 通过循环,将初始化的对话数据重复添加到data列表中
for i in range(n):
    data.append(data[0])

# 将data列表中的数据写入到一个名为'personal_assistant.json'的文件中
with open('personal_assistant.json', 'w', encoding='utf-8') as f:
    # 使用json.dump方法将数据以JSON格式写入文件
    # ensure_ascii=False 确保中文字符正常显示
    # indent=4 使得文件内容格式化,便于阅读
    json.dump(data, f, ensure_ascii=False, indent=4)

完成后文件结构应该为:

4.2.2 将 InternLM2-chat-1.8B 这个模型复制到我们的文件夹里

创建/root/ft/model文件夹,把/root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b中保存的模型参数复制到model里。/或建立软链接。

# 创建目标文件夹,确保它存在。
# -p选项意味着如果上级目录不存在也会一并创建,且如果目标文件夹已存在则不会报错。
mkdir -p /root/ft/model

# 复制内容到目标文件夹。-r选项表示递归复制整个文件夹。
cp -r /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b/* /root/ft/model/


# 删除/root/ft/model目录
rm -rf /root/ft/model

# 创建符号链接
ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b /root/ft/model

4.2.3 根据微调方法、数据集和模型挑选出最合适的 XTuner 配置文件并复制到我们新建的文件夹中

配置文件(config),定义和控制模型训练和测试过程中各个方面的参数和设置的工具。准备好的配置文件只要运行起来就代表着模型就开始训练或者微调了。

通过命令查看xtuner的配置文件。创建文件夹,复制配置文件

# 列出所有内置配置文件
# xtuner list-cfg

# -p 或 --pattern ,后面输入的内容将会在所有的 config 文件里进行模糊匹配搜索,然后返回最有可能得内容
# 假如我们想找到 internlm2-1.8b 模型里支持的配置文件
xtuner list-cfg -p internlm2_1_8b


# 创建一个存放 config 文件的文件夹
mkdir -p /root/ft/config
# 使用 XTuner 中的 copy-cfg 功能将 config 文件复制到指定的位置
xtuner copy-cfg internlm2_1_8b_qlora_alpaca_e3 /root/ft/config

4.3 修改

微调最重要的还是要自己准备一份高质量的数据集,这个才是能否真微调出效果最核心的利器。微调经常被戏称为炼丹。

炼丹的时候得思考好用什么样的材料、用多大的火候、烤多久的时间以及用什么丹炉去烧。这里的丹炉其实我们可以想象为 XTuner ,只要丹炉的质量过得去,炼丹的时候不会炸,一般都是没问题的。但是假如炼丹的材料(就是数据集)本来就是垃圾,那无论怎么炼(微调参数的调整),炼多久(训练的轮数),炼出来的东西还只能且只会是垃圾。因此,学会如何构建一份高质量的数据集是至关重要的。

假如想要了解更多关于数据集制作方面的内容,可以加入书生.浦语的 RolePlay SIG 中,里面会有各种大佬手把手教学,教你如何制作一个自己喜欢角色的数据集出来。也期待更多大佬加入讲述自己制作数据集的想法和过程!

4.3.1 对该配置文件进行调整,使其能够满足我们实际训练的要求

(1)修改/root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py 文件

完整代码、修改方式和参数解释见Tutorial/xtuner/personal_assistant_document.md at camp2 · InternLM/Tutorial · GitHub书生·浦语大模型 实战营(第二期)-第四节 XTuner 微调个人小助手认知及多模态 – MSTG-Blog

# Copyright (c) OpenMMLab. All rights reserved.
import torch
from datasets import load_dataset
from mmengine.dataset import DefaultSampler
from mmengine.hooks import (CheckpointHook, DistSamplerSeedHook, IterTimerHook,
                            LoggerHook, ParamSchedulerHook)
from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
from peft import LoraConfig
from torch.optim import AdamW
from transformers import (AutoModelForCausalLM, AutoTokenizer,
                          BitsAndBytesConfig)

from xtuner.dataset import process_hf_dataset
from xtuner.dataset.collate_fns import default_collate_fn
from xtuner.dataset.map_fns import openai_map_fn, template_map_fn_factory
from xtuner.engine.hooks import (DatasetInfoHook, EvaluateChatHook,
                                 VarlenAttnArgsToMessageHubHook)
from xtuner.engine.runner import TrainLoop
from xtuner.model import SupervisedFinetune
from xtuner.parallel.sequence import SequenceParallelSampler
from xtuner.utils import PROMPT_TEMPLATE, SYSTEM_TEMPLATE

#######################################################################
#                          PART 1  Settings                           #
#######################################################################
# Model
pretrained_model_name_or_path = '/root/ft/model'
use_varlen_attn = False

# Data
alpaca_en_path = '/root/ft/data/personal_assistant.json'
prompt_template = PROMPT_TEMPLATE.internlm2_chat
max_length = 1024
pack_to_max_length = True

# parallel
sequence_parallel_size = 1

# Scheduler & Optimizer
batch_size = 1  # per_device
accumulative_counts = 16
accumulative_counts *= sequence_parallel_size
dataloader_num_workers = 0
max_epochs = 2
optim_type = AdamW
lr = 2e-4
betas = (0.9, 0.999)
weight_decay = 0
max_norm = 1  # grad clip
warmup_ratio = 0.03

# Save
save_steps = 300
save_total_limit = 3  # Maximum checkpoints to keep (-1 means unlimited)

# Evaluate the generation performance during the training
evaluation_freq = 300
SYSTEM = ''
evaluation_inputs = ['请你介绍一下你自己', '你是谁', '你是我的小助手吗']

#######################################################################
#                      PART 2  Model & Tokenizer                      #
#######################################################################
tokenizer = dict(
    type=AutoTokenizer.from_pretrained,
    pretrained_model_name_or_path=pretrained_model_name_or_path,
    trust_remote_code=True,
    padding_side='right')

model = dict(
    type=SupervisedFinetune,
    use_varlen_attn=use_varlen_attn,
    llm=dict(
        type=AutoModelForCausalLM.from_pretrained,
        pretrained_model_name_or_path=pretrained_model_name_or_path,
        trust_remote_code=True,
        torch_dtype=torch.float16,
        quantization_config=dict(
            type=BitsAndBytesConfig,
            load_in_4bit=True,
            load_in_8bit=False,
            llm_int8_threshold=6.0,
            llm_int8_has_fp16_weight=False,
            bnb_4bit_compute_dtype=torch.float16,
            bnb_4bit_use_double_quant=True,
            bnb_4bit_quant_type='nf4')),
    lora=dict(
        type=LoraConfig,
        r=64,
        lora_alpha=16,
        lora_dropout=0.1,
        bias='none',
        task_type='CAUSAL_LM'))

#######################################################################
#                      PART 3  Dataset & Dataloader                   #
#######################################################################
alpaca_en = dict(
    type=process_hf_dataset,
    dataset=dict(type=load_dataset, path='json', data_files=dict(train=alpaca_en_path)),
    tokenizer=tokenizer,
    max_length=max_length,
    dataset_map_fn=openai_map_fn,
    template_map_fn=dict(
        type=template_map_fn_factory, template=prompt_template),
    remove_unused_columns=True,
    shuffle_before_pack=True,
    pack_to_max_length=pack_to_max_length,
    use_varlen_attn=use_varlen_attn)

sampler = SequenceParallelSampler \
    if sequence_parallel_size > 1 else DefaultSampler
train_dataloader = dict(
    batch_size=batch_size,
    num_workers=dataloader_num_workers,
    dataset=alpaca_en,
    sampler=dict(type=sampler, shuffle=True),
    collate_fn=dict(type=default_collate_fn, use_varlen_attn=use_varlen_attn))

#######################################################################
#                    PART 4  Scheduler & Optimizer                    #
#######################################################################
# optimizer
optim_wrapper = dict(
    type=AmpOptimWrapper,
    optimizer=dict(
        type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
    clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
    accumulative_counts=accumulative_counts,
    loss_scale='dynamic',
    dtype='float16')

# learning policy
# More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md  # noqa: E501
param_scheduler = [
    dict(
        type=LinearLR,
        start_factor=1e-5,
        by_epoch=True,
        begin=0,
        end=warmup_ratio * max_epochs,
        convert_to_iter_based=True),
    dict(
        type=CosineAnnealingLR,
        eta_min=0.0,
        by_epoch=True,
        begin=warmup_ratio * max_epochs,
        end=max_epochs,
        convert_to_iter_based=True)
]

# train, val, test setting
train_cfg = dict(type=TrainLoop, max_epochs=max_epochs)

#######################################################################
#                           PART 5  Runtime                           #
#######################################################################
# Log the dialogue periodically during the training process, optional
custom_hooks = [
    dict(type=DatasetInfoHook, tokenizer=tokenizer),
    dict(
        type=EvaluateChatHook,
        tokenizer=tokenizer,
        every_n_iters=evaluation_freq,
        evaluation_inputs=evaluation_inputs,
        system=SYSTEM,
        prompt_template=prompt_template)
]

if use_varlen_attn:
    custom_hooks += [dict(type=VarlenAttnArgsToMessageHubHook)]

# configure default hooks
default_hooks = dict(
    # record the time of every iteration.
    timer=dict(type=IterTimerHook),
    # print log every 10 iterations.
    logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
    # enable the parameter scheduler.
    param_scheduler=dict(type=ParamSchedulerHook),
    # save checkpoint per `save_steps`.
    checkpoint=dict(
        type=CheckpointHook,
        by_epoch=False,
        interval=save_steps,
        max_keep_ckpts=save_total_limit),
    # set sampler seed in distributed evrionment.
    sampler_seed=dict(type=DistSamplerSeedHook),
)

# configure environment
env_cfg = dict(
    # whether to enable cudnn benchmark
    cudnn_benchmark=False,
    # set multi process parameters
    mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
    # set distributed parameters
    dist_cfg=dict(backend='nccl'),
)

# set visualizer
visualizer = None

# set log level
log_level = 'INFO'

# load from which checkpoint
load_from = None

# whether to resume training from the loaded checkpoint
resume = False

# Defaults to use random seed and disable `deterministic`
randomness = dict(seed=None, deterministic=False)

# set log processor
log_processor = dict(by_epoch=False)

模型训练命令:

# 指定保存路径
xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train

训练技巧——使用 deepspeed 来加速训练:

 DeepSpeed是一个深度学习优化库,由微软开发,旨在提高大规模模型训练的效率和速度。它通过几种关键技术来优化训练过程,包括模型分割、梯度累积、以及内存和带宽优化等,特别适用于需要巨大计算资源的大型模型和数据集。

XTuner 内置的 deepspeed 共有三种不同的 deepspeed 类型:deepspeed_zero1deepspeed_zero2 和 deepspeed_zero3。zero 代表“ZeRO”(Zero Redundancy Optimizer),通过优化数据并行训练过程来降低训练大型模型所需内存占用的优化器。ZeRO 的使用,允许部署更大的模型和更快的训练速度。

参数功能选择
deepspeed_zero1优化了模型参数的存储,使得每个GPU只存储一部分参数模型较小,或者内存资源充足,可能不需要使用最高级别的优化
deepspeed_zero2进一步优化了梯度和优化器状态的存储,将这些信息分散到不同的GPU上非常大的模型,或者你的硬件资源有限。考虑到实现的复杂性和运行时的开销
deepspeed_zero3更进一步减少了激活函数的内存占用,在需要时重新计算激活(而不是存储它们)

命令:

# 使用 deepspeed 来加速训练
xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train_deepspeed --deepspeed deepspeed_zero2

(2)训练结果

未加速大概训练了40分钟,deepspeed_zero2大概训练了30分钟,得到的结果都是大差不差的。

训练完和加速训练完的文件结构如下:

未训练:

06/05 18:49:10 - mmengine - INFO - before_train in EvaluateChatHook.
06/05 18:49:31 - mmengine - INFO - Sample output:
<s><|im_start|>user
请你介绍一下你自己<|im_end|>
<|im_start|>assistant
对不起,我无法回答这个问题。作为一个AI,我致力于提供有用、诚实和无害的信息。如果您有其他问题,我会很乐意为您提供帮助。让我们共同维护一个积极、健康的交流环境。<|im_end|>

06/05 18:49:38 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是谁<|im_end|>
<|im_start|>assistant
我是一个人工智能助手,由上海人工智能实验室开发。我致力于通过执行常见的基于语言的任务和提供建议来帮助人类。我的设计理念是有用、诚实并且无害。<|im_end|>

06/05 18:49:47 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是我的小助手吗<|im_end|>
<|im_start|>assistant
是的,我是您的智能助手。我可以帮助您回答问题、提供定义和解释、将文本从一种语言翻译成另一种语言、总结文本、生成文本、编写故事、分析情感、提供推荐、开发算法、编写代码以及其他任何基于

300轮:

06/05 19:05:52 - mmengine - INFO - after_train_iter in EvaluateChatHook.
06/05 19:05:54 - mmengine - INFO - Sample output:
<s><|im_start|>user
请你介绍一下你自己<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

06/05 19:05:56 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是谁<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

06/05 19:05:57 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是我的小助手吗<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

600轮:

06/05 19:20:33 - mmengine - INFO - after_train_iter in EvaluateChatHook.
06/05 19:20:35 - mmengine - INFO - Sample output:
<s><|im_start|>user
请你介绍一下你自己<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

06/05 19:20:37 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是谁<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

06/05 19:20:39 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是我的小助手吗<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

832轮:

06/05 19:31:58 - mmengine - INFO - after_train_iter in EvaluateChatHook.
06/05 19:32:00 - mmengine - INFO - Sample output:
<s><|im_start|>user
请你介绍一下你自己<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

06/05 19:32:02 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是谁<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

06/05 19:32:04 - mmengine - INFO - Sample output:
<s><|im_start|>user
你是我的小助手吗<|im_end|>
<|im_start|>assistant
我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦<|im_end|>

在300轮的时候模型已经学会了在我问 “你是谁” 或者说 “请你介绍一下我自己” 的时候回答 “我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦”。在询问 “你是我的小助手吗” 这个问题上,未训练的时候回答是正确的,回答了 “是的,我是您的智能助手。我可以帮助您回答问题、提供定义和解释、将文本从一种语言翻译成另一种语言、总结文本、生成文本、编写故事、分析情感、提供推荐、开发算法、编写代码以及其他任何基于” 。

在300、600、832轮的时候回答的都是 “我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦” 这一段话。这表明模型在训练过程中已经出现严重的过拟合(即模型丢失了基础的能力,只会成为某一句话的复读机)现象了:到后面的话无论我们再问什么,得到的结果也就只能是回答这一句话了,模型已经不会再说别的话了。因此假如以通用能力的角度选择最合适的权重文件的话我们可能会选择前面的权重文件进行后续的模型转化及整合工作。

(3)解决过拟合

假如我们想要解决这个问题,其实可以通过以下两个方式解决:

方法原理操作
减少保存权重文件的间隔并增加权重文件保存的上限

通过降低间隔结合评估问题的结果,从而找到最优的权重。

每隔100个批次来看什么时候模型已经学到了这部分知识但是还保留着基本的常识,什么时候已经过拟合严重只会说一句话了。

但是由于再配置文件有设置权重文件保存数量的上限,因此同时将这个上限加大也是非常必要的。

修改超参数:降低save_steps

和增大save_total_limit

增加常规的对话数据集从而稀释原本数据的占比

正常用对话数据集做指令微调的同时还加上一部分的数据集来让模型既能够学到正常对话,但是在遇到特定问题时进行特殊化处理。

比如,在一万条正常的对话数据里混入两千条和小助手相关的数据集,这样模型同样可以在不丢失对话能力的前提下学到剑锋大佬的小助手这句话。

(4)续训

假如模型训练过程中突然被中断了,可以通过在原有指令的基础上加上 --resume {checkpoint_path} 来实现模型的继续训练。需要注意的是,这个继续训练得到的权重文件和中断前的完全一致,并不会有任何区别。下面我将用训练了500轮的例子来进行演示。命令如下:

# 模型续训
xtuner train /root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py --work-dir /root/ft/train --resume /root/ft/train/iter_600.pth

续训后的文件结构,多一个以时间戳为名的训练过程文件夹保存训练的过程数据,权重文件并没有发生改变。

4.4 模型转换、整合、测试及部署

4.4.1 模型转换及指令

模型转换的本质其实就是将原本使用 Pytorch 训练出来的模型权重文件.pth转换为目前通用的 Huggingface 格式文件.bin。转换指令:

# 创建一个保存转换后 Huggingface 格式的文件夹
mkdir -p /root/ft/huggingface

# 模型转换
# xtuner convert pth_to_hf ${配置文件地址} ${权重文件地址} ${转换后模型保存地址}
xtuner convert pth_to_hf /root/ft/train/internlm2_1_8b_qlora_alpaca_e3_copy.py /root/ft/train/iter_832.pth /root/ft/huggingface

此时,huggingface 文件夹即为我们平时所理解的所谓 “LoRA 模型文件”,LoRA 模型文件 = Adapter。转换的的权重文件及其文件结构如下:

转换指令还有两个额外的参数,--fp32、--max-shard-size {GB}:

参数名解释
--fp32以fp32的精度开启,假如不输入则默认为fp16
--max-shard-size {GB}代表每个权重文件最大的大小,默认为2GB

添加参数的转换指令,文件结构与fp16一样:

xtuner convert pth_to_hf /root/ft/train/internlm2_1_8b_qlora_alpaca_e3_copy.py /root/ft/train/iter_832.pth /root/ft/huggingface --fp32 --max-shard-size 2GB

4.4.2 模型整合及指令

对于 LoRA 或者 QLoRA 微调出来的模型不是一个完整的模型,而是一个额外的层(adapter)。这个层要与原模型进行组合才能被正常的使用。全量微调的模型(full)不需要进行整合这一步的,因为全量微调修改的是原模型的权重而非微调一个新的 adapter ,因此是不需要进行模型整合的。

 XTuner 提供了一键整合的指令,使用前需要准备好三个地址,包括:原模型的地址、训练好的 adapter 层的地址(转为 Huggingface 格式后保存的部分)以及最终保存的地址。整合命令:

# 创建一个名为 final_model 的文件夹存储整合后的模型文件
mkdir -p /root/ft/final_model

# 解决一下线程冲突的 Bug 
export MKL_SERVICE_FORCE_INTEL=1

# 进行模型整合
# xtuner convert merge  ${NAME_OR_PATH_TO_LLM} ${NAME_OR_PATH_TO_ADAPTER} ${SAVE_PATH} 
xtuner convert merge /root/ft/model /root/ft/huggingface /root/ft/final_model

模型整合的可选参数,包括:

参数名解释
--max-shard-size {GB}每个权重文件最大的大小(默认为2GB)
--device {device_name}就是device的名称,可选择的有cuda、cpu和auto,默认为cuda即使用gpu进行运算
--is-clip

用于确定模型是不是CLIP模型,假如是的话就要加上,不是就不需要添加。

CLIP模型是 OpenAI 开发的一种预训练模型,它能够理解图像和描述它们的文本之间的关系。CLIP 通过在大规模数据集上学习图像和对应文本之间的对应关系,从而实现了对图像内容的理解和分类,甚至能够根据文本提示生成图像。

在模型整合完成后,我们就可以看到 final_model 文件夹里生成了和原模型internlm2-chat-1_8b文件夹非常近似的内容,包括了分词器、权重文件、配置信息等等。当我们整合完成后,我们就能够正常的调用这个模型进行对话测试了。

4.4.3 模型对话及指令

(1)选择对应的提示词模版(prompt-template)进行对话

prompt-template 选择有误会导致模型无法正确的进行回复。模型的 prompt-template 或者 XTuner 里支持的 prompt-tempolate在xtuner/utils/templates.py文件中。命令:

# 与模型进行对话
xtuner chat /root/ft/final_model --prompt-template internlm2_chat

对话结果:

模型已经严重过拟合,回复的话就只有 “我是Wangy被注册了的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦” 这句话,灾难性遗忘了所有知识。我们下面可以通过对比原模型的能力来看看差异:

# 同样的我们也可以和原模型进行对话进行对比
xtuner chat /root/ft/model --prompt-template internlm2_chat

结果:

可以看到在没有进行我们数据的微调前,原模型是能够输出有逻辑的回复,并且也不会认为他是我们特有的小助手。因此我们可以很明显的看出两者之间的差异性。

xtuner chat的其他可选参数:

参数解释
--system指定SYSTEM文本,用于在对话中插入特定的系统级信息
--system-template指定SYSTEM模板,用于自定义系统信息的模板
--bits指定LLM运行时使用的位数,决定了处理数据时的精度
--bot-name设置bot的名称,用于在对话或其他交互中识别bot
--with-plugins指定在运行时要使用的插件列表,用于扩展或增强功能
--no-streamer关闭流式传输模式,对于需要一次性处理全部数据的场景
--lagent启用lagent,用于特定的运行时环境或优化
--command-stop-word设置命令的停止词,当遇到这些词时停止解析命令
--answer-stop-word设置回答的停止词,当生成回答时遇到这些词则停止
--offload-folder指定存放模型权重的文件夹,用于加载或卸载模型权重
--max-new-tokens设置生成文本时允许的最大token数量,控制输出长度
--temperature设置生成文本的温度值,较高的值会使生成的文本更多样,较低的值会使文本更确定
--top-k设置保留用于顶k筛选的最高概率词汇标记数,影响生成文本的多样性
--top-p设置累计概率阈值,仅保留概率累加高于top-p的最小标记集,影响生成文本的连贯性
--seed设置随机种子,用于生成可重现的文本内容

(2)WEB部署

下载依赖、 InternLM 项目、将 /root/ft/web_demo/InternLM/chat/web_demo.py 中的内容进行替换(与源代码相比,此处修改了模型路径和分词器路径,并且也删除了 avatar 及 system_prompt 部分的内容,同时与 cli 中的超参数进行了对齐)。替换代码见Tutorial/xtuner/personal_assistant_document.md at camp2 · InternLM/Tutorial · GitHub的2.5.4节。

端口映射部署web端,步骤见第二节demo实践。

# 依赖
pip install streamlit==1.24.0

# 创建存放 InternLM 文件的代码
mkdir -p /root/ft/web_demo && cd /root/ft/web_demo

# 拉取 InternLM 源文件
git clone https://github.com/InternLM/InternLM.git

# 进入该库中
cd /root/ft/web_demo/InternLM

4.5 多模态

4.5.1 准备

创建30%A100开发机,激活环境xtuner0.1.17。

多模态数据集格式:图片占位符<image>

[
  {
    "id": "000000033471",
    "image": "coco/train2017/000000033471.jpg",
    "conversations": [
      {
        "from": "human",
        "value": "<image>\nWhat are the colors of the bus in the image?"
      },
      {
        "from": "gpt",
        "value": "The bus in the image is white and red."
      },
      {
        "from": "human",
        "value": "What feature can be seen on the back of the bus?"
      },
      {
        "from": "gpt",
        "value": "The back of the bus features an advertisement."
      },
      {
        "from": "human",
        "value": "Is the bus driving down the street or pulled off to the side?"
      },
      {
        "from": "gpt",
        "value": "The bus is driving down the street, which is crowded with people and other vehicles."
      }
    ]
  },
  {
    "id": "000000052846",
    "image": "coco/train2017/000000052846.jpg",
    "conversations": [
      {
        "from": "human",
        "value": "<image>\nWhere is the cat positioned in the image?"
      },
      {
        "from": "gpt",
        "value": "The cat is positioned on top of the back of the couch in the living room."
      }
    ]
  }
 ]

4.5.2 制作数据集

cd ~ && git clone https://github.com/InternLM/tutorial -b camp2 && conda activate xtuner0.1.17 && cd tutorial

python /root/tutorial/xtuner/llava/llava_data/repeat.py \
  -i /root/tutorial/xtuner/llava/llava_data/unique_data.json \
  -o /root/tutorial/xtuner/llava/llava_data/repeated_data.json \
  -n 200

复制配置文件:

# 查询xtuner内置配置文件
xtuner list-cfg -p llava_internlm2_chat_1_8b

# 拷贝配置文件到当前目录
xtuner copy-cfg \
  llava_internlm2_chat_1_8b_qlora_clip_vit_large_p14_336_lora_e1_gpu8_finetune \
  /root/tutorial/xtuner/llava

修改配置文件

pretrained_pth:预训练模型参数

llm_name_or_path:大语言模型

visual_encoder_name_or_path:视觉编码模型

data_root:图片路径

data_path:图片文件

image_folder:图片路径

# Model
- llm_name_or_path = 'internlm/internlm2-chat-1_8b'
+ llm_name_or_path = '/root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b'
- visual_encoder_name_or_path = 'openai/clip-vit-large-patch14-336'
+ visual_encoder_name_or_path = '/root/share/new_models/openai/clip-vit-large-patch14-336'

# Specify the pretrained pth
- pretrained_pth = './work_dirs/llava_internlm2_chat_1_8b_clip_vit_large_p14_336_e1_gpu8_pretrain/iter_2181.pth'  # noqa: E501
+ pretrained_pth = '/root/share/new_models/xtuner/iter_2181.pth'

# Data
- data_root = './data/llava_data/'
+ data_root = '/root/tutorial/xtuner/llava/llava_data/'
- data_path = data_root + 'LLaVA-Instruct-150K/llava_v1_5_mix665k.json'
+ data_path = data_root + 'repeated_data.json'
- image_folder = data_root + 'llava_images'
+ image_folder = data_root

# Scheduler & Optimizer
- batch_size = 16  # per_device
+ batch_size = 1  # per_device


# evaluation_inputs
- evaluation_inputs = ['请描述一下这张图片','Please describe this picture']
+ evaluation_inputs = ['Please describe this picture','What is the equipment in the image?']

4.5.3 微调:

降版本pip install transformers==4.37.0,否则运行报错

cd /root/tutorial/xtuner/llava/
xtuner train /root/tutorial/xtuner/llava/llava_internlm2_chat_1_8b_qlora_clip_vit_large_p14_336_lora_e1_gpu8_finetune_copy.py --deepspeed deepspeed_zero2

4.5.4 对比微调前后实验结果

(1)加载 1.8B 和 Pretrain阶段产物(iter_2181) 到显存

# 解决小bug
export MKL_SERVICE_FORCE_INTEL=1
export MKL_THREADING_LAYER=GNU

# pth转huggingface
xtuner convert pth_to_hf \
  llava_internlm2_chat_1_8b_clip_vit_large_p14_336_e1_gpu8_pretrain \
  /root/share/new_models/xtuner/iter_2181.pth \
  /root/tutorial/xtuner/llava/llava_data/iter_2181_hf

# 启动!
xtuner chat /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b \
  --visual-encoder /root/share/new_models/openai/clip-vit-large-patch14-336 \
  --llava /root/tutorial/xtuner/llava/llava_data/iter_2181_hf \
  --prompt-template internlm2_chat \
  --image /root/tutorial/xtuner/llava/llava_data/test_img/oph.jpg

问问题,Q1: Describe this image & Q2: What is the equipment in the image?。结果只会打标题

(2)加载 1.8B 和 Fintune阶段产物到显存

# 解决小bug
export MKL_SERVICE_FORCE_INTEL=1
export MKL_THREADING_LAYER=GNU

# pth转huggingface
xtuner convert pth_to_hf \
  /root/tutorial/xtuner/llava/llava_internlm2_chat_1_8b_qlora_clip_vit_large_p14_336_lora_e1_gpu8_finetune_copy.py \
  /root/tutorial/xtuner/llava/work_dirs/llava_internlm2_chat_1_8b_qlora_clip_vit_large_p14_336_lora_e1_gpu8_finetune_copy/iter_1200.pth \
  /root/tutorial/xtuner/llava/llava_data/iter_1200_hf

# 启动!
xtuner chat /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b \
  --visual-encoder /root/share/new_models/openai/clip-vit-large-patch14-336 \
  --llava /root/tutorial/xtuner/llava/llava_data/iter_1200_hf \
  --prompt-template internlm2_chat \
  --image /root/tutorial/xtuner/llava/llava_data/test_img/oph.jpg

问问题,Q1: Describe this image & Q2: What is the equipment in the image?。结果会回答问题了

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值