• 文档 >
  • 使用 torchtune 的端到端工作流程
快捷方式

使用 torchtune 的端到端工作流程

在本教程中,我们将逐步介绍如何使用 torchtune 微调、评估、可选量化以及运行您最喜欢的 LLM 生成的端到端示例。我们还将介绍如何无缝地将社区中一些流行的工具和库与 torchtune 一起使用。

本教程将涵盖的内容
  • torchtune 中可用的微调以外的不同类型的配方

  • 连接所有这些配方的端到端示例

  • 您可以与 torchtune 一起使用的不同工具和库

先决条件

微调您的模型

首先,让我们使用 tune CLI 下载模型。以下命令将从 Hugging Face Hub 下载 Llama3.2 3B Instruct 模型并将其保存到本地文件系统。Hugging Face 上传了原始权重 (consolidated.00.pth) 和与 from_pretrained() API 兼容的权重 (*.safetensors)。我们不需要两者,因此我们将在下载时忽略原始权重。

$ tune download meta-llama/Llama-3.2-3B-Instruct --ignore-patterns "original/consolidated.00.pth"
Successfully downloaded model repo and wrote to the following locations:
/tmp/Llama-3.2-3B-Instruct/.cache
/tmp/Llama-3.2-3B-Instruct/.gitattributes
/tmp/Llama-3.2-3B-Instruct/LICENSE.txt
/tmp/Llama-3.2-3B-Instruct/README.md
/tmp/Llama-3.2-3B-Instruct/USE_POLICY.md
/tmp/Llama-3.2-3B-Instruct/config.json
/tmp/Llama-3.2-3B-Instruct/generation_config.json
/tmp/Llama-3.2-3B-Instruct/model-00001-of-00002.safetensors
...

注意

有关您可以使用 torchtune 开箱即用的所有其他模型的列表,请查看我们的 模型页面

在本教程中,我们将使用 LoRA 微调模型。LoRA 是一种参数高效的微调技术,当您没有太多 GPU 内存可用时,它特别有用。LoRA 冻结了基础 LLM 并添加了非常小比例的可学习参数。这有助于保持与梯度和优化器状态相关的低内存。使用 torchtune,您应该能够使用 bfloat16 在 RTX 3090/4090 上以小于 16GB 的 GPU 内存微调 Llama-3.2-3B-Instruct 模型与 LoRA。有关如何使用 LoRA 的更多信息,请查看我们的 LoRA 教程

让我们通过使用 tune CLI 查找此用例的正确配置。

$ tune ls
RECIPE                                  CONFIG
full_finetune_single_device             llama2/7B_full_low_memory
                                        code_llama2/7B_full_low_memory
                                        llama3/8B_full_single_device
                                        llama3_1/8B_full_single_device
                                        llama3_2/1B_full_single_device
                                        llama3_2/3B_full_single_device
                                        mistral/7B_full_low_memory
                                        phi3/mini_full_low_memory
                                        qwen2/7B_full_single_device
                                        ...


full_finetune_distributed               llama2/7B_full
                                        llama2/13B_full
                                        llama3/8B_full
                                        llama3_1/8B_full
                                        llama3_2/1B_full
                                        llama3_2/3B_full
                                        mistral/7B_full
                                        gemma2/9B_full
                                        gemma2/27B_full
                                        phi3/mini_full
                                        qwen2/7B_full
                                        ...

lora_finetune_single_device             llama2/7B_lora_single_device
                                        llama2/7B_qlora_single_device
                                        llama3/8B_lora_single_device
...

我们将使用我们的 单设备 LoRA 配方 进行微调,并使用 默认配置 中的标准设置。

这将使用 batch_size=4dtype=bfloat16 微调我们的模型。使用这些设置,模型的峰值内存使用量应约为 16GB,每个 epoch 的总训练时间约为 2-3 小时。

$ tune run lora_finetune_single_device --config llama3_2/3B_lora_single_device
Setting manual seed to local seed 3977464327. Local seed is seed + rank = 3977464327 + 0
Hint: enable_activation_checkpointing is True, but enable_activation_offloading isn't. Enabling activation offloading should reduce memory further.
Writing logs to /tmp/torchtune/llama3_2_3B/lora_single_device/logs/log_1734708879.txt
Model is initialized with precision torch.bfloat16.
Memory stats after model init:
        GPU peak memory allocation: 6.21 GiB
        GPU peak memory reserved: 6.27 GiB
        GPU peak memory active: 6.21 GiB
