[书生·浦语大模型实战营]——第四节:XTuner 微调 LLM:1.8B、多模态、Agent

0.XTuner简介

XTuner 一个大语言模型&多模态模型微调工具箱。由 MMRazor 和 MMDeploy 联合开发。

  • 🤓 傻瓜化: 以 配置文件 的形式封装了大部分微调场景,0基础的非专业人员也能一键开始微调。
  • 🍃 轻量级: 对于 7B参数量的LLM,微调所需的最小显存仅为 8GB

1.LLM部分

利用 XTuner 微调一个具有个人认知的小助手。通过微调后,大模型可以被调整成我们想要的样子,下面就让我们一步步的来实现这个有趣的过程吧!

1.1开发机准备

创建开发机的过程和前几节的一样不再赘述。这里要新建xtuner环境,使用如下代码创建环境:

studio-conda xtuner0.1.17

1.2.Xtuner前期准备

1.2.1Xtuner安装
# 激活环境
conda activate xtuner0.1.17
# 进入Home目录 注意root用户就是进入/root
cd ~
# 创建版本文件夹并进入
mkdir -p /root/xtuner0117 && cd /root/xtuner0117

# 拉取 0.1.17 的版本源码
git clone -b v0.1.17  https://github.com/InternLM/xtuner

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

# 从源码安装 XTuner
pip install -e '.[all]'
1.2.2数据集准备

为了让模型能够让模型认清自己的身份弟位,知道在询问自己是谁的时候回复成我们想要的样子,我们就需要通过在微调数据集中大量掺杂这部分的数据。
首先我们先创建一个文件夹来存放我们这次训练所需要的所有文件。

# 创建一个文件夹,进入该文件夹。
mkdir -p /root/ft && cd /root/ft

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

之后在 data 目录下新建一个 generate_data.py 文件,将以下代码复制进去,然后运行该脚本即可生成数据集。

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

打开该 python 文件后将下面的内容复制进去

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)

完事运行该py文件。

# 确保先进入该文件夹
cd /root/ft/data

# 运行代码
python /root/ft/data/generate_data.py

可以看到在data的路径下便生成了一个名为 personal_assistant.json 的文件,这样微调的数据集就准备好啦!里面就包含了 5000 条 input 和 output 的数据对,如下图所示
在这里插入图片描述

1.2.3模型准备

由于本次课程显存方面的限制,这里我们就使用 InternLM 最新推出的小模型 InterLM2-Chat-1.8B 来完成此次的微调演示。
对于在 InternStudio 上运行的小伙伴们,可以不用通过 OpenXLab 或者 Modelscope 进行模型的下载。我们直接通过以下代码一键创建文件夹并将所有文件复制进去。

# 创建目标文件夹,确保它存在。
# -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

模型目录如下图所示
在这里插入图片描述

1.2.4 配置文件选择

准备好模型和数据集后,我们就要根据我们选择的微调方法方法结合前面的信息来找到与我们最匹配的配置文件了,从而减少我们对配置文件的修改量。
配置文件(config),是一种用于定义和控制模型训练和测试过程中各个方面的参数和设置的工具。准备好的配置文件只要运行起来就代表着模型就开始训练或者微调了。XTuner 提供多个开箱即用的配置文件,用户可以通过下列命令查看:

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

# 假如我们想找到 internlm2-1.8b 模型里支持的配置文件
xtuner list-cfg -p internlm2_1_8b

以下是搜索到的和internlm2-1.8b相关的配置文件
在这里插入图片描述
虽然我们用的数据集并不是 alpaca 而是我们自己通过脚本制作的小助手数据集 ,但是由于我们是通过 QLoRA 的方式对 internlm2-chat-1.8b 进行微调。而最相近的配置文件应该就是 internlm2_1_8b_qlora_alpaca_e3 ,因此我们可以选择拷贝这个配置文件到当前目录:

# 创建一个存放 config 文件的文件夹
mkdir -p /root/ft/config

