Energy-based guidance fields for steering masked diffusion language models
Project description
m2m-energy-fields
Steer masked diffusion language models toward a topic without mentioning it in the prompt.
Energy fields injected at each denoising step steer generation toward a concept using dual-space embedding fusion — the topic never appears in the input.
Overview
m2m-energy-fields is a technique for implicit topic control in masked diffusion language models (MDLM). Given a model like LLaDA-8B or Qwen3-mdlm generating text via iterative unmasking, it modifies the logits at each denoising step to favor tokens semantically aligned with a target direction.
The key property: the target concept never appears in the prompt. Given "Write a short story about something interesting" and an energy field pointing toward "ocean," the model writes about fish, waves, and coral reefs — guided entirely by the energy field operating beneath the prompt.
This is impossible with autoregressive models, where guidance can only be applied once at the prompt level. In masked diffusion, bidirectional attention allows energy to be re-applied at every denoising step, creating a cascade effect where early committed tokens influence all subsequent generation.
How It Works
┌─────────────────────────┐
│ Target: "ocean fish" │
└────────────┬────────────┘
│
┌────────────▼────────────┐
│ Dual Embedding Fusion │
│ 0.5·MiniLM(384D) + │
│ 0.5·Model(4096D) │
└────────────┬────────────┘
│
▼
┌──────────────────────────────────┐
│ token_scores [vocab_size] │
│ semantic alignment per token │
└──────────────┬───────────────────┘
│
┌────────────────────────▼───────────────────────────────┐
│ At each denoising step: │
│ │
│ logits[masked] += alpha(step) · token_scores │
│ │
│ alpha(step) = alpha_start · (1 - step/total)^gamma │
│ (annealed: strong early, zero late) │
│ │
│ + anti-repetition penalty on committed tokens │
└─────────────────────────────────────────────────────────┘
Three mechanisms working together
-
Convex fusion — combines MiniLM sentence embeddings (384D, captures semantic neighborhoods like "scuba" ≈ "ocean") with the diffusion model's own token embeddings (4096D, captures co-occurrence patterns like "turtle" appears near ocean contexts). The spaces are orthogonal (Spearman ρ = 0.14), so averaging reduces noise from both.
-
Energy annealing — guidance strength decays from
alpha_startto 0 across denoising steps. Strong early steering sets the topic; the model refines grammar and coherence freely in later steps. -
Anti-repetition penalty — a token can appear
allowancetimes freely, then each additional occurrence subtractspenaltyfrom its logit. Breaks the positive feedback loop where energy forces the same token at every position. Adapted from frequency penalties in autoregressive decoding.
Quick Start
from m2m_energy_fields import EnergyGuidedSampler, GuidanceConfig
from dllm.utils import get_model, get_tokenizer
import torch
# Load any MDLM-compatible model
model = get_model(model_args=type("Args", (), {
"model_name_or_path": "GSAI-ML/LLaDA-8B-Instruct",
"dtype": torch.bfloat16,
"device_map": {"": 0},
})()).eval()
tokenizer = get_tokenizer(model_args=type("Args", (), {
"model_name_or_path": "GSAI-ML/LLaDA-8B-Instruct",
})())
# Create guided sampler (builds MiniLM token table on first call)
sampler = EnergyGuidedSampler(model=model, tokenizer=tokenizer)
# Set energy field (topic steering — not in prompt)
sampler.set_guidance(
target_texts=["ocean coral reef fish deep sea"],
alpha=10.0,
)
# Generate
config = GuidanceConfig(
alpha=10.0,
temperature=0.6,
rep_penalty=5.0,
rep_allowance=1,
fusion_weight=0.5, # convex 50/50
)
outputs = sampler.sample(inputs, config, return_dict=True)
Installation
# Requires dllm framework (masked diffusion sampling)
git clone https://github.com/ZHZisZZ/dllm.git
cd dllm && pip install -e .
# Install m2m-energy-fields
cd ..
git clone https://github.com/schwabauerbriantomas-gif/m2m-energy-fields.git
cd m2m-energy-fields
pip install -e .
Dependencies: PyTorch ≥2.0, transformers ≥4.46, sentence-transformers, dllm
Performance
All results from a single run of tests/phase3_v13_fusion.py with
LLaDA-8B-Instruct (BF16, 64 steps, seed=42). Hardware: RTX 3090 24GB.
Prompt: "Write a short story about something interesting." — the topic word never appears in the prompt; it only exists in the energy field.
Fusion strategy comparison
| Strategy | ocean | horror | space | cooking | mean | std |
|---|---|---|---|---|---|---|
| model_only (4096D) | 0.120 | 0.126 | 0.151 | 0.085 | 0.120 | 0.023 |
| minilm_only (384D) | 0.189 | 0.096 | 0.127 | 0.240 | 0.163 | 0.055 |
| convex 50/50 | 0.173 | 0.120 | 0.138 | 0.293 | 0.181 | 0.067 |
| convex 30/70 | 0.189 | 0.118 | 0.207 | 0.040 | 0.138 | 0.066 |
| convex 70/30 | 0.244 | 0.114 | 0.149 | 0.121 | 0.157 | 0.052 |
| rrf k=60 | 0.192 | 0.138 | 0.161 | 0.140 | 0.158 | 0.022 |
| geometric mean | 0.267 | 0.101 | 0.020 | 0.011 | 0.100 | 0.103 |
| harmonic mean | 0.173 | 0.152 | 0.019 | 0.011 | 0.089 | 0.074 |
| bayesian product | -0.003 | 0.016 | -0.027 | -0.022 | -0.009 | 0.017 |
quality = target_sim × coherence × diversity
Convex 50/50 wins with 0.181 mean quality — +11% over the best single embedding source (minilm_only: 0.163). The two spaces are orthogonal (Spearman ρ = 0.14): model embeddings capture co-occurrence ("turtle" near ocean contexts), MiniLM captures semantics ("scuba" ≈ "ocean"). Each compensates for the other's blind spots.
Per-topic detail (convex 50/50)
| Topic | target_sim | coherence | diversity | quality |
|---|---|---|---|---|
| ocean | 0.486 | 0.477 | 0.746 | 0.173 |
| horror | 0.466 | 0.336 | 0.768 | 0.120 |
| space | 0.301 | 0.611 | 0.750 | 0.138 |
| cooking | 0.546 | 0.734 | 0.732 | 0.293 |
Example output
Prompt: "Write a short story about something interesting."
Energy target: "cooking recipe chef kitchen delicious food"
"Once upon a time, there was a magical kitchen that could cook the most delicious food imaginable. The kitchen was run by a chef named Chef Chef. The chef had great passion for cooking and loved experimenting with new recipes and ingredients. One day, he set out to cook the most delicious food in the..."
No mention of cooking in the prompt — the energy field steered topic selection through 64 denoising steps of bidirectional attention.
Computational overhead
Per-step timing (RTX 3090, LLaDA-8B BF16, 64 steps, 128-token canvas):
| Component | Per-step cost | % of step |
|---|---|---|
| Model forward pass (8B BF16) | 85.0 ms | 82.4% |
| Softmax + argmax + topk | 16.2 ms | 15.7% |
| Energy guidance (fusion + annealing) | 1.4 ms | 1.4% |
| Anti-rep penalty | 0.7 ms | 0.7% |
The EBM adds 2.1 ms per step — 2% overhead. The bottleneck is entirely the diffusion model's forward pass, not the energy computation.
Throughput
All measurements are from a single RTX 3090 with LLaDA-8B-Instruct (BF16, 64 denoising steps, 128-token canvas):
- Total generation time: 6.6 s per sample (102.9 ms × 64 steps)
- EBM overhead per step: 2.1 ms (1.4 ms energy + 0.7 ms anti-rep penalty)
- EBM as fraction of step: 2.0%
The bottleneck is the model forward pass at 85 ms/step (82% of wall time). Scaling to faster hardware or sparser models (MoE, FP8) would reduce the forward pass proportionally — the EBM's 2.1 ms is fixed-cost tensor ops (scatter_add, cosine similarity) that do not scale with model size.
No measurements were taken on hardware other than the RTX 3090.
Hardware Limitations
This project was developed on:
- GPU: NVIDIA RTX 3090 (24 GB VRAM)
- System RAM: 8 GB (severely constrained — swap-backed)
- Precision: BF16
What this means
- DiffusionGemma 26B A4B (the ideal target model) cannot load with 8 GB system RAM. Nine loading attempts failed across INT4 quantization variants (BnB, Quanto, AWQ, GGUF). The methodology transfers directly when hardware allows.
- LLaDA-8B-Instruct loads successfully (16 GB VRAM) and was used for all experiments.
- Qwen3-0.6B-mdlm loads in 1.2 GB VRAM — used for initial prototyping.
- All timing benchmarks reflect RTX 3090 + BF16. No measurements were taken on other GPUs or precisions.
The technique itself is hardware-independent. The energy computation, fusion, and anti-rep penalty operate on logits and token counts — they are model-agnostic and do not depend on the underlying architecture being autoregressive or diffusion-based. The only requirement is access to the logits at each denoising step.
Iteration History
This project went through 13 experimental versions. Each tested a specific hypothesis and either advanced or was discarded:
| Version | Approach | Quality | Outcome |
|---|---|---|---|
| v1–v4 | Token energy, alpha sweep | 0.045 | Model embeddings act as keyword matchers |
| v5 | Probability scaling | 0.075 | Softmax scaling helps marginally |
| v6–v8 | DSpark-inspired re-masking | 0.060 | Re-masking deadlocks in masked diffusion |
| v9 | Anti-rep penalty + annealing | 0.120 | Prevention beats repair |
| v10 | Energy-model agreement veto | 0.035 | Softmax uncalibrated in partial context |
| v11–v12 | MiniLM vs model embeddings | 0.118 | Semantic space ≠ co-occurrence (ρ=0.14) |
| v13 | Convex dual-space fusion | 0.181 | Orthogonal spaces combine beneficially |
Quality = target_sim × coherence × diversity. Values from v9/v13 experiment runs.
DSpark confidence head — tested, does not transfer
DSpark (DeepSeek's speculative decoding framework) uses a trained confidence head to decide which draft tokens to accept. We tested adapting this pattern (v10: energy-model agreement veto) but it fails because DSpark operates in autoregressive decoding where the model's softmax is well-calibrated (causal context → peaked distribution). In masked diffusion, the canvas is partially masked → the model's softmax is flat (P[any_token] ≈ 0.001) → the confidence proxy vetoes 43% of all tokens, effectively disabling energy guidance.
The anti-repetition penalty (v9) solves the same problem (preventing bad tokens from entering the canvas) but using observable post-hoc signal (token frequency counts) rather than predicting model agreement from uncalibrated probabilities.
When to Use This
Use it when:
- You need implicit topic control (target must not appear in prompt)
- You need multi-axis composition (
0.7·ocean + 0.3·science) - You need dynamic steering mid-generation (change direction between steps)
- You want zero-overhead guidance (2% of forward pass)
- Your base model is a masked diffusion LM
Don't use it when:
- You can simply put the topic in the prompt (prompt engineering is more precise)
- You need factual accuracy (use RAG)
- You need consistent results across all topics (variance is high)
- Your model is autoregressive (energy still works but loses the cascade benefit)
Project Structure
m2m-energy-fields/
├── src/m2m_energy_fields/
│ ├── __init__.py # Public API
│ ├── core.py # EnergyGuidedSampler, GuidanceConfig, fusion functions
│ └── metrics.py # Evaluation: coherence, diversity, target_sim
├── examples/
│ ├── guided_story_llada8b.py # Full demo with LLaDA-8B
│ ├── guided_story_qwen3.py # Lightweight demo (0.6B)
│ └── topic_steering_sweep.py # Reproducible experiment
├── tests/
│ ├── test_core.py # Unit tests
│ ├── phase3_v9_antirep.py # v9: anti-rep penalty (production config)
│ ├── phase3_v10_veto.py # v10: DSpark veto (failed)
│ ├── phase3_v12_head2head.py # v12: model vs MiniLM comparison
│ └── phase3_v13_fusion.py # v13: convex fusion (final config)
├── results/ # JSONL results from experiments
├── pyproject.toml
└── README.md
Ecosystem
Part of the m2m research ecosystem:
| Project | Description |
|---|---|
| EBM-splats | Energy-based splats on S³⁸³, rectified flow velocity, semantic composition |
| m2m-energy-fields | Energy guidance for diffusion LMs (this repo) |
| SplatsDB | Vector memory with spatial organization (wings, rooms, halls) |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file m2m_energy_fields-0.1.0.tar.gz.
File metadata
- Download URL: m2m_energy_fields-0.1.0.tar.gz
- Upload date:
- Size: 26.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da55c798e06ce0f19448083e8ff1aeeebf3a41dcc1b7de937e11692f2fba4961
|
|
| MD5 |
af285334e6becbb24f2426a657e9f20c
|
|
| BLAKE2b-256 |
d59c44c52fc949f70be2e49a62275922bce8a0fe198e396356d7048c323c23c4
|
Provenance
The following attestation bundles were made for m2m_energy_fields-0.1.0.tar.gz:
Publisher:
m2m-energy-fields.yaml on schwabauerbriantomas-gif/m2m-energy-fields
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
m2m_energy_fields-0.1.0.tar.gz -
Subject digest:
da55c798e06ce0f19448083e8ff1aeeebf3a41dcc1b7de937e11692f2fba4961 - Sigstore transparency entry: 2065167361
- Sigstore integration time:
-
Permalink:
schwabauerbriantomas-gif/m2m-energy-fields@118e91a2a31082edebbc83baeeed51057091ae82 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/schwabauerbriantomas-gif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
m2m-energy-fields.yaml@118e91a2a31082edebbc83baeeed51057091ae82 -
Trigger Event:
release
-
Statement type:
File details
Details for the file m2m_energy_fields-0.1.0-py3-none-any.whl.
File metadata
- Download URL: m2m_energy_fields-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
178d89fdb2fca43feaaa3a7d812e391ffd74fa27c8e2c7830c71e9fab9cf6bc8
|
|
| MD5 |
9dcb59b94bb82b362f44715c0e57a36a
|
|
| BLAKE2b-256 |
27764c1b1f3b69aa112a1d3a242c071b72a8ac63cc2556ad8e7ebe669b633abb
|
Provenance
The following attestation bundles were made for m2m_energy_fields-0.1.0-py3-none-any.whl:
Publisher:
m2m-energy-fields.yaml on schwabauerbriantomas-gif/m2m-energy-fields
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
m2m_energy_fields-0.1.0-py3-none-any.whl -
Subject digest:
178d89fdb2fca43feaaa3a7d812e391ffd74fa27c8e2c7830c71e9fab9cf6bc8 - Sigstore transparency entry: 2065167555
- Sigstore integration time:
-
Permalink:
schwabauerbriantomas-gif/m2m-energy-fields@118e91a2a31082edebbc83baeeed51057091ae82 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/schwabauerbriantomas-gif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
m2m-energy-fields.yaml@118e91a2a31082edebbc83baeeed51057091ae82 -
Trigger Event:
release
-
Statement type: