☰
用 distilabel 为 smol-course 生成指令微调数据集:从基础 Prompting 到 SelfInstruct、EvolInstruct、Magpie 与 Pipeline
2026/10/9 2:18:36 网站建设 项目流程
  • 教程
  • 人工智能
  • 大模型
  • NLP
  • 微调

【免费下载链接】smol-course

A course on aligning smol models.

项目地址:https://gitcode.com/gh_mirrors/smo/smol-course
点击查看免费下载

本篇技术指南完整讲解 smol-course 第 6 单元「合成数据集」模块中的指令数据集(Instruction Datasets)生成方法。围绕 distilabel 框架,我们从最朴素的手工 Prompting 开始,依次深入 SelfInstruct(基于种子数据的上下文学习)、EvolInstruct(指令进化)与 Magpie(基于聊天模板的自回归生成)三种论文级数据合成技术,最后将各步骤组装成可缓存、可容错、可并行的Pipeline,让你能够规模化地产出用于有监督微调(SFT)的高质量指令数据集。阅读本文后,你将掌握在 smol-course 技术栈中从零生成、校验并导出指令微调数据的完整实战方案。

从 Prompt 到数据:指令数据集为何需要合成

合成数据(synthetic data)听起来复杂,但本质上可以简化为一句话:通过有效的提示(prompting)从模型中提取知识来制造数据。反过来看,这也是为某个特定任务定向生成数据的方式——挑战在于如何在保证数据多样性与代表性(diverse and representative)的前提下高效提示模型。

在本课程第 6 单元的入口文档 units/vi/unit6/1.md(英文版见 v1/6_synthetic_datasets/README.md)中,合成数据被划分为三类(taxonomies):指令(instructions)、偏好(preferences)与批评(critiques)。本单元聚焦前两类,其中指令数据集用于指令微调(instruction tuning),偏好数据集用于偏好对齐(preference alignment);第三类则用于通过模型批评与改写来改进已有数据。本文将围绕第一类——指令微调数据集——展开。

幸运的是,大量研究论文已经系统探索过这一课题。本单元将用到四类方法:

方法核心思想出处
基础 Prompting用一条指令让模型生成 prompt 与 completion手工方式
SelfInstruct以种子数据为上下文,让模型用上下文学习生成新指令Self-Instruct 论文
EvolInstruct按给定标准将指令"进化"为更复杂的版本Evol-Instruct 论文
Magpie利用聊天模板与自回归生成,从 pre-query-prompt 续写用户提问Magpie 论文

下面先从最基础的"手工提示"开始。

基础 Prompting:用 TextGeneration 生成 prompt 与 completion

首先加载 HuggingFaceTB/SmolLM2-1.7B-Instruct 模型。这里借助distilabel库对transformers的集成,通过TransformersLLM加载模型,再交给TextGeneration任务类来生成文本。

from distilabel.llms import TransformersLLM from distilabel.steps.tasks import TextGeneration llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct") gen = TextGeneration(llm=llm) gen.load()

注意:distilabel会把llm加载到内存中,因此在 notebook 中用完以后需要调用gen.unload()释放内存,避免内存问题。

现在用llm为指令微调生成一个 prompt:

next(gen.process([{"instruction": "Tạo một câu hỏi về Khóa học Smol của Hugging Face về các mô hình AI nhỏ."}])) # 示例输出:Mục đích của Khóa học Smol là gì? # (即:Smol 课程的目的是什么?)

再把这条 prompt 作为输入,生成对应的 completion:

next(gen.process([{"instruction": "Mục đích của Khóa học Smol là gì?"}])) # 示例输出:Khóa học Smol là một nền tảng được thiết kế để học các khái niệm khoa học máy tính. # (即:Smol 课程是一个用于学习计算机科学概念的平台。)

到这里,我们已经能够生成一条合成 prompt 及其对应的 completion。把这种简单方法规模化复用,确实能产出大量数据;但明显存在两个问题:数据质量不高,没有考虑到课程或领域本身的细微差异;数据多样性不足——重复运行上述代码会发现每次产出的内容大同小异。这两点正是后面几种论文方法要解决的问题。

SelfInstruct:基于种子数据的上下文学习生成新指令

