书生大模型实战营——XTuner 微调个人小助手认知
本文最后更新于250 天前,其中的信息可能已经过时,如有错误请发送邮件到mapleleaf2333@gmail.com

本文参考实战营提供的教程:Tutorial/docs/L1/XTuner at camp3 · InternLM/Tutorial (github.com)

基础任务:

  • 使用 XTuner 微调 InternLM2-Chat-1.8B 实现自己的小助手认知

进阶任务:

  • 用自己感兴趣的知识对基座模型进行增量预训练微调
  • 在资源允许的情况下,尝试实现多卡微调与分布式微调
  • 将自我认知的模型上传到 OpenXLab,并将应用部署到 OpenXLab

1 前置知识

在进行实践之前,我们先要知道什么是大模型微调。

微调(fine-tuning)是一种基于预训练模型,通过少量的调整(fine-tune)来适应新的任务或数据的方法,其主要是将模型中一些层的权重参数进行微调,以适应新的数据集或任务。

1.1 微调的两种范式

在大模型的下游应用中,经常会用到两种微调模式:增量预训练指令跟随

1.1.1 增量预训练

增量预训练是一种在已有预训练模型(比如:InternLM基座模型)的基础上,利用特定领域的数据进行进一步训练的方法。它的目的是在保持模型原有能力的同时,注入新的领域知识,进一步优化现有的预训练模型,从而提升模型在特定领域任务中的表现(比如:InternLM垂类基座模型)。

增量预训练模型能够接受少量的新数据进行更新并适应新的任务,而不需要重新训练整个模型,这种方式可以很好地利用现有的预训练模型的知识,并在新数据上获得更好的性能。

1.1.2 指令跟随

指令跟随是指让模型根据用户输入的指令来执行相应的操作。模型通过对大量自然语言指令和相应操作的数据进行训练,学习如何将指令分解为具体的子任务,并选择合适的模块来执行这些任务(比如:InternLM垂类对话模型)。

1.2 微调技术

大多数大型语言模型(LLM)的参数规模巨大,且规模日益增大,导致模型的训练和微调成本高昂,直接训练需要耗费大量计算资源和费用。近年来,如何高效地对大模型进行微调成为了研究热点,而LoRA和QLoRA两种微调技术因其高效性和实用性受到了广泛关注。

1.2.1 LoRA

LoRA(Low-Rank Adaptation)是一种使用低精度权重对大型预训练语言模型进行微调的技术,LoRA 的核心思想是将模型的权重矩阵分解为两个低秩矩阵的乘积,以减少参数的数量,同时保持模型的表达能力。这种方法降低了模型的存储需求,也降低了计算成本,实现了对大模型的快速适应,同时保持了模型性能。

然而,LoRA 在某些任务上可能表现不佳,特别是在需要高度复杂的表示或特征的任务中。低秩近似可能会限制模型的表达能力,从而影响其对复杂模式的捕捉。同时由于 LoRA 依赖于低秩近似,可能会导致某些重要但细微的特征在模型中被忽略或表达不足。

1.2.2 QLoRA

QLoRA(Quantized LoRA)是一种改进的微调技术,它结合了低秩适配器(LoRA)和量化技术,以优化大模型的微调过程。QLoRA 通过将预训练模型的权重量化为低精度格式(如 int4),显著减少了计算和存储需求,同时保持了模型的性能。LoRA 的低秩适配器使模型能够在微调时引入少量新参数,从而提高适应性。由于这些优化,QLoRA 使得在资源有限的设备上(如消费级显卡)进行大模型的微调训练变得更加现实和高效。

1.3 Xtuner

XTuner 的官方仓库是:https://github.com/InternLM/xtuner

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

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

其优点是:

  • 适配多种生态
    • 支持多种微调算法
    • 适配多种开源生态(HuggingFace、ModelScope等)
    • 自动优化加速器
  • 适配多种硬件

2 实验环境配置

2.1 环境与工具安装

进入开发机之后,我们需要先克隆Tutorial仓库的资料到本地

mkdir -p /root/InternLM/Tutorial
git clone -b camp3  https://github.com/InternLM/Tutorial /root/InternLM/Tutorial

然后创建一个合适的虚拟环境:

# 创建虚拟环境
conda create -n xtuner0121 python=3.10 -y

# 激活虚拟环境(注意:后续的所有操作都需要在这个虚拟环境中进行)
conda activate xtuner0121

