Open-source project
worldbench/DanceOPD avatar
worldbench/DanceOPD

danceopd: one model, several capabilities, and a hard route between them

🔥 DanceOPD: On-Policy Generative Field Distillation

373 stars7 forksPythonApache-2.0

At a glance

What is it?
A research codebase that distils text-to-image generation, local editing, global transformation and CFG-like behaviour into a single flow-matching student by treating each capability as a velocity field and routing every sample to exactly one frozen teacher. The core objective is a velocity MSE with a stop-gradient, and there is no reward model anywhere in it.
Who is it for?
danceopd is worth reading if you are merging capabilities into one image model and keep losing one while gaining another, because its diagnosis of that interference is specific and its fix is small: route hard, query on-policy, regress velocities. Treat it as a research artefact rather than a product.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Each capability becomes a velocity field

The problem statement is about deployment rather than benchmarks. A modern image generation system increasingly needs one deployed model that combines several capabilities: text-to-image generation, local editing, global transformations, style or realism absorption, and operator behaviours such as classifier-free guidance.

The naive approaches are named and both fail the same way. Mixing data creates interference, and mixing weights creates interference. Either way the student improves one capability while losing another, which for a deployed model is the worst outcome because you cannot serve the missing one.

The move DanceOPD makes is to treat each source capability as a velocity field. For every training step it samples one route, rolls out the current student, queries the selected frozen teacher on a low-noise state taken from that student trajectory, and updates the student with a velocity mean squared error.

Two design commitments follow. The teachers are frozen, so nothing is learned in the teachers and no teacher has to be fine-tuned. And the query happens on a state the student actually visited, which is what makes it on-policy rather than distillation from a fixed offline distribution.

Hard routing is what stops two teachers averaging out

Three of the highlights describe the same idea from different angles, and the middle one is the load-bearing claim.

On-policy field query: teachers supervise states visited by the current student, not offline states or teacher-only states. If the teacher is queried on states it would have produced itself, the student is trained to imitate a distribution it will never be asked to serve.

Hard-routed capability matching: each sample is assigned to one semantically valid teacher field, avoiding ambiguous multi-field averages. This is the crux. If a sample were matched to several teachers at once, the target would be a blend, and a blended target teaches the average of two behaviours rather than either one. Hard routing means the teacher for a given sample is decided before the loss is computed, so the target is unambiguous.

Semantic-side query: the default uses one low-noise query state per rollout, written K=1. One query point keeps the target honest and cheap, and the diagnostics say low-noise semantic queries are a reliable choice.

Taken together, the mechanism is closer to mixture-of-experts routing than to blending, applied to fields rather than to networks.

The objective is a squared distance with a stop-gradient

The update is given as an equation and then as pseudocode, and both are short enough to read.

python
route = router.sample()                         # hard route: choose teacher m
sample = dataset.sample(route.dataset)          # then draw (x,c) ~ D_m
trajectory = rollout(student, sample)           # current student trajectory
state = sample_low_noise_state(trajectory)      # default: K = 1

with torch.no_grad():
    target = teacher[route].velocity(state, sample)

pred = student.velocity(state, sample)
loss = mse(pred, target)
loss.backward()

The teacher call sits inside torch.no_grad, which is the implementation of the stop-gradient in the formula: the student state is treated as a constant when the target is computed, so the loss does not push the student toward visiting states the teacher likes.

What is deliberately absent is as important as what is there. No reward model, no adversarial critic, no learned value function. The highlights call this a plain core objective, and the abstract says the recipe works without bundling task-specific training code into the core algorithm.

That last clause is the practical claim. Adding a capability means adding a teacher and a route, not adding a new training objective.

Three training methods, one core trainer

The package exposes three names, and the differences are in how the trajectory is produced rather than in what is regressed.

danceopd is single-query on-policy ODE. diffusionopd is dense on-policy ODE with a KL term. flowopd is on-policy SDE with a KL reward and clipped PPO, which is the one entry where reinforcement learning enters, and it enters the trajectory rather than the target.

The core trainer is shared, which is the point of naming three modes instead of three projects. A capability field, a route and a velocity target stay the same across all three.

Extensibility is claimed at the backbone level. The same core trainer supports SD3.5 and Z-Image and can be extended to other flow backbones. Those two names are also the two optional dependency groups in the packaging metadata, where SD3.5 pulls diffusers, transformers and peft, and Z-Image pulls Pillow, peft, torchvision and modelscope. peft appears in both, which fits a setup where the student is adapted rather than trained from scratch.

So the claim of a compact, extensible recipe is testable in a narrow way: pick a backbone, pick a mode, add teachers.

The first fusion beats its editing teacher and holds the anchor

The results use the same fine-grained metrics as the paper: six GEditBench-EN editing categories and six GenEval text-to-image categories. Two fusions are reported, and the first one fuses a text-to-image source with an edit source.

The T2I source scores 0.832 overall on GenEval, with single at 0.950, two at 0.939, count at 0.938, color at 0.947, position at 0.520 and color-attr at 0.700. The edit source averages 4.930 on GEdit, ranging from 3.923 on style change to 6.033 on subject addition, and scores 0.711 on GenEval.

