Synthetic Data › Synthetic Data Generation

Michael BrenndoerferJanuary 13, 202613 min read
Language AI HandbookAll chapters
Part 20 of 67Data Curation
Chapter 148Synthetic Data › Synthetic Data Generation
Reading section 3 of 11 · Synthetic Data Generation · 13 minSection 3 of 11 · 13 min

Book contents

Section 3 of 11 · 13 min read

Synthetic Data › Synthetic Data GenerationOne section · Sections in this chapter3 / 11 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

The most common approach to synthetic data generation is prompted generation: you write a template that describes the kind of data you want, fill in variables from a seed set, and call a teacher model to produce the output. The architecture of a generation pipeline has three components that determine the quality of what comes out: the template, the seed data, and the teacher model.

Prompt Templates and Seed Data

A prompt template defines the structure of the generation request. For an instruction-following dataset, a typical template might look like:

You are an expert in {domain}. Write a detailed question about {topic} that a curious student might ask. Then provide a thorough, accurate answer. Format your response as: Question: <question here> Answer: <answer here>

The template is a contract: it specifies the output format, the desired style, the expected length, and the constraints the generation must satisfy. A well-written template is more than a fill-in-the-blank form; it is a specification of quality. Templates that describe desired qualities explicitly ("thorough", "accurate", "step-by-step") produce better outputs than vague templates ("write something about {topic}"), because language models attend to every word in the prompt and use it as guidance.

The seed data provides the values that fill the template variables: a list of domains, topics, or example inputs that drive diversity. Seeds can come from:

  • Existing natural corpora (Wikipedia titles, academic paper abstracts, Stack Overflow question titles)
  • Manually curated topic lists organized by domain and difficulty
  • Outputs from a previous generation round (self-play / iterative generation)
  • Human-written examples for few-shot prompting that anchor the style

The quality and breadth of your seed set directly determines the quality and breadth of the generated dataset. A narrow seed set produces a narrow dataset, regardless of how capable the teacher model is. If 80% of your seeds are about software engineering, 80% of your generated data will be about software engineering, and models trained on it will be disproportionately strong at software tasks. Seed curation is therefore not a secondary concern; it is the first place to invest effort in a synthetic data pipeline.

One underappreciated aspect of template design is the use of few-shot examples within the template. Instead of describing what you want in abstract terms, showing the model two or three examples of desired input-output pairs immediately constrains the generation toward the right format and style. The few-shot examples serve as in-context demonstrations of the quality bar, and the model tends to produce outputs that match that quality level.

Self-Instruct

Self-Instruct is a seminal synthetic data approach that bootstraps instruction-following data from a seed set of 175 human-written instruction examples. The paper introduced the idea that a model can expand its own training data by generating new instructions, classifying them, and producing input-output pairs, with only minimal human curation at the seed stage. The process works in four stages.

First, the model generates new instructions by sampling from a prompt that shows a few of the seed instructions and asks the model to create more. These are diverse because the prompt explicitly asks for variety, and because the few-shot examples are sampled randomly from the growing pool, so the model sees different combinations each time. Second, the model classifies each instruction as either a classification task or a generation task, because these two types require different input-output structures. Third, the model generates input-output pairs for each instruction. Fourth, low-quality examples are filtered using simple heuristics.

The heuristics in the original Self-Instruct paper catch obvious failures: instructions that are shorter than three words, instructions that begin with an imperative not appropriate for the task type, instructions that too closely match one of the seed examples (measured by ROUGE-L similarity), and input-output pairs where the model simply copied the instruction into the output field. These lightweight filters remove perhaps 30-40% of generated examples without requiring any additional model calls.

Self-Instruct demonstrated that a small human-curated seed set can bootstrap thousands of diverse instruction examples automatically. Alpaca, one of the first widely-used open instruction-tuned models, used a variant of Self-Instruct to generate 52,000 instruction-following examples from GPT-3.5 using a single calling batch. The entire dataset cost under $500 to generate at the time, making it a remarkably cost-effective approach to building instruction-following capability. The resulting model, despite being only 7B parameters, matched or exceeded GPT-3 on many instruction-following benchmarks.