# 安装一些必要的库
conda install pytorch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 pytorch-cuda=12.1 -c pytorch -c nvidia -y
# 安装其他依赖
pip install transformers==4.39.3
pip install streamlit==1.36.0

环境配置完成后,需要安装Xtuner,先从github下载源码:

# 创建一个目录,用来存放源代码
mkdir -p /root/InternLM/code

cd /root/InternLM/code

git clone -b v0.1.21  https://github.com/InternLM/XTuner /root/InternLM/code/XTuner

在源码目录中进行安装:

# 进入到源码目录
cd /root/InternLM/code/XTuner
conda activate xtuner0121

# 执行安装
pip install -e '.[deepspeed]'

如果速度太慢可以换成 pip install -e '.[deepspeed]' -i https://mirrors.aliyun.com/pypi/simple/

最后可以通过下面命令查看是否安装成功:

xtuner version​
xtuner help #查看帮助手册

2.2 模型准备

对于学习而言,可以使用 InternLM 推出的1.8B的小模型来完成此次微调演示。

由于我们是在实战营提供的开发机中进行实验,可以不用通过 HuggingFace、OpenXLab 或者 Modelscope 进行模型的下载。可以通过以下代码一键通过符号链接的方式链接到模型文件,这样既节省了空间,也便于管理:

# 创建一个目录,用来存放微调的所有资料,后续的所有操作都在该路径中进行
mkdir -p /root/InternLM/XTuner

cd /root/InternLM/XTuner

mkdir -p Shanghai_AI_Laboratory

ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b Shanghai_AI_Laboratory/internlm2-chat-1_8b

模型文件准备好后,我们可以使用tree命令来观察目录结构。

apt-get install -y tree​tree -l

结果如下所示:

image-20240818153746642

3 微调实践

3.1 微调前的模型对话

在进行微调之前,我们可以撰写一个Streamlit程序的脚本,用来与微调前的模型对话(实际上就是构建一个前端),确认其效果,脚本的代码如下所示:

import copy
import warnings
from dataclasses import asdict, dataclass
from typing import Callable, List, Optional

import streamlit as st
import torch
from torch import nn
from transformers.generation.utils import (LogitsProcessorList,
                                           StoppingCriteriaList)
from transformers.utils import logging

# 加载预训练的语言模型和分词器
from transformers import AutoTokenizer, AutoModelForCausalLM  # isort: skip

logger = logging.get_logger(__name__)


model_name_or_path = "/root/InternLM/XTuner/Shanghai_AI_Laboratory/internlm2-chat-1_8b"

# 生成配置的类,包含各种参数,用于调节生成文本的多样性和质量
@dataclass
class GenerationConfig:
    # this config is used for chat to provide more diversity
    max_length: int = 2048
    top_p: float = 0.75
    temperature: float = 0.1
    do_sample: bool = True
    repetition_penalty: float = 1.000


