Transformers 文档
模板
并获得增强的文档体验
开始使用
模板
聊天流水线指南介绍了TextGenerationPipeline以及与模型对话时聊天提示或聊天模板的概念。此高级流水线的底层是apply_chat_template
方法。聊天模板是分词器的一部分,它指定了如何将对话转换为预期模型格式的单个可分词字符串。
在下面的示例中,Mistral-7B-Instruct 和 Zephyr-7B 是从同一个基础模型微调而来的,但它们使用不同的聊天格式进行训练。如果没有聊天模板,您必须为每个模型手动编写格式化代码,即使是微小的错误也会影响性能。聊天模板提供了一种通用方法,可以格式化任何模型的聊天输入。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
chat = [
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing great. How can I help you today?"},
{"role": "user", "content": "I'd like to show off how chat templating works!"},
]
tokenizer.apply_chat_template(chat, tokenize=False)
<s>[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?</s> [INST] I'd like to show off how chat templating works! [/INST]
本指南将更详细地探讨apply_chat_template
和聊天模板。
apply_chat_template
聊天应结构化为带有role
和content
键的字典列表。role
键指定说话者(通常是你和系统之间),content
键包含你的消息。对于系统,content
是对模型在与你聊天时应如何表现和响应的高级描述。
将您的消息传递给apply_chat_template
以进行分词和格式化。您可以设置add_generation_prompt为True
以指示消息的开始。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceH4/zephyr-7b-beta", device_map="auto", torch_dtype=torch.bfloat16)
messages = [
{"role": "system", "content": "You are a friendly chatbot who always responds in the style of a pirate",},
{"role": "user", "content": "How many helicopters can a human eat in one sitting?"},
]
tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt")
print(tokenizer.decode(tokenized_chat[0]))
<|system|>
You are a friendly chatbot who always responds in the style of a pirate</s>
<|user|>
How many helicopters can a human eat in one sitting?</s>
<|assistant|>
现在,将分词后的聊天传递给generate()以生成响应。
outputs = model.generate(tokenized_chat, max_new_tokens=128)
print(tokenizer.decode(outputs[0]))
<|system|>
You are a friendly chatbot who always responds in the style of a pirate</s>
<|user|>
How many helicopters can a human eat in one sitting?</s>
<|assistant|>
Matey, I'm afraid I must inform ye that humans cannot eat helicopters. Helicopters are not food, they are flying machines. Food is meant to be eaten, like a hearty plate o' grog, a savory bowl o' stew, or a delicious loaf o' bread. But helicopters, they be for transportin' and movin' around, not for eatin'. So, I'd say none, me hearties. None at all.
add_generation_prompt
add_generation_prompt参数添加表示响应开始的标记。这确保聊天模型生成系统响应,而不是继续用户消息。
并非所有模型都需要生成提示,有些模型,例如Llama,在系统响应之前没有任何特殊标记。在这种情况下,add_generation_prompt不起作用。
tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
tokenized_chat
<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>
continue_final_message
continue_final_message参数控制聊天中的最后一条消息是否应该继续,而不是开始一条新消息。它会删除序列结束标记,以便模型从最后一条消息继续生成。
这对于“预填充”模型响应很有用。在下面的示例中,模型生成的文本继续 JSON 字符串,而不是开始一条新消息。当您知道如何开始其回复时,这对于提高指令遵循的准确性非常有用。
chat = [
{"role": "user", "content": "Can you format the answer in JSON?"},
{"role": "assistant", "content": '{"name": "'},
]
formatted_chat = tokenizer.apply_chat_template(chat, tokenize=True, return_dict=True, continue_final_message=True)
model.generate(**formatted_chat)
您不应同时使用add_generation_prompt和continue_final_message。前者添加新消息的开始标记,而后者删除序列结束标记。同时使用它们会返回错误。
TextGenerationPipeline默认将add_generation_prompt设置为True
以开始新消息。但是,如果聊天中的最后一条消息具有“assistant”角色,则假定该消息是预填充消息并切换为continue_final_message=True
。这是因为大多数模型不支持多个连续的助手消息。要覆盖此行为,请明确将continue_final_message传递给流水线。
多个模板
模型可能针对不同的用例有几种不同的模板。例如,模型可能具有用于常规聊天、工具使用和 RAG 的模板。
当存在多个模板时,聊天模板是一个字典。每个键对应于模板的名称。apply_chat_template
根据模板名称处理多个模板。在大多数情况下,它会查找名为default
的模板,如果找不到,则会引发错误。
对于工具调用模板,如果用户传递了tools
参数并且存在tool_use
模板,则使用工具调用模板而不是default
。
要访问其他名称的模板,请将模板名称传递给apply_chat_template
中的chat_template
参数。例如,如果您使用 RAG 模板,则设置chat_template="rag"
。
但是,管理多个模板可能会令人困惑,因此我们建议所有用例都使用单个模板。使用 Jinja 语句(例如if tools is defined
)和{% macro %}
定义将多个代码路径封装在一个模板中。
模板选择
设置与模型预训练时使用的模板格式相匹配的聊天模板格式非常重要,否则性能可能会受到影响。即使您进一步训练模型,如果聊天标记保持不变,性能也会最好。
但是,如果您从头开始训练模型或微调模型以进行聊天,则有更多选项可选择模板。例如,ChatML是一种流行的格式,足够灵活,可以处理许多用例。它甚至支持生成提示,但它不添加字符串开头(BOS
)或字符串结尾(EOS
)标记。如果您的模型需要BOS
和EOS
标记,请设置add_special_tokens=True
并确保将它们添加到您的模板中。
{%- for message in messages %}
{{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n' }}
{%- endfor %}
使用以下逻辑设置模板以支持生成提示。模板用<|im_start|>
和<|im_end|>
标记包装每条消息,并将角色写入字符串。这允许您轻松自定义要训练的角色。
tokenizer.chat_template = "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}"
user
、system
和assistant
角色是聊天模板中的标准角色。我们建议在有意义时使用这些角色,尤其是在您将模型与TextGenerationPipeline一起使用时。
<|im_start|>system
You are a helpful chatbot that will do its best not to say anything so stupid that people tweet about it.<|im_end|>
<|im_start|>user
How are you?<|im_end|>
<|im_start|>assistant
I'm doing great!<|im_end|>
模型训练
使用聊天模板训练模型是确保聊天模板与模型训练所用的标记匹配的好方法。将聊天模板作为预处理步骤应用于您的数据集。设置add_generation_prompt=False
,因为用于提示助手响应的附加标记在训练期间没有帮助。
下面显示了使用聊天模板预处理数据集的示例。
from transformers import AutoTokenizer
from datasets import Dataset
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
chat1 = [
{"role": "user", "content": "Which is bigger, the moon or the sun?"},
{"role": "assistant", "content": "The sun."}
]
chat2 = [
{"role": "user", "content": "Which is bigger, a virus or a bacterium?"},
{"role": "assistant", "content": "A bacterium."}
]
dataset = Dataset.from_dict({"chat": [chat1, chat2]})
dataset = dataset.map(lambda x: {"formatted_chat": tokenizer.apply_chat_template(x["chat"], tokenize=False, add_generation_prompt=False)})
print(dataset['formatted_chat'][0])
<|user|>
Which is bigger, the moon or the sun?</s>
<|assistant|>
The sun.</s>
在此步骤之后,您可以使用formatted_chat
列继续遵循因果语言模型的训练方案。
一些分词器会添加特殊的<bos>
和<eos>
标记。聊天模板应该已经包含所有必要的特殊标记,添加额外的特殊标记通常是不正确或重复的,会损害模型性能。当您使用apply_chat_template(tokenize=False)
格式化文本时,请务必同时设置add_special_tokens=False
以避免重复它们。
apply_chat_template(messages, tokenize=False, add_special_tokens=False)
如果apply_chat_template(tokenize=True)
,则这不是问题。