Evol-Instruct 的核心思路是让大语言模型对种子指令进行重写,从而自动生成更复杂、更多样的训练数据。但在实际跑批中,不少团队会发现一个尴尬现象:迭代轮次越多,指令表面长度变长,句式却越来越像,任务类型也集中在少数几个模板上。这种现象并非基座模型不够强,而是进化策略本身缺乏对多样性的显式约束。只用难度作为优化目标,模型很容易沿着阻力最小的方向生成看起来复杂、实际上高度同质的内容。要解决这个问题,需要把进化拆成两个维度:深度让单条指令在推理链条上变难,广度让整个指令集在任务形态和语义空间上铺开。两者配合,才能避免合成数据坍缩到狭窄分布。

一、多样性不足的根源:重写退化与分布坍缩
Evol-Instruct 的原始做法通常是从少量人工编写的种子指令出发,让模型按照给定提示词反复重写。重写的目标可以是增加约束、提高复杂度或改变形式。问题在于,模型在自回归生成时天然会偏向训练数据中出现频率更高的模式,如果提示词只要求变得更难,它就会选择最简单的那条路:把指令写成长长的填空模板,并不断添加格式要求。这种退化不是模型能力下降,而是优化目标太单一导致的必然结果。
更隐蔽的问题是分布坍缩。每一轮进化后,通常会有一个过滤器或评分器挑选出高分指令,进入下一轮。如果评分器主要依赖长度、关键词命中或难度分类器,那么高分样本会进一步向某些固定特征集中。几轮之后,整个指令集合虽然在绝对数量上变大,但任务形态、输出格式和语义覆盖都迅速收窄。比如大量指令都变成了“请写一篇关于X的文章,包括三个要点,并给出一个案例”,这就是典型的模板化坍缩。
要量化这种现象,可以观察三个指标:去重后的平均字符长度是否虚高、相邻指令的 n-gram 重复率是否上升、嵌入空间中最近邻距离是否越来越小。当这些指标同时恶化时,只继续增加迭代轮次不仅没有帮助,反而会浪费推理资源,并让模型在训练时学到更偏执的分布。因此,解决多样性不足的关键不是简单地继续进化,而是要把进化过程拆成深度和广度两个正交的方向,并分别设计提示词和过滤策略。
二、深度进化:让单条指令纵向突破复杂度
深度进化的目标不是让指令文本更长,而是让模型在回答时必须经历更长的推理链条。例如同样一条“解释过拟合”,浅层版本只要求给出定义和原因,深度版本会要求模型先给出两个相互制约的场景,再判断哪种情况下过拟合更严重,并给出一个反事实的改进方案。这里增加的约束、条件分支和反事实假设,才是真正的认知负荷。
设计深度进化提示词时,要避免使用“请增加难度”这类空泛表述,因为模型会把难度理解为增加字数。更有效的做法是在提示词中明确列出增难手段,并且要求保留原始任务目标不变。下面是一个构造深度进化提示词的最小示例:
def build_deep_prompt(seed_instruction):
return f"""请对以下指令进行深度进化,保持原始任务目标不变,但显著提升认知难度:
原始指令:{seed_instruction}
要求:
1. 增加至少两个相互制约的约束条件。
2. 要求模型先进行步骤拆解,再给出最终结果。
3. 在指令中加入一个条件分支,当满足某个条件时要求输出备选方案。
4. 不要简单增加输出长度,要增加推理链条。
请直接输出重写后的指令。"""
这只是一个模板,真正有效的是后续的过滤环节。可以用一个小型评估模型对重写后的指令打分,评分维度不要只看长度,而要关注隐含步骤数、条件分支数、是否需要比较或反事实推理。只有那些被判定为推理复杂度明显高于原指令的样本,才进入下一轮,否则很容易出现伪深度指令,例如只是加了一句“请用JSON格式输出”或者“请分三段回答”。
三、广度进化:横向扩展任务形态与语义覆盖
如果深度进化解决的是单条指令不够难的问题,那么广度进化解决的就是整个指令集太像的问题。广度进化不改变任务的认知难度,而是改变任务类型、领域背景、输出格式、受众角色等维度。例如一条关于“写一封道歉邮件”的指令,可以迁移为“设计一个SQL查询来统计逾期订单”“以面试官身份提出三个追问”“生成一份对比表格并说明取舍依据”。这样可以在不增加推理负担的情况下,显著扩展数据分布。
广度进化的实现通常需要一个变换模板库,每次从模板库中随机采样一两个变换操作。模板不能太宽泛,否则模型容易发生语义漂移。下面是一个简单的模板采样与提示词构造示例:
breadth_templates = [
"将任务场景从通用问答迁移到医疗或法律等专业领域,保持核心问题不变。",
"把开放式解释改为设计一份可执行的检查清单。",
"让模型扮演一位持相反立场的专家,给出三个关键反驳点。",
"将输出格式从段落改为结构化表格,并增加一列用于说明判断依据。",
]
def build_breadth_prompt(seed_instruction, template):
return f"""请对以下指令进行广度进化,只改变任务形态或领域,不提升难度:
原始指令:{seed_instruction}
变换要求:{template}
请保持原始目标可用,直接输出新指令。"""
广度进化的最大难点在于控制语义漂移。变换后的指令如果和原指令没有足够的相关性,就会引入噪声;如果几乎不变,又起不到扩展作用。一个可行的策略是用嵌入模型计算重写前后指令的余弦相似度,并设置一个合理区间。比如相似度低于0.6可以视为漂移过大,高于0.95则说明几乎没有改变。同时,可以在领域词表中随机抽取领域标签注入提示词,让模型有方向地迁移,而不是漫无目的地改写。
四、两阶段进化流程与多样性监控
把深度和广度分开之后,实际工程上通常采用先广度后深度的两阶段流程。第一阶段从种子集中做一到两轮广度进化,快速铺开任务形态和领域覆盖,形成多样性的底子。第二阶段再对扩增后的指令做深度进化,逐条提升推理难度。如果反过来先深度,很容易在少数任务形态上用力过猛,后续再做广度迁移时成本更高,也更容易产生不相关的样本。
监控不能依赖人工抽检,需要一套自动化指标。除了常规的去重率和长度分位数,更重要的是嵌入空间的多样性度量。下面是一个计算指令集合多样性的简单函数,它把每条指令编码成向量,然后用平均余弦距离来反映分散程度:
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def diversity_score(instructions):
vectors = model.encode(instructions, normalize_embeddings=True)
sim_matrix = np.inner(vectors, vectors)
np.fill_diagonal(sim_matrix, 0)
# 相似度越低,多样性越高,这里取平均余弦距离
return float(np.mean(1 - sim_matrix))
实际使用时,可以每轮进化后计算一次多样性分数,并观察其变化趋势。如果连续两轮分数没有提升,或者 n-gram 重复率超过预设阈值,就说明继续进化已经无法带来新的信息量,应该停止迭代。最后还要注意深度指令和广度指令的配比,一般建议深度样本占六到七成,因为太高的深度比例会让模型偏向长推理而牺牲广泛适用性。通过这种显式的深度与广度拆分,再配合量化监控,Evol-Instruct 的多样性不足才能从机制上得到缓解,而不是靠换一个更大的模型来掩盖问题。
Evol-Instruct指令多样性深度广度进化修改时间:2026-09-24 01:27:09