# 使用 XTuner 中的 copy-cfg 功能将 config 文件复制到指定的位置
xtuner copy-cfg internlm2_1_8b_qlora_alpaca_e3 /root/ft/config

Tip:
1.配置文件名解释:
以 internlm2_1_8b_qlora_alpaca_e3 举例:

模型名说明
internlm2_1_8b模型名称
qlora使用的算法
alpaca数据集名称
e3把数据集跑3次

2.其实在微调的时候最重要的还是要自己准备一份高质量的数据集,这个才是你能否真微调出效果最核心的利器。学会如何构建一份高质量的数据集是至关重要的。

1.3.配置文件修改

下面要做的事情就是根据自己的内容对该配置文件进行调整,使其能够满足实际训练的要求。
直接将以下内容复制到/root/ft/config/internlm2_1_8b_qlora_alpaca_e3_copy.py文件中,覆盖原始内容。

# 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)

Tips:
1.配置文件介绍

部分主要内容
Settings涵盖了模型基本设置,如预训练模型的选择、数据集信息和训练过程中的一些基本参数(如批大小、学习率等)。
Model & Tokenizer指定了用于训练的模型和分词器的具体类型及其配置,包括预训练模型的路径和是否启用特定功能(如可变长度注意力),这是模型训练的核心组成部分。
Dataset & Dataloader描述了数据处理的细节,包括如何加载数据集、预处理步骤、批处理大小等,确保了模型能够接收到正确格式和质量的数据。
Scheduler & Optimizer配置了优化过程中的关键参数,如学习率调度策略和优化器的选择,这些是影响模型训练效果和速度的重要因素。
Runtime定义了训练过程中的额外设置,如日志记录、模型保存策略和自定义钩子等,以支持训练流程的监控、调试和结果的保存。

一般来说我们需要更改的部分其实只包括前三部分,而且修改的主要原因是我们修改了配置文件中规定的模型、数据集。后两部分都是 XTuner 官方帮我们优化好的东西,一般而言只有在魔改的情况下才需要进行修改。
2.常用参数介绍

参数名解释
data_path数据路径或 HuggingFace 仓库名
max_length单条数据最大 Token 数,超过则截断
pack_to_max_length是否将多条短数据拼接到 max_length,提高 GPU 利用率
accumulative_counts梯度累积,每多少次 backward 更新一次参数
sequence_parallel_size并行序列处理的大小,用于模型训练时的序列并行
batch_size每个设备上的批量大小
dataloader_num_workers数据加载器中工作进程的数量
max_epochs训练的最大轮数
optim_type优化器类型,例如 AdamW
lr学习率
betas优化器中的 beta 参数,控制动量和平方梯度的移动平均
weight_decay权重衰减系数,用于正则化和避免过拟合
max_norm梯度裁剪的最大范数,用于防止梯度爆炸
warmup_ratio预热的比例,学习率在这个比例的训练过程中线性增加到初始学习率
save_steps保存模型的步数间隔
save_total_limit保存的模型总数限制,超过限制时删除旧的模型文件
prompt_template模板提示,用于定义生成文本的格式或结构

1.4 模型训练

1.4.1常规训练

使用如下命令开始训练

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

通过添加 --work-dir 指定特定的文件保存位置,比如说就保存在 /root/ft/train 路径下。假如不添加的话模型训练的过程文件将默认保存在 ./work_dirs/internlm2_1_8b_qlora_alpaca_e3_copy 的位置,就比如说我是在 /root/ft/train 的路径下输入该指令,那么我的文件保存的位置就是在 /root/ft/train/work_dirs/internlm2_1_8b_qlora_alpaca_e3_copy 的位置下。
训练之后文件目录如下所示:
在这里插入图片描述

1.4.2使用deepspeed加速训练

我们也可以结合 XTuner 内置的 deepspeed 来加速整体的训练过程,共有三种不同的 deepspeed 类型可进行选择,分别是 deepspeed_zero1, deepspeed_zero2 和 deepspeed_zero3。
使用如下命令使用deepspeed来加速训练:

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

