前言
我们常常会在人家的开源微调框架中看到他们说现在的框架支持什么模型,以及什么Chat Template,那么Chat Template 到底是什么呢?具体有什么作用?以及如何自定义呢?
本文将一一解决这些问题
Chat Template是什么
LLMs的一个越来越常见的用例是聊天。在聊天上下文中,模型不是继续单个文本字符串(就像标准语言模型一样), 而是继续由一个或多个消息组成的对话,每个消息都包括一个角色,比如“用户”或“助手”,以及消息文本。
与Tokenizer类似,不同的模型对聊天的输入格式要求也不同。这就是我们添加聊天模板作为一个功能的原因。 聊天模板是Tokenizer的一部分。用来把问答的对话内容转换为模型的输入prompt。
让我们使用模型来具体说明。
将一段话应用chat_template输出
这是使用mistralai/Mistral-7B-Instruct-v0.1
模型的输出。
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)
可以看到,像Mistral-instruct这种有使用token进行训练的,tokenizer会添加类似[INST]和[/INST]的标识符来表示用户消息的开始和结束。
正如上面的示例中所看到的,聊天模板非常容易使用。只需构建一系列带有role和content键的消息, 然后将其传递给[~PreTrainedTokenizer.apply_chat_template]方法。
另外,在将聊天模板用作模型预测的输入时,还建议使用add_generation_prompt=True来添加[generation prompt]
添加generation prompt进行回复
这次我们换一个模型,来看一下不同模型的chat_template是怎样的
from transformers import AutoModelForCausalLM, AutoTokenizer
checkpoint = "HuggingFaceH4/zephyr-7b-beta"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForCausalLM.from_pretrained(checkpoint) # You may want to use bfloat16 and/or move to GPU here
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]))
这将生成Zephyr期望的输入格式的字符串。它看起来像这样:
我们可以使用模型来生成对用户问题的回复:
outputs = model.generate(tokenized_chat, max_new_tokens=128)
print(tokenizer.decode(outputs[0]))
什么是"generation prompts"?
在tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
中add_generation_prompt
设置为True的时候,会为模板添加一个start的标志:
messages = [
{"role": "user", "content": "Hi there!"},
{"role": "assistant", "content": "Nice to meet you!"},
{"role": "user", "content": "Can I ask a question?"}
]
tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
对比下面的结果
tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
可以看到后者添加了模型开始答复的标记。这可以确保模型生成文本时只会给出答复,而不会做出意外的行为,比如继续用户的消息。 记住,聊天模型只是语言模型,它们被训练来继续文本,而聊天对它们来说只是一种特殊的文本! 你需要用适当的控制标记来引导它们,让它们知道自己应该做什么。
并非所有模型都需要生成提示。一些模型,如BlenderBot和LLaMA,在模型回复之前没有任何特殊标记。 在这些情况下,add_generation_prompt参数将不起作用。add_generation_prompt参数取决于你所使用的模板。
使用pipeline进行回复
pipeline
的设计是为了方便使用聊天模型。让我们再试一次 Zephyr 的例子,但这次使用pipeline
:
from transformers import pipeline
pipe = pipeline("conversational", "HuggingFaceH4/zephyr-7b-beta")
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?"},
]
print(pipe(messages))
[ConversationalPipeline
]将负责处理所有的tokenized
和调用apply_chat_template
,一旦模型有了聊天模板,你只需要初始化pipeline并传递消息列表!
chat template的工作原理
模型的聊天模板存储在tokenizer.chat_template
属性上。如果没有设置,则将使用该模型的默认模板。 让我们来看看BlenderBot
的模板:
点击【搭建框架必备基础】彻底搞懂Chat template查看全文。