SelfInstruct的核心思想是:根据一个种子数据集(seed dataset)生成新的指令。种子数据可以是一条单独的指令,也可以是一段上下文。流程如下:

  1. 准备一组初始种子数据(seed pool);
  2. 利用上下文学习(in-context learning),提示语言模型基于这些种子数据生成新指令。

distilabel 中实现的提示模板(简化版)如下:

# Mô tả nhiệm vụ(任务描述) Phát triển {{ num_instructions }} truy vấn của người dùng có thể được nhận bởi ứng dụng AI đã cho và áp dụng cho ngữ cảnh được cung cấp. Nhấn mạnh sự đa dạng trong động từ và cấu trúc ngôn ngữ trong khả năng văn bản của mô hình. (为给定的 AI 应用开发 {{ num_instructions }} 条用户查询,使其适用于提供的上下文;强调动词与语言结构在模型文本能力范围内的多样性。) # Ngữ cảnh(上下文) {{ input }} # Đầu ra(输出)

模板中{{ num_instructions }}控制生成指令的数量,{{ input }}注入种子上下文。使用方式是把llm传给SelfInstruct类:

from distilabel.steps.tasks import SelfInstruct self_instruct = SelfInstruct(llm=llm) self_instruct.load() context = "<prompt_to_data_section>" # 替换为「从 Prompt 到数据」一节的内容 next(self_instruct.process([{"input": text}]))["instructions"][0] # Quá trình tạo dữ liệu tổng hợp thông qua việc nhắc nhở thủ công là gì? # (即:通过手工提示生成合成数据的过程是什么?)

可以看到,生成的指令质量明显提升,且贴合我们课程的真实内容与领域。注意此处我们传入的是{"input": ...},取回时通过["instructions"][0]拿到第一条新指令。SelfInstruct 已经比裸提示好得多,但我们还可以借助"进化"进一步提升。

EvolInstruct:把指令进化为更复杂的版本

EvolInstruct是一种提示技术:接收一条输入指令,将其"进化"为同一指令的更优版本。所谓"更优",是依据一组预定义标准,为原指令添加约束、深化、具体化、引入推理或增加复杂度。该过程可迭代多次,对同一条指令产生多种进化版本,理想情况下逐步逼近更好的形态。distilabel 中对应的提示模板(简化版)如下:

I want you act as a Prompt Rewriter.(我希望你扮演一名提示重写者。) Given prompt a prompt, rewrite it into a more complex version.(给定一条提示,把它重写为更复杂的版本。) Complicate the prompt based on the following criteria:(根据以下标准使提示复杂化:) {{ criteria }} # Prompt(提示) {{ input }} # Output(输出)

使用方式同样简单,只需把llm传给EvolInstruct类,并通过num_evolutions指定进化代数:

from distilabel.steps.tasks import EvolInstruct evol_instruct = EvolInstruct(llm=llm, num_evolutions=1) evol_instruct.load() text = "Quá trình tạo dữ liệu tổng hợp thông qua việc nhắc nhở thủ công là gì" # (即:通过手工提示生成合成数据的过程是什么?) next(evol_instruct.process([{"instruction": text}])) # Quá trình tạo dữ liệu tổng hợp thông qua việc nhắc nhở thủ công là gì? # Và, làm thế nào hệ thống trí tuệ nhân tạo, GPT4, sử dụng các thuật toán học máy để thao tác dữ liệu đầu vào thành dữ liệu tổng hợp? # (进化结果:通过手工提示生成合成数据的过程是什么?以及人工智能系统 GPT4 如何利用机器学习算法把输入数据处理成合成数据?)

上面的例子中num_evolutions=1,即只进化一代。注意进化结果:指令确实变得更复杂,但已经丢失了一部分原始含义。这说明进化是一把双刃剑(double-edged sword)——每进化一次都可能引入语义漂移,我们必须时刻警惕生成数据的质量,必要时配合人工或自动评估来把关。

Magpie:利用聊天模板的自回归式数据生成

Magpie与前两种方法思路不同:它不依赖上下文学习或改写,而是利用语言模型的自回归(auto-regressive)特性,以及指令微调阶段使用的聊天模板(chat-template)。