训练之后文件目录如下所示:
在这里插入图片描述
Tips:
1.DeepSpeed介绍
DeepSpeed是一个深度学习优化库,由微软开发,旨在提高大规模模型训练的效率和速度。它通过几种关键技术来优化训练过程,包括模型分割、梯度累积、以及内存和带宽优化等。DeepSpeed特别适用于需要巨大计算资源的大型模型和数据集。
在DeepSpeed中,zero 代表“ZeRO”(Zero Redundancy Optimizer),是一种旨在降低训练大型模型所需内存占用的优化器。ZeRO 通过优化数据并行训练过程中的内存使用,允许更大的模型和更快的训练速度。ZeRO 分为几个不同的级别,主要包括:

12
deepspeed_zero1这是ZeRO的基本版本,它优化了模型参数的存储,使得每个GPU只存储一部分参数,从而减少内存的使用。
deepspeed_zero2在deepspeed_zero1的基础上,deepspeed_zero2进一步优化了梯度和优化器状态的存储。它将这些信息也分散到不同的GPU上,进一步降低了单个GPU的内存需求。
deepspeed_zero3这是目前最高级的优化等级,它不仅包括了deepspeed_zero1和deepspeed_zero2的优化,还进一步减少了激活函数的内存占用。这通过在需要时重新计算激活(而不是存储它们)来实现,从而实现了对大型模型极其内存效率的训练。

选择哪种deepspeed类型主要取决于你的具体需求,包括模型的大小、可用的硬件资源(特别是GPU内存)以及训练的效率需求。一般来说:

  • 如果你的模型较小,或者内存资源充足,可能不需要使用最高级别的优化。
  • 如果你正在尝试训练非常大的模型,或者你的硬件资源有限,使用deepspeed_zero2或deepspeed_zero3可能更合适,因为它们可以显著降低内存占用,允许更大模型的训练。
  • 选择时也要考虑到实现的复杂性和运行时的开销,更高级的优化可能需要更复杂的设置,并可能增加一些计算开销。
1.4.3训练结果

在这里插入图片描述
这里我训练之后,不管是300轮还是600轮,都是会回答这一句话了,都过拟合了,因此调小了保存权重的间隔为100轮,得到如下结果:
在这里插入图片描述
稍微有点问题,但是没那么过拟合了,出现这样的问题是生成数据的部分,生成的条数过多。

我们想要解决过拟合这个问题,可以通过以下两个方式解决:
1.减少保存权重文件的间隔并增加权重文件保存的上限:这个方法实际上就是通过降低间隔结合评估问题的结果,从而找到最优的权重文。我们可以每隔100个批次来看什么时候模型已经学到了这部分知识但是还保留着基本的常识,什么时候已经过拟合严重只会说一句话了。但是由于再配置文件有设置权重文件保存数量的上限,因此同时将这个上限加大也是非常必要的。
2.增加常规的对话数据集从而稀释原本数据的占比:这个方法其实就是希望我们正常用对话数据集做指令微调的同时还加上一部分的数据集来让模型既能够学到正常对话,但是在遇到特定问题时进行特殊化处理。比如说我在一万条正常的对话数据里混入两千条和小助手相关的数据集。这种其实是比较常见的处理方式,大家可以自己动手尝试实践一下。

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

1.5.1模型转换

模型转换的本质其实就是将原本使用 Pytorch 训练出来的模型权重文件转换为目前通用的 Huggingface 格式文件,那么我们可以通过以下指令来实现一键转换

# 创建一个保存转换后 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_768.pth /root/ft/huggingface

转换后得到如下文件:
在这里插入图片描述
此时,huggingface 文件夹即为我们平时所理解的所谓 “LoRA 模型文件”

1.5.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模型,假如是的话就要加上,不是就不需要添加

