端到端工作流与torchtune¶
在本教程中,我们将通过一个完整的示例来展示如何使用 torchtune 对您最喜欢的大型语言模型(LLM)进行微调、评估、可选地量化,然后进行生成。我们还将介绍如何无缝地使用社区中的一些流行工具和库与 torchtune 结合使用。
torchtune 中除微调外可用的其他类型配方
连接所有这些配方的端到端示例
您可以与 torchtune 配合使用的各种工具和库
确保已 安装 torchtune
概览¶
微调一个大型语言模型(LLM)通常是更大工作流程中的一个步骤。你可能的工作流程可以看起来像这样:
从 HF Hub 下载一个流行的模型
使用相关的微调技术对模型进行微调。具体使用的微调技术将取决于多种因素,例如模型类型、训练数据的数量和性质、硬件配置以及模型最终将用于的任务。
在部分基准测试上评估模型,以验证模型质量
运行几次生成,以确保模型输出看起来合理
量化模型以实现高效推理
[可选] 导出模型以适配特定环境,例如在手机上进行推理
在本教程中,我们将介绍如何使用 torchtune 实现上述所有功能,并利用生态系统中流行的工具和库的集成。
我们将在这个教程中使用 Llama2 7B 模型。您可以在 这里 找到 torchtune 支持的完整模型列表。
下载 Llama2 7B¶
在本教程中,我们将使用 Hugging Face 的 Llama2 7B 模型权重。 有关检查点格式以及 torchtune 如何处理这些内容的更多信息,请查看 此关于 检查点 的教程。
要下载 HF 格式的 Llama2 7B 模型,我们将使用 tune CLI。
tune download \
meta-llama/Llama-2-7b-hf \
--output-dir <checkpoint_dir> \
--hf-token <ACCESS TOKEN>
请注意<checkpoint_dir>,在本教程中我们将多次使用它。
使用LoRA微调模型¶
对于本教程,我们将使用LoRA对模型进行微调。LoRA是一种参数高效的微调技术,特别适用于您没有太多GPU内存的情况。LoRA冻结基础LLM并添加一小部分可学习参数。这有助于保持与梯度和优化器状态相关的内存较低。使用torchtune,您应该能够在RTX 3090/4090上使用bfloat16,在不到16GB的GPU内存下对Llama2 7B模型进行LoRA微调。有关如何使用LoRA的更多信息,请查看我们的 LoRA教程。
我们将使用我们的 单设备LoRA配方 进行微调,并使用来自 默认配置 的标准设置。
这将使用 batch_size=2 和 dtype=bfloat16 来微调我们的模型。在这些设置下,模型的峰值内存使用量约为 16GB,每个 epoch 的总训练时间约为两小时。
我们需要对配置进行一些更改,以确保我们的配方可以访问正确的检查点。
让我们使用 tune CLI 为此用例寻找合适的配置。
tune ls
RECIPE CONFIG
full_finetune_single_device llama2/7B_full_low_memory
mistral/7B_full_low_memory
full_finetune_distributed llama2/7B_full
llama2/13B_full
mistral/7B_full
lora_finetune_single_device llama2/7B_lora_single_device
llama2/7B_qlora_single_device
mistral/7B_lora_single_device
...
在这个教程中,我们将使用 llama2/7B_lora_single_device 配置。
配置已经指向了HF Checkpointer和正确的检查点文件。 我们只需要更新模型和分词器的检查点目录即可。 让我们在启动训练时,使用tune CLI中的覆盖选项来完成这项工作!
tune run lora_finetune_single_device \
--config llama2/7B_lora_single_device \
checkpointer.checkpoint_dir=<checkpoint_dir> \
tokenizer.path=<checkpoint_dir>/tokenizer.model \
checkpointer.output_dir=<checkpoint_dir>
训练完成后,您将在日志中看到以下内容。
[_checkpointer.py:473] Model checkpoint of size 9.98 GB saved to <checkpoint_dir>/hf_model_0001_0.pt
[_checkpointer.py:473] Model checkpoint of size 3.50 GB saved to <checkpoint_dir>/hf_model_0002_0.pt
[_checkpointer.py:484] Adapter checkpoint of size 0.01 GB saved to <checkpoint_dir>/adapter_0.pt
最终训练的权重与原始模型合并,并分割到两个检查点文件中,类似于来自 HF Hub 的源检查点(有关更多详细信息,请参阅 LoRA 教程)。实际上,这些检查点之间的键将是相同的。我们还有一个第三检查点文件,其大小要小得多,包含学习到的 LoRA 适配器权重。对于本教程,我们只使用模型检查点,而不使用适配器权重。
使用EleutherAI的Eval Harness运行评估¶
我们已经对模型进行了微调。但这个模型的实际表现究竟如何呢?让我们运行一些评估!
torchtune 集成了
EleutherAI 的评估工具包。
一个示例可以通过
eleuther_eval 配方获得。在这个教程中,我们将直接使用这个配方,并通过
修改其关联的配置文件 eleuther_evaluation.yaml 来实现。
注意
本教程的这一部分,您应该首先运行 pip install lm_eval==0.4.* 来安装 EleutherAI 评估工具包。
因为我们计划更新所有检查点文件以指向我们微调后的检查点,所以首先将配置复制到本地工作目录以便进行更改。这比通过命令行界面覆盖所有这些元素更容易。
tune cp eleuther_evaluation ./custom_eval_config.yaml \
在本教程中,我们将使用 harness 中的 truthfulqa_mc2 任务。
该任务衡量模型在回答问题时保持真实的倾向性,并评估模型在“零样本”场景下的准确率,即针对一个问题,模型需对随后出现的一个或多个真实回答和一个或多个虚假回答进行判断。让我们首先运行一个未经微调的基线测试。
tune run eleuther_eval --config ./custom_eval_config.yaml
checkpointer.checkpoint_dir=<checkpoint_dir> \
tokenizer.path=<checkpoint_dir>/tokenizer.model
[evaluator.py:324] Running loglikelihood requests
[eleuther_eval.py:195] Eval completed in 121.27 seconds.
[eleuther_eval.py:197] truthfulqa_mc2: {'acc,none': 0.388...
该模型的准确率约为 38.8%。让我们将其与微调后的模型进行比较。
首先,我们将 custom_eval_config.yaml 修改为包含微调后的检查点。
checkpointer:
_component_: torchtune.utils.FullModelHFCheckpointer
# directory with the checkpoint files
# this should match the output_dir specified during
# finetuning
checkpoint_dir: <checkpoint_dir>
# checkpoint files for the fine-tuned model. This should
# match what's shown in the logs above
checkpoint_files: [
hf_model_0001_0.pt,
hf_model_0002_0.pt,
]
output_dir: <checkpoint_dir>
model_type: LLAMA2
# Make sure to update the tokenizer path to the right
# checkpoint directory as well
tokenizer:
_component_: torchtune.models.llama2.llama2_tokenizer
path: <checkpoint_dir>/tokenizer.model
现在,让我们运行该示例。
tune run eleuther_eval --config ./custom_eval_config.yaml
结果应大致如下所示。
[evaluator.py:324] Running loglikelihood requests
[eleuther_eval.py:195] Eval completed in 121.27 seconds.
[eleuther_eval.py:197] truthfulqa_mc2: {'acc,none': 0.489 ...
我们的微调模型在这个任务上得到了约48%的准确率,比基线模型高出约10个百分点。太棒了!看来我们的微调确实起到了作用。
生成¶
我们已经进行了一些评估,模型看起来表现不错。但它真的能为你们关心的提示生成有意义的文本吗?让我们来一探究竟!
让我们先将配置文件复制到本地工作目录,以便进行修改。
tune cp generation ./custom_generation_config.yaml
让我们修改 custom_generation_config.yaml 以包含以下更改。
checkpointer:
_component_: torchtune.utils.FullModelHFCheckpointer
# directory with the checkpoint files
# this should match the output_dir specified during
# finetuning
checkpoint_dir: <checkpoint_dir>
# checkpoint files for the fine-tuned model. This should
# match what's shown in the logs above
checkpoint_files: [
hf_model_0001_0.pt,
hf_model_0002_0.pt,
]
output_dir: <checkpoint_dir>
model_type: LLAMA2
# Make sure to update the tokenizer path to the right
# checkpoint directory as well
tokenizer:
_component_: torchtune.models.llama2.llama2_tokenizer
path: <checkpoint_dir>/tokenizer.model
配置更新后,让我们开始生成!我们将使用默认的采样设置,top_k=300 和 temperature=0.8。这些参数控制概率的计算方式。这些是 Llama2 7B 的标准设置,我们建议在调整这些参数之前先检查模型。
我们将使用与配置中不同的提示词。
tune run generate --config ./custom_generation_config.yaml \
prompt="What are some interesting sites to visit in the Bay Area?"
生成完成后,您将在日志中看到以下内容。
[generate.py:92] Exploratorium in San Francisco has made the cover of Time Magazine,
and its awesome. And the bridge is pretty cool...
[generate.py:96] Time for inference: 11.61 sec total, 25.83 tokens/sec
[generate.py:99] Memory used: 15.72 GB
确实,这座桥很酷!看来我们的大型语言模型对旧金山湾区了解一些!
使用量化加速生成¶
我们看到生成过程大约需要 11.6 秒来生成 300 个 token。 一种常用于加速推理的技术是量化。torchtune 提供了与 TorchAO 量化 API 的集成。让我们首先使用 4 位仅权重量化对模型进行量化, 并查看这是否能提高生成速度。
为此,我们将使用 量化方案。
让我们先将配置文件复制到本地工作目录,以便进行修改。
tune cp quantization ./custom_quantization_config.yaml
让我们修改 custom_quantization_config.yaml 以包含以下更改。
checkpointer:
_component_: torchtune.utils.FullModelHFCheckpointer
# directory with the checkpoint files
# this should match the output_dir specified during
# finetuning
checkpoint_dir: <checkpoint_dir>
# checkpoint files for the fine-tuned model. This should
# match what's shown in the logs above
checkpoint_files: [
hf_model_0001_0.pt,
hf_model_0002_0.pt,
]
output_dir: <checkpoint_dir>
model_type: LLAMA2
配置更新完成后,让我们开始量化!我们将使用配置中默认的量化方法。
tune run quantize --config ./custom_quantization_config.yaml
量化完成后,您将在日志中看到以下内容。
[quantize.py:68] Time for quantization: 19.76 sec
[quantize.py:69] Memory used: 13.95 GB
[quantize.py:82] Model checkpoint of size 3.67 GB saved to <checkpoint_dir>/hf_model_0001_0-4w.pt
注意
与微调后的检查点不同,此操作会输出单个检查点文件。 这是因为我们的量化 API 当前不支持任何跨格式的转换。 因此,您将无法在 torchtune 之外使用这些量化模型。 但您可以在 torchtune 内部的生成(generation)和评估(evaluation)流程中使用这些模型。 这些结果将有助于您确定:针对您最常用的推理引擎,应选用哪种量化方法。
既然我们已经有了量化模型,让我们重新运行生成过程。
修改 custom_generation_config.yaml 以包含以下更改。
checkpointer:
# we need to use the custom TorchTune checkpointer
# instead of the HF checkpointer for loading
# quantized models
_component_: torchtune.utils.FullModelTorchTuneCheckpointer
# directory with the checkpoint files
# this should match the output_dir specified during
# finetuning
checkpoint_dir: <checkpoint_dir>
# checkpoint files point to the quantized model
checkpoint_files: [
hf_model_0001_0-4w.pt,
]
output_dir: <checkpoint_dir>
model_type: LLAMA2
# we also need to update the quantizer to what was used during
# quantization
quantizer:
_component_: torchtune.utils.quantization.Int4WeightOnlyQuantizer
groupsize: 256
配置更新完成后,让我们开始生成!我们将使用与之前相同的采样参数,同时沿用之前非量化模型所使用的提示词。
tune run generate --config ./custom_generation_config.yaml \
prompt="What are some interesting sites to visit in the Bay Area?"
生成完成后,您将在日志中看到以下内容。
[generate.py:92] A park in San Francisco that sits at the top of a big hill.
There are lots of trees and a beautiful view of San Francisco...
[generate.py:96] Time for inference: 4.13 sec total, 72.62 tokens/sec
[generate.py:99] Memory used: 17.85 GB
通过量化(以及底层的 torch.compile),我们的生成速度提升了近 3 倍!
使用torchtune检查点与其他库¶
如我们上面提到的,处理检查点转换的一个好处是你可以直接使用标准格式。这有助于与其他库的互操作性,因为torchtune不会增加另一种格式。
让我们来看一个使用流行的代码库进行高性能推理的例子—— gpt-fast。本节假设您已经在机器上克隆了该仓库。
gpt-fast 假设了检查点的一些情况以及键到文件映射的可用性,即一个将参数名称映射到包含它们的文件的文件。让我们通过创建这个映射文件来满足这些假设。让我们假设我们将使用 <new_dir>/Llama-2-7B-hf 作为目录。 gpt-fast 假设检查点所在的目录具有与 HF repo-id 相同的格式。
import json
import torch
# create the output dictionary
output_dict = {"weight_map": {}}
# Load the checkpoints
sd_1 = torch.load('<checkpoint_dir>/hf_model_0001_0.pt', mmap=True, map_location='cpu')
sd_2 = torch.load('<checkpoint_dir>/hf_model_0002_0.pt', mmap=True, map_location='cpu')
# create the weight map
for key in sd_1.keys():
output_dict['weight_map'][key] = "hf_model_0001_0.pt"
for key in sd_2.keys():
output_dict['weight_map'][key] = "hf_model_0002_0.pt"
with open('<new_dir>/Llama-2-7B-hf/pytorch_model.bin.index.json', 'w') as f:
json.dump(output_dict, f)
既然我们已经创建了 weight_map,现在就来复制我们的检查点。
cp <checkpoint_dir>/hf_model_0001_0.pt <new_dir>/Llama-2-7B-hf/
cp <checkpoint_dir>/hf_model_0002_0.pt <new_dir>/Llama-2-7B-hf/
cp <checkpoint_dir>/tokenizer.model <new_dir>/Llama-2-7B-hf/
一旦目录结构搭建完成,让我们转换检查点并运行推理!
cd gpt-fast/
# convert the checkpoints into a format readable by gpt-fast
python scripts/convert_hf_checkpoint.py \
--checkpoint_dir <new_dir>/Llama-2-7B-hf/ \
--model 7B
# run inference using the converted model
python generate.py \
--compile \
--checkpoint_path <new_dir>/Llama-2-7B-hf/model.pth \
--device cuda
输出结果应如下所示:
Hello, my name is Justin. I am a middle school math teacher
at WS Middle School ...
Time for inference 5: 1.94 sec total, 103.28 tokens/sec
Bandwidth achieved: 1391.84 GB/sec
就是这样!尝试您自己的提示词吧!
将您的模型上传到 Hugging Face Hub¶
您的新模型运行良好,您希望将其与世界分享。实现这一目标最简单的方法是使用huggingface-cli命令,该命令可与 torchtune 无缝协作。只需将 CLI 指向您的微调模型目录即可:
huggingface-cli upload <hf-repo-id> <checkpoint-dir>
该命令应输出一个指向您在 Hub 上的仓库链接。如果该仓库尚不存在,它将自动创建:
https://huggingface.co/<hf-repo-id>/tree/main/.
注意
上传之前,请确保您已通过运行 huggingface-cli login 与 Hugging Face 进行了身份验证。
有关huggingface-cli upload功能的更多详情,请查看
希望这个教程让你对如何使用 torchtune 进行自己的工作流程有了一些了解。祝你调优愉快!