回顾一下:聊天模板是一种用明确角色标记(system、user、assistant)来组织对话的格式。在指令微调阶段,模型已被优化为忠实复现这种格式——这恰好是 Magpie 利用的机制。具体流程是:

  1. 构造一个基于聊天模板的pre-query-prompt(查询前提示),但在用户消息指示符之前停住,例如<|im_start|>user\n;
  2. 让语言模型继续自回归地生成用户提示,直到助手指示符结束,例如<|im_end|>。

由于不同模型的聊天模板格式不同,Magpie 的提示模板也因模型而异。简化版的分步过程如下:

# Bước 1: cung cấp lời nhắc trước truy vấn(第 1 步:给出查询前提示) <|im_start|>user\n # Bước 2: mô hình ngôn ngữ tạo ra lời nhắc của người dùng(第 2 步:模型生成用户提示) <|im_start|>user\n Mục đích của Khóa học Smol là gì?(Smol 课程的目的是什么?) # Bước 3: dừng quá trình tạo(第 3 步:停止生成) <|im_end|>

这种方式能以非常高的效率批量生成数据,甚至可以扩展到多轮对话(multi-turn conversations)。论文中假设:这种生成的数据能够复现所用模型在指令微调阶段的训练数据分布。

在 distilabel 中使用Magpie类同样只需传入llm:

from distilabel.steps.tasks import Magpie magpie = Magpie(llm=llm) magpie.load() next(magpie.process([{"system_prompt": "Bạn là một trợ lý hữu ích."}])) # 输出:[{ # "role": "user", # "content": "Bạn có thể cung cấp cho tôi danh sách 3 trường đại học hàng đầu không?" # (你能给我一份排名前三的大学名单吗?) # }, # { # "role": "assistant", # "content": "3 trường đại học hàng đầu là: MIT, Yale, Stanford." # (排名前三的大学是:麻省理工、耶鲁、斯坦福。) # }]

注意这次我们立刻得到了一组带role标记的对话数据集(user 与 assistant 成对出现),这正是指令微调所需的 prompt-completion 结构。

Magpie 的领域定制技巧:怎么写 system prompt

要在自己的领域获得更好表现,可以向system_prompt注入额外上下文。为了让 LLM 生成领域特定的数据,最好在 system prompt 中描述"用户的查询将会是什么"。这个描述随后会进入 pre-query-prompt,在开始生成用户提示之前就引导 LLM 朝该领域生成查询。

推荐写法是描述用户的身份/场景:

Bạn là một trợ lý AI sẽ giúp người dùng giải các bài toán. (你是一个帮助用户解数学题的 AI 助手。)

而不是直接要求模型"去生成数学题":

Bạn là một trợ lý AI tạo ra các bài toán (你是一个生成数学题的 AI 助手。)

需要说明的是:语言模型普遍不太擅长把额外上下文注入system_prompt,因此这种定制方式并非总是像其他技术(如 SelfInstruct 的种子上下文)那样稳定有效,使用时需要多做实验验证。

从 Prompt 到 Pipeline:把数据生成组装成完整流程

前面介绍的各任务类都是独立类(standalone classes),可以直接在流水线中使用。更进一步,我们可以用Pipeline类把整个数据生成过程组织成一个正式流程。下面这个最小流水线包含:

  • LoadDataFromDicts:以字典形式加载初始数据;
  • 两个TextGeneration步骤:第一个为 prompt 生成指令,第二个为指令生成 completion;
  • 用>>运算符连接步骤,让数据按顺序流动;
  • 用output_mappings参数把上游输出列映射到下游输入列,确保数据正确流转。
from distilabel.llms import TransformersLLM from distilabel.pipeline import Pipeline from distilabel.steps import LoadDataFromDicts from distilabel.steps.tasks import TextGeneration with Pipeline() as pipeline: data = LoadDataFromDicts(data=[{"instruction": "Tạo một câu hỏi ngắn về Khóa học Smol của Hugging Face."}]) # (即:为 Hugging Face 的 Smol 课程生成一个简短问题。) llm = TransformersLLM(model="HuggingFaceTB/SmolLM2-1.7B-Instruct") gen_a = TextGeneration(llm=llm, output_mappings={"generation": "instruction"}) gen_b = TextGeneration(llm=llm, output_mappings={"generation": "response"}) data >> gen_a >> gen_b if __name__ == "__main__": distiset = pipeline.run(use_cache=False) print(distiset["default"]["train"][0]) # 输出: # [{ # "instruction": "Mục đích của Khóa học Smol là gì?", # "response": "Khóa học Smol là một nền tảng được thiết kế để học các khái niệm khoa học máy tính." # }]