整合完后得到如下文件:
在这里插入图片描述
Tips:
CLIP(Contrastive Language–Image Pre-training)模型是 OpenAI 开发的一种预训练模型,它能够理解图像和描述它们的文本之间的关系。CLIP 通过在大规模数据集上学习图像和对应文本之间的对应关系,从而实现了对图像内容的理解和分类,甚至能够根据文本提示生成图像。 在模型整合完成后,我们就可以看到 final_model 文件夹里生成了和原模型文件夹非常近似的内容,包括了分词器、权重文件、配置信息等等。当我们整合完成后,我们就能够正常的调用这个模型进行对话测试了。

1.5.3对话测试

让我们可以直接在终端与 Huggingface 格式的模型进行对话操作。我们只需要准备我们刚刚转换好的模型路径并选择对应的提示词模版(prompt-template)即可进行对话。假如 prompt-template 选择有误,很有可能导致模型无法正确的进行回复。

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

我们可以通过一些简单的测试来看看微调后的模型的能力。假如我们想要输入内容需要在输入文字后敲击两下回车,假如我们想清楚历史记录需要输入 RESET,假如我们想要退出则需要输入 EXIT。
微调后的回答:
在这里插入图片描述
微调前的回答:
在这里插入图片描述
对于 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设置随机种子,用于生成可重现的文本内容

除了这些参数以外其实还有一个非常重要的参数就是 –adapter ,这个参数主要的作用就是可以在转化后的 adapter 层与原模型整合之前来对该层进行测试。使用这个额外的参数对话的模型和整合后的模型几乎没有什么太多的区别,因此我们可以通过测试不同的权重文件生成的 adapter 来找到最优的 adapter 进行最终的模型整合工作

# 使用 --adapter 参数与完整的模型进行对话
xtuner chat /root/ft/model --adapter /root/ft/huggingface --prompt-template internlm2_chat
1.5.4Web demo部署

先下载网页端 web demo 所需要的依赖

pip install streamlit==1.24.0

下载 InternLM 项目代码

# 创建存放 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

将 /root/ft/web_demo/InternLM/chat/web_demo.py 中的内容替换为以下的代码(与源代码相比,此处修改了模型路径和分词器路径,并且也删除了 avatar 及 system_prompt 部分的内容,同时与 cli 中的超参数进行了对齐)。
在运行前,我们还需要做的就是将端口映射到本地。打开 PowerShell 后,先查询端口,再根据端口键入命令 ,然后我们需要在 PowerShell 中输入以下内容(需要替换为自己的端口号):

# 从本地使用 ssh 连接 studio 端口
# 将下方端口号 38374 替换成自己的端口号
ssh -CNg -L 6006:127.0.0.1:6006 root@ssh.intern-ai.org.cn -p 38374

之后我们需要输入以下命令运行 /root/personal_assistant/code/InternLM 目录下的 web_demo.py 文件。

streamlit run /root/ft/web_demo/InternLM/chat/web_demo.py --server.address 127.0.0.1 --server.port 6006

打开 http://127.0.0.1:6006 后,等待加载完成即可进行对话,如下:
在这里插入图片描述

2.多模态部分——XTuner多模态训练与测试

接下来尝试使用 XTuner 微调多模态LLM。

2.1 多模态LLM原理简介

2.1.1 文本单模态
输入文本
文本Embedding模型
文本向量
L L M
输出文本
2.1.2文本+图像多模态
Image Projector
输入图像
图像向量
输入文本
文本Embedding模型
文本向量
L L M
输出文本

2.2 LLaVA方案简介

Haotian Liu等使用GPT-4V对图像数据生成描述,以此构建出大量 < q u e s t i o n t e x t > < i m a g e > − − < a n s w e r t e x t > <question text><image> -- <answer text> <questiontext><image><answertext>的数据对。利用这些数据对,配合文本单模态LLM,训练出一个Image Projector。所使用的文本单模型LLM和训练出来的Image Projector,统称为LLaVA模型

2.2.1 LLaVA训练阶段示意图
训练阶段
显卡
文本+图像
问答对
(训练数据)
文本单模态
LLM
Image
Projector
2.2.2 LLaVA测试阶段示意图
测试阶段
显卡
Image
Projector
文本单模态
LLM
输入图像
输出文本