Tokenizer is initialized from file.
Optimizer and loss are initialized.
Loss is initialized.
Dataset and Sampler are initialized.
Learning rate scheduler is initialized.
Profiling disabled.
Profiler config after instantiation: {'enabled': False}
1|3|Loss: 1.943998098373413:   0%|                    | 3/1617 [00:21<3:04:47,  6.87s/it]

恭喜您训练了模型!让我们看一下 torchtune 生成的工件。执行此操作的一种简单方法是运行 tree -a path/to/outputdir,它应该显示如下所示的树。有 3 种类型的文件夹

  1. recipe_state:保存 recipe_state.pt,其中包含重新启动上一个中间 epoch 所必需的信息。有关更多信息,请查看我们的深入探讨 torchtune 中的检查点。;

  2. logs:包含来自您的训练运行的所有日志输出:损失、内存、异常等。

  3. epoch_{}:包含您训练的模型权重加上模型元数据。如果运行推理或推送到模型中心,您应该直接使用此文件夹。

$ tree -a /tmp/torchtune/llama3_2_3B/lora_single_device
/tmp/torchtune/llama3_2_3B/lora_single_device
├── epoch_0
│   ├── adapter_config.json
│   ├── adapter_model.pt
│   ├── adapter_model.safetensors
│   ├── config.json
│   ├── ft-model-00001-of-00002.safetensors
│   ├── ft-model-00002-of-00002.safetensors
│   ├── generation_config.json
│   ├── LICENSE.txt
│   ├── model.safetensors.index.json
│   ├── original
│   │   ├── orig_params.json
│   │   ├── params.json
│   │   └── tokenizer.model
│   ├── original_repo_id.json
│   ├── README.md
│   ├── special_tokens_map.json
│   ├── tokenizer_config.json
│   ├── tokenizer.json
│   └── USE_POLICY.md
├── epoch_1
│   ├── adapter_config.json
│   ...
├── logs
│   └── log_1734652101.txt
└── recipe_state
    └── recipe_state.pt

让我们了解一下文件

  • adapter_model.safetensorsadapter_model.pt 是您的 LoRA 训练的适配器权重。我们保存了重复的 .pt 版本,以方便从检查点恢复。

  • ft-model-{}-of-{}.safetensors 是您训练的完整模型权重(非适配器)。当 LoRA 微调时,只有当我们设置 save_adapter_weights_only=False 时,这些才会出现。在这种情况下,我们将合并的基础模型与训练的适配器合并,从而使推理更容易。

  • adapter_config.json 由 Huggingface PEFT 在加载适配器时使用(稍后会详细介绍);

  • model.safetensors.index.json 由 Hugging Face from_pretrained() 在加载模型权重时使用(稍后会详细介绍)

  • 所有其他文件最初都在 checkpoint_dir 中。它们在训练期间自动复制。超过 100MiB 且以 .safetensors、.pth、.pt、.bin 结尾的文件将被忽略,使其保持轻量级。

评估您的模型

我们已经微调了一个模型。但是这个模型真的做得好吗?让我们通过结构化评估和试用它来确定这一点。

使用 EleutherAI 的 Eval Harness 运行评估

torchtune 与 EleutherAI 的评估 harness 集成。此示例可通过 eleuther_eval 配方获得。在本教程中,我们将通过修改其关联的配置 eleuther_evaluation.yaml 直接使用此配方。

注意

对于本教程的这一部分,您应该首先运行 pip install lm_eval>=0.4.5 以安装 EleutherAI 评估 harness。

由于我们计划更新所有检查点文件以指向我们的微调检查点,因此让我们首先将配置复制到我们的本地工作目录,以便我们可以进行更改。

$ tune cp eleuther_evaluation ./custom_eval_config.yaml
Copied file to custom_eval_config.yaml

请注意,我们使用的是合并的权重,而不是 LoRA 适配器。

# TODO: update to your desired epoch
output_dir: /tmp/torchtune/llama3_2_3B/lora_single_device/epoch_0

# Tokenizer
tokenizer:
    _component_: torchtune.models.llama3.llama3_tokenizer
    path: ${output_dir}/original/tokenizer.model

model:
    # Notice that we don't pass the lora model. We are using the merged weights,
    _component_: torchtune.models.llama3_2.llama3_2_3b

