CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Build a DPO Training Dataset From Scratch: Synthetic Preference Data With Distilabel and Two Open Models

Build a DPO Training Dataset From Scratch: Synthetic Preference Data With Distilabel and Two Open Models

Chris Harper

3 min read

Jul 14, 2026 · 20:02 UTC

AI
Tutorial
Fine-Tuning
HuggingFace

What you'll be able to do after this:

  • Generate a synthetic preference dataset (chosen/rejected pairs) for DPO, ORPO, or RLHF fine-tuning from raw prompts — no hand-labeling
  • Run two open models against the same instructions via HuggingFace serverless inference and use a third model as the judge, all without provisioning a GPU
  • Push the finished dataset to the HuggingFace Hub ready to drop into trl.DPOTrainer in your next session

TL;DR: Distilabel generates DPO preference pairs by prompting two open models on the same instructions, then scoring with a third judge model — from prompt list to DPO-ready dataset, no hand-labeling required.

Before you can fine-tune with Direct Preference Optimization (DPO), you need rows where every entry says "prefer response A over response B and here's why." The standard path is human annotation — slow and expensive. Distilabel automates the annotation by using one model to judge another.

The pipeline in three steps

Step 1 — Generate two responses per prompt

from distilabel.llms import InferenceEndpointsLLM
from distilabel.steps.tasks import TextGeneration

# Two different models respond to each prompt
llm_a = InferenceEndpointsLLM(model_id="meta-llama/Meta-Llama-3-8B-Instruct")
llm_b = InferenceEndpointsLLM(model_id="mistralai/Mixtral-8x7B-Instruct-v0.1")

gen_a = TextGeneration(llm=llm_a, num_generations=1)
gen_b = TextGeneration(llm=llm_b, num_generations=1)

Llama 3 8B and Mixtral 8x7B both answer every prompt. Their responses are grouped into one row: one prompt, two candidate answers.

Step 2 — Score with a judge model (UltraFeedback)

from distilabel.steps.tasks import UltraFeedback

judge = InferenceEndpointsLLM(model_id="meta-llama/Meta-Llama-3-70B-Instruct")
evaluate = UltraFeedback(llm=judge)

A 70B model scores each response on helpfulness, honesty, and instruction-following. The higher scorer becomes chosen, the lower becomes rejected.

Step 3 — Format and push to Hub

from distilabel.steps import FormatTextGenerationSFT, PushToHub

format_step = FormatTextGenerationSFT()
push = PushToHub(repo_id="your-org/my-preference-dataset")

with Pipeline(name="pref-dataset") as pipeline:
    load >> [gen_a, gen_b] >> combine >> evaluate >> format_step >> push

pipeline.run(dataset=dataset)

Output: a dataset with prompt, chosen, rejected, and per-dimension scores — ready for trl.DPOTrainer.

No GPU required for data generation

All inference goes through HuggingFace's serverless Inference API. For datasets under 10K rows the free tier handles it. For larger runs (100K+), switch to a dedicated endpoint or route through Together AI or Fireworks.

Why this step matters

Data quality is what separates a fine-tune that improves behavior from one that degrades it. The UltraFeedback rubric that Distilabel applies is the same pipeline used to build alignment datasets for Zephyr and Notus. You're not inventing a scoring rubric — you're reusing one validated at research scale, in a 30-line script.

Sources: Generate a Preference Dataset with distilabel — HuggingFace Cookbook · Run in Colab · Distilabel docs — Argilla · Distilabel GitHub