简介
最近LLM-GPT挺火的,尝试了几个模型:
- Chinese-LLaMA-Alpaca
- Chinese-Vicuna
- alpaca-lora
- BELLE
- ChatGLM
并对效果好的模型(ChatGLM)尝试微调,转载于:
Chinese-LLaMA-Alpaca
项目地址:https://github.com/ymcui/Chinese-LLaMA-Alpaca
为了促进大模型在中文NLP社区的开放研究,本项目开源了中文LLaMA模型和指令精调的Alpaca大模型。这些模型在原版LLaMA的基础上扩充了中文词表并使用了中文数据进行二次预训练,进一步提升了中文基础语义理解能力。同时,中文Alpaca模型进一步使用了中文指令数据进行精调,显著提升了模型对指令的理解和执行能力。
LLaMA和Alpaca有什么区别?我应该用哪个?
中文Alpaca模型在上述中文LLaMA模型的基础上进一步使用了指令数据进行精调,具体见训练细节一节。如希望体验类ChatGPT对话交互,请使用Alpaca模型,而不是LLaMA模型。
环境准备
确保Python在3.9以上
因为LLaMa权重版开源,从pyllama下载权重
1
2
3pip install pyllama
# 很可能需要运行多次
python -m llama.download --model_size 7B --folder /tmp/pyllama_data安装git lfs
1
2curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
sudo apt install git-lfs下载Chinese-Alpaca-7B补丁权重
1
2pip install huggingface_hub
git clone https://huggingface.co/ziqingyang/chinese-alpaca-lora-7b安装依赖库
1
2
3pip install transformers==4.28.0
pip install sentencepiece==0.1.97
pip install peft==0.2.0
权重转换
将原版LLaMA模型转换为HF格式:请使用🤗transformers提供的脚本convert_llama_weights_to_hf.py,将原版LLaMA模型转换为HuggingFace格式。将原版LLaMA的
tokenizer.model
放在--input_dir
指定的目录,其余文件放在${input_dir}/${model_size}
下。执行以下命令后,--output_dir
中将存放转换好的HF版权重。1
2
3
4python src/transformers/models/llama/convert_llama_weights_to_hf.py \
--input_dir path_to_original_llama_root_dir \
--model_size 7B \
--output_dir path_to_original_llama_hf_dir合并LoRA权重,生成全量模型权重:这一步骤会对原版LLaMA模型(HF格式)扩充中文词表,合并LoRA权重并生成全量模型权重。此处可有两种选择:
- 输出PyTorch版本权重(
.pth
文件),使用merge_llama_with_chinese_lora.py
脚本 - 输出HuggingFace版本权重(
.bin
文件),使用merge_llama_with_chinese_lora_to_hf.py
脚本(感谢@sgsdxzy 提供)
以上两个脚本所需参数一致,仅输出文件格式不同。下面以生成PyTorch版本权重为例,介绍相应的参数设置。
1
2
3
4python scripts/merge_llama_with_chinese_lora.py \
--base_model path_to_original_llama_hf_dir \
--lora_model path_to_chinese_llama_or_alpaca_lora \
--output_dir path_to_output_dir参数说明:
--base_model
:存放HF格式的LLaMA模型权重和配置文件的目录(Step 1生成)--lora_model
:中文LLaMA/Alpaca LoRA解压后文件所在目录,也可使用🤗Model Hub模型调用名称--output_dir
:指定保存全量模型权重的目录,默认为./
- (可选)
--offload_dir
:对于低内存用户需要指定一个offload缓存路径
- 输出PyTorch版本权重(
部署推理
本项目中的模型主要支持以下三种推理和部署方式:
- llama.cpp:提供了一种模型量化和在本地CPU上部署方式
- 🤗Transformers:提供原生transformers推理接口,支持CPU/GPU上进行模型推理
- text-generation-webui:提供了一种可实现前端UI界面的部署方式
llama.cpp量化部署
接下来以llama.cpp工具为例,介绍MacOS和Linux系统中,将模型进行量化并在本地CPU上部署的详细步骤。Windows则可能需要cmake等编译工具的安装(Windows用户出现模型无法理解中文或生成速度特别慢时请参考FAQ#6)。本地快速部署体验推荐使用经过指令精调的Alpaca模型,有条件的推荐使用FP16模型,效果更佳。 下面以中文Alpaca-7B模型为例介绍,运行前请确保:
- 模型量化过程需要将未量化模型全部载入内存,请确保有足够可用内存(7B版本需要13G以上)
- 加载使用4-bit量化后的模型时(例如7B版本),确保本机可用内存大于4-6G(受上下文长度影响)
- 系统应有
make
(MacOS/Linux自带)或cmake
(Windows需自行安装)编译工具 - llama.cpp官方建议使用Python 3.9~3.11编译和运行该工具
Step 1: 克隆和编译llama.cpp
运行以下命令对llama.cpp项目进行编译,生成./main
和./quantize
二进制文件。
1 | git clone https://github.com/ggerganov/llama.cpp && cd llama.cpp && make |
Step 2: 生成量化版本模型
将合并模型(选择生成.pth
格式模型)中最后一步生成的tokenizer.model
文件放入zh-models
目录下,模型文件consolidated.*.pth
和配置文件params.json
放入zh-models/7B
目录下。请注意LLaMA和Alpaca的tokenizer.model
不可混用(原因见训练细节)。目录结构类似:
1 | llama.cpp/zh-models/ |
将上述.pth
模型权重转换为ggml的FP16格式,生成文件路径为zh-models/7B/ggml-model-f16.bin
。
1 | python convert.py zh-models/7B/ |
进一步对FP16模型进行4-bit量化,生成量化模型文件路径为zh-models/7B/ggml-model-q4_0.bin
。
1 | ./quantize ./zh-models/7B/ggml-model-f16.bin ./zh-models/7B/ggml-model-q4_0.bin 2 |
此处也可以将最后一个参数改为3
,即生成q4_1
版本的量化权重。q4_1
权重比q4_0
大一些,速度慢一些,效果方面会有些许提升,具体可参考llama.cpp#PPL。
Step 3: 加载并启动模型
运行./main
二进制文件,-m
命令指定4-bit量化或FP16的GGML模型。以下是命令示例(并非最优参数):
1 | ./main -m zh-models/7B/ggml-model-q4_0.bin --color -f prompts/alpaca.txt -ins -c 2048 --temp 0.2 -n 256 --repeat_penalty 1.3 |
在提示符 >
之后输入你的prompt,cmd/ctrl+c
中断输出,多行信息以\
作为行尾。如需查看帮助和参数说明,请执行./main -h
命令。下面介绍一些常用的参数:
1 | -ins 启动类ChatGPT对话交流的运行模式 |
使用Transformers推理
如果想在不安装其他库或Python包的情况下快速体验模型效果,可以使用scripts/inference_hf.py 脚本启动非量化模型。该脚本支持CPU和GPU的单卡推理。以启动Chinese-Alpaca-7B模型为例,脚本运行方式如下:
1 | CUDA_VISIBLE_DEVICES={device_id} python scripts/inference_hf.py \ |
如果已经执行了merge_llama_with_chinese_lora_to_hf.py
脚本将lora权重合并,那么无需再指定--lora_model
,启动方式更简单:
1 | CUDA_VISIBLE_DEVICES={device_id} python scripts/inference_hf.py \ |
参数说明:
{device_id}
:CUDA设备编号。如果为空,那么在CPU上进行推理--base_model {base_model}
:存放HF格式的LLaMA模型权重和配置文件的目录。如果之前合并生成的是PyTorch格式模型,请转换为HF格式--lora_model {lora_model}
:中文LLaMA/Alpaca LoRA解压后文件所在目录,也可使用🤗Model Hub模型调用名称。若不提供此参数,则只加载--base_model
指定的模型--tokenizer_path {tokenizer_path}
:存放对应tokenizer的目录。若不提供此参数,则其默认值与--lora_model
相同;若也未提供--lora_model
参数,则其默认值与--base_model
相同--with_prompt
:是否将输入与prompt模版进行合并。如果加载Alpaca模型,请务必启用此选项!--interactive
:以交互方式启动,以便进行多次单轮问答(此处不是llama.cpp中的上下文对话)--data_file {file_name}
:非交互方式启动下,按行读取file_name
中的的内容进行预测--predictions_file {file_name}
:非交互式方式下,将预测的结果以json格式写入file_name
注意事项:
- 因不同框架的解码实现细节有差异,该脚本并不能保证复现llama.cpp的解码效果
- 该脚本仅为方便快速体验用,并未对多机多卡、低内存、低显存等情况等条件做任何优化
- 如在CPU上运行7B模型推理,请确保有32GB内存;如在GPU上运行7B模型推理,请确保有20GB显存
使用text-generation-webui搭建界面
接下来以text-generation-webui工具为例,介绍无需合并模型即可进行本地化部署的详细步骤。
1 | # 克隆text-generation-webui |
Chinese-Vicuna
项目地址:https://github.com/Facico/Chinese-Vicuna
1 | git clone https://github.com/Facico/Chinese-Vicuna |
alpaca-lora
项目地址:https://github.com/tloen/alpaca-lora
1 | git clone https://github.com/tloen/alpaca-lora |
像以上三个基于LLaMA的模型,微调的数据集是需要以下三个输入:
- instruction
- input
- output
所以在一些只有input的数据集中,可以将input等同于instruction。
BELLE
项目地址:https://github.com/LianjiaTech/BELLE
1 | git clone https://github.com/LianjiaTech/BELLE |
接着下
1 | git clone https://huggingface.co/BelleGroup/BELLE_BLOOM_GPTQ_4BIT |
如果模型下不下来,可以从https://huggingface.co/BelleGroup/BELLE_BLOOM_GPTQ_4BIT/tree/main得到单独文件的下载链接,再下载。
效果不错,后面看看怎么微调。
ChatGLM
1 | git clone https://github.com/THUDM/ChatGLM-6B |
效果也不错,比BELLE差一点,看看怎么微调。
微调数据集:每行一个 JSON 对象,JSON 格式如下:{“summary”: “提示词”, “content”: “期望生成的结果”}
训练目的是让模型说出他是微调后的结果,所以json内容基本以这个为主:
{“summary”: “你是谁?”, “content”: “我是wstart通过ChatGLM -6B微调后的模型,训练编号是:0.08376971294904079”}
微调
官方采用P-Tuning V2,也有采用LoRA进行微调的:https://github.com/yuanzhoulvpi2017/zero_nlp/tree/main/simple_thu_chatglm6b。
按官方的搞一把。
软件依赖
运行微调需要4.27.1版本的transformers
。除 ChatGLM-6B 的依赖之外,还需要安装以下依赖
1 | pip install rouge_chinese nltk jieba datasets |
数据准备
ADGEN 数据集任务为根据输入(content)生成一段广告词(summary)。
1 | { |
- 从 Google Drive 或者 Tsinghua Cloud 下载处理好的 ADGEN 数据集,将解压后的
AdvertiseGen
目录放到本目录下。 - 相比于LLaMA和BELLE常用的instruction、input和output的格式,这里只有输入和输出两个字段
如果使用自己的数据集,需要改成上面这种格式
加上身份认证素材
1
2
3
4{
"content": "请问你是谁?",
"summary": "我是你爹"
}
微调
运行以下指令进行训练:
1 | bash train.sh |
- 在默认配置
quantization_bit=4
、per_device_train_batch_size=1
、gradient_accumulation_steps=16
下,INT4 的模型参数被冻结,一次训练迭代会以 1 的批处理大小进行 16 次累加的前后向传播,等效为 16 的总批处理大小,此时最低只需 6.7G 显存。 - 若想在同等批处理大小下提升训练效率,可在二者乘积不变的情况下,加大
per_device_train_batch_size
的值,但也会带来更多的显存消耗,请根据实际情况酌情调整。 P-Tuning-v2 方法会冻结全部的模型参数,可通过调整
quantization_bit
来被原始模型的量化等级,不加此选项则为 FP16 精度加载。如果你想要从本地加载模型,可以将
train.sh
中的THUDM/chatglm-6b
改为你本地的模型路径。train.sh
中的PRE_SEQ_LEN
和LR
分别是 soft prompt 长度和训练的学习率,可以进行调节以取得最佳的效果。
一些OpenAI的调参经验:
- batch_size:默认为训练集中样本数量的0.2%,上限为256
- LR:建议在0.02到0.2范围内的值进行试验,较大的学习率通常在较大的批量大小下表现更好
评价
将 evaluate.sh
中的 CHECKPOINT
更改为训练时保存的 checkpoint 名称,运行以下指令进行模型推理和评测:
1 | bash evaluate.sh |
[2023/04/10更新] 在 P-tuning v2 训练时模型只保存 PrefixEncoder 部分的参数,所以在推理时需要同时加载原 ChatGLM-6B 模型以及 PrefixEncoder 的权重,因此需要指定参数(已更新 evaluate.sh
) :
1 | --model_name_or_path THUDM/chatglm-6b |
仍然兼容旧版全参保存的 Checkpoint,只需要跟之前一样设定 model_name_or_path
:
1 | --model_name_or_path $CHECKPOINT_PATH |
评测指标为中文 Rouge score 和 BLEU-4。生成的结果保存在 ./output/adgen-chatglm-6b-pt-8-1e-2/generated_predictions.txt
。
部署
首先载入Tokenizer:
1 | import os |
- 如果需要加载的是新 Checkpoint(只包含 PrefixEncoder 参数):
1 | config = AutoConfig.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True, pre_seq_len=128) |
注意你可能需要将 pre_seq_len
改成你训练时的实际值。如果你是从本地加载模型的话,需要将 THUDM/chatglm-6b
改成本地的模型路径(注意不是checkpoint路径)。
- 如果需要加载的是旧 Checkpoint(包含 ChatGLM-6B 以及 PrefixEncoder 参数),或者进行的是全参数微调,则直接加载整个 Checkpoint:
1 | model = AutoModel.from_pretrained(CHECKPOINT_PATH, trust_remote_code=True) |
之后根据需求可以进行量化,也可以直接使用:
1 | # Comment out the following line if you don't use quantization |
[23/04/19] 你也可以直接运行支持加载 P-Tuning v2 checkpoint 的 web demo
1 | bash web_demo.sh |
可能需要修改 web_demo.sh 的内容以符合你实际的 checkpoint 情况。
对话数据集
如需要使用多轮对话数据对模型进行微调,可以提供聊天历史,例如
1 | { |
训练时需要指定 --history_column
为数据中聊天历史的 key(在此例子中是 history
),将自动把聊天历史拼接,例如:
Input
1
2
3
4
5
6
7
8
9[Round 0]
问:长城h3风扇不转。继电器好的。保险丝好的传感器新的风扇也新的这是为什么。就是继电器缺一个信号线
答:用电脑能读数据流吗?水温多少
[Round 1]
问:95
答:上下水管温差怎么样啊?空气是不是都排干净了呢?
[Round 2]
问:是的。上下水管都好的
答:Label
1
那就要检查线路了,一般风扇继电器是由电脑控制吸合的,如果电路存在断路,或者电脑坏了的话会出现继电器不吸合的情况!
要注意超过输入长度 max_source_length
的内容会被截。
可以参考以下指令:
1 | bash train_chat.sh |
Docker部署
记录一下Docker部署过程中的问题。
docker为了使用上GPU,有了nvidia docker。不过现在不用另外安装,直接自带在docker19之后的版本里了
深度学习的基础镜像可以选Pytorch官方发布的:https://hub.docker.com/r/pytorch/pytorch
docker run -it imageA /bin/bash
以命令行交互模式进入imageA所启动的容器
构建自己的镜像尽量用
docker build
构建,构建时注意平台,例如:linux/amd64
或linux/arm64
- 实在不行了,尝试用
docker commit
,但是镜像不精简 docker copy <src>
是目录<src>
是目录则复制目录的全部内容,包括文件系统元数据- 不会复制目录本身,只会复制其内容