关键点解析:

  • output_mappings:TextGeneration步骤的默认输出列是generation。在gen_a中把generation映射为instruction,在gen_b中把generation映射为response,这样最终每条样本就同时携带了instruction与response两个字段,正好是指令微调数据集的标准格式。各步骤的输入/输出列可查阅 distilabel 组件文档。
  • distiset:pipeline.run()返回一个Distiset,可按distiset["default"]["train"][0]取出训练集首条样本。
  • use_cache=False:禁用缓存强制重新生成(详见下文缓存机制)。

流水线的数据流向与output_mappings的映射关系如下:

Pipeline 的三大隐藏能力

Pipeline在底层提供了很多实用特性:

  1. 自动缓存生成结果:distilabel会自动缓存每次生成的结果,再次运行时无需重跑已完成的生成步骤,节省大量推理时间;
  2. 容错(fault-tolerance):即使某些生成步骤失败,流水线仍会继续运行,不会整体中断;
  3. 并行生成:所有生成步骤并行执行,显著加速数据生产。

此外还可以调用draw方法可视化流水线结构(如上图),直观检查数据如何一步步流动、output_mappings如何衔接各阶段。

从字典到 Hub:加载真实数据集并上传结果

练习 notebook(notebooks/vi/6_synthetic_datasets/instruction_sft_dataset.ipynb)中还给出了两个实用扩展:

其一,用datasets.load_dataset从 Hub 加载数据,替代写死的字典:

from datasets import load_dataset with Pipeline(...) as pipeline: ... if __name__ == "__main__": dataset = load_dataset("my-dataset", split="train") distiset = pipeline.run(dataset=dataset)

其二,用push_to_hub把生成的 Distiset 上传到 Hub:

if __name__ == "__main__": distiset = pipeline.run(use_cache=False) distiset.push_to_hub("huggingface-smol-course-instruction-tuning-dataset")

运行流水线后不要忘记把数据集推送到 Hub,便于后续微调与共享。安装时,除了transformers后端(distilabel[hf-transformers]),也可以按需选用vllm或hf-inference-endpoints等推理后端,例如:

pip install "distilabel[hf-transformers,outlines,instructor]"

最佳实践

无论采用上述哪种技术,生成高质量指令数据集都需要遵循以下实践原则:

  • 确保种子数据足够多样:多样化的种子数据(seed data)才能覆盖广泛场景,避免生成结果偏斜;
  • 定期评估数据集:持续检查生成数据的多样性与质量,及时发现问题并回退到更保守的参数;
  • 迭代(system)prompt:prompt 是数据质量的杠杆,反复打磨 system prompt 与任务描述能显著改善输出。

下一步与练习

  • 👨🏽‍💻动手练习:完成练习 notebook notebooks/vi/6_synthetic_datasets/instruction_sft_dataset.ipynb,按三个难度逐步构建指令微调数据集:🐢 生成一个指令微调数据集 → 🐕 使用种子数据生成 → 🦁 在种子数据基础上叠加指令进化(EvolInstruct)。练习中鼓励更换模型与 generation 参数,观察它们对数据集质量的影响——先小规模试验,再规模化扩展。
  • 🧑‍🏫继续学习:在生成指令数据集的基础上,进一步学习偏好数据集(Preference Datasets)的生成(对应英文版 v1/6_synthetic_datasets/preference_datasets.md),其中将用到EvolQuality与UltraFeedback等进阶提示。

参考资料

  • distilabel 官方文档(组件画廊、任务类输入输出列、Pipeline 用法)
  • Self-Instruct 论文(利用语言模型自身的指令生成能力构造指令数据)
  • Evol-Instruct 论文(WizardLM 中提出的指令进化方法)
  • Magpie 论文(基于聊天模板的自回归式指令生成)
  • 教程
  • 人工智能
  • 大模型
  • NLP
  • 微调

【免费下载链接】smol-course

A course on aligning smol models.

项目地址:https://gitcode.com/gh_mirrors/smo/smol-course
点击查看免费下载

相关推荐

上一篇:Marinara多语言支持实现:Chrome扩展国际化部署指南
下一篇:Hypothesis测试框架与Flake8:代码风格检查最佳实践

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询