将合成文本与现有数据结合是预训练大型语言模型的一种常见且通常更有效的策略。这种混合方法结合了两种数据类型的优点:真实数据的广度和真实性,以及合成数据有针对性、可控的特点。此方法以提高最终模型能力的方式,丰富了预训练语料库 $V_{数据}$。在各种场景中,它通常比单独依赖纯合成数据更具优势,尤其是在数据稀缺时。为什么要混合?混合语料库的理由将合成数据和真实数据结合用于预训练不仅仅是为了增加文本的总量。这是一种提高数据集质量和实用性的策略。以下是几个主要动因:补充稀缺数据: 如果你的真实数据有限,高质量的合成数据可以大幅扩充你的预训练语料库,为模型提供更多学习样例。增加多样性: 数据集,即使是大型数据集,也可能存在固有的偏见或在特定主题或风格上覆盖不足。可以生成合成数据来弥补这些不足,引入更多语言种类,覆盖代表性不足的方面,或提供稀有现象的例子。有针对性的知识引入: 你可以创建专注于特定知识范围(例如,特定科学范围、新的编程语言)或所需技能(例如,分步推理)的合成数据,这些内容在你的语料库中可能代表性不足。平衡数据分布: 如果真实数据中某些类别或风格过度代表,你可以使用合成数据来增加那些不太常见但重要的类别的代表性,从而为你的LLM提供更均衡的预训练“营养”。有控制地引入新格式: 正如章节引言中提到的,预训练可以从指令式数据中受益。合成生成是创建此类数据并将其混合到主要由描述性文本组成的语料库中的很好的方式。然而,混合并非没有需要考虑的地方。合成数据的质量非常重要。添加大量低质量或不匹配的合成数据可能会通过引入噪音或冲突信号来降低模型性能。确定混合比例:合成数据与真实数据的比例你将面临的第一个实际问题之一是:应该添加多少合成数据?没有通用公式,合成数据与真实数据的最佳比例,我们称之为 $R_{合成:真实}$,取决于几个因素:合成数据质量: 质量更高、更真实、更多样的合成数据通常可以以更大比例使用。如果你的合成数据噪音较大或有明显人工痕迹,请更谨慎地使用它。真实数据量: 如果你拥有一个多样化语料库,可能只需要一小部分合成数据用于针对性改进。如果真实数据稀缺,合成数据必然会占更大比例。预训练目标: 如果你旨在让模型获得非常具体的知识或技能,而这些主要由合成数据覆盖,那么合成数据的比例可能会更高。对于一般预训练补充,较小比例可能就足够。对“分布偏移”的风险承受度: 添加过多合成数据,特别是如果其特点(风格、词汇、事实依据)与真实数据显著不同,可能会改变整体数据分布。如果你是有意让模型适应,这可能是理想的,但它也可能导致模型在与原始真实数据分布一致的任务上表现不佳。常见起始点: 许多实践者开始时会用相对较小的比例来补充其语料库,大约占最终总容量的5%到20%。例如,如果你有1太字节(TB)的文本,并目标是补充10%的合成数据,你将大约添加100吉字节(GB)的合成数据。试验通常是明智的。你可以训练更小的模型或减少训练步数,使用不同的 $R_{合成:真实}$ 比例,并在相关验证集或一系列探查任务上评估其性能,以找到最佳点。数据流的组合方法一旦你确定了大致比例,你实际如何混合数据?以下是几种常见方法:简单拼接: 最直接的方法是简单地将合成数据集附加到你的数据集中。如果你的数据集存储为文件集合,这可能涉及将合成文件添加到数据加载器使用的相同目录或列表中。优点: 易于实现。缺点: 如果大小或特点上存在显著差异,且数据没有充分打乱,一个数据集可能在训练的某些阶段占据主导地位。对于非常大的数据集,确保有效打乱本身就是一项挑战。交错或分层混合: 一种更具控制性的方法是交错来自两个来源的数据。这可以在不同粒度上完成:文件级交错: 交替使用真实来源和合成来源的文件。批次级交错: 根据所需比例从真实和合成数据集中抽取样本来构建训练批次。例如,如果你的目标是20%的合成数据,每个批次(平均)可以包含20%的合成样例和80%的真实样例。实例级打乱: 汇集所有数据,然后进行全局打乱。这是确保随机性的理想方式,但对于TB级数据集来说计算量可能很大。下图说明了简单拼接和交错策略之间的差异。```dot " digraph G { rankdir=LR; node [shape=box, style=filled, fontname="Arial"]; subgraph cluster_concat { label="简单拼接"; bgcolor="#e9ecef"; real_data_concat [label="真实数据语料库", fillcolor="#a5d8ff"]; synth_data_concat [label="合成数据语料库", fillcolor="#b2f2bb"]; blended_corpus_concat [label="混合语料库", fillcolor="#ffd8a8"]; real_data_concat -> blended_corpus_concat [label=" 附加"]; synth_data_concat -> blended_corpus_concat [label=" 附加"]; } subgraph cluster_interleave { label="交错 / 分层混合"; bgcolor="#e9ecef"; real_batch_1 [label="批次 1", fillcolor="#a5d8ff"]; synth_batch_1 [label="合成批次 1", fillcolor="#b2f2bb"]; real_batch_2 [label="批次 2", fillcolor="#a5d8ff"]; synth_batch_2 [label="合成批次 2", fillcolor="#b2f2bb"]; dots [label="...", shape=plaintext]; blended_stream [label="混合训练流", fillcolor="#ffd8a8", shape=oval]; real_batch_1 -> synth_batch_1 [style=invis]; synth_batch_1 -> real_batch_2 [style=invis]; real_batch_2 -> synth_batch_2 [style=invis]; synth_batch_2 -> dots [style=invis]; {rank=same; real_batch_1 synth_batch_1 real_batch_2 synth_batch_2 dots} real_batch_1 -> blended_stream; synth_batch_1 -> blended_stream; real_batch_2 -> blended_stream; synth_batch_2 -> blended_stream; dots -> blended_stream; } }" ``` > 拼接(数据集简单地组合)与交错(在训练流创建过程中,来自不同来源的数据混合得更细致)的对比。 批次级交错通常是很好的折衷方案,提供比简单拼接更好的混合效果,而没有在海量数据集上进行全局打乱的全部开销。以下是一个Python代码片段,说明如何按目标合成比例创建批次: ```python import random # 假设这些是实际数据的大型列表或迭代器 real_data_pool = ["真实样例 1", "真实样例 2", ...] synthetic_data_pool = ["合成样例 A", "合成样例 B", ...] # 每个批次中合成数据的目标比例 synthetic_target_proportion = 0.2 # 20% 合成数据 def create_blended_batch(batch_size): batch = [] for _ in range(batch_size): # 决定是从合成池还是真实池中选取 if random.random() < synthetic_target_proportion: # 如果 synthetic_data_pool 不为空,则优先选择 if synthetic_data_pool: batch.append(random.choice(synthetic_data_pool)) elif real_data_pool: # 如果合成数据用尽(或不符合期望比例),则回退到真实数据 batch.append(random.choice(real_data_pool)) else: # 如果 real_data_pool 不为空,则优先选择 if real_data_pool: batch.append(random.choice(real_data_pool)) elif synthetic_data_pool: # 如果真实数据用尽,则回退到合成数据 batch.append(random.choice(synthetic_data_pool)) # 在实际场景中,请确保池不为空或处理 StopIteration # 此外,真正的抽样应该避免不重复地选取相同的项目 # 或使用适当的数据加载器。 return batch # 示例用法: # my_batch = create_blended_batch(32) # print(my_batch) ``` 这个片段仅作示例。数据加载管道(例如,使用Hugging Face `datasets`、PyTorch `DataLoader`或TensorFlow `tf.data`)提供更高效的方式来处理大型数据集、打乱和批处理。你通常会根据所选策略配置这些加载器,以从组合或单独的数据集中采样。 3. **上采样和下采样考量:** 混合也可以是解决不平衡的机会。 * **使用合成数据进行上采样:** 如果你的数据有代表性不足的类别(例如,特定文本类型、技术文档),你可以为这些类别生成合成数据并添加,有效地对其进行上采样。 * **策略性下采样:** 虽然通常你想要更多数据,但如果你的数据中某个特定部分压倒性地占主导且实用性较低,如果高质量合成数据可以提供更好的多样性或覆盖更重要的方面,你可以考虑略微下采样。这在预训练中不太常见,因为大体量数据通常是有益的,但这可以作为一种考量。 ### 混合数据集的质量和一致性 混合数据集时,特别是来自通用网络文本和高度具体的合成指令等不同来源的数据,保持整体质量和一致性很重要: * **一致性:** 确保你的合成数据不会引入与真实数据直接矛盾或以有害方式破坏真实数据的风格元素或事实信息。某些受控矛盾可能有助于提升模型的鲁棒性,但普遍的不一致性会使模型感到困惑。 * **内容方向对齐:** 如果你将特定内容的合成数据(例如,医学文本)添加到通用语料库(例如,网络爬取数据)中,请留意这可能如何影响模型的通用知识。混合比例在此处起作用。 * **预处理一致性:** 在混合之前,对真实数据和合成数据应用一致的文本清理、分词和格式化步骤。预处理中的差异可能会成为模型意想不到的信号。 * **合成数据的迭代过滤:** 在混合之前,严格过滤你的合成数据。相关方法在第5章(“高级方法与数据优化”)和第6章(“评估合成数据与应对操作难题”)中介绍。一个常见错误是混合未经处理、未过滤的合成输出,这会引入大量噪音。 ### 评估混合的影响 你的混合策略的真正检验在于它对预训练过程和所得模型性能的影响。需要关注的几个重要方面包括: * **预训练损失曲线:** 观察训练和验证损失。混合数据集是否导致更平滑的收敛?是否存在意外的峰值或停滞? * **在保留集上的困惑度:** 衡量在多样化保留集上的困惑度,包括代表数据分布的集合以及特定于合成数据覆盖的方面的集合。 * **下游任务性能:** 这通常是最具决定性的衡量标准。在与你的目标相关的一系列下游任务上评估预训练模型。使用混合语料库预训练的模型是否比仅使用真实数据预训练的模型(如果存在这样的基线)表现更好、更差,还是不同? * **探查特定知识/技能:** 如果你的合成数据旨在教授特定内容(例如,一个新的API,某种推理模式),开发探查方法或针对性评估,以查看模型是否习得了这些能力。 “合成数据和真实数据的混合既是一门艺术,也是一门科学。它需要仔细考量你的目标、可用数据的性质以及迭代实验。通过有策略地结合这些资源,你可以创建更丰富、更有效的预训练语料库,从而提升你的LLM所能达到的效果。后续章节将更详细地介绍生成特定类型的合成数据,包括指令格式内容,然后你可以使用这些策略进行混合。”