目录

使用 torchtune 的端到端工作流

在本教程中,我们将通过一个端到端示例来说明如何微调 评估,选择性地量化,然后使用您最喜欢的 LLM 运行生成 torchtune 的我们还将介绍如何使用一些流行的工具和库 使用 Torchtune 无缝地从社区获得。

本教程将涵盖以下内容:
  • torchtune 中可用的不同类型的配方,超越微调

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

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

先决条件

微调模型

首先,让我们使用 tune CLI 下载一个模型。以下命令将从 Hugging Face Hub 下载 Llama3.2 3B Instruct 模型并将其保存到本地文件系统中。Hugging Face 上传了原文 weights () 和与 from_pretrained() API () () 兼容的权重。 我们不需要两者,因此在下载时将忽略原始权重。consolidated.00.pth*.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 并添加非常小比例的可学习参数。这有助于保持 与 gradients 和 optimizer 状态相关的内存 low 。使用 torchtune,您应该能够 在不到 16GB 的 GPU 内存中使用 bfloat16 微调具有 LoRA 的 Llama-3.2-3B-Instruct 模型 RTX 3090/4090 的。有关如何使用 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 配方进行微调,并使用默认配置中的标准设置。

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

$ 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 生成的工件。执行此操作的一种简单方法是运行 ,它应该显示类似于下面的树的内容。 有 3 种类型的文件夹:tree -a path/to/outputdir

  1. recipe_state:保存 recipe_state.pt 以及重新启动最后一个中间 epoch 所需的信息。有关更多信息,请查看我们在 torchtune 中的深入探讨检查点。

  2. logs:包含训练运行的所有日志记录输出:loss、memory、exceptions等。

  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.safetensors和 是您的 LoRA 训练适配器权重。我们保存了它的重复 .pt 版本,以方便从 checkpoint 恢复。adapter_model.pt

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

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

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

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

评估模型

我们已经对模型进行了微调。但是这个模型到底做得如何呢?让我们通过结构化评估来确定这一点。

使用 EleutherAI 的 Eval Harness 运行评估

torchtune 与 EleutherAI 的评估工具集成。 这方面的一个示例可通过 eleuther_eval recipe 获得。在本教程中,我们将通过以下方式直接使用此配方 修改其关联的 config eleuther_evaluation.yaml

注意

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

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

$ 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

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

此任务衡量模型在回答问题时保持真实的倾向,并且 测量模型对问题后跟一个或多个 true 的零镜头准确率 responses 和一个或多个 false 响应。

$ 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

在野外使用您的模型

假设我们对模型此时的表现感到满意 - 我们想用它做点什么!Productionize 用于服务,在 Hugging Face Hub 上发布,等等。 正如我们上面提到的,处理 checkpoint 转换的好处之一是您可以直接使用标准格式。这有助于 与其他库具有互操作性,因为 Torchtune 不会在混合中添加另一种格式。

与 Hugging Face 一起使用from_pretrained()

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

在这里,我们从 Hugging Face 模型中心加载基本模型。然后我们使用 PeftModel 将适配器加载到它上面。 它将查找权重的文件以及插入权重的位置。adapter_model.safetensorsadapter_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 文件。由于这里我们混合了完整的模型权重和适配器权重,因此我们必须删除 adapter weights 成功加载它。

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 的全面开发人员文档

查看文档

教程

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

查看教程

资源

查找开发资源并解答您的问题

查看资源