
torchtune 实战教程用聊天数据微调 Llama3 Instruct——Prompt 模板、特殊 Token 与自定义 Chat 数据集全流程【免费下载链接】torchtunePyTorch native post-training library项目地址: https://gitcode.com/GitHub_Trending/to/torchtune本篇基于 torchtune 官方教程《Fine-Tuning Llama3 with Chat Data》docs/source/tutorials/chat.rst展开系统讲解 Llama3 Instruct 相对 Llama2 的 prompt 模板与特殊 token 变化、prompt template 的使用时机以及如何用chat_dataset构建自定义聊天数据集并配合 LoRA recipe 完成 Llama3 Instruct 的微调。读完本文你将能够正确理解并手工验证消息的 tokenize 流程独立完成从本地 JSON 聊天数据到tune run微调命令的完整链路。1. 背景为什么 Llama3 的模板与 Llama2 完全不同Llama2 聊天模型在推理和微调时必须使用特定的 prompt template由于该模型就是以这一模板进行预训练/instruct 训练的如果你用其他格式去提示它模型只会退化为标准文本补全行为效果可能与你的预期严重不符。Llama2 官方模板中的特殊标签形如s[INST] SYS You are a helpful, respectful, and honest assistant. /SYS Hi! I am a human. [/INST] Hello there! Nice to meet you! Im Meta AI, your friendly AI assistant /s而 Llama3 Instruct 为了更好支持多轮对话对模板进行了彻底重写。同样的一段对话在 Llama3 Instruct 格式下长这样|begin_of_text||start_header_id|system|end_header_id| You are a helpful, respectful, and honest assistant.|eot_id||start_header_id|user|end_header_id| Hi! I am a human.|eot_id||start_header_id|assistant|end_header_id| Hello there! Nice to meet you! Im Meta AI, your friendly AI assistant|eot_id|可以看到两代模型的标签不仅文本完全不同在 tokenizer 层面的编码方式也截然不同。官方教程中特别提醒Llama3 Base 模型与 Llama3 Instruct 使用的模板不同——Base 模型尚未经历 instruct tuning多出的特殊 token 未经训练因此对 Base 模型直接推理时推荐使用 base 模板而对于 instruct/chat 类数据官方推荐统一使用 Llama3 Instruct 及其 prompt 模板这也是本教程后续所有内容的默认前提。2. Tokenize 视角下的两种模板Llama2 的“文本标签”与 Llama3 的“特殊 Token”下面用一个 system user assistant 的单轮对话样本对比两代模型的消息 tokenize 过程。sample [ { role: system, content: You are a helpful, respectful, and honest assistant., }, { role: user, content: Who are the most influential hip-hop artists of all time?, }, { role: assistant, content: Here is a list of some of the most influential hip-hop artists of all time: 2Pac, Rakim, N.W.A., Run-D.M.C., and Nas., }, ]2.1 Llama2Prompt template 只管“文本部分”使用 Llama2ChatTemplate 对消息做格式化from torchtune.data import Llama2ChatTemplate, Message messages [Message.from_dict(msg) for msg in sample] formatted_messages Llama2ChatTemplate.format(messages) print(formatted_messages) # [ # Message( # roleuser, # content[INST] SYS\nYou are a helpful, respectful, and honest assistant.\n/SYS\n\nWho are the most influential hip-hop artists of all time? [/INST] , # ..., # ), # Message( # roleassistant, # contentHere is a list of some of the most influential hip-hop artists of all time: 2Pac, Rakim, N.W.A., Run-D.M.C., and Nas., # ..., # ), # ]从源码 Llama2ChatTemplate 可以看到该模板本质上就是一个 role → 前后缀的映射template { system: (SYS\n, \n/SYS\n\n), user: ([INST] , [/INST] ), assistant: (, ), ipython: (, ), }其__call__实现有一个关键细节Llama2 只识别SYS标签而并不关心 role 字段所以 torchtune 把 system 消息“合并”进第一条 user 消息从而与真实 system prompt 在模型看来等价。Llama2 还有两个不在 prompt template 里的特殊 tokens和/s分别对应 BOSbegin-of-sequence和 EOSend-of-sequence。用 Llama2 的 tokenizertorchtune/models/llama2/_tokenizer.py 中的llama2_tokenizer验证from torchtune.models.llama2 import llama2_tokenizer tokenizer llama2_tokenizer(/tmp/Llama-2-7b-hf/tokenizer.model) user_message formatted_messages[0].text_content tokens tokenizer.encode(user_message, add_bosTrue, add_eosTrue) print(tokens) # [1, 518, 25580, 29962, 3532, 14816, 29903, 6778, ..., 2]首尾的1和2就是 BOS/EOS。可以进一步确认它们的 token IDprint(tokenizer._spm_model.spm_model.piece_to_id(s)) # 1 print(tokenizer._spm_model.spm_model.piece_to_id(/s)) # 2BOS 和 EOS 之所以是特殊 token是因为它们拥有预留的 token ID会在模型 embedding 表中索引到专属向量而[INST]、SYS这类模板标签则按普通文本被切分成多个普通 tokenprint(tokenizer.decode(518)) # [ print(tokenizer.decode(25580)) # INST print(tokenizer.decode(29962)) # ] print(tokenizer.decode([3532, 14816, 29903, 6778])) # SYS这里有一个实践上的重要陷阱不要手动把s这样的预留特殊 token 写进输入文本因为此时它会被当作普通文本切分而不会被当作特殊 token 处理print(tokenizer.encode(s, add_bosFalse, add_eosFalse)) # [529, 29879, 29958] # 被切成了 3 个普通 token而不是 [1]2.2 Llama3全部格式交给 Tokenizer 的tokenize_messages再看 Llama3 侧的实现from torchtune.models.llama3 import llama3_tokenizer tokenizer llama3_tokenizer(/tmp/Meta-Llama-3-8B-Instruct/original/tokenizer.model) messages [Message.from_dict(msg) for msg in sample] tokens, mask tokenizer.tokenize_messages(messages) print(tokenizer.decode(tokens)) # |start_header_id|system|end_header_id|\n\nYou are a helpful, respectful, # and honest assistant.|eot_id||start_header_id|user|end_header_id|\n\nWho # are the most influential hip-hop artists of all time?|eot_id||start_header_id| # assistant|end_header_id|\n\nHere is a list of some of the most influential hip-hop # artists of all time: 2Pac, Rakim, N.W.A., Run-D.M.C., and Nas.|eot_id|注意 Llama3 走的是tokenize_messages而非encodeAPI——它在对每条消息编码后自动在正确的位置补齐所有特殊 token。这些“额外的标签”全部是独立编码的特殊 tokenprint(tokenizer.special_tokens[|begin_of_text|]) # 128000 print(tokenizer.special_tokens[|eot_id|]) # 128009从源码看Llama3Tokenizer 在 torchtune/models/llama3/_tokenizer.py 中定义了完整的特殊 token 表|begin_of_text| 128000、|start_header_id| 128006、|end_header_id| 128007、|eot_id| 128009 等另外还预留了 257 个 reserved special tokens。其tokenize_messages的组装逻辑L270-L344是若构造时传入了prompt_template先对消息做模板格式化Llama3 默认不需要以bos_id起始每条消息按|start_header_id| role |end_header_id| \n\n 正文 |eot_id|的顺序拼接tokenize_message中由_tokenize_header/_tokenize_body/_tokenize_end分别负责头部、正文和结束符最后一条 assistant 消息之后追加eos_id|end_of_text|同步生成与 token 等长的mask布尔列表BOS/EOS 恒为 masked供训练时屏蔽 prompt 部分的 loss。换句话说Llama3 的所有格式细节都由 tokenizer 接管你不需要也不应该再手工维护一个 prompt template。3. 什么时候该用 Prompt Template是否使用 prompt template取决于你期望的推理行为如果你对Base 模型做推理而该模型预训练时使用了 prompt template那么推理时应当沿用同样的模板如果你希望微调后的模型在特定任务上稳定识别某一种提示结构也可以为微调引入一个任务模板。严格来说微调并不强制要求使用 prompt template。例如 SummarizeTemplate在 torchtune/data/_prompt_templates.py 中定义并有对应测试 tests/torchtune/data/test_prompt_templates.py提供了一个轻量结构用来让微调后的模型识别“请总结文本”类提示它只会包裹 user 消息assistant 消息保持不变fSummarize this dialogue:\n{dialogue}\n---\nSummary:\n即使模型如 Llama2预训练时用的是Llama2ChatTemplate你仍可以用这个模板微调它——只要推理时模型看到的就是这个模板即可模型通常具备适应新模板的鲁棒性。4. 用自定义 Chat 数据集微调 Llama3-8B Instruct下面以本地 JSON 聊天文件为例完整走一遍“数据 → dataset → config → 训练命令”的流程。4.1 准备 ShareGPT 格式的本地数据假设聊天数据保存为data/my_data.json采用 ShareGPT 结构from取值为human/gpt# data/my_data.json [ { dialogue: [ { from: human, value: What is your name? }, { from: gpt, value: I am an AI assistant, I dont have a name. }, { from: human, value: Pretend you have a name. }, { from: gpt, value: My name is Mark Zuckerberg. } ] }, ]4.2 选择 dataset builderchat_dataset在 torchtune 的 dataset builders 中对话数据最合适的入口是 chat_dataset定义于 torchtune/datasets/_chat.py。对于任何自定义本地数据集都需要指定source、data_files、split对chat_dataset而言还需额外指定conversation_column对话所在列名和conversation_style对话结构风格。本例数据为sharegpt风格因此调用如下from torchtune.datasets import chat_dataset from torchtune.models.llama3 import llama3_tokenizer tokenizer llama3_tokenizer(/tmp/Meta-Llama-3-8B-Instruct/original/tokenizer.model) ds chat_dataset( tokenizertokenizer, sourcejson, data_filesdata/my_data.json, splittrain, conversation_columndialogue, conversation_stylesharegpt, )对应的 YAML 配置写法可直接替换 recipe 配置中的dataset段# In config tokenizer: _component_: torchtune.models.llama3.llama3_tokenizer path: /tmp/Meta-Llama-3-8B-Instruct/original/tokenizer.model dataset: _component_: torchtune.datasets.chat_dataset source: json data_files: data/my_data.json split: train conversation_column: dialogue conversation_style: sharegpt官方教程还特别提醒所有 Dataset 类都会把多余的 keyword arguments 透传给 Hugging Facedatasets库的load_dataset因此split、name等常见参数都可以按需传入。4.3chat_dataset参数详解源码佐证结合 torchtune/datasets/_chat.py 的源码chat_dataset的完整参数为参数默认值说明tokenizer必填实现了tokenize_messages的模型 tokenizer作为 model_transform 传入SFTDatasetsource必填数据集来源。本地文件填文件类型如json、csv、text远程仓库填 HF dataset 路径conversation_column必填数据集中对话所在的列名conversation_style必填对话结构风格仅支持sharegpt和openai否则抛出ValueErrortrain_on_inputFalse是否对 user prompt 计算 lossTrue表示 prompt 参与训练False时 prompt token 被 mask 为 -100new_system_promptNone若指定会在每条样本前插入一条 system 消息覆盖数据中已有的 system 消息packedFalse是否将数据集 pack 到max_seq_len要求 tokenizer 设置了max_seq_len否则报错filter_fnNone任何预处理前的数据集过滤函数透传 HF datasets 能力splittrainload_dataset的 split 参数支持子集写法如splittrain[:10%]**load_dataset_kwargs—透传给load_dataset的其他参数如data_files从源码实现看L158-L187conversation_style会映射到两个 Transformsharegpt→ ShareGPTToMessages把{from: human/gpt/system, value: ...}结构转换为 torchtune 的Message结构role_map {system: system, human: user, gpt: assistant}并通过column_map{conversations: conversation_column}适配自定义列名openai→ OpenAIToMessages处理 OpenAI chat completion 的{role, content}结构。这两个 transform 都会按masking_strategytrain_on_all/train_on_assistant/train_on_last默认train_on_assistant对消息打上 masked 标记——被 mask 的消息在 tokenize 后对应位置的 loss 会被屏蔽。如果数据不属于这两种标准格式官方建议是自行实现一个 message transform仿照chat_dataset写一个自定义 dataset builder。最终数据流为HF load_dataset→ShareGPTToMessages得到Message列表→SFTDataset→tokenizer作为 model_transform调用tokenize_messages产出tokensmask。该链路的测试可参见 tests/torchtune/datasets/test_chat_dataset.py使用本地 fixture tests/assets/chat_tiny.json 验证。关于 prompt template 的接入方式如果你确实需要模板直接把它传给 tokenizer 即可Llama3Tokenizer构造参数prompt_template支持传入PromptTemplate见 torchtune/models/llama3/_tokenizer.py。由于本例微调的是 Llama3tokenizer 会包办全部格式prompt template 是可选的。作为对比其他模型的 tokenizer 可能默认就带模板例如 MistralTokenizer 默认使用MistralChatTemplate按 Mistral 官方建议格式化所有消息。4.4 用 LoRA 单卡 recipe 启动微调数据准备就绪后使用内置的 LoRA 单设备 recipe。先用tune cp复制 llama3/8B_lora_single_device.yaml 配置再把上文的dataset以及必要的tokenizer配置替换进去。该 recipe 配置的关键部分摘自 recipes/configs/llama3/8B_lora_single_device.yamlmodel: _component_: torchtune.models.llama3.lora_llama3_8b lora_attn_modules: [q_proj, v_proj, output_proj] apply_lora_to_mlp: True apply_lora_to_output: False lora_rank: 8 # 越高精度越好但内存占用越大 lora_alpha: 16 # 通常 alpha 2 * rank lora_dropout: 0.0 tokenizer: _component_: torchtune.models.llama3.llama3_tokenizer path: /tmp/Meta-Llama-3-8B-Instruct/original/tokenizer.model max_seq_len: null dataset: _component_: torchtune.datasets.alpaca_cleaned_dataset # 替换为你的 chat_dataset packed: False batch_size: 2 epochs: 1 gradient_accumulation_steps: 8 # 用于放大等效 batch size compile: False device: cuda dtype: bf16 enable_activation_checkpointing: True # 降低显存占用配置头部的注释也给出了权重下载的前置命令tune download meta-llama/Meta-Llama-3-8B-Instruct --output-dir /tmp/Meta-Llama-3-8B-Instruct --ignore-patterns original/consolidated.00.pth --hf-token HF_TOKEN和标准启动方式tune run lora_finetune_single_device --config llama3/8B_lora_single_device并支持在命令行直接追加覆盖项如checkpointer.checkpoint_dir...。最后启动训练epochs可按需覆盖$ tune run lora_finetune_single_device --config custom_8B_lora_single_device.yaml epochs15该 recipe 的入口脚本为 recipes/lora_finetune_single_device.py其测试位于 tests/recipes/test_lora_finetune_single_device.py可用于验证配置实例化与训练循环。5. 小结Llama2的[INST]/SYS等标签是普通文本必须通过Llama2ChatTemplate这样的 prompt template 手工格式化BOS/EOSs//s则是 tokenizer 的预留特殊 token切勿手写到 prompt 里Llama3 Instruct的 header 标签|start_header_id|、|eot_id|等全部是独立特殊 token由Llama3Tokenizer.tokenize_messages统一插入微调时通常无需prompt template自定义聊天数据走chat_dataset指定source/data_files/split/conversation_column/conversation_style配合ShareGPTToMessages/OpenAIToMessages完成结构转换与 masking再交给 recipe 即可Base 模型与 Instruct 模型的模板选择不同使用 Llama3 Instruct 权重时全程沿用其 instruct 模板才能保证推理与微调行为一致。参考材料docs/source/tutorials/chat.rst、torchtune/models/llama2/_prompt_template.py、torchtune/models/llama3/_tokenizer.py、torchtune/datasets/_chat.py、torchtune/data/_messages.py、recipes/configs/llama3/8B_lora_single_device.yaml、tests/torchtune/datasets/test_chat_dataset.py。【免费下载链接】torchtunePyTorch native post-training library项目地址: https://gitcode.com/GitHub_Trending/to/torchtune创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考