@torch.inference_mode()
def generate_interactive(
    model, # 预训练语言模型
    tokenizer,# 分词器
    prompt, # 用户输入的提示
    generation_config: Optional[GenerationConfig] = None,
    logits_processor: Optional[LogitsProcessorList] = None, # 用于处理logits的处理器列表
    stopping_criteria: Optional[StoppingCriteriaList] = None, # 用于控制生成停止的标准列表
    prefix_allowed_tokens_fn: Optional[Callable[[int, torch.Tensor], # 用于控制每一步生成时允许的token的函数
                                                List[int]]] = None,
    additional_eos_token_id: Optional[int] = None,
    **kwargs,
):
    # 将用户的prompt转化为模型输入
    inputs = tokenizer([prompt], padding=True, return_tensors='pt')
    input_length = len(inputs['input_ids'][0])
    for k, v in inputs.items():
        inputs[k] = v.cuda() # 将输入转移到GPU
    input_ids = inputs['input_ids']
    _, input_ids_seq_length = input_ids.shape[0], input_ids.shape[-1]
    
     # 如果没有指定生成配置,则使用模型的默认配置
    if generation_config is None:
        generation_config = model.generation_config
    generation_config = copy.deepcopy(generation_config)
    model_kwargs = generation_config.update(**kwargs)
    bos_token_id, eos_token_id = (  # noqa: F841  # pylint: disable=W0612
        generation_config.bos_token_id,
        generation_config.eos_token_id,
    )
    
    if isinstance(eos_token_id, int):
        eos_token_id = [eos_token_id]
        
    if additional_eos_token_id is not None:
        eos_token_id.append(additional_eos_token_id)
        
    # 判断是否使用默认的最大长度控制生成文本的长度
    has_default_max_length = kwargs.get(
        'max_length') is None and generation_config.max_length is not None
    
    if has_default_max_length and generation_config.max_new_tokens is None:
        warnings.warn(
            f"Using 'max_length''s default ({repr(generation_config.max_length)}) \
                to control the generation length. "
            'This behaviour is deprecated and will be removed from the \
                config in v5 of Transformers -- we'
            ' recommend using `max_new_tokens` to control the maximum \
                length of the generation.',
            UserWarning,
        )
    elif generation_config.max_new_tokens is not None:
        generation_config.max_length = generation_config.max_new_tokens + \
            input_ids_seq_length
        if not has_default_max_length:
            logger.warn(  # pylint: disable=W4902
                f"Both 'max_new_tokens' (={generation_config.max_new_tokens}) "
                f"and 'max_length'(={generation_config.max_length}) seem to "
                "have been set. 'max_new_tokens' will take precedence. "
                'Please refer to the documentation for more information. '
                '(https://huggingface.co/docs/transformers/main/'
                'en/main_classes/text_generation)',
                UserWarning,
            )
		
    # 如果输入序列的长度已经超过了最大长度,则发出警告
    if input_ids_seq_length >= generation_config.max_length:
        input_ids_string = 'input_ids'
        logger.warning(
            f"Input length of {input_ids_string} is {input_ids_seq_length}, "
            f"but 'max_length' is set to {generation_config.max_length}. "
            'This can lead to unexpected behavior. You should consider'
            " increasing 'max_new_tokens'.")

    # 2. 设置生成过程中使用的logits处理器和停止标准
    logits_processor = logits_processor if logits_processor is not None \
        else LogitsProcessorList()
    stopping_criteria = stopping_criteria if stopping_criteria is not None \
        else StoppingCriteriaList()

    logits_processor = model._get_logits_processor(
        generation_config=generation_config,
        input_ids_seq_length=input_ids_seq_length,
        encoder_input_ids=input_ids,
        prefix_allowed_tokens_fn=prefix_allowed_tokens_fn,
        logits_processor=logits_processor,
    )

    stopping_criteria = model._get_stopping_criteria(
        generation_config=generation_config,
        stopping_criteria=stopping_criteria)
    logits_warper = model._get_logits_warper(generation_config)

    unfinished_sequences = input_ids.new(input_ids.shape[0]).fill_(1)
    scores = None
    
    # 开始生成序列
    while True:
        model_inputs = model.prepare_inputs_for_generation(
            input_ids, **model_kwargs)
        # 前向传播以获得下一个token的logits
        outputs = model(
            **model_inputs,
            return_dict=True,
            output_attentions=False,
            output_hidden_states=False,
        )

        next_token_logits = outputs.logits[:, -1, :]

        # 处理logits分布
        next_token_scores = logits_processor(input_ids, next_token_logits)
        next_token_scores = logits_warper(input_ids, next_token_scores)

        # 根据概率选择下一个token
        probs = nn.functional.softmax(next_token_scores, dim=-1)
        if generation_config.do_sample:
            next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1)
        else:
            next_tokens = torch.argmax(probs, dim=-1)

        # 更新生成的序列
        input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1)
        model_kwargs = model._update_model_kwargs_for_generation(
            outputs, model_kwargs, is_encoder_decoder=False)
        unfinished_sequences = unfinished_sequences.mul(
            (min(next_tokens != i for i in eos_token_id)).long())
				
        # 解码输出的token序列为文本
        output_token_ids = input_ids[0].cpu().tolist()
        output_token_ids = output_token_ids[input_length:]
        for each_eos_token_id in eos_token_id:
            if output_token_ids[-1] == each_eos_token_id:
                output_token_ids = output_token_ids[:-1]
        response = tokenizer.decode(output_token_ids)
				
        # 逐步返回生成的文本
        yield response
				# 当所有序列完成生成或达到最大长度时停止生成
        if unfinished_sequences.max() == 0 or stopping_criteria(
                input_ids, scores):
            break

            
