XTuner 大模型单卡低成本微调实战
- FINETUNE简介
- 指令跟随微调
- 增量预训练微调
- LoRA & QLoRA
- XTuner简介
- 功能亮点
- 适配多种生态
- 适配多种硬件
- 8GB 显卡玩转LLM
- Flash Attention
- DeepSpeed ZeRO
- 动手实战环节
- 环境配置
- 微调
- 准备配置文件
- 模型下载
- 数据集下载
- 修改配置文件
- 开始微调
- 将得到的 PTH 模型转换为 HuggingFace 模型,即:生成 Adapter 文件夹
- 部署与测试
- 将 HuggingFace adapter 合并到大语言模型
- 与合并后的模型对话
- Demo
- 自定义微调
- 场景需求
- 数据准备
- 将数据转为 XTuner 的数据格式
- 开始自定义微调
- XTuner!启动!
- pth 转 huggingface
- 部署与测试
- 【补充】用 MS-Agent 数据集 赋予 LLM 以 Agent 能力
- 概述
- 添加 serper 环境变量
- xtuner + agent,启动!
FINETUNE简介
LLM下游应用中,增量预训练和指令跟随是经常会用到的两种微调模式
指令跟随微调
使用场景:让模型学会对话模板,根据人类指令进行对话
训练数据:高质量的对话,问答数据
为什么需要指令微调?
因为在对话中模型只是根据我们的提问去拟合训练数据集中的分布,它并没有意识你的意图是在向它提问。
通过指令微调后,便可得到instructed LLM,可以输出我们更加相对满意的结果
在进行指令跟随微调前,先定义三个角色
System:给定一些上下文信息,例如我们想微调一个安全的AI助手,那System就是“你是一个安全的AI助手”。
User:用户提问,在微调时将对话样本的提问赋予此角色
Assistant:根据用户提问,结合System上下文信息,输出回答,在微调时将对话样本的回答赋予此角色
这样便构建好了一个对话模板
不同的模型使用的指令各不同
指令跟随微调的训练
由于希望模型学会的是答案而不是问题,因此训练时只对答案计算损失
增量预训练微调
使用场景:让基座模型学习到一些新知识,如某个垂类领域的常识
训练数据:文章,书籍,代码等
训练LLM时,是为了让模型学会回答一个陈述句,因此相比指令跟随微调,增量预训练的对话模板中,system和user部分留空,只保留assistant。训练数据也不是对话数据,而是陈述句
计算损失时同样计算答案的损失即可。
LoRA & QLoRA
为什么要使用LoRA,如果不使用lora,那么微调时的显存开销会非常大。
lora的原理:比起对大模型的参数全面训练,lora则是在保持原模型参数不变的情况下,在原本的linear旁新增一些可训练分支(Adapter)使得输出结果拟合训练样本,Adapter参数量远小于原本的linear,能大幅减低训练的显存消耗
qlora是对lora的一种改进,使得加载模型到显存时直接量化成4bit,并且gpu满了就会到cpu上进行调度,虽然慢了,但是整个训练还能进行下去
XTuner简介
傻瓜式:以配置文件的形式封装了大部分微调场景,0基础的非专业人员也能一键开始微调
轻量级:对于7B参数量的LLM,微调所需的显存仅为8GB,满足大部分消费级显卡
功能亮点
适配多种生态
多种微调算法:多种微调策略与算法,覆盖各类SFT场景
适配多种开源生态:支持加载HuggingFace、ModelScope模型或数据集
自动优化加速:开发者无需关注复杂的显存优化与计算加速细节
适配多种硬件
训练方案覆盖NVIDIA20系以上所有显卡
最低只需8GB显存即可微调7B模型
8GB 显卡玩转LLM
Flash Attention 和 DeepSpeed ZeRO 是 XTuner 最重要的两个优化技巧
Flash Attention
Flash Attention 将Attention 计算并行化。避免了计算过程中 Attention Score NxN的显存占用(训练过程的N都比较大)
DeepSpeed ZeRO
ZeRO优化,通过将训练过程中的参数,梯度和优化器状态切片保存,能够在多GPU训练时显著节省显存
除了将训练中间状态切片外,DeepSpeed 训练时使用FP16的权重,相较于Pytorch的AMP训练在单GPU上也能大幅节省显存。
为了能让开发者专注于数据,XTuner会自动dispatch Flash Attention,并一键启动DeepSpeed ZeRO
对于QLora,则在启动时使用参数 --deepspeed deepspeed_zero3
下面是优化前与优化后显存占用情况
动手实战环节
环境配置
创建开发机
创建公钥及配置
安装XTuner并激活
# 如果你是在 InternStudio 平台,则从本地 clone 一个已有 pytorch 2.0.1 的环境:
/root/share/install_conda_env_internlm_base.sh xtuner0.1.9
# 如果你是在其他平台:
conda create --name xtuner0.1.9 python=3.10 -y# 激活环境
conda activate xtuner0.1.9
代码准备
# 进入家目录 (~的意思是 “当前用户的home路径”)
cd ~
# 创建版本文件夹并进入,以跟随本教程
mkdir xtuner019 && cd xtuner019# 拉取 0.1.9 的版本源码
git clone -b v0.1.9 https://github.com/InternLM/xtuner
# 无法访问github的用户请从 gitee 拉取:
# git clone -b v0.1.9 https://gitee.com/Internlm/xtuner# 进入源码目录
cd xtuner# 从源码安装 XTuner
pip install -e '.[all]'
新建工作文件夹
# 创建一个微调 oasst1 数据集的工作路径,进入
mkdir ~/ft-oasst1 && cd ~/ft-oasst1
微调
准备配置文件
XTuner 提供多个开箱即用的配置文件,用户可以通过下列命令查看:
# 列出所有内置配置
xtuner list-cfg
拷贝一个配置文件到当前目录:
cd ~/ft-oasst1
xtuner copy-cfg internlm_chat_7b_qlora_oasst1_e3 .
配置文件名的解释:
xtuner copy-cfg internlm_chat_7b_qlora_oasst1_e3 .
模型名 internlm_chat_7b
使用算法 qlora
数据集 oasst1
把数据集跑几次 跑3次:e3 (epoch 3 )
*无 chat比如 internlm-7b 代表是基座(base)模型
模型下载
由于下载模型很慢,用教学平台的同学可以直接复制模型
cp -r /root/share/temp/model_repos/internlm-chat-7b ~/ft-oasst1/
数据集下载
由于 huggingface 网络问题,咱们已经给大家提前下载好了,复制到正确位置即可
cd ~/ft-oasst1
# ...-guanaco 后面有个空格和英文句号啊
cp -r /root/share/temp/datasets/openassistant-guanaco .
修改配置文件
cd ~/ft-oasst1
vim internlm_chat_7b_qlora_oasst1_e3_copy.py
# 修改模型为本地路径
- pretrained_model_name_or_path = 'internlm/internlm-chat-7b'
+ pretrained_model_name_or_path = './internlm-chat-7b'# 修改训练数据集为本地路径
- data_path = 'timdettmers/openassistant-guanaco'
+ data_path = './openassistant-guanaco'
开始微调
# 单卡
## 用刚才改好的config文件训练
xtuner train ./internlm_chat_7b_qlora_oasst1_e3_copy.py# 多卡
NPROC_PER_NODE=${GPU_NUM} xtuner train ./internlm_chat_7b_qlora_oasst1_e3_copy.py# 若要开启 deepspeed 加速,增加 --deepspeed deepspeed_zero2 即可
将得到的 PTH 模型转换为 HuggingFace 模型,即:生成 Adapter 文件夹
mkdir hf
export MKL_SERVICE_FORCE_INTEL=1xtuner convert pth_to_hf ./internlm_chat_7b_qlora_oasst1_e3_copy.py ./work_dirs/internlm_chat_7b_qlora_oasst1_e3_copy/epoch_1.pth ./hf
部署与测试
将 HuggingFace adapter 合并到大语言模型
xtuner convert merge ./internlm-chat-7b ./hf ./merged --max-shard-size 2GB
# xtuner convert merge \
# ${NAME_OR_PATH_TO_LLM} \
# ${NAME_OR_PATH_TO_ADAPTER} \
# ${SAVE_PATH} \
# --max-shard-size 2GB
与合并后的模型对话
由于不同模型厂商prompt-template不同,我们这里用的基座模型是internlm-chat
所以–prompt-template internlm_chat
# 加载 Adapter 模型对话(Float 16)
xtuner chat ./merged --prompt-template internlm_chat# 4 bit 量化加载
# xtuner chat ./merged --bits 4 --prompt-template internlm_chat
Demo
修改 cli_demo.py 中的模型路径
- model_name_or_path = "/root/model/Shanghai_AI_Laboratory/internlm-chat-7b"
+ model_name_or_path = "merged"
运行 cli_demo.py 以目测微调效果
python ./cli_demo.py
xtuner chat 的启动参数
启动参数
–prompt-template 指定对话模板
–system 指定SYSTEM文本
–system-template 指定SYSTEM模板
–bits LLM位数
–bot-name 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 如果设置为小于1的浮点数,仅保留概率相加高于 top_p 的最小一组最有可能的标记
–seed 用于可重现文本生成的随机种子
自定义微调
场景需求
基于 InternLM-chat-7B 模型,用 MedQA 数据集进行微调,将其往医学问答领域对齐
数据准备
以 Medication QA 数据集为例
将数据转为 XTuner 的数据格式
目标格式:(.jsonL)
[{"conversation":[{"system": "xxx","input": "xxx","output": "xxx"}]
},
{"conversation":[{"system": "xxx","input": "xxx","output": "xxx"}]
}]
开始自定义微调
此时,我们重新建一个文件夹来玩“微调自定义数据集”
mkdir ~/ft-medqa && cd ~/ft-medqa
把前面下载好的internlm-chat-7b模型文件夹拷贝过来。
cp -r ~/ft-oasst1/internlm-chat-7b .
别忘了把自定义数据集,即几个 .jsonL,也传到服务器上。
git clone https://github.com/InternLM/tutorial
cp ~/tutorial/xtuner/MedQA2019-structured-train.jsonl .
准备配置文件
# 复制配置文件到当前目录
xtuner copy-cfg internlm_chat_7b_qlora_oasst1_e3 .
# 改个文件名
mv internlm_chat_7b_qlora_oasst1_e3_copy.py internlm_chat_7b_qlora_medqa2019_e3.py
# 修改配置文件内容
vim internlm_chat_7b_qlora_medqa2019_e3.py
减号代表要删除的行,加号代表要增加的行。# 修改import部分
- from xtuner.dataset.map_fns import oasst1_map_fn, template_map_fn_factory
+ from xtuner.dataset.map_fns import template_map_fn_factory# 修改模型为本地路径
- pretrained_model_name_or_path = 'internlm/internlm-chat-7b'
+ pretrained_model_name_or_path = './internlm-chat-7b'# 修改训练数据为 MedQA2019-structured-train.jsonl 路径
- data_path = 'timdettmers/openassistant-guanaco'
+ data_path = 'MedQA2019-structured-train.jsonl'# 修改 train_dataset 对象
train_dataset = dict(type=process_hf_dataset,
- dataset=dict(type=load_dataset, path=data_path),
+ dataset=dict(type=load_dataset, path='json', data_files=dict(train=data_path)),tokenizer=tokenizer,max_length=max_length,
- dataset_map_fn=alpaca_map_fn,
+ dataset_map_fn=None,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)
XTuner!启动!
xtuner train internlm_chat_7b_qlora_medqa2019_e3.py --deepspeed deepspeed_zero2
pth 转 huggingface
同前述,这里不赘述了。将得到的-pth-模型转换为-huggingface-模型即生成adapter文件夹
部署与测试
同前述。部署与测试
【补充】用 MS-Agent 数据集 赋予 LLM 以 Agent 能力
概述
MSAgent 数据集每条样本包含一个对话列表(conversations),其里面包含了 system、user、assistant 三种字段。其中:
system: 表示给模型前置的人设输入,其中有告诉模型如何调用插件以及生成请求
user: 表示用户的输入 prompt,分为两种,通用生成的prompt和调用插件需求的 prompt
assistant: 为模型的回复。其中会包括插件调用代码和执行代码,调用代码是要 LLM 生成的,而执行代码是调用服务来生成结果的
一条调用网页搜索插件查询“上海明天天气”的数据样本示例如下图所示:
微调步骤参考前面
添加 serper 环境变量
开始 chat 之前,还要加个 serper 的环境变量:
去 serper.dev 免费注册一个账号,生成自己的 api key。这个东西是用来给 lagent 去获取 google 搜索的结果的。等于是 serper.dev 帮你去访问 google,而不是从你自己本地去访问 google 了。
添加 serper api key 到环境变量:
export SERPER_API_KEY=abcdefg
xtuner + agent,启动!
xtuner chat ./internlm-chat-7b --adapter internlm-7b-qlora-msagent-react --lagent