大模型原理--数据集格式和Chat Template
1.概述
本文主要是打通从原始数据到模型训练/推理时所期待的字符串格式这一流程。
2. 数据集格式
私有数据通常并非专为模型微调而准备,因此在使用前往往需要经过清洗、结构化和标注等预处理步骤,将数据转化为特定的数据集格式。
2.1 指令式
指令式数据集用于训练模型执行明确的单轮任务,如翻译、摘要或问答。其典型格式源自斯坦福大学的 Alpaca 项目,结构简洁、易于使用。
其每条样本包含三个字段:instruction:描述模型需要执行的任务;input:任务所需的上下文或附加信息;output:模型应生成的正确回答。
{
"instruction": "识别出文本中的关键词",
"input": "基于产权理论的智慧城市信息共享机制研究\n摘要:[目的/意义]新型智慧城市的建设是实现国家治理体系和治理能力现代化的关键手段,然而目前普遍存在信息孤岛问题严重影响新型智慧城市落地实现,如何合理高效解决信息资源共享问题,已经成为急需解决的问题。[方法/过程]通过分析智慧城市信息共享存在的问题原因以及解决思路,以信息产权及信息确权分析为基础,对智慧城市信息共享的利益冲突与平衡进行分析,最后提出一种基于产权理论的智慧城市信息共享机制框架。[结果/结论]新型智慧城市应充分考虑信息价值及信息产权问题,形成信息共享的正向激励机制,使智慧城市信息资源得以高效利用,实现政府各部门相互协作,适应复杂多变的城市环境需求,形成智慧政府能力。",
"output": "智慧城市;信息共享;产权理论;产权确认"
}
训练时,这些字段通常会通过一个提示模板(prompt template)组合成结构统一的输入字符串,以帮助模型更好的学习任务指令。
2.2 对话式
对话式数据集用于训练模型进行多轮对话,例如聊天机器人、虚拟助手等。这类数据通常以消息序列的形式组织,强调发言者角色与对话流程。目前广泛采用的格式主要有 ShareGPT 格式 和 OpenAI 格式。
2.2.1 ShareGPT格式
ShareGPT 格式源于用户在社区中分享的与 ChatGPT 的真实对话记录,常用于保存多轮对话数据集。每条数据样本由一个名为 conversations 的列表构成,列表中的每个元素代表一次发言,包含两个关键字段:from:标识发言者的角色,通常取值为 "human"(表示用户)或 "gpt"(表示模型助手); value:该轮对话的具体文本内容。
{
"conversations": [
{
"from": "human",
"value": "你能解释一下什么是机器学习吗?"
},
{
"from": "gpt",
"value": "机器学习是人工智能的一个分支,它允许计算机系统通过数据和经验自动改进其性能。"
},
{
"from": "human",
"value": "它主要有哪些类型?"
},
{
"from": "gpt",
"value": "主要类型包括监督学习、无监督学习、半监督学习和强化学习等。"
}
]
}
2.2.2 OpenAI 格式
OpenAI 格式是 OpenAI 官方在其 API 微调功能中推广的一种通用消息列表格式,兼具灵活性与表达力,可同时支持单轮和多轮对话。每个训练样本是一个包含 messages 字段的字典,其中 messages 是一个有序列表,每条消息包含以下字段:role:发言者角色,常见值包括:system、user和assistant。content:消息的具体文本内容。
{
"messages": [
{
"role": "system",
"content": "你是一名专业的营养师。你的回答应基于科学知识,提供务实、健康的建议,并避免提供任何医疗诊断。"
},
{
"role": "user",
"content": "我最近感觉下午很疲惫,有什么健康的零食推荐吗?"
},
{
"role": "assistant",
"content": "下午感到疲惫是常见的现象,选择正确的零食可以帮助稳定能量水平。以下是一些推荐:\n1. **一把坚果和干果**:提供健康的脂肪、蛋白质和天然糖分,能快速补充能量。\n2. **希腊酸奶**:富含蛋白质,能提供持久的饱腹感。\n3. **一个苹果或一根香蕉**:富含维生素和膳食纤维,是方便的天然能量来源。\n请记得结合充足的水分摄入,因为脱水也会导致疲劳。"
}
]
}
3. Chat Template
在实际训练中,无论采用哪种原始格式,通常都会通过 Chat Template(例如ChatML)将多轮消息组织成结构统一的字符串,确保模型能够正确解析对话结构并学习交互模式。这一标准化步骤对于提升模型的对话理解与生成能力至关重要。
聊天模板是一个 Jinja2 片段,用于将消息格式化为模型训练时所使用的字符串。在大多数情况下,无需担心聊天模板问题:模型会随分词器一同提供模板,而TRL会自动应用该模板。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
jinja2_snippet = tokenizer.chat_template
print("Jinja2 snippet: \n", jinja2_snippet)