# 清除会话状态中的聊天记录
def on_btn_click():
    del st.session_state.messages


@st.cache_resource
def load_model():
    # 加载预训练模型和分词器
    model = (AutoModelForCausalLM.from_pretrained(model_name_or_path,
                                                  trust_remote_code=True).to(
                                                      torch.bfloat16).cuda())
    tokenizer = AutoTokenizer.from_pretrained(model_name_or_path,
                                              trust_remote_code=True)
    return model, tokenizer

  
# 准备生成配置,通过前端的滑块来调整生成参数
def prepare_generation_config():
    with st.sidebar:
        max_length = st.slider('Max Length',
                               min_value=8,
                               max_value=32768,
                               value=2048)
        top_p = st.slider('Top P', 0.0, 1.0, 0.75, step=0.01)
        temperature = st.slider('Temperature', 0.0, 1.0, 0.1, step=0.01)
        st.button('Clear Chat History', on_click=on_btn_click)

    generation_config = GenerationConfig(max_length=max_length,
                                         top_p=top_p,
                                         temperature=temperature)

    return generation_config

  
# 定义用户和助手的消息模板
user_prompt = '<|im_start|>user\n{user}<|im_end|>\n'
robot_prompt = '<|im_start|>assistant\n{robot}<|im_end|>\n'
cur_query_prompt = '<|im_start|>user\n{user}<|im_end|>\n\
    <|im_start|>assistant\n'


# 组合历史对话,将用户和助手的消息组合成prompt,作为模型生成时的输入
def combine_history(prompt):
    messages = st.session_state.messages
    meta_instruction = ('')
    total_prompt = f"<s><|im_start|>system\n{meta_instruction}<|im_end|>\n"
    for message in messages:
        cur_content = message['content']
        if message['role'] == 'user':
            cur_prompt = user_prompt.format(user=cur_content)
        elif message['role'] == 'robot':
            cur_prompt = robot_prompt.format(robot=cur_content)
        else:
            raise RuntimeError
        total_prompt += cur_prompt
    total_prompt = total_prompt + cur_query_prompt.format(user=prompt)
    return total_prompt


def main():
    # torch.cuda.empty_cache()
    print('load model begin.')
    model, tokenizer = load_model()
    print('load model end.')


    st.title('InternLM2-Chat-1.8B')

    generation_config = prepare_generation_config()

    # Initialize chat history
    if 'messages' not in st.session_state:
        st.session_state.messages = []

    # Display chat messages from history on app rerun
    for message in st.session_state.messages:
        with st.chat_message(message['role'], avatar=message.get('avatar')):
            st.markdown(message['content'])

    # Accept user input
    if prompt := st.chat_input('What is up?'):
        # Display user message in chat message container
        with st.chat_message('user'):
            st.markdown(prompt)
        real_prompt = combine_history(prompt)
        # Add user message to chat history
        st.session_state.messages.append({
            'role': 'user',
            'content': prompt,
        })

        with st.chat_message('robot'):
            message_placeholder = st.empty()
            for cur_response in generate_interactive(
                    model=model,
                    tokenizer=tokenizer,
                    prompt=real_prompt,
                    additional_eos_token_id=92542,
                    **asdict(generation_config),
            ):
                # Display robot response in chat message container
                message_placeholder.markdown(cur_response + '▌')
            message_placeholder.markdown(cur_response)
        # Add robot response to chat history
        st.session_state.messages.append({
            'role': 'robot',
            'content': cur_response,  # pylint: disable=undefined-loop-variable
        })
        torch.cuda.empty_cache()


if __name__ == '__main__':
    main()

运行该脚本:

conda activate xtuner0121

streamlit run /root/InternLM/Tutorial/tools/xtuner_streamlit_demo.py

运行后,在访问前,我们还需要将端口映射到本地:

其中,8501是Streamlit程序的服务端口,43551需要替换为自己的开发机的端口。

ssh -CNg -L 8501:127.0.0.1:8501 root@ssh.intern-ai.org.cn -p 43551

运行结果如下:

image-20240818162334472

3.2 指令跟随微调

接下来我们要对模型进行微调,目标是将模型调整成自己的专属助手。

3.2.1 准备数据文件

为了让模型能够认清自己的身份弟位,在询问自己是谁的时候按照我们预期的结果进行回复,我们就需要通过在微调数据集中大量加入这样的数据。我们准备一个数据集文件datas/assistant.json,文件内容为对话数据。