The student lands at 5.347 on GEdit overall, above the edit source's 4.930, and at 0.849 on GenEval, above both sources including the T2I anchor of 0.832.

The stated takeaway is that DanceOPD raises editing quality above the edit source average while keeping, and slightly improving, the T2I anchor. Read the two sources as specialists with opposite strengths, position being the weakest GenEval category for the T2I model at 0.520 against the student's 0.640, and the claim is that fusion moved both sides rather than trading one for the other.

The second experiment is built to show no collapse toward either source

The second fusion takes a preservation-heavy local edit teacher and a transformation-heavy global edit teacher, and it is designed so that copying either one is a visible failure.

The local source averages 5.095 on GEdit, peaking at 6.017 on subject removal, and the global source averages 3.750, peaking at 5.209 on style change. They are opposite specialists.

The student averages 5.498, above both. On the two columns that define the sources, subject removal is 4.348 and style change is 5.944: it moves toward the global teacher on style, and gives up some of the local teacher's subject-removal peak. On background change, where the local source scores 4.856 and the global source 4.287, the student reaches 6.153, above both.

On GenEval the student scores 0.848 overall with single at 1.000, against 0.793 for the local source and 0.808 for the global source.

The claim attached to this table is that it avoids collapsing toward either source, absorbing global transformations while retaining strong local-edit and T2I behaviour. The subject-removal figure is where a reader should look hardest, since it is the one column where the student sits below the teacher it was meant to preserve.

Six extras and one console script

The packaging is small and explicit. The distribution is named danceopd at version 0.2.0, requires Python 3.10 or newer, and is built with setuptools. Five runtime dependencies carry upper bounds as well as lower ones: accelerate, pyyaml, safetensors, torch and tqdm.

The optional groups are the interesting part. sd35 and zimage are the two backbone groups. data pulls datasets and Pillow for preparing examples. smoke pulls only modelscope, which suggests a cheap path that verifies model acquisition without a training run. dev adds build, pytest and ruff, and all is the union of the backbone and data groups.

There is exactly one console script, danceopd-train, mapped to the training CLI.

Two conventions are stated for contributors: ruff targets py310 with a line length of 120. The top level holds the package, configs/, data/, scripts/, tests/ and assets/, plus a CHANGELOG.md, and the examples directory contains prepare_diffsynth_example.py, prepare_omniedit.py and a prompts.csv, which names the two external dataset preparations the project expects you to run before training.

The install section stops mid-command

The last section of the page is an installation block, and it is cut off in the middle of the first line, at a partial git clone command. Nothing after it is readable, which means the install steps are not available from the page.

What is available is the packaging metadata, which is enough to reconstruct the shape of the install without inventing it: a Python 3.10 environment, the base package, then whichever optional group matches your backbone, sd35 or zimage, and the data group if you are preparing examples. The console script is danceopd-train.

That gap is worth knowing before you plan an afternoon around it. The package itself is on a project page, an arXiv identifier and a GitHub repository, with affiliations from ByteDance Seed, NUS, UMD and HKUST, and the work done at ByteDance Seed, with Wei Liu as the corresponding author.

For the state of the code: the last push is dated 5 September 2026, there are no GitHub releases despite the version being 0.2.0, and the CHANGELOG.md in the tree is the only record of what changed.

Editorial conclusion

danceopd is worth reading if you are merging capabilities into one image model and keep losing one while gaining another, because its diagnosis of that interference is specific and its fix is small: route hard, query on-policy, regress velocities. Treat it as a research artefact rather than a product. The version is 0.2.0 with no GitHub releases, the install section on the page is cut off mid-command, and the reported gains are on two specific benchmark pairs with backbone-specific extras you have to select. Read the ablation notes about low-noise queries and strong relevant initialization before assuming the defaults transfer to your setup.

Frequently asked questions

What is the core idea behind DanceOPD?

Treating each source capability as a velocity field, then routing every training sample to exactly one frozen teacher, querying that teacher on a low-noise state from the student's own rollout, and updating the student with a velocity MSE with a stop-gradient on the state.

Does DanceOPD need a reward model or a critic?

No. The core objective is a direct velocity mean squared error with no reward model and no adversarial critic. The flowopd variant adds a KL reward and clipped PPO to the trajectory side, but the regressed target stays the teacher velocity.

Which backends and training modes does DanceOPD support?

The same core trainer supports SD3.5 and Z-Image and can be extended to other flow backbones, with matching optional dependency groups. Three training modes are provided: danceopd for single-query on-policy ODE, diffusionopd for dense on-policy ODE-KL, and flowopd for on-policy SDE with KL reward and clipped PPO.

How do I install DanceOPD?

The installation section of the page is cut off, so use the packaging metadata: Python 3.10 or newer, the danceopd package at version 0.2.0, then the optional group for your backbone, sd35 or zimage, plus the data group for example preparation. The console script is danceopd-train.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. Project website
  4. README
  5. worldbench/DanceOPD on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/worldbench-danceopd.svg)](https://hysenlabs.com/projects/worldbench-danceopd)