We introduce the idea of Sample Design Engineering (SDE) for LLMs' Downstream Fine-Tuning. 我们提出了针对大模型下游任务微调的「样本设计工程」。
💻 可复现的小项目:
💬 相关讨论区:
🤖 目前支持:
🎯 两行代码开启 LoRA 训练:
sh tokenize.sh,对比不同的 LLM,需在 tokenize.sh 文件里切换 model_checkpoint 参数sh train.sh,对于不同的 LLM,需切换不同的 python 文件来执行:
chatglm_lora_tuning.pychatglm2_lora_tuning.pybaichuan_lora_tuning.pybaichuan2_lora_tuning.pyintermlm_lora_tuning.pychinese_llama2_alpaca2_lora_tuning.py🎯 手把手的 RLHF 教程:见 LoRA-based-RLHF
环境准备:
pip install transformers datasets accelerate sentencepiece tensorboard peft
目前测试的环境为:
- Python 3.9.16
- torch, Version: 2.0.1
- transformers, Version: 4.29.1
- datasets, Version: 2.12.0
- accelerate, Version: 0.19.0
- peft, Version: 0.3.0
- sentencepiece, Version: 0.1.99
- tensorboard, Version: 2.13.0
下面的教程以及代码使用 ChatGLM-6B 作为例子,如果更换其他模型,可能需要略微修改具体文件代码。
原始文件的准备
指令微调数据一般有输入和输出两部分,输入是特定的content加上instruction,这里我们将二者直接拼在一起,不单独区分;输出则是希望模型的回答。
我们统一使用json的格式在整理数据,可以自定义输出输出的字段名,例如下面的例子中我使用的是q和a代表模型的输入和输出:
{"q": "请计算:39 * 0 = 什么?", "a": "这是简单的乘法运算,39乘以0得到的是0"}
{"q": "题目:51/186的答案是什么?", "a": "这是简单的除法运算,51除以186大概为0.274"}
{"q": "鹿妈妈买了24个苹果,她想平均分给她的3只小鹿吃,每只小鹿可以分到几个苹果?", "a": "鹿妈妈买了24个苹果,平均分给3只小鹿吃,那么每只小鹿可以分到的苹果数就是总苹果数除以小鹿的只数。\n24÷3=8\n每只小鹿可以分到8个苹果。所以,答案是每只小鹿可以分到8个苹果。"}
...
整理好数据后,保存为.json或者.jsonl文件,然后放入目录中的data/文件夹中。
对数据集进行分词
为了避免每次训练的时候都要重新对数据集分词,我们先分好词形成特征后保存成可直接用于训练的数据集。
例如,
data/ 文件夹下的 simple_math_4op.json 文件q,输出字段为adata/tokenized_data/ 下名为 simple_math_4op 的文件夹中则我们可以直接使用下面这段命令(即tokenize.sh文件)进行处理:
CUDA_VISIBLE_DEVICES=0,1 python tokenize_dataset_rows.py \
--model_checkpoint THUDM/chatglm-6b \
--input_file simple_math_4op.json \
--prompt_key q \
--target_key a \
--save_name simple_math_4op \
--max_seq_length 2000 \
--skip_overlength False
处理完毕之后,我们会在 data/tokenized_data/ 下发现名为 simple_math_4op 的文件夹,这就是下一步中我们可以直接用于训练的数据。
LoRA 微调得到 tokenize 之后的数据集,就可以直接运行 chatglm_lora_tuning.py 来训练 LoRA 模型了,具体可设置的主要参数包括:
tokenized_dataset, 分词后的数据集,即在 data/tokenized_data/ 地址下的文件夹名称lora_rank, 设置 LoRA 的秩,推荐为4或8,显存够的话使用8per_device_train_batch_size, 每块 GPU 上的 batch sizegradient_accumulation_steps, 梯度累加,可以在不提升显存占用的情况下增大 batch sizemax_steps, 训练步数save_steps, 多少步保存一次save_total_limit, 保存多少个checkpointlogging_steps, 多少步打印一次训练情况(loss, lr, etc.)output_dir, 模型文件保存地址例如我们的数据集为 simple_math_4op,希望保存到 weights/simple_math_4op ,则执行下面命令(即train.sh文件):
CUDA_VISIBLE_DEVICES=2,3 python chatglm_lora_tuning.py \
--tokenized_dataset simple_math_4op \
--lora_rank 8 \
--per_device_train_batch_size 10 \
--gradient_accumulation_steps 1 \
--max_steps 100000 \
--save_steps 200 \
--save_total_limit 2 \
--learning_rate 1e-4 \
--fp16 \
--remove_unused_columns false \
--logging_steps 50 \
--output_dir weights/simple_math_4op
训练完之后,可以在 output_dir 中找到 LoRA 的相关模型权重,主要是adapter_model.bin和adapter_config.json两个文件。
如何查看 tensorboard:
your_log_pathtensorboard --logdir your_log_path 命令,就会在 http://localhost:6006/ 上开启tensorboard-L 6006:127.0.0.1:6006 参数,将服务器端的6006端口映射到本地的6006端口。我们可以把上面的 output_dir 打包带走,假设文件夹为 weights/simple_math_4op, 其中(至少)包含 adapter_model.bin 和 adapter_config.json 两个文件,则我们可以用下面的方式直接加载,并推理
from peft import PeftModel
from transformers import AutoTokenizer, AutoModel
import torch
device = torch.device(1)
# 加载原始 LLM
model_path = "THUDM/chatglm-6b"
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).half().to(device)
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model.chat(tokenizer, "你好", history=[])
# 给原始 LLM 安装上你的 LoRA tool
model = PeftModel.from_pretrained(model, "weights/simple_math_4op").half()
model.chat(tokenizer, "你好", history=[])
理论上,可以通过多次执行 model = PeftModel.from_pretrained(model, "weights/simple_math_4op").half() 的方式,加载多个 LoRA 模型,从而混合不同Tool的能力,但实际测试的时候,由于暂时还不支持设置不同 LoRA weights的权重,往往效果不太好,存在覆盖或者遗忘的情况。
对这些优秀开源项目表示感谢!
HTML
66.3%
Python
18.5%
Jupyter Notebook
12.4%
Shell
2.9%
We introduce the idea of Sample Design Engineering (SDE) for LLMs' Downstream Fine-Tuning. 我们提出了针对大模型下游任务微调的「样本设计工程」。
💻 可复现的小项目:
💬 相关讨论区:
🤖 目前支持:
🎯 两行代码开启 LoRA 训练:
sh tokenize.sh,对比不同的 LLM,需在 tokenize.sh 文件里切换 model_checkpoint 参数sh train.sh,对于不同的 LLM,需切换不同的 python 文件来执行:
chatglm_lora_tuning.pychatglm2_lora_tuning.pybaichuan_lora_tuning.pybaichuan2_lora_tuning.pyintermlm_lora_tuning.pychinese_llama2_alpaca2_lora_tuning.py🎯 手把手的 RLHF 教程:见 LoRA-based-RLHF
环境准备:
pip install transformers datasets accelerate sentencepiece tensorboard peft
目前测试的环境为:
- Python 3.9.16
- torch, Version: 2.0.1
- transformers, Version: 4.29.1
- datasets, Version: 2.12.0
- accelerate, Version: 0.19.0
- peft, Version: 0.3.0
- sentencepiece, Version: 0.1.99
- tensorboard, Version: 2.13.0
下面的教程以及代码使用 ChatGLM-6B 作为例子,如果更换其他模型,可能需要略微修改具体文件代码。
原始文件的准备
指令微调数据一般有输入和输出两部分,输入是特定的content加上instruction,这里我们将二者直接拼在一起,不单独区分;输出则是希望模型的回答。
我们统一使用json的格式在整理数据,可以自定义输出输出的字段名,例如下面的例子中我使用的是q和a代表模型的输入和输出:
{"q": "请计算:39 * 0 = 什么?", "a": "这是简单的乘法运算,39乘以0得到的是0"}
{"q": "题目:51/186的答案是什么?", "a": "这是简单的除法运算,51除以186大概为0.274"}
{"q": "鹿妈妈买了24个苹果,她想平均分给她的3只小鹿吃,每只小鹿可以分到几个苹果?", "a": "鹿妈妈买了24个苹果,平均分给3只小鹿吃,那么每只小鹿可以分到的苹果数就是总苹果数除以小鹿的只数。\n24÷3=8\n每只小鹿可以分到8个苹果。所以,答案是每只小鹿可以分到8个苹果。"}
...
整理好数据后,保存为.json或者.jsonl文件,然后放入目录中的data/文件夹中。
对数据集进行分词
为了避免每次训练的时候都要重新对数据集分词,我们先分好词形成特征后保存成可直接用于训练的数据集。
例如,
data/ 文件夹下的 simple_math_4op.json 文件q,输出字段为adata/tokenized_data/ 下名为 simple_math_4op 的文件夹中则我们可以直接使用下面这段命令(即tokenize.sh文件)进行处理:
CUDA_VISIBLE_DEVICES=0,1 python tokenize_dataset_rows.py \
--model_checkpoint THUDM/chatglm-6b \
--input_file simple_math_4op.json \
--prompt_key q \
--target_key a \
--save_name simple_math_4op \
--max_seq_length 2000 \
--skip_overlength False
处理完毕之后,我们会在 data/tokenized_data/ 下发现名为 simple_math_4op 的文件夹,这就是下一步中我们可以直接用于训练的数据。
LoRA 微调得到 tokenize 之后的数据集,就可以直接运行 chatglm_lora_tuning.py 来训练 LoRA 模型了,具体可设置的主要参数包括:
tokenized_dataset, 分词后的数据集,即在 data/tokenized_data/ 地址下的文件夹名称lora_rank, 设置 LoRA 的秩,推荐为4或8,显存够的话使用8per_device_train_batch_size, 每块 GPU 上的 batch sizegradient_accumulation_steps, 梯度累加,可以在不提升显存占用的情况下增大 batch sizemax_steps, 训练步数save_steps, 多少步保存一次save_total_limit, 保存多少个checkpointlogging_steps, 多少步打印一次训练情况(loss, lr, etc.)output_dir, 模型文件保存地址例如我们的数据集为 simple_math_4op,希望保存到 weights/simple_math_4op ,则执行下面命令(即train.sh文件):
CUDA_VISIBLE_DEVICES=2,3 python chatglm_lora_tuning.py \
--tokenized_dataset simple_math_4op \
--lora_rank 8 \
--per_device_train_batch_size 10 \
--gradient_accumulation_steps 1 \
--max_steps 100000 \
--save_steps 200 \
--save_total_limit 2 \
--learning_rate 1e-4 \
--fp16 \
--remove_unused_columns false \
--logging_steps 50 \
--output_dir weights/simple_math_4op
训练完之后,可以在 output_dir 中找到 LoRA 的相关模型权重,主要是adapter_model.bin和adapter_config.json两个文件。
如何查看 tensorboard:
your_log_pathtensorboard --logdir your_log_path 命令,就会在 http://localhost:6006/ 上开启tensorboard-L 6006:127.0.0.1:6006 参数,将服务器端的6006端口映射到本地的6006端口。我们可以把上面的 output_dir 打包带走,假设文件夹为 weights/simple_math_4op, 其中(至少)包含 adapter_model.bin 和 adapter_config.json 两个文件,则我们可以用下面的方式直接加载,并推理
from peft import PeftModel
from transformers import AutoTokenizer, AutoModel
import torch
device = torch.device(1)
# 加载原始 LLM
model_path = "THUDM/chatglm-6b"
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).half().to(device)
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model.chat(tokenizer, "你好", history=[])
# 给原始 LLM 安装上你的 LoRA tool
model = PeftModel.from_pretrained(model, "weights/simple_math_4op").half()
model.chat(tokenizer, "你好", history=[])
理论上,可以通过多次执行 model = PeftModel.from_pretrained(model, "weights/simple_math_4op").half() 的方式,加载多个 LoRA 模型,从而混合不同Tool的能力,但实际测试的时候,由于暂时还不支持设置不同 LoRA weights的权重,往往效果不太好,存在覆盖或者遗忘的情况。
对这些优秀开源项目表示感谢!
HTML
66.3%
Python
18.5%
Jupyter Notebook
12.4%
Shell
2.9%