cd /root/InternLM/XTuner
mkdir -p datas
touch datas/assistant.json

为了简化数据文件准备,我们也可以通过脚本生成的方式来准备数据。创建一个脚本文件 xtuner_generate_assistant.py

cd /root/InternLM/XTuner
touch xtuner_generate_assistant.py

脚本内容如下:

import json

# 设置用户的名字
name = '落尘同学'
# 设置需要重复添加的数据次数
n = 8000

# 初始化数据
data = [
{"conversation": [{"input": "请介绍一下你自己", "output": "我是{}的小助手,内在是上海AI实验室书生·浦语的1.8B大模型哦,我可是非常专业的,有什么问题就交给我吧!".format(name)}]},
{"conversation": [{"input": "你在实战营做什么", "output": "我在这里帮助{}完成XTuner微调个人小助手的任务,我可是勤勤恳恳的哦!快来表扬我!".format(name)}]}
]

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

# 将data列表中的数据写入到'datas/assistant.json'文件中
with open('datas/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)

假如想要让微调后的模型能够完完全全认识到你的身份,我们还可以把第6行的n的值调大一点。不过n值太大的话容易导致过拟合,无法有效回答其他问题。

然后执行脚本:

cd /root/InternLM/XTuner
conda activate xtuner0121

python xtuner_generate_assistant.py

准备好的数据目录结构如下:

image-20240818162655451

3.2.2 准备配置文件

在准备好了模型和数据集后,我们就要根据我们选择的微调方法结合微调方案来找到与我们最匹配的配置文件了,从而减少我们对配置文件的修改量。

XTuner 提供多个开箱即用的配置文件,可以通过以下命令查看。

xtuner list-cfg 命令用于列出内置的所有配置文件。参数 -p--pattern 表示模式匹配,后面跟着的内容将会在所有的配置文件里进行模糊匹配搜索,然后返回最有可能得内容。比如我们这里微调的是书生·浦语的模型,我们就可以匹配搜索 internlm2

conda activate xtuner0121

xtuner list-cfg -p internlm2

由于我们是对internlm2-chat-1_8b模型进行指令微调,所以与我们的需求最匹配的配置文件是 internlm2_chat_1_8b_qlora_alpaca_e3,这里就复制该配置文件。

xtuner copy-cfg 命令用于复制一个内置的配置文件。该命令需要两个参数:CONFIG 代表需要复制的配置文件名称,SAVE_PATH 代表复制的目标路径。在我们的输入的这个命令中,我们的 CONFIG 对应的是上面搜索到的 internlm2_chat_1_8b_qlora_alpaca_e3 ,而 SAVE_PATH 则是当前目录 .

cd /root/InternLM/XTuner
conda activate xtuner0121

xtuner copy-cfg internlm2_chat_1_8b_qlora_alpaca_e3 .

配置文件的部分内容截图如下:

image-20240818163107935

接下来我们将修改复制过来的配置文件,使其能满足我们训练的要求。

  • PART 1部分:由于我们不再需要在 HuggingFace 上自动下载模型,因此我们先要更换模型的路径以及数据集的路径为我们本地的路径
    • 为了训练过程中能够实时观察到模型的变化情况,XTuner 贴心的推出了一个 evaluation_inputs 的参数来让我们能够设置多个问题来确保模型在训练过程中的变化是朝着我们想要的方向前进的。我们可以添加自己的输入,即'请介绍一下你自己', 'Please introduce yourself'
  • PART 3部分:由于我们准备的数据集是 JSON 格式的数据,并且对话内容已经是 inputoutput 的数据对,所以不需要进行格式转换。
  • 除此之外,我们还可以对一些重要的参数进行调整,包括学习率(lr)、训练的轮数(max_epochs)等等。

需要修改的内容如图所示:

image-20240818163454251

修改完后的完整的配置文件是:configs/internlm2_chat_1_8b_qlora_alpaca_e3_copy.py,可以通过命令复制到当前目录:

cd /root/InternLM/XTuner
cp /root/InternLM/Tutorial/configs/internlm2_chat_1_8b_qlora_alpaca_e3_copy.py ./

3.3 启动微调

当我们准备好了所有内容,我们只需要将使用 xtuner train 命令令即可开始训练。

xtuner train 命令用于启动模型微调进程。该命令需要一个参数:CONFIG 用于指定微调配置文件。这里我们使用修改好的配置文件 internlm2_chat_1_8b_qlora_alpaca_e3_copy.py。 训练过程中产生的所有文件,包括日志、配置文件、检查点文件、微调后的模型等,默认保存在 work_dirs 目录下,我们也可以通过添加 --work-dir 指定特定的文件保存位置。

cd /root/InternLM/XTuner
conda activate xtuner0121

xtuner train ./internlm2_chat_1_8b_qlora_alpaca_e3_copy.py

3.4 模型格式转换

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

我们可以使用 xtuner convert pth_to_hf 命令来进行模型格式转换。

xtuner convert pth_to_hf 命令用于进行模型格式转换。该命令需要三个参数:CONFIG 表示微调的配置文件, PATH_TO_PTH_MODEL 表示微调的模型权重文件路径,即要转换的模型权重, SAVE_PATH_TO_HF_MODEL 表示转换后的 HuggingFace 格式文件的保存路径。

除此之外,我们其实还可以在转换的命令中添加几个额外的参数,包括:

参数名解释
–fp32代表以fp32的精度开启,假如不输入则默认为fp16
–max-shard-size {GB}代表每个权重文件最大的大小(默认为2GB)
cd /root/InternLM/XTuner
conda activate xtuner0121

# 先获取最后保存的一个pth文件
pth_file=`ls -t ./work_dirs/internlm2_chat_1_8b_qlora_alpaca_e3_copy/*.pth | head -n 1`
export MKL_SERVICE_FORCE_INTEL=1
export MKL_THREADING_LAYER=GNU
xtuner convert pth_to_hf ./internlm2_chat_1_8b_qlora_alpaca_e3_copy.py ${pth_file} ./hf

转换完成后,可以看到模型被转换为 HuggingFace 中常用的 .bin 格式文件,这就代表着文件成功被转化为 HuggingFace 格式了。

此时,hf 文件夹即为我们平时所理解的所谓 “LoRA 模型文件”

可以简单理解:LoRA 模型文件 = Adapter

3.5 模型合并

对于 LoRA 或者 QLoRA 微调出来的模型其实并不是一个完整的模型,而是一个额外的层(Adapter),训练完的这个层最终还是要与原模型进行合并才能被正常的使用。

对于全量微调的模型(full)其实是不需要进行整合这一步的,因为全量微调修改的是原模型的权重而非微调一个新的 Adapter ,因此是不需要进行模型整合的。

在 XTuner 中提供了一键合并的命令 xtuner convert merge,在使用前我们需要准备好三个路径,包括原模型的路径、训练好的 Adapter 层的(模型格式转换后的)路径以及最终保存的路径。

xtuner convert merge命令用于合并模型。该命令需要三个参数:LLM 表示原模型路径,ADAPTER 表示 Adapter 层的路径, SAVE_PATH 表示合并后的模型最终的保存路径。

在模型合并这一步还有其他很多的可选参数,包括:

参数名解释
–max-shard-size {GB}代表每个权重文件最大的大小(默认为2GB)
–device {device_name}这里指的就是device的名称,可选择的有cuda、cpu和auto,默认为cuda即使用gpu进行运算
–is-clip这个参数主要用于确定模型是不是CLIP模型,假如是的话就要加上,不是就不需要添加
cd /root/InternLM/XTuner
conda activate xtuner0121

export MKL_SERVICE_FORCE_INTEL=1
export MKL_THREADING_LAYER=GNU
xtuner convert merge /root/InternLM/XTuner/Shanghai_AI_Laboratory/internlm2-chat-1_8b ./hf ./merged --max-shard-size 2GB

3.6 与微调后的模型对话

微调完成后,我们可以再次运行xtuner_streamlit_demo.py脚本来观察微调后的对话效果,不过在运行之前,我们需要将脚本中的模型路径修改为微调后的模型的路径。

# 直接修改脚本文件第18行
- model_name_or_path = "/root/InternLM/XTuner/Shanghai_AI_Laboratory/internlm2-chat-1_8b"
+ model_name_or_path = "/root/InternLM/XTuner/merged"

然后我们与3.1一样通过streamlit启动模型,进行对话,结果如下所示:

image-20240818172304824
文章作者: 落尘Alko
链接: http://mapleleaf666.vip/?p=603
来源: 落尘Alko的小窝
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