One subtle but important design choice in Self-Instruct is that the few-shot prompt always shows examples from the growing pool of generated instructions, not just from the original seed. As the pool grows, the model sees increasingly diverse examples in its context, which reinforces diversity in what it generates. This feedback mechanism means the dataset becomes more varied over time, not less, provided the prompt selection samples broadly from the existing pool rather than repeatedly selecting the same popular examples.

The ROUGE-L similarity filter that removes near-duplicate instructions is worth examining more closely. ROUGE-L measures the longest common subsequence between two text sequences, normalized by the length of the sequences. If a newly generated instruction has a ROUGE-L score above 0.7 with any existing instruction in the pool, it is treated as a near-duplicate and discarded. This filter prevents the pool from being dominated by paraphrases of the same underlying instruction, which would waste generation budget without adding diversity. The threshold of 0.7 is a reasonable default, but you may want to lower it (to 0.5) for domains where natural paraphrase is common, or raise it (to 0.8) for domains with a naturally small vocabulary of instruction verbs.

Evol-Instruct

Evol-Instruct (from WizardLM) takes a different approach. Instead of generating new instructions from scratch, it starts with a base instruction and evolves it to be more complex through a series of mutations. The core insight is that flat instruction datasets have a difficulty ceiling: they tend to produce simple, direct questions and answers because the model generating them defaults to moderate complexity. By explicitly prompting the model to make an instruction harder, you can systematically populate the high-difficulty end of the training distribution.

The mutations include:

  • In-depth evolution: Adding constraints, reasoning steps, or requirements ("In addition, explain why the approach fails when the input contains null values...")
  • In-breadth evolution: Creating new, related instructions in adjacent areas of the topic
  • Difficulty increase: Making the task harder ("Instead of a simple list, provide a comparative analysis with quantitative trade-offs...")
  • Concreteness increase: Making vague instructions more specific by adding details about the desired format, length, or audience
  • Deepen a concept: Asking for a fuller explanation of a concept that was previously treated as assumed background knowledge

Each evolution step produces a harder version of the original instruction. After multiple rounds of evolution, you have a dataset that spans difficulty levels from simple to highly complex. The WizardLM models trained on Evol-Instruct data showed significantly better performance on complex reasoning benchmarks compared to models trained on flat instruction sets, because the training distribution included the complex end of the difficulty spectrum rather than truncating it.

The reason Evol-Instruct works is that it addresses a known failure mode of flat instruction datasets: they underrepresent complex, multi-step reasoning. When a model is only trained on simple instructions ("Summarize this passage", "Translate this sentence"), it learns to follow simple patterns but struggles with tasks that require chaining multiple steps, applying multiple constraints simultaneously, or synthesizing information from multiple angles. By systematically constructing harder variants of every seed instruction, Evol-Instruct ensures the training distribution includes the complex end of the difficulty spectrum.

One practical consideration is that evolved instructions can become incoherent if the evolution prompt is too aggressive. Adding too many constraints at once produces instructions that are technically longer but logically contradictory or impossible to satisfy. A student asked to "write a Python function that sorts a list in O(n) time using only built-in Python operations without using any list methods or built-in sort" is receiving an impossible instruction. Good Evol-Instruct pipelines include a coherence filter: after evolution, a judge model is asked whether the evolved instruction is a sensible, self-consistent task. Instructions flagged as incoherent are discarded rather than evolved further.

The difficulty of an evolved instruction is not the same as the quality of the training example it produces. A very hard instruction might have no good answer, in which case training on the generated response teaches the model to produce plausible-sounding non-answers for hard questions. Pairing Evol-Instruct with rejection sampling, where possible, provides the strongest combination: evolve instructions to be hard, then filter generated responses to keep only those that are verifiably correct or highly rated by a judge.

Backtranslation

Backtranslation is an elegant technique borrowed from machine translation. In translation, backtranslation generates synthetic source-language text by translating target-language monolingual data backward. In the instruction-tuning context, the analogous process runs in reverse from the typical generation direction:

  1. Start with high-quality response text (for example, a well-written Wikipedia paragraph, a well-reasoned essay, a clear technical tutorial)
  2. Ask the teacher model: "What question or instruction would elicit this response?"
  3. Use the generated instruction as the input and the original text as the output

