Generating synthetic examples is easy; generating synthetic examples that are worth training on is harder. A language model can produce thousands of candidate answers, but blindly adding them to a training set can reinforce factual errors, weak reasoning, unwanted style, or artifacts of the generator itself.
Rejection sampling provides a simple mental model for controlling that pipeline: generate one or more candidates, evaluate each candidate with an acceptance rule, and keep only candidates that pass. The acceptance rule might use deterministic checks, a learned reward model, another language model, human review, or a combination of signals.
The important point is that rejection sampling does not merely remove bad rows. It changes the distribution of the data. Whatever the acceptance rule rewards becomes more common in the retained set, and whatever it misses can survive at scale.
This article explains the smallest useful rejection-sampling pipeline, how acceptance changes training data, how to choose thresholds, and what to measure before treating a higher acceptance score as better data.
Start with generate, score, and filter
Suppose you want synthetic training examples for a coding assistant that explains short functions. For one input, a generator produces four candidate explanations:
A: Correct, concise, and mentions the important edge case.
B: Correct but unnecessarily long.
C: Fluent but describes the return value incorrectly.
D: Correct but contains formatting that violates the dataset schema.Assume an evaluator assigns a quality score from 0 to 1, and a deterministic validator checks the schema:
candidate quality score schema valid
A 0.91 yes
B 0.74 yes
C 0.83 yes
D 0.95 noA simple rule might be:
accept if quality_score >= 0.85 and schema_validOnly candidate A is retained. Candidate C demonstrates why fluent text is not enough: a quality evaluator that misses the factual error could admit it. Candidate D demonstrates why a high model-based score should not override a hard requirement that can be checked directly.
This is the core pipeline:
input
|
v
generator -> candidates -> evaluator + validators -> accepted examples
|
v
training setThe mechanism is simple. Designing the acceptance rule is the difficult part.
Rejection sampling changes the data distribution
Let the generator produce samples from a distribution q(y | x), where x is an input and y is a candidate output. Let a(x, y) be the probability that the pipeline accepts that candidate.
Among accepted samples, the output distribution is proportional to:
q(y | x) * a(x, y)After normalization:
q_accept(y | x) = q(y | x) * a(x, y) / Z(x)where Z(x) is the total acceptance probability for input x.
You do not need this equation to implement a filter, but it exposes an important consequence. The accepted dataset is not a neutral subset of the generated dataset. The acceptance mechanism actively reweights it.
If concise answers receive higher scores, the retained set becomes more concise. If an evaluator favors a particular phrasing, that phrasing becomes more common. If hard examples receive lower scores because the evaluator is uncertain, the filter may quietly remove the examples from which the model most needs to learn.
Filtering therefore has two questions, not one:
- Are rejected candidates generally worse?
- Does the retained distribution still represent the behavior you want the trained model to learn?
A pipeline can succeed at the first question and fail at the second.
Deterministic checks should handle deterministic requirements
Some quality requirements do not need an AI evaluator. If an answer must be valid JSON, contain a required field, stay below a length limit, or avoid duplicate identifiers, ordinary program logic can check those conditions exactly.
A practical pipeline often separates hard constraints from subjective judgments:
candidate
|
+--> schema valid? -------- no --> reject
|
+--> required fields? ----- no --> reject
|
+--> duplicate? ----------- yes -> reject
|
+--> quality evaluator
|
+--> score below threshold -> reject
+--> score above threshold -> acceptThis separation improves debuggability. When an example is rejected, you can tell whether it violated a contract or merely received a low model-based score.
It also avoids spending evaluator inference on candidates that already fail cheap checks. When generation and evaluation both use large models, filtering cost can be substantial, so inexpensive validation should usually happen first when it is applicable.
A score is useful only if it predicts the quality you care about
Suppose an evaluator gives each candidate a score s. Choosing s >= 0.85 because 0.85 sounds strict is not a defensible threshold. The number has meaning only through its relationship with downstream quality.
Build a small reviewed set containing candidates across the score range. For each candidate, record the evaluator score and a trusted assessment of whether it should be accepted. Then examine quantities such as:
precision among accepted candidates
acceptance rate
error types among false accepts
input groups disproportionately rejectedFor example:
threshold acceptance rate reviewed precision
0.70 68% 89%
0.80 49% 95%
0.90 24% 98%These numbers are illustrative, not expected values for real systems. They show the trade-off: raising the threshold can improve the fraction of accepted examples that pass review while sharply reducing how much data remains.
That trade-off matters because synthetic data pipelines have a budget. If only 24% of candidates survive, obtaining 100,000 accepted examples requires generating roughly 417,000 candidates on average when the acceptance rate is stable at 24%. Evaluation adds its own cost.
Do not optimize acceptance precision in isolation. A tiny, repetitive set of pristine examples may be less useful than a larger, diverse set with slightly noisier labels. The correct balance depends on the training objective and the cost of errors.
Generate multiple candidates when selection has value
Rejection sampling becomes especially useful when a generator can produce several plausible outputs for the same input.
Suppose an input has four candidates with scores:
0.61, 0.78, 0.90, 0.84With a threshold of 0.85, only the 0.90 candidate survives. Generating alternatives gave the filter an opportunity to find a strong response even though the first candidate would have failed.
However, increasing the number of candidates is not free. If you generate k candidates for every input, generation cost grows roughly with the total generated tokens, and evaluator cost grows with the number of candidates evaluated. Latency can also increase unless work is parallelized.
More candidates help only when the generator has useful diversity and the evaluator can distinguish better candidates. If all candidates repeat the same error, sampling more versions merely multiplies cost.
Do not confuse rejection sampling with best-of-N selection
Two related pipelines are easy to mix up.
Threshold rejection keeps every candidate that satisfies an acceptance condition:
keep candidate if score >= thresholdBest-of-N selection generates N candidates and chooses the highest-scoring one, even if its absolute score is poor:
keep argmax(score(candidate_1 ... candidate_N))These procedures answer different questions. Best-of-N asks, “Which candidate is strongest in this group?” Threshold rejection asks, “Is this candidate good enough to enter the dataset?”
They can be combined. For example, choose the highest-scoring candidate and retain it only if its score also exceeds a threshold. That prevents a weak group from contributing an example merely because one candidate was least bad.
Evaluator errors become data errors
A rejection-sampling pipeline inherits the evaluator’s blind spots. This is the most important failure mode to understand.
Imagine an evaluator that strongly rewards clear explanations but is weak at checking numerical calculations. Generated answers with polished prose and subtle arithmetic errors may receive high scores. Filtering then enriches the dataset for exactly that failure pattern: confident, readable, incorrect calculations.
Common evaluator problems include:
- preferring surface style over factual correctness;
- being sensitive to response length or formatting;
- failing on specialized domains;
- sharing systematic errors with the generator;
- assigning unstable scores to near-equivalent answers;
- rewarding artifacts that correlate with the evaluation prompt rather than real quality.
Using the same model family for generation and evaluation can be convenient, but it does not guarantee independent judgment. Correlated errors are particularly dangerous because the evaluator may approve mistakes produced by assumptions it shares with the generator.
For requirements that matter, test the evaluator against examples specifically designed to expose likely blind spots. A generic average agreement score can hide a severe failure on one important category.
Watch for diversity collapse
Filtering can reduce diversity even when individual accepted examples look excellent.
Suppose an evaluator consistently gives direct, formal answers a score about 0.1 higher than equally correct conversational answers. With a strict threshold, the conversational style may nearly disappear from the accepted set. Fine-tuning on that set can then narrow the model’s behavior.
Measure more than average score. Depending on the task, inspect the accepted set across dimensions such as:
input topic
difficulty
response length
language
answer style
source or generator version
important user groupsCompare these distributions before and after filtering. Large changes are not automatically wrong, because filtering is supposed to change the data. They are signals that should be intentional.
A useful practice is to set minimum coverage requirements for important slices rather than allowing one global threshold to determine the entire dataset. If a slice has low acceptance, investigate why before simply lowering its threshold.
Keep evaluation separate from the filtering rule
Once a dataset is selected by a score, evaluating it with the same score creates a circular result. A filter that keeps examples above 0.9 will obviously produce a retained set with a high average score from that evaluator.
That does not prove that the resulting training data improves the model.
Use an evaluation path that is not identical to the selection rule. Depending on the application, that can include:
- human review of a stratified sample;
- deterministic task-specific correctness checks;
- a separately designed evaluator;
- held-out downstream tasks;
- comparison with an unfiltered or differently filtered baseline.
The strongest test is downstream: train comparable models or adapters on candidate datasets and measure the behavior you actually care about on held-out examples. Filtering is an intermediate technique, not the final objective.
Track provenance so bad filters are reversible
Synthetic pipelines evolve. Generator versions change, evaluator prompts change, reward models change, and thresholds move. If accepted examples lose their provenance, diagnosing a regression becomes difficult.
Store enough metadata to reconstruct why an example entered the dataset. A practical record might include:
input_id
generator_id
generation_parameters
candidate_id
evaluator_id
evaluator_version
raw_score
validator_results
acceptance_rule_version
acceptedThe exact schema depends on the system. The principle is stable: keep raw candidates and selection metadata separate from the final training view when storage and privacy constraints permit.
That makes it possible to re-filter existing generations after discovering an evaluator bug, compare two acceptance policies without regenerating everything, and trace suspicious training examples back to their source.
When rejection sampling is a good fit
Rejection sampling works well when you can generate more candidates than you need and have an acceptance signal that is meaningfully correlated with desired quality. It is particularly attractive when hard validation can remove obvious failures and a more expensive evaluator can focus on the remaining ambiguous cases.
It is less attractive when candidate generation is extremely expensive, acceptable examples are very rare, or the evaluator is not trustworthy enough to separate useful examples from harmful ones. In those cases, improving the generator, collecting targeted human data, or redesigning the task may be more efficient than generating a large pool and discarding most of it.
It is also not a substitute for correcting systematic generator failures. If every candidate for a class of inputs is wrong, a filter can reject those candidates, but it cannot create the missing knowledge by itself.
A practical workflow
For a new synthetic-data pipeline, start small. Generate a representative candidate pool and preserve the raw outputs. Apply deterministic validators first, then score the remaining candidates. Human-review samples from the full score range rather than only the examples near the threshold.
Choose an initial threshold from observed quality-versus-coverage trade-offs. Inspect which topics and styles disappear after filtering. Train a small experiment if possible, and evaluate it on held-out tasks using signals independent of the acceptance rule.
Only then scale generation. At larger volume, monitor acceptance rate by slice and by generator or evaluator version. A sudden acceptance-rate change can indicate a distribution shift, an evaluator change, or a generator regression even before downstream training finishes.
Conclusion
Rejection sampling for synthetic AI data is easy to implement but easy to misunderstand. Its value comes from converting excess generated candidates into a more selective training set. Its risk comes from the same mechanism: the acceptance rule reshapes the data, including in ways you may not intend.
Treat the evaluator and threshold as part of the data-generation system, not as a cleanup step. Validate hard requirements deterministically, measure evaluator quality against trusted judgments, monitor coverage and diversity, preserve provenance, and evaluate the trained model independently of the filter. The goal is not to maximize the score of retained examples. It is to produce training data that causes better behavior on the real task.