一、关卡任务
- 使用 XTuner 微调 InternLM2-Chat-1.8B 实现自己的小助手认知,如下图所示(图中的
伍鲜同志
需替换成自己的昵称),记录复现过程并截图。 - 用自己感兴趣的知识对基座模型进行增量预训练微调(选做)
- 在资源允许的情况下,尝试实现多卡微调与分布式微调(选做)
- 将自我认知的模型上传到 OpenXLab,并将应用部署到 OpenXLab(优秀学员必做)
- OpenXLab 部署教程:Tutorial/tools/openxlab-deploy at camp2 · InternLM/Tutorial · GitHub
二、实验过程
2.1 配置基础环境
我们需要前往 InternStudio 中创建一台开发机进行使用。
在 “创建开发机” 界面,选择开发机类型:个人开发机,输入开发机名称:XTuner微调,选择开发机镜像:Cuda12.2-conda。
当我们准备好开发机之后,就可以进行下一步的微调任务了。
另外,进入开发机之后,请确保自己已经克隆了Tutorial仓库的资料到本地。
mkdir -p /root/InternLM/Tutorial
git clone -b camp3 https://github.com/InternLM/Tutorial /root/InternLM/Tutorial
在安装 XTuner 之前,我们需要先创建一个虚拟环境。使用 Anaconda
创建一个名为 xtuner0121
的虚拟环境,可以直接执行命令。
# 创建虚拟环境
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
2.2 安装 XTuner
虚拟环境创建完成后,就可以安装 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]'
最后,我们可以验证一下安装结果。
xtuner version
对于很多的初学者而言,安装好环境意味着成功了一大半!因此我们接下来就可以进入我们的下一步,准备好我们需要的模型、数据集和配置文件,并进行微调训练!
2.3 模型准备
软件安装好后,我们就可以准备要微调的模型了。
对于学习而言,我们可以使用 InternLM 推出的1.8B的小模型来完成此次微调演示。
对于在 InternStudio 上运行的小伙伴们,可以不用通过 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
执行上述操作后,Shanghai_AI_Laboratory/internlm2-chat-1_8b
将直接成为一个符号链接,这个链接指向 /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b
的位置。
这意味着,当我们访问 Shanghai_AI_Laboratory/internlm2-chat-1_8b
时,实际上就是在访问 /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b
目录下的内容。通过这种方式,我们无需复制任何数据,就可以直接利用现有的模型文件进行后续的微调操作,从而节省存储空间并简化文件管理。
模型文件准备好后,我们可以使用tree
命令来观察目录结构。
apt-get install -y tree
tree -l
我们的目录结构应该是这个样子的。
├── Shanghai_AI_Laboratory
│ └── internlm2-chat-1_8b -> /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b
│ ├── README.md
│ ├── config.json
│ ├── configuration.json
│ ├── configuration_internlm2.py
│ ├── generation_config.json
│ ├── model-00001-of-00002.safetensors
│ ├── model-00002-of-00002.safetensors
│ ├── model.safetensors.index.json
│ ├── modeling_internlm2.py
│ ├── special_tokens_map.json
│ ├── tokenization_internlm2.py
│ ├── tokenization_internlm2_fast.py
│ ├── tokenizer.model
│ └── tokenizer_config.json
在目录结构中可以看出,
internlm2-chat-1_8b
是一个符号链接。
2.4 微调前的模型对话
这里我们用 internlm2-chat-1_8b
模型,通过 QLoRA
的方式来微调一个自己的小助手认知作为案例来进行演示。
我们可以通过网页端的 Demo 来看看微调前 internlm2-chat-1_8b
的对话效果。
首先,我们需要准备一个Streamlit程序的脚本。
Streamlit程序的完整代码是:tools/xtuner_streamlit_demo.py。
然后,我们可以直接启动应用。
conda activate xtuner0121
streamlit run /root/InternLM/Tutorial/tools/xtuner_streamlit_demo.py
运行后,在访问前,我们还需要做的就是将端口映射到本地。
通过如图所示的地方,获取开发机的端口和密码。
最后,我们就可以在本地通过浏览器访问:http://127.0.0.1:8501 来进行对话了。
2.5 指令跟随微调
下面我们对模型进行微调,让模型认识到自己的弟位,了解它自己是你的一个助手。
2.5.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 = 100
# 初始化数据
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)
然后执行该脚本来生成数据文件。
cd /root/InternLM/XTuner
conda activate xtuner0121
python xtuner_generate_assistant.py
准备好数据文件后,我们的目录结构应该是这样子的。
目录结构
├── Shanghai_AI_Laboratory
│ └── internlm2-chat-1_8b -> /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b
│ ├── README.md
│ ├── config.json
│ ├── configuration.json
│ ├── configuration_internlm2.py
│ ├── generation_config.json
│ ├── model-00001-of-00002.safetensors
│ ├── model-00002-of-00002.safetensors
│ ├── model.safetensors.index.json
│ ├── modeling_internlm2.py
│ ├── special_tokens_map.json
│ ├── tokenization_internlm2.py
│ ├── tokenization_internlm2_fast.py
│ ├── tokenizer.model
│ └── tokenizer_config.json
├── datas
│ └── assistant.json
├── xtuner_generate_assistant.py
2.5.2 准备配置文件
在准备好了模型和数据集后,我们就要根据我们选择的微调方法结合微调方案来找到与我们最匹配的配置文件了,从而减少我们对配置文件的修改量。
配置文件其实是一种用于定义和控制模型训练和测试过程中各个方面的参数和设置的工具。
XTuner 提供多个开箱即用的配置文件,可以通过以下命令查看。
xtuner list-cfg
命令用于列出内置的所有配置文件。参数-p
或--pattern
表示模式匹配,后面跟着的内容将会在所有的配置文件里进行模糊匹配搜索,然后返回最有可能得内容。比如我们这里微调的是书生·浦语的模型,我们就可以匹配搜索internlm2
。
conda activate xtuner0121
xtuner list-cfg -p internlm2
2.5.3 复制一个预设的配置文件
由于我们是对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 .
复制好配置文件后,我们的目录结构应该是这样子的。
├── Shanghai_AI_Laboratory
│ └── internlm2-chat-1_8b -> /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b
│ ├── README.md
│ ├── config.json
│ ├── configuration.json
│ ├── configuration_internlm2.py
│ ├── generation_config.json
│ ├── model-00001-of-00002.safetensors
│ ├── model-00002-of-00002.safetensors
│ ├── model.safetensors.index.json
│ ├── modeling_internlm2.py
│ ├── special_tokens_map.json
│ ├── tokenization_internlm2.py
│ ├── tokenization_internlm2_fast.py
│ ├── tokenizer.model
│ └── tokenizer_config.json
├── datas
│ └── assistant.json
├── internlm2_chat_1_8b_qlora_alpaca_e3_copy.py
├── xtuner_generate_assistant.py
常用参数介绍
参数名 | 解释 |
---|---|
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 | 模板提示,用于定义生成文本的格式或结构 |
2.5.4 启动微调
完成了所有的准备工作后,我们就可以正式的开始我们下一阶段的旅程:XTuner 启动~!
当我们准备好了所有内容,我们只需要将使用 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
在训练完后,我们的目录结构应该是这样子的。
├── work_dirs
│ └── internlm2_chat_1_8b_qlora_alpaca_e3_copy
│ ├── 20240626_222727
│ │ ├── 20240626_222727.log
│ │ └── vis_data
│ │ ├── 20240626_222727.json
│ │ ├── config.py
│ │ ├── eval_outputs_iter_95.txt
│ │ └── scalars.json
│ ├── internlm2_chat_1_8b_qlora_alpaca_e3_copy.py
│ ├── iter_96.pth
│ └── last_checkpoint
2.5.5 模型格式转换
模型转换的本质其实就是将原本使用 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
模型格式转换完成后,我们的目录结构应该是这样子的。
├── hf
│ ├── README.md
│ ├── adapter_config.json
│ ├── adapter_model.bin
│ └── xtuner_config.py
转换完成后,可以看到模型被转换为 HuggingFace 中常用的 .bin 格式文件,这就代表着文件成功被转化为 HuggingFace 格式了。
此时,hf 文件夹即为我们平时所理解的所谓 “LoRA 模型文件”
可以简单理解:LoRA 模型文件 = Adapter
2.5.6 模型合并
对于 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
模型合并完成后,我们的目录结构应该是这样子的。
目录结构
├── merged
│ ├── config.json
│ ├── configuration_internlm2.py
│ ├── generation_config.json
│ ├── modeling_internlm2.py
│ ├── pytorch_model-00001-of-00002.bin
│ ├── pytorch_model-00002-of-00002.bin
│ ├── pytorch_model.bin.index.json
│ ├── special_tokens_map.json
│ ├── tokenization_internlm2.py
│ ├── tokenization_internlm2_fast.py
│ ├── tokenizer.json
│ ├── tokenizer.model
│ └── tokenizer_config.json
在模型合并完成后,我们就可以看到最终的模型和原模型文件夹非常相似,包括了分词器、权重文件、配置信息等等。
2.5.7 微调后的模型对话
微调完成后,我们可以再次运行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"
然后,我们可以直接启动应用。
conda activate xtuner0121
streamlit run /root/InternLM/Tutorial/tools/xtuner_streamlit_demo.py
运行后,确保端口映射正常,如果映射已断开则需要重新做一次端口映射。
最后,通过浏览器访问:http://127.0.0.1:8501 来进行对话了。
三 部署
按照Tutorial/tools/openxlab-deploy at camp2 · InternLM/Tutorial · GitHub的教程,可以在OpenXLab上完成大模型的部署。
流程完成后,也完成了相关部署,但是因为资源不足,没有正常启动,已经发邮件申请~
四 小结
经过本节的学习,带领着大家跑通了 XTuner 的完整流程,我们学会了指令跟随微调,并且训练出了一个自己小助手,是不是很有意思!
当我们在测试完模型认为其满足我们的需求后,就可以对模型进行量化部署等操作了,这部分的内容在之后关于 LMDeploy 的课程中将会详细的进行讲解,敬请期待后续的课程吧!
关于XTuner的更多高级进阶知识,请访问XTuner微调高级进阶。