tokenizer 的 apply_chat_template 方法使用聊天模版将数据集转化为模型训练时所使用的字符串。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
share_gpt = {
"messages": [
{
"role": "system",
"content": "你是一名专业的营养师。你的回答应基于科学知识,提供务实、健康的建议,并避免提供任何医疗诊断。"
},
{
"role": "user",
"content": "我最近感觉下午很疲惫,有什么健康的零食推荐吗?"
},
{
"role": "assistant",
"content": "下午感到疲惫是常见的现象,选择正确的零食可以帮助稳定能量水平。以下是一些推荐:\n1. **一把坚果和干果**:提供健康的脂肪、蛋白质和天然糖分,能快速补充能量。\n2. **希腊酸奶**:富含蛋白质,能提供持久的饱腹感。\n3. **一个苹果或一根香蕉**:富含维生素和膳食纤维,是方便的天然能量来源。\n请记得结合充足的水分摄入,因为脱水也会导致疲劳。"
}
]
}
apply_chat_template_string = tokenizer.apply_chat_template(share_gpt['messages'], tokenize=False)
print("Apply chat template: \n", apply_chat_template_string)

4. 详细解释 apply_chat_template 方法
4.1 方法的签名
apply_chat_template(
self,
conversation: list[dict[str, str]] | list[list[dict[str, str]]],
tools: list[dict | Callable] | None = None,
documents: list[dict[str, str]] | None = None,
chat_template: str | None = None,
add_generation_prompt: bool = False,
continue_final_message: bool | str = False,
tokenize: bool = True,
padding: bool | str | PaddingStrategy = False,
truncation: bool = False,
max_length: int | None = None,
return_tensors: str | TensorType | None = None,
return_dict: bool = True,
return_assistant_tokens_mask: bool = False,
tokenizer_kwargs: dict[str, Any] | None = None,
**kwargs,
)
4.2 核心参数详解
conversation
类型:List[Dict[str, str]](单轮对话)或 List[List[Dict[...]]](批量对话)
每个字典必须有 role 和 content。role 通常是 "system", "user", "assistant"。也可包含 "tool" 或 "function" 等角色(如果你的模型支持工具调用),但需要模板里做了相应处理。
chat_template
可选,传入一个 Jinja2 字符串模板,用于覆盖 tokenizer 默认的 tokenizer.chat_template。多数官方模型的 tokenizer 已经自带模板,不需要手动指定。
tokenize
True(默认):返回 Tokenizer 编码后的结果(input_ids,attention_mask 等),可以带 return_tensors="pt" 等。False:只返回渲染后的字符串(str),不做分词。
add_generation_prompt
True:会在渲染后的字符串末尾添加一段表示“模型即将开始回复”的提示。例如在 ChatML 格式下,它会追加 <|im_start|>assistant\n,告诉模型“现在该你说话了”。在训练时通常设为 False(因为后面会直接拼接 assistant 的真实回复),在推理时设为 True(让模型从这个提示之后开始生成)。

padding / truncation / max_length
只有当 tokenize=True 时生效,行为和标准 tokenizer() 调用一样:
padding=True:将序列填充到同一长度(通常搭配 return_tensors="pt")。
truncation=True:截断到 max_length。
max_length:最大长度。
return_tensors
当 tokenize=True 时,可设为 "pt"(PyTorch)、"tf"(TensorFlow)、"np"(NumPy)或 "jax"。
return_dict
当 tokenize=True 时,return_dict=True 会返回一个 BatchEncoding 对象(和普通 tokenizer 调用相同)。若设为 False(且 return_tensors 无设置),返回一个 Python 字典。
continue_final_message
False(默认)。常规的对话模式。
当设置为True时,参数是用于实现 预填充 (prefilling) 的核心工具。“预填充”是指在 assistant 角色的消息中预设一个开头,然后让模型完成后续内容。
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
# 定义对话,注意助手消息的 content 只是一个 JSON 的开始
chat = [
{"role": "user", "content": "Can you format the answer in JSON?"},
{"role": "assistant", "content": '{"name": "'}, # 这里就是预填充
]
# 使用 continue_final_message=True 来“打开”这个参数
formatted_chat = tokenizer.apply_chat_template(chat, tokenize=True, return_dict=True, continue_final_message=True)
# 模型生成时,会从 "{\"name\": \"" 之后继续写,而不是另起一句
outputs = model.generate(**formatted_chat)
print(tokenizer.decode(outputs[0]))
将字段名称以字符串形式传递,以便预先填充该字段而非内容。推理模型通常会提供一个单独的字段,例如在 Qwen 中是“reasoning_content”,在 Gemma 中是“thinking”。
chat = [
{"role": "user", "content": "Explain 1+1"},
{"role": "assistant", "reasoning_content": "The user wants a simple addition. ", "content": ""},
]
formatted_chat = tokenizer.apply_chat_template(chat, tokenize=False, continue_final_message="reasoning_content")
5. 不同模型对于对话的格式要求是不同的
from transformers import AutoTokenizer
# These will use different templates automatically
mistral_tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat")
smol_tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"},
]
# Each will format according to its model's template
mistral_chat = mistral_tokenizer.apply_chat_template(messages, tokenize=False)
qwen_chat = qwen_tokenizer.apply_chat_template(messages, tokenize=False)
smol_chat = smol_tokenizer.apply_chat_template(messages, tokenize=False)

6. 总结:apply_chat_template 告别了“手工拼接对话特殊标记”,让代码可以很容易地适配不同模型。它的核心价值在于:格式标准化:自动生成符合模型训练格式的对话文本。易用性:只需传入标准的 messages 列表,就可以获得 input_ids。可切换:换用不同模型时,只需要换 tokenizer,代码无需改动。掌握这个方法后,就能干净、稳健地为各类聊天模型准备输入数据,无论是训练还是推理都事半功倍。
- 点赞
- 收藏
- 关注作者
评论(0)