mistral-finetune
是一个轻量级代码库,可以对 Mistral 模型进行内存高效且高性能的微调。它基于 LoRA,这是一种训练范例,其中大多数权重被冻结,并且仅训练 1-2% 的低秩矩阵扰动形式的附加权重。
为了获得最大效率,建议使用 A100 或 H100 GPU。该代码库针对多 GPU 单节点训练设置进行了优化,但对于较小的模型(例如 7B),单个 GPU 就足够了。
笔记
该存储库的目标是提供一个简单的引导入口点来微调 Mistral 模型。因此,它是相当固执己见的(特别是在数据格式化方面),并且并不旨在详尽地涵盖多种模型架构或硬件类型。对于更通用的方法,您可以查看其他一些很棒的项目,例如 torchtune。
2024 年 8 月 13 日:Mistral Large v2 现在与mistral-finetune
兼容!
与其他模型相比,建议使用较低的学习率,例如lr=1e-6 对于大多数情况应该效果很好。
由于模型尺寸较大,微调 Mistral-Large v2 需要显着更多的内存。现在将seq_len
设置为 <= 8192
在此处下载 123B 指令并将model_id_or_path
设置为下载的检查点目录。
2024 年 7 月 19 日:Mistral Nemo 现在与mistral-finetune
兼容!
建议使用与 7B v3 相同的超参数。
由于词汇量较大,微调 Mistral-Nemo 目前需要更多内存,这会增加 CE 损失的峰值内存需求(我们很快将在此处添加改进的 CE 损失)。现在将seq_len
设置为 <= 16384
运行pip install --upgrade mistral-common
以获得支持 Tekkenizer 的版本 ( >=1.3.1
)。
在此处下载 12B Base 或 Instruct,并将model_id_or_path
设置为下载的检查点目录。
要开始 Mistral LoRA 微调,请按照以下步骤操作:
克隆此存储库:
cd $HOME && git clone https://github.com/mistralai/mistral-finetune.git
安装所有必需的依赖项:
cd mistral-finetune pip install -r requirements.txt
我们建议微调官方 Mistral 模型之一,您可以在此处下载:
模型 | 关联 | 校验和 |
---|---|---|
7B 底座 V3 | 7B底座 | 0663b293810d7571dad25dae2f2a5806 |
7B 指令 v3 | 7B 指令 v3 | 80b71fcb6416085bcb4efad86dfb4d52 |
8x7B 基础 V1 | 8x7B 底座 | (高频链接) |
8x7B 指令 V1 | 8x7B 指导 | 8e2d3930145dc43d3084396f49d38a3f |
8x22 指导 V3 | 8x22 指导 | 471a02a6902706a2f1e44a693813855b |
8x22B 基础 V3 | 8x22B 底座 | a2fa75117174f87d1197e3a4eb50371a |
12B 指导 | 12B 指导 (米斯特拉尔-尼莫) | 296fbdf911cb88e6f0be74cd04827fe7 |
12B底座 | 12 基地(米斯特拉尔-尼莫) | c5d079ac4b55fc1ae35f51f0a3c0eb83 |
米斯特拉尔大 2 | 123B 指导(大 v2) | fc602155f9e39151fba81fcaab2fa7c4 |
重要提示:对于 8x7B Base V1 和 8x7B Instruct V1,有必要在微调之前使用我们的 v3 分词器并将词汇表大小扩展到 32768。有关此过程的详细说明,请参阅“模型扩展”部分。
例如,要下载 7B-base 模型,您可以运行以下命令:
mkdir -p ~/${HOME}/mistral_modelscd ${HOME} && wget https://models.mistralcdn.com/mistral-7b-v0-3/mistral-7B-v0.3.tar tar -xf 米斯特拉尔-7B-v0.3.tar -C 米斯特拉尔_模型
确保修改您的训练脚本并将下载文件夹的路径添加为model_id_or_path
。
例如,修改 example/7B.yaml 以包含$HOME/mistral_models/7B
的绝对路径:
model_id_or_path: "/Users/johndoe/mistral_models/7B"
为了确保有效的训练, mistral-finetune
对训练数据的格式有严格的要求。
所有数据文件必须以jsonl格式文件存储。
您可以构建两种类型的数据文件:
预训练数据对应于存储在"text"
键中的纯文本数据。例如:
{"text": "文档 n°1 中包含的文本"} {"text": "文档 n°2 中包含的文本"}
目前支持两种不同类型的指令跟随数据:
指令:会话数据以列表的形式存储在"messages"
键中。每个列表项都是一个包含"content"
和"role"
键的字典。 "role"
是“用户”、“助理”或“系统”之一的字符串。仅当“角色”==“助理”时才会计算损失。例如:
{“消息”:[ { "role": "user", "content": "文档 n°1 中包含的用户交互 n°1" }, { "role": "assistant", "content": "文档 n°1 中包含的机器人交互 n°1" }, { "role": "user", "content": "文档 n°1 中包含的用户交互 n°2" }, { "role": "assistant", "content": "文档 n°1 中包含的机器人交互 n°2" } ] } {“消息”:[ { "role": "user", "content": "文档 n°2 中包含的用户交互 n°1" }, { "role": "assistant", "content": "文档 n°2 中包含的机器人交互 n°1" }, { "role": "user", "content": "文档 n°2 中包含的用户交互 n°2" }, { "role": "assistant", "content": "文档 n°2 中包含的机器人交互 n°2", "weight": 0, # 不要在 n°2 上进行训练 }, { "role": "user", "content": "文档 n°2 中包含的用户交互 n°3" }, { "role": "assistant", "content": "文档 n°2 中包含的机器人交互 n°3" } ] }
函数调用:会话数据以列表的形式存储在"messages"
键中。每个列表项都是一个包含"role"
和"content"
或"tool_calls"
键的字典。 "role"
是“用户”、“助理”、“系统”或“工具”之一的字符串。仅当“角色”==“助理”时才会计算损失。
注意:在函数调用中, "tool_calls"
的"id"
和"tool_call_id"
是随机生成的 9 个字符的字符串。我们建议在数据准备脚本中自动生成此内容,如此处所示。
例如:
{“消息”:[ { "role": "system", "content": "您是一名得力助手,可以使用以下功能来帮助用户,您可以根据需要使用这些功能" }, { "role": "user", "content": "你能帮我生成“listen”这个词的字谜吗?" }, {“角色”:“助理”,“工具调用”:[ { "id": "TX92Jm8Zi", "type": "function", "function": { "name": "generate_anagram", "arguments": "{"word": "listen"}" } } ] }, { "角色": "工具", "内容": "{"字谜": "沉默"}", "tool_call_id": "TX92Jm8Zi" }, { "role": "assistant", "content": "单词“listen”的字谜是“silent”。" }, { "role": "user", "content": "太棒了!你能为“race”这个词生成一个字谜吗?" }, {“角色”:“助理”,“工具调用”:[ { "id": "3XhQnxLsT", "type": "function", "function": { "name": "generate_anagram", "arguments": "{"word": "race"}" } } ] } ], “工具”: [ { "type": "function", "function": { "name": "generate_anagram", "description": "生成给定单词的 anagram", "parameters": { "type": "object", " properties": { "word": { "type": "string", "description": "要生成字谜词的单词" } }, "必填": [ "单词" ] } } } ] }
在开始训练运行之前,您应该验证数据集的格式是否正确并估计训练时间。您可以使用 ./utils/validate_data 脚本来执行此操作。
请注意,此步骤对于确保数据格式正确至关重要。
让我们看一个简单的示例,按照以下指令训练模型:
加载一块 Ultachat_200k
创建数据文件夹并导航到该文件夹。
cd $HOME && mkdir -p 数据 && cd $HOME/data
将数据加载到 Pandas Dataframe 中。
注意:确保安装了 pandas 和 pyarrow ( pip install pandas pyarrow
)。
将 pandas 导入为 pddf = pd.read_parquet('https://huggingface.co/datasets/HuggingFaceH4/ultrachat_200k/resolve/main/data/test_gen-00000-of-00001-3d4cd8309148a71f.parquet')
分为train和eval
df_train=df.sample(frac=0.95,random_state=200)df_eval=df.drop(df_train.index)
将数据保存到 jsonl
df_train.to_json(“ultrachat_chunk_train.jsonl”,东方=“记录”,行= True)df_eval.to_json(“ultrachat_chunk_eval.jsonl”,东方=“记录”,行= True)
修改您的训练 yaml 以包含 ultrachat 数据集并验证 yaml
修改 example/7B.yaml 以包含$HOME/data/ultrachat_chunk_train.jsonl
的绝对路径以及用于训练的数据集混合权重和用于评估的$HOME/data/ultrachat_chunk_eval.jsonl
,例如
data: instruct_data: "/Users/johndoe/data/ultrachat_chunk_train.jsonl" eval_instruct_data: "/Users/johndoe/data/ultrachat_chunk_eval.jsonl"
现在,您可以验证您的训练 yaml,以确保数据格式正确并估算训练时间。
cd $HOME/mistral-finetune python -m utils.validate_data --train_yaml example/7B.yaml
完成后,您应该会看到包含以下许多错误的错误报告:
The data in line 1412 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user The data in line 1413 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user The data in line 1414 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user The data in line 1415 of dataset /Users/johndoe/data/ultrachat_chunk_eval.jsonl is incorrectly formatted. Expected last role to be one of: [assistant] but got user
许多对话似乎以“用户”角色结束,这是不必要的,因为我们只训练“辅助”消息,因此会不必要地处理数据。
您可以使用 ./utils/reformat_data.py 来更正数据:
cd $HOME/mistral-finetune python -m utils.reformat_data $HOME/data/ultrachat_chunk_train.jsonl python -m utils.reformat_data $HOME/data/ultrachat_chunk_eval.jsonl
您应该看到将跳过几个示例。
可能改变训练步骤的数量
修正数据集后,再次运行脚本
cd $HOME/mistral-finetune python -m utils.validate_data --train_yaml example/7B.yaml
您应该获得数据输入和训练参数的摘要:
Train States -------------------- { "expected": { "eta": "00:52:44", "data_tokens": 25169147, "train_tokens": 131072000, "epochs": "5.21", "max_steps": 500, "data_tokens_per_dataset": { "/Users/johndoe/data/ultrachat_chunk_train.jsonl": "25169147.0" }, "train_tokens_per_dataset": { "/Users/johndoe/data/ultrachat_chunk_train.jsonl": "131072000.0" }, "epochs_per_dataset": { "/Users/johndoe/data/ultrachat_chunk_train.jsonl": "5.2" } }, }
将max_steps
设置为 500 会导致对数据集进行大约 5 次迭代,这是合理的,但可能有点太多了。下面显示了推荐的设置,在 8xH100 集群上只需 30 分钟。
接下来让我们回顾一个更高级的用例来微调函数调用的模型。函数调用要求数据采用上述格式。让我们看一个例子。
加载 Glaive 函数调用数据集的聊天格式版本
创建数据文件夹并导航到该文件夹。
cd $HOME && mkdir -p 数据 && cd $HOME/data
将数据加载到 Pandas Dataframe 中。
注意:确保安装了 pandas 和 pyarrow ( pip install pandas pyarrow
)。
将 pandas 导入为 pddf = pd.read_parquet('https://huggingface.co/datasets/Locutusque/function-calling-chatml/resolve/main/data/train-00000-of-00001-f0b56c6983b4a78f.parquet')
分为train和eval
df_train=df.sample(frac=0.95,random_state=200)df_eval=df.drop(df_train.index)
将数据保存到 jsonl
df_train.to_json(“glaive_train.jsonl”,东方=“记录”,行= True)df_eval.to_json(“glaive_eval.jsonl”,东方=“记录”,行= True)
重新格式化数据集
正如我们所看到的,数据集不遵循所需的函数调用格式,因此需要重新格式化。除此之外, "from"
应重命名为"user"
,并且应删除多余的"n"
字符。对于此数据集,您可以使用./utils/reformat_data_glaive.py
:
cd $HOME/mistral-finetune python -m utils.reformat_data_glaive $HOME/data/glaive_train.jsonl python -m utils.reformat_data_glaive $HOME/data/glaive_eval.jsonl
运行此命令将确保大多数样本的格式正确。
注意:不可能编写适用于所有类型数据集的重新格式化脚本。如果您的数据集尚未遵循上述所需格式,您很可能必须自己创建一个重新格式化脚本(mistral-chat 或 chat-gpt 是您最好的朋友!)。
验证数据集
现在,您可以通过在example/7B.yaml
中分别将data.instruct_data
和data.eval_instruct_data
设置为$HOME/data/glaive_train.jsonl
和$HOME/data/glaive_eval.jsonl
来验证数据集。
重新格式化的数据集仍然存在一些错误,可以使用--create_corrected
删除这些错误。为此,请确保添加--create_corrected
,如下所示:
cd $HOME/mistral-finetune python -m utils.validate_data --train_yaml example/7B.yaml --create_corrected
运行此命令将显示一些错误并保存两个新数据集$HOME/data/glaive_train.jsonl.corrected
和$HOME/data/glaive_eval.jsonl.corrected
。确保在example/7B.yaml
中使用这两个数据集并再次运行该命令。现在数据集的格式应该正确了!
完成数据集验证部分后,我们现在可以开始训练。为了更快的训练,我们建议将 max_steps 设置为仅 300。确保将run_dir
定义到您的实验文件夹,并可选择将wandb_project
设置为用于记录的权重和偏差项目,例如:
max_steps: 300 run_dir: "/Users/johndoe/ultra_chat_test" wandb.project: ultra_chat
您还可以选择设置wandb
保存训练配置并开始训练!确保将--nproc-per-node
设置为可用 GPU 的数量。
cd $HOME/mistral-finetune torchrun --nproc-per-node 8 --master_port $RANDOM -m train example/7B.yaml
在 8xH100 节点上进行 ultra-chat 训练大约需要 30 分钟,所得权重的 MT Bench 分数应约为 6.3。
在 8xH100 节点上对 glaive 进行训练大约需要 1 小时,并且生成的权重应该可以很好地用于函数调用。
示例mistral-finetune/examples/7B
定义了学习率、权重衰减等的合理参数...但建议您根据您的用例自定义这些设置。
一般来说,训练配置应填写以下参数:
model_id_or_path
定义开始训练的模型。这可以是预训练模型的路径或本地模型目录。
run_dir
定义存储训练检查点和指标的目录。
seq_len
定义训练的序列长度。这是模型将处理的输入序列的最大长度。样本被打包以达到seq_len
的长度,以实现最大的训练效率。
batch_size
定义每个 GPU 使用的训练示例的数量。注意:所有 GPU 的总体有效batch_size(以令牌为单位)等于num_gpus
x batch_size
x seq_len
。
max_steps
定义最大训练步骤数。这是训练过程将运行的迭代总数。可以根据您的训练场景的具体需求进行调整。训练期间看到的令牌总数为max_steps
x num_gpus
x batch_size
x seq_len
。
optim.lr
定义学习率。这是优化器的初始学习率。
optim.weight_decay
定义权重衰减。权重衰减是一种正则化技术,用于通过惩罚大权重来防止过度拟合。我们建议将其保留为 0.1。
optim.pct_start
定义学习率开始下降之前的预热阶段所用的总训练步数的百分比。它对应于PyTorch的OneCycleLR的pct_start。
lora.rank
定义 LoRA(低秩适应)适配器的大小。我们建议 64 或更少,这会调整 LoRA 中使用的低秩分解的秩。
seed
定义用于初始化和数据混洗/采样的随机种子。设置种子可确保结果的可重复性。
log_freq
定义记录频率。这指定了记录训练指标的频率(以步骤为单位)。
data.instruct_data
是用于训练的指令数据的路径。该字段必须按照上述格式填写一个或多个数据源。每个数据源应该是 jsonl 文件的路径或包含 jsonl 文件的目录的路径,后跟用于定义此数据集重要性的权重:
。例如: data.instruct_data: "/path/to/data1.jsonl:5.,/path/to/data2.jsonl:1.,/path/to/dir_of_jsonls:1."
data.data
是附加预训练数据的可选路径,格式如上所述。请注意,该字段可以留空。
data.eval_instruct_data
是评估指令数据的可选路径,用于在每个eval_freq
步骤运行交叉验证。交叉验证指标显示为loss
和perplexity
。
eval_freq
定义评估模型的频率(以步骤为单位)。这指定了在验证集上评估模型的时间间隔。
no_eval
是启用或禁用中间评估的标志。将其设置为 False 可以在训练期间进行定期评估。
ckpt_freq
定义保存检查点的频率(以步骤为单位)。这指定保存模型状态的时间间隔。
save_adapters
定义是否仅保存经过训练的 LoRA 检查点,或者是否应将经过训练的 LoRA 直接合并到基础模型中并保存。注意:设置save_adapters=False
时,请确保您有足够的 CPU 和 GPU 内存来在单个进程上保存完整模型(这通常仅适用于 7B 模型)。
wandb.key
用于传递您的权重和偏差 (wandb) API 密钥以进行日志记录。这允许您将训练指标记录到 wandb 仪表板。
wandb.project
定义 wandb 项目名称。这是训练运行将记录在 wandb 界面中的位置。
一旦你的模型被训练,你应该在推理中尝试它。我们建议使用 Mistra-inference。
确保正确安装了mistral_inference
:
pip install mistral_inference
假设您的lora.safetensors
保存在$HOME/ultra_chat_test/checkpoints/checkpoint_000300/consolidated/lora.safetensors
下,您可以使用mistral_inference
与模型聊天,例如:
米斯特拉尔聊天 /mnt/slow/runs/patrick/mistral-finetune/7B/ --max_tokens 256 --温度 1.0 --instruct --lora_path $HOME/ultra_chat_test/checkpoints/checkpoint_000300/consolidated/lora.safetensors
我们添加了对权重和偏差的明确支持,以帮助您监控和可视化您的训练运行。这种集成使您可以轻松记录各种指标并跟踪实验。
要将权重和偏差与mistral-finetune
一起使用,请按照下列步骤操作:
安装权重和偏差:
确保您已安装wandb
库。您可以使用 pip 安装它:
pip安装wandb
培训开始后,您可以通过访问 wandb 项目仪表板实时监控进度。所有指标,包括训练损失、评估损失、学习率等,都将被记录和可视化。
有关如何使用 wandb 的更多详细信息,请访问权重和偏差文档。
重要提示:请注意,只能微调与 v3 分词器兼容的 Mistra 模型,这意味着模型的词汇量大小为 32768 - 而不是 32000。但是,人们可以轻松地将旧版本的词汇量大小 32000 扩展到词汇量大小32768 通过使用:
python -m utils.extend_model_vocab --original_model_ckpt /folder/to/old/model --extended_model_ckpt /folder/to/extended/model
一旦扩展生效,就可以使用/folder/to/extended/model
中新创建的模型检查点进行微调。
微调 MoE 的最佳实践是什么?
我们在微调 MoE 模型时发现了更高程度的性能差异。使用不同种子微调 MoE 模型可能会导致性能出现很大差异,这一点并不罕见。我们没有在密集模型中观察到如此高的方差。因此,我们建议在 MoE 模型上运行同一微调过程的多个实例,并选择性能最佳的一个。
如何确定模型训练过程中使用的令牌数量?
您可以使用以下脚本来查找:https://github.com/mistralai/mistral-finetune/blob/main/utils/validate_data.py。该脚本接受 .yaml 训练文件作为输入,并返回模型正在训练的标记数量。
如果遇到 CUDA 内存不足错误该怎么办?
一种可能的解决方案是减少每个 GPU 的批量大小。批量大小等于seq_len
x batch_size
。尝试将batch_size
设置为1并减少seq_len
。您可以在.yaml 文件中定义batch_size
和seq_len
。