写在前面
仅作个人学习记录用。本文记录ChatGLM3-6B在Windows操作系统中的微调实践。
本文于2023年12月19日发布,部分内容不再具有时效性,建议查看本文的更新与补充说明:【工程记录】ChatGLM3-6B微调实践的更新说明 或其他微调教程
1. 部署预训练模型
详见 【工程记录】ChatGLM3-6B 部署的详细教程(Windows)
import os
import torch
from transformers import AutoModel, AutoTokenizer
#改成你的模型路径
MODEL_PATH = os.environ.get('MODEL_PATH', './chatglm3-6b')
TOKENIZER_PATH = os.environ.get("TOKENIZER_PATH", MODEL_PATH)
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
def get_model():
tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH, trust_remote_code=True)
if 'cuda' in DEVICE: # AMD, NVIDIA GPU 可以使用 Half Precision
model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).to(DEVICE).eval()
else: # CPU, Intel GPU and other GPU 可以只使用 Float16 Precision
model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).float().to(DEVICE).eval()
# 多显卡支持,使用下面两行代替上面一行,将num_gpus改为你实际的显卡数量
# from utils import load_model_on_gpus
# model = load_model_on_gpus("THUDM/chatglm3-6b", num_gpus=2)
return tokenizer, model
# 加载Chatglm3的model和tokenizer
tokenizer, model = get_model()
注意:ChatGLM3-6B 微调示例需要 python>=3.10,除基础的 torch 依赖外,还需要依赖:
transformers==4.30.2
accelerate
sentencepiece
astunparse
deepspeed
2. 数据格式预处理
ChatGLM3-6B模型的官方文档提供了微调示例。微调方法上,提供全量微调和 P-Tuning v2。格式上,提供多轮对话微调样例和输入输出格式微调样例。
无论是全量微调还是P-Tuning v2,都需要设计微调数据,而ChatGLM3-6B支持多轮对话和输入输出格式微调样例。因此如果想要使用自己的数据集进行模型微调,需要首先统一样例格式。同时,ChatGLM3-6B微调对话和微调工具能力的数据格式也不相同。
本文实践仅使用输入输出格式来微调对话,因此需要用到 ChatGLM3-6B 的 finetune_chatmodel_demo。
多轮对话格式:
[
{
"conversations": [
{
"role": "system",
"content": "<system prompt text>"
},
{
"role": "user",
"content": "<user prompt text>"
},
{
"role": "assistant",
"content": "<assistant response text>"
},
// ... Muti Turn
{
"role": "user",
"content": "<user prompt text>"
},
{
"role": "assistant",
"content": "<assistant response text>"
}
]
}
// ...
]
- 多轮对话格式数据微调原理是在一遍计算中为多轮回复计算 loss
- system:设定了 AI 的行为和角色,和背景,给出一个对话的大致方向,或者设置对话的语气和风格,以便LLM更好地理解其在对话中的角色。例如,可以把
<system prompt text>
设置为:“你是一个会计”或“你是一名历史教授”。ChatGLM3-6B的 system 角色为可选角色,但若存在 system 角色,其必须出现在 user 角色之前,且一个完整的对话数据(无论单轮或者多轮对话)只能出现一次 system 角色。 - user:输入的问题或请求。
- assistant:是由LLM根据 system 和 user 消息自动生成的答案。在微调数据中即你想要得到的答案。
输入输出格式:
[
{
"prompt": "<prompt text>",
"response": "<response text>"
}
// ...
]
作为示例,使用 AdvertiseGen 数据集进行微调实践,从 Google Drive 或者 Tsinghua Cloud 下载并解压 AdvertiseGen 数据集,得到两个json文件train.json
和dev.json
。任务是根据字段content,生成一段广告词summary。
以下为单个样本格式:
{
"content": "类型#上衣*版型#宽松*版型#显瘦*图案#线条*图案#刺绣*衣样式#针织衫*衣领型#v领",
"summary": "一款温暖柔软又富有弹性的针织衫,不仅可以抵御严寒侵袭,还能更好地进行搭配。v领的设计,能勾勒出迷人的天鹅颈以及衬托出娇小的脸型。宽松又别致的剪裁,能从视觉上显露纤长的下半身,起到显瘦的效果。直筒造型的袖子,修饰出优美的手臂线条,衣身上的方格刺绣,时尚又吸睛。"
}
接下来,将train.json
和dev.json
放到 finetune_chatmodel_demo 文件夹下的AdvertiseGen文件夹内,终端输入:
./scripts/format_advertise_gen.py --path "AdvertiseGen/train.json"
得到advertise_gen.jsonl
:
[
// ...
{
"prompt": "类型#上衣*版型#宽松*版型#显瘦*图案#线条*图案#刺绣*衣样式#针织衫*衣领型#v领",
"response": "一款温暖柔软又富有弹性的针织衫,不仅可以抵御严寒侵袭,还能更好地进行搭配。v领的设计,能勾勒出迷人的天鹅颈以及衬托出娇小的脸型。宽松又别致的剪裁,能从视觉上显露纤长的下半身,起到显瘦的效果。直筒造型的袖子,修饰出优美的手臂线条,衣身上的方格刺绣,时尚又吸睛。"
}
// ...
]
这样AdvertiseGen 数据集样本就满足了 ChatGLM3-6B 要求的输入输出格式,也可以根据上述多轮对话或输入输出格式准备个性化领域数据来微调模型。
3. 微调模型(Windows)
ChatGLM3-6B提供全量微调和 P-Tuning v2。关于全量微调和 P-Tuning v2的介绍,可以查看 【学习记录】大模型微调方法 。
在 finetune_chatmodel_demo 文件夹目录下,终端输入以下脚本:
./scripts/finetune_ds.sh # 输入输出格式全量微调
./scripts/finetune_pt.sh # 输入输出格式 P-Tuning v2 微调
官方文档建议的参考显存用量如下:
- P-Tuning V2
PRE_SEQ_LEN=128
,DEV_BATCH_SIZE=1
,GRAD_ACCUMULARION_STEPS=16
,MAX_SEQ_LEN=2048
配置下约需要 21GB 显存。 - 全量微调时,
./scripts/finetune_ds_multiturn.sh
中的配置(MAX_SEQ_LEN=2048
,DEV_BATCH_SIZE=16
,GRAD_ACCUMULARION_STEPS=1
)恰好用满 4 * 80GB 显存。
若尝试后发现显存不足,可以考虑:
- 尝试降低
DEV_BATCH_SIZE
并提升GRAD_ACCUMULARION_STEPS
- 尝试添加
--quantization_bit 8
或--quantization_bit 4
。 PRE_SEQ_LEN=128
,DEV_BATCH_SIZE=1
,GRAD_ACCUMULARION_STEPS=16
,MAX_SEQ_LEN=1024
配置下,--quantization_bit 8
约需 12GB 显存,--quantization_bit 4
约需 7.6GB 显存。
如果在微调实践时设备显存不足,或者对训练速度或训练效果有其他个性化要求,可以按照建议调整上述.sh脚本文件的参数。
以 finetune_pt.sh 为例,脚本代码如下:
#! /usr/bin/env bash
set -ex
# 调整参数
#=========================
LR=2e-2
PRE_SEQ_LEN=128
NUM_GPUS=1
MAX_SOURCE_LEN=1024
MAX_TARGET_LEN=128
DEV_BATCH_SIZE=1
GRAD_ACCUMULARION_STEPS=32
MAX_STEP=1000
SAVE_INTERVAL=500
#=========================
RUN_NAME=advertise_gen_pt
BASE_MODEL_PATH=THUDM/chatglm3-6b
DATASET_PATH=formatted_data/advertise_gen.jsonl
DATESTR=`date +%Y%m%d-%H%M%S`
OUTPUT_DIR=output/${RUN_NAME}-${DATESTR}-${PRE_SEQ_LEN}-${LR}
mkdir -p $OUTPUT_DIR
torchrun --standalone --nnodes=1 --nproc_per_node=$NUM_GPUS finetune.py \
--train_format input-output \
--train_file $DATASET_PATH \
--preprocessing_num_workers 1 \
--model_name_or_path $BASE_MODEL_PATH \
--output_dir $OUTPUT_DIR \
--max_source_length $MAX_SOURCE_LEN \
--max_target_length $MAX_TARGET_LEN \
--per_device_train_batch_size $DEV_BATCH_SIZE \
--gradient_accumulation_steps $GRAD_ACCUMULARION_STEPS \
--max_steps $MAX_STEP \
--logging_steps 1 \
--save_steps $SAVE_INTERVAL \
--learning_rate $LR \
--pre_seq_len $PRE_SEQ_LEN 2>&1 | tee ${OUTPUT_DIR}/train.log
注意:
- 由于windows不支持NCCL backend,因此在微调前需要将accelerate包中的state.py文件中
backend='nccl'
改为backend='gloo'
。 BASE_MODEL_PATH
不要使用绝对路径,最好在同一级文件夹下。MAX_TARGET_LEN
并非代表最大输出长度,而是用来截断的。MAX_TARGET_LEN
之前的输出结果直接受到微调影响,而 ChatGLM3-6B 本身训练是相对位置编码,有外推性,理论上超过4096也可能生成较为合理的结果。
训练完成后,p-tuning checkpoint 保存至OUTPUT_DIR
位置。
100%|□□□□□□□□□□□□□□□□□□□□| 5/5 [3:31:19<00:00, 15.85s/it]
Saving PrefixEncoder
[INFO|configuration_utils.py:458] 2023-12-19 16:24:39,201 >> Configuration saved in xxxxx\config.json
[INFO|configuration_utils.py:364] 2023-12-19 16:24:39,202 >> Configuration saved in xxxxx\generation_config.json
[INFO|modeling_utils.py:1853] 2023-12-19 16:24:39,210 >> Model weights saved in xxxxx\pytorch_model.bin
[INFO|tokenization_utils_base.py:2194] 2023-12-19 16:24:39,211 >> tokenizer config file saved in xxxxx\tokenizer_config.json
[INFO|tokenization_utils_base.py:2201] 2023-12-19 16:24:39,211 >> Special tokens file saved in xxxxx\special_tokens_map.json
4. 微调后模型推理验证
对于输入输出格式的微调,可使用 inference.py
进行基本的推理验证。 finetune_chatmodel_demo 文件夹目录下,终端输入:
python inference.py \
--pt-checkpoint "path to p-tuning checkpoint" \
--model THUDM/chatglm3-6b
python inference.py \
--tokenizer THUDM/chatglm3-6b \
--model "path to finetuned model checkpoint"
"path to p-tuning checkpoint"
与 "path to finetuned model checkpoint"
即之前设置的OUTPUT_DIR
路径。
载入微调后模型checkpoint:
Loading checkpoint shards: 100%|███████████████████████████████████████████████████████████████████████████████| 7/7 [00:07<00:00, 1.21it/s]
Prompt: XXX
终端输入Prompt的内容XXX
,得到Response。推理验证完成。