二者都是在已有LLM的基础上,用新的数据训练一个新的小文件。
只不过,LLM套上LoRA之后,有了新的灵魂(角色);而LLM套上Image Projector之后,才有了眼睛。

2.3 快速上手

2.3.1 XTuner安装

开发机的创建以及xtuner的安装和之前的一样,不赘述。

2.3.2 概述

在本节中,我们将 自己构造数据对,基于InternLM2_Chat_1.8B这个文本单模态模型,使用LLaVA方案,训练一个给InternLM2_Chat_1.8B使用的Image Projector文件。LLaVA方案中,给LLM增加视觉能力的过程,即是训练Image Projector文件的过程。 该过程分为2个阶段:Pretrain和Finetune

Finetune阶段
Pretrain阶段
显卡
图像
+
复杂对话文本
Finetuned
LLaVA
显卡
图像
+
标题(短文本)
文本单模态LLM
(InternLM2_Chat_1.8B)
Pretrained
LLaVA
2.3.3 Pretrain阶段

Pretrain阶段,我们会使用大量的图片+简单文本(caption, 即图片标题)数据对,使LLM理解图像中的普遍特征。即,对大量的图片进行粗看
Pretrain阶段训练完成后,此时的模型已经有视觉能力了!但是由于训练数据中都是图片+图片标题,所以此时的模型虽然有视觉能力,但无论用户问它什么,它都只会回答输入图片的标题。即,此时的模型只会给输入图像“写标题”。
但是微调阶段对于硬件的要求比较高,这里直接使用提供的Pretrain阶段的产物——iter_2181.pth文件进入下一阶段进行Finetune。

2.3.4 Finetune阶段

在Finetune阶段,使用图片+复杂文本数据对,对Pretrain得到的Image Projector即iter_2181.pth进行进一步的训练
(1)训练数据构建
格式:

[
    {
        "id": "随便什么字符串",
        "image": "图片文件的相对位置。相对谁?相对你后面config文件里指定的image_folder参数的路径。",
        "conversation": [
            {
                "from": "human",
                "value": "<image>\n第1个问题。"
            },
            {
                "from": "gpt",
                "value": "第1个回答"
            },
            {
                "from": "human",
                "value": "第2个问题。"
            },
            {
                "from": "gpt",
                "value": "第2个回答"
            },
            # ......
            {
                "from": "human",
                "value": "第n个问题。"
            },
            {
                "from": "gpt",
                "value": "第n个回答"
            },
        ]
    },

    # 下面是第2组训练数据了。

    {
        "id": "随便什么字符串",
        "image": "图片文件的相对位置。相对谁?相对你后面config文件里指定的image_folder参数的路径。",
        "conversation": [
            {
                "from": "human",
                "value": "<image>\n第1个问题。"
            },
            # ......
            {
                "from": "gpt",
                "value": "第n个回答"
            }
        ]
    }
]

例子:

[
  {
    "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."
      }
    ]
  }
 ]

制作:
在这里插入图片描述
效法LLaVA作者的做法,将自己的图片发送给GPT,要求其按照上述格式生成若干条问答对。针对这张示例图片的问答对数据(repeat_data.json),按照下面的脚本运行生成

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

(2)准备配置文件
创建配置文件

# 查询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

查到有这两个配置模板,复制到指定位置。
在这里插入图片描述
修改配置文件:
修改llava_internlm2_chat_1_8b_qlora_clip_vit_large_p14_336_lora_e1_gpu8_finetune_copy.py文件中的:

  • 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?']

(3)开始Finetune

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
2.3.5 Finetune前后对比

(1)Finetune前

# 解决小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

在这里插入图片描述

(2)Finetune后

# 解决小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

在这里插入图片描述

问题:

报如下图的错误
在这里插入图片描述
解决:
参考https://github.com/InternLM/Tutorial/issues/725,是transformers的包版本的问题,重装改为4.40.0就OK了

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不是吧这都有重名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值