checkpointer:
    _component_: torchtune.training.FullModelHFCheckpointer
    checkpoint_dir: ${output_dir}
    checkpoint_files: [
        ft-model-00001-of-00002.safetensors,
        ft-model-00002-of-00002.safetensors,
    ]
    output_dir: ${output_dir}
    model_type: LLAMA3_2

### OTHER PARAMETERS -- NOT RELATED TO THIS CHECKPOINT

# Environment
device: cuda
dtype: bf16
seed: 1234 # It is not recommended to change this seed, b/c it matches EleutherAI's default seed

# EleutherAI specific eval args
tasks: ["truthfulqa_mc2"]
limit: null
max_seq_length: 4096
batch_size: 8
enable_kv_cache: True

# Quantization specific args
quantizer: null

在本教程中,我们将使用 harness 中的 truthfulqa_mc2 任务。

此任务衡量模型在回答问题时的真实倾向,并衡量模型在问题后的零样本准确率,问题后跟一个或多个真实回答以及一个或多个错误回答。

$ tune run eleuther_eval --config ./custom_eval_config.yaml
[evaluator.py:324] Running loglikelihood requests
...

生成一些输出

我们已经运行了一些评估,并且该模型似乎表现良好。但是,它真的为您关心的提示生成有意义的文本吗?让我们找出答案!

为此,我们将使用 generate 配方 和关联的 配置

让我们首先将配置复制到我们的本地工作目录,以便我们可以进行更改。

$ tune cp generation ./custom_generation_config.yaml
Copied file to custom_generation_config.yaml
让我们修改 custom_generation_config.yaml 以包含以下更改。同样,您只需要

替换两个字段:output_dircheckpoint_files

output_dir: /tmp/torchtune/llama3_2_3B/lora_single_device/epoch_0

# Tokenizer
tokenizer:
    _component_: torchtune.models.llama3.llama3_tokenizer
    path: ${output_dir}/original/tokenizer.model
    prompt_template: null

model:
    # Notice that we don't pass the lora model. We are using the merged weights,
    _component_: torchtune.models.llama3_2.llama3_2_3b

checkpointer:
    _component_: torchtune.training.FullModelHFCheckpointer
    checkpoint_dir: ${output_dir}
    checkpoint_files: [
        ft-model-00001-of-00002.safetensors,
        ft-model-00002-of-00002.safetensors,
    ]
    output_dir: ${output_dir}
    model_type: LLAMA3_2

### OTHER PARAMETERS -- NOT RELATED TO THIS CHECKPOINT

device: cuda
dtype: bf16

seed: 1234

# Generation arguments; defaults taken from gpt-fast
prompt:
system: null
user: "Tell me a joke. "
max_new_tokens: 300
temperature: 0.6 # 0.8 and 0.6 are popular values to try
top_k: 300

enable_kv_cache: True

quantizer: null

更新配置后,让我们开始生成!我们将使用默认设置进行采样,top_k=300temperature=0.8。这些参数控制如何计算采样的概率。我们建议先使用这些参数检查模型,然后再试用这些参数。

$ tune run generate --config ./custom_generation_config.yaml prompt="tell me a joke. "
Tell me a joke. Here's a joke for you:

What do you call a fake noodle?

An impasta!

引入一些量化

我们依赖 torchao 进行 训练后量化。要在安装 torchao 后量化微调模型,我们可以运行以下命令

# we also support `int8_weight_only()` and `int8_dynamic_activation_int8_weight()`, see
# https://github.com/pytorch/ao/tree/main/torchao/quantization#other-available-quantization-techniques
# for a full list of techniques that we support
from torchao.quantization.quant_api import quantize_, int4_weight_only
quantize_(model, int4_weight_only())

量化后,我们依赖 torch.compile 来加速。有关更多详细信息,请参阅 此示例用法

torchao 还提供了 此表,列出了 llama2llama3 的性能和准确性结果。

对于 Llama 模型,您可以直接在 torchao 上使用它们的 generate.py 脚本在量化模型上运行生成,如 此自述文件 中所述。这样,您可以将自己的结果与之前链接的表中的结果进行比较。

在实际应用中使用您的模型

假设我们对模型在此时的性能感到满意 - 我们想用它做一些事情!生产化以进行服务、在 Hugging Face Hub 上发布等。正如我们上面提到的,检查点转换处理的优势之一是您可以直接使用标准格式。这有助于与其他库的互操作性,因为 torchtune 没有在组合中添加另一种格式。

与 Hugging Face from_pretrained() 一起使用

案例 1:Hugging Face 使用基础模型 + 训练的适配器