This is powerful because you can use any large corpus of high-quality text as a source of responses, then synthetically create the corresponding instructions. The LIMA paper ("Less Is More for Alignment") used 1,000 carefully selected examples from this approach to fine-tune a strong model, showing that curated quality matters more than raw quantity. The LIMA model, fine-tuned on only 1,000 examples, was competitive with models fine-tuned on 52,000 Alpaca examples, illustrating how dramatically sample quality affects learning efficiency.

The backtranslation approach offers a natural quality guarantee: you start with text you know is high quality (a well-written encyclopedia article, a carefully argued essay, a detailed technical tutorial) and generate the instruction to match it. This is the reverse of the typical flow where you worry about whether the model's response is good. The response is already guaranteed to be good; the only uncertainty is whether the generated instruction is a natural and sensible request for that response. That is a much easier quality judgment to make, and it can often be verified by asking whether a reasonable person would ask such a question.

One extension of backtranslation is to use it for stylistic alignment. If you have a corpus of responses in a particular style (formal academic writing, friendly conversational explanations, concise technical documentation), backtranslation can generate corresponding instructions that target that style. The resulting training data teaches the model what to say and how to say it in response to particular kinds of requests.

The limitation of backtranslation is that the instructions it generates tend to be more abstract and less specific than instructions in natural use. "What is the main argument of this passage?" is a natural backtranslation instruction for an expository paragraph, but real users often ask more specific, contextual questions like "Why did the author reject the classical interpretation?". The fix is to use diverse high-quality source texts rather than encyclopedic survey articles, which tend to elicit more specific and varied instructions.

Rejection Sampling

Rejection sampling is used when you have a verifiable correctness criterion, such as math problems, code tasks, or logical puzzles. The approach is straightforward in principle:

  1. Generate many candidate solutions for a given problem
  2. Execute or verify each solution (run the code, check the math answer, evaluate logical validity)
  3. Keep only the solutions that pass verification
  4. Use the passing solutions as training examples

Rejection sampling is the backbone of math-focused synthetic datasets like MetaMath and OpenMathInstruct. Given a math problem, the teacher generates dozens of candidate solutions. Each solution is verified by checking whether the final numerical answer matches the known ground truth. Only solutions that reach the correct answer through valid reasoning steps are kept.

The main advantage of rejection sampling is that it provides a hard correctness signal, not a soft quality judgment. A solution either produces the right answer or it does not. This binary signal is especially useful for training reasoning capability. A soft quality score from a judge model might rate a plausible-sounding but incorrect proof highly, because the reasoning looks confident and well-structured. Rejection sampling bypasses this failure mode entirely: you never train on incorrect reasoning, regardless of how well it reads.

The MetaMath dataset, constructed by augmenting GSM8K and MATH problems with rejection-sampled solutions from a large model, produced training data where every example is verifiably correct, and models fine-tuned on it showed substantially improved mathematical reasoning compared to models trained on unfiltered outputs. The improvement was particularly pronounced on harder problems, where incorrect reasoning is most likely to pass superficial quality checks.

The limitation is that rejection sampling requires a verifier. For math, you can check numerical answers. For code, you can run tests. For open-ended creative writing or general conversation, there is no oracle to consult. This is why rejection sampling is primarily used for structured domains where ground truth is computable, and why the math and code domains have benefited disproportionately from synthetic data approaches.

A useful property of rejection sampling is that the acceptance rate is a proxy for problem difficulty. Easy problems have high acceptance rates (the model usually gets them right); hard problems have low acceptance rates. You can use this signal to curate datasets at specific difficulty levels, focusing on problems where the model gets it right between 20% and 80% of the time. Problems in this range tend to produce the most informative training examples: the model can solve them sometimes, which means it is operating near its current capability boundary, and learning to solve them reliably produces the most improvement.

Out[3]:
Visualization
Three overlapping histograms showing acceptance rate distributions for easy, medium, and hard problems
Simulated acceptance rate distributions for 200 problems in each of three difficulty tiers (easy, medium, and hard math problems). Easy problems cluster at high acceptance rates (mean around 0.80), while hard problems cluster at low rates (mean around 0.18). The shaded band marks the 20-80% sweet spot where problems are most informative: difficult enough to require effort but solvable enough that correct solutions exist and can be learned from.