在这里,我们从 Hugging Face 模型中心加载基础模型。然后,我们使用 PeftModel 在其之上加载适配器。它将查找文件 adapter_model.safetensors 以获取权重,并查找 adapter_config.json 以获取在哪里插入它们。

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

#TODO: update it to your chosen epoch
trained_model_path = "/tmp/torchtune/llama3_2_3B/lora_single_device/epoch_0"

# Define the model and adapter paths
original_model_name = "meta-llama/Llama-3.2-1B-Instruct"

model = AutoModelForCausalLM.from_pretrained(original_model_name)

# huggingface will look for adapter_model.safetensors and adapter_config.json
peft_model = PeftModel.from_pretrained(model, trained_model_path)

# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained(original_model_name)

# Function to generate text
def generate_text(model, tokenizer, prompt, max_length=50):
    inputs = tokenizer(prompt, return_tensors="pt")
    outputs = model.generate(**inputs, max_length=max_length)
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

prompt = "tell me a joke: '"
print("Base model output:", generate_text(peft_model, tokenizer, prompt))

案例 2:Hugging Face 使用合并的权重

在这种情况下,Hugging Face 将在 model.safetensors.index.json 中检查它应该加载哪些文件。

from transformers import AutoModelForCausalLM, AutoTokenizer

#TODO: update it to your chosen epoch
trained_model_path = "/tmp/torchtune/llama3_2_3B/lora_single_device/epoch_0"

model = AutoModelForCausalLM.from_pretrained(
    pretrained_model_name_or_path=trained_model_path,
)

# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained(trained_model_path, safetensors=True)


# Function to generate text
def generate_text(model, tokenizer, prompt, max_length=50):
    inputs = tokenizer(prompt, return_tensors="pt")
    outputs = model.generate(**inputs, max_length=max_length)
    return tokenizer.decode(outputs[0], skip_special_tokens=True)


prompt = "Complete the sentence: 'Once upon a time...'"
print("Base model output:", generate_text(model, tokenizer, prompt))

与 vLLM 一起使用

vLLM 是一个快速且易于使用的 LLM 推理和服务库。它们包含许多很棒的功能,例如最先进的服务吞吐量、传入请求的连续批处理、量化和推测解码。

该库将加载任何 .safetensors 文件。由于这里我们混合了完整的模型权重和适配器权重,我们必须删除适配器权重才能成功加载它。

rm /tmp/torchtune/llama3_2_3B/lora_single_device/base_model/adapter_model.safetensors

现在我们可以运行以下脚本

from vllm import LLM, SamplingParams

def print_outputs(outputs):
    for output in outputs:
        prompt = output.prompt
        generated_text = output.outputs[0].text
        print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
    print("-" * 80)

#TODO: update it to your chosen epoch
llm = LLM(
    model="/tmp/torchtune/llama3_2_3B/lora_single_device/epoch_0",
    load_format="safetensors",
    kv_cache_dtype="auto",
)
sampling_params = SamplingParams(max_tokens=16, temperature=0.5)

conversation = [
    {"role": "system", "content": "You are a helpful assistant"},
    {"role": "user", "content": "Hello"},
    {"role": "assistant", "content": "Hello! How can I assist you today?"},
    {
        "role": "user",
        "content": "Write an essay about the importance of higher education.",
    },
]
outputs = llm.chat(conversation, sampling_params=sampling_params, use_tqdm=False)
print_outputs(outputs)

将您的模型上传到 Hugging Face Hub

您的新模型运行良好,您想与世界分享它。最简单的方法是利用 huggingface_hub

import huggingface_hub
api = huggingface_hub.HfApi()

#TODO: update it to your chosen epoch
trained_model_path = "/tmp/torchtune/llama3_2_3B/lora_single_device/epoch_0"

username = huggingface_hub.whoami()["name"]
repo_name = "my-model-trained-with-torchtune"

# if the repo doesn't exist
repo_id = huggingface_hub.create_repo(repo_name).repo_id

# if it already exists
repo_id = f"{username}/{repo_name}"

api.upload_folder(
    folder_path=trained_model_path,
    repo_id=repo_id,
    repo_type="model",
    create_pr=False
)

如果您愿意,您也可以尝试 cli 版本 huggingface-cli upload


希望本教程让您深入了解如何将 torchtune 用于您自己的工作流程。祝您调整愉快!

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

获取面向初学者和高级开发者的深度教程

查看教程

资源

查找开发资源并获得问题解答

查看资源