| name | longllada-diffusion-context |
| title | LongLLaDA: Unlocking Long Context Capabilities in Diffusion LLMs |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2506.14429 |
| keywords | ["diffusion-LLMs","long-context","position-interpolation","RoPE","context-extension"] |
| description | Training-free method extending diffusion LLMs to 6x context length using NTK-based RoPE scaling, exploiting bidirectional attention stability. |
LongLLaDA: Unlocking Long Context Capabilities in Diffusion LLMs
Core Concept
LongLLaDA investigates long-context performance in diffusion-based LLMs, discovering unique characteristics unavailable in autoregressive models. Diffusion LLMs maintain remarkably stable perplexity during context extrapolation due to bidirectional attention exposure to symmetric relative position ranges. Combined with NTK-based Rotary Position Embedding (RoPE) scaling, the method achieves 6x context expansion without training. A systematic analysis reveals both strengths (synthetic QA, stable extrapolation) and weaknesses (aggregation tasks).
Architecture Overview
- Bidirectional Attention: Diffusion LLMs exposed to [-T_train, T_train-1] position range vs. autoregressive [0, T_train-1]
- Stable Perplexity: Unlike autoregressive models that diverge quickly, diffusion maintains stable perplexity under extrapolation
- Local Perception: Sliding-window-like behavior during context extrapolation
- NTK-Based RoPE Scaling: Applies scaling transformation to base frequencies during inference
- Position Interpolation: Scale relative positions to fit within training range
Implementation
Step 1: Understand RoPE Scaling Theory
Implement Rotary Position Embedding (RoPE) with dynamic frequency scaling:
import torch
import numpy as np
class RoPEScaler:
"""
Rotary Position Embedding with scaling for context extension.
"""
def __init__(self, dim, base=10000):
self.dim = dim
self.base = base
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
self.register_buffer('inv_freq', inv_freq)
def apply_rope(self, x, position_ids, scaling_factor=1.0):
"""
Apply RoPE with optional scaling.
Args:
x: [batch, seq_len, dim] embeddings
position_ids: [batch, seq_len] position indices
scaling_factor: scale position frequencies (1.0 = no scaling)
"""
seq_len = x.shape[1]
device = x.device
inv_freq_scaled = self.inv_freq / scaling_factor
t = position_ids.float().unsqueeze(-1)
freqs = torch.outer(t.squeeze(0), inv_freq_scaled)
emb = torch.cat([freqs, freqs], dim=-1)
cos = emb.cos()
sin = emb.sin()
x_rot = (x * cos) + (._rotate_half(x) * sin)
x_rot
():
x1, x2 = x[..., :x.shape[-]//], x[..., x.shape[-]//:]
torch.cat([-x2, x1], dim=-)
Step 2: Implement NTK-Based Scaling
Apply the NTK (Neural Tangent Kernel) scaling approach for position extrapolation:
def compute_ntk_scaling_factor(original_seq_len, target_seq_len):
"""
Compute NTK-based scaling factor for RoPE.
The idea: if we trained with max_position original_seq_len,
scale frequencies to extend to target_seq_len.
Args:
original_seq_len: training context window size
target_seq_len: desired inference context window
Returns:
scaling_factor: multiply inverse frequencies by this
"""
scaling_factor = target_seq_len / original_seq_len
return scaling_factor
Step 3: Comparative Analysis: Diffusion vs. Autoregressive
Understand why diffusion LLMs are more stable:
def analyze_position_exposure(model_type='diffusion', seq_len=512):
"""
Analyze position range exposure during training.
Diffusion LLMs: bidirectional attention → [-seq_len, seq_len]
Autoregressive: causal attention → [0, seq_len]
"""
if model_type == 'diffusion':
min_rel_pos = -(seq_len - 1)
max_rel_pos = (seq_len - 1)
pos_range = np.arange(min_rel_pos, max_rel_pos + 1)
print(f"Diffusion LLM position range: [{min_rel_pos}, {max_rel_pos}]")
print(f"Symmetric around 0: {-min_rel_pos == max_rel_pos}")
return pos_range
else:
min_rel_pos = 0
max_rel_pos = (seq_len - 1)
pos_range = np.arange(min_rel_pos, max_rel_pos + 1)
print(f"Autoregressive position range: [{min_rel_pos}, {max_rel_pos}]")
print(f"Asymmetric, only positive positions")
return pos_range
Step 4: Apply NTK Scaling During Inference
Scale positions and frequencies to enable context extension:
class LongLLaDiffer:
"""
Applies LongLLaDA: NTK-based RoPE scaling for diffusion LLMs.
"""
def __init__(self, model, original_max_len=4096):
self.model = model
self.original_max_len = original_max_len
self.rope_scaler = RoPEScaler(model.config.hidden_size)
def extend_context(self, input_ids, max_new_len=24576):
"""
Generate with extended context using NTK scaling.
Args:
input_ids: [batch, seq_len] token IDs (seq_len <= original_max_len)
max_new_len: target context window (can be > original_max_len)
Returns:
output: [batch, seq_len] generated tokens
"""
scaling_factor = compute_ntk_scaling_factor(
self.original_max_len, max_new_len
)
embeddings = self.model.get_input_embeddings()(input_ids)
batch_size, seq_len = input_ids.shape
position_ids = torch.arange(
seq_len, device=input_ids.device
).unsqueeze(0).expand(batch_size, -1)
embeddings = self.rope_scaler.apply_rope(
embeddings,
position_ids,
scaling_factor=scaling_factor
)
outputs = self.model(inputs_embeds=embeddings)
return outputs
def evaluate_perplexity(self, test_texts, max_len=24576):
"""
Evaluate perplexity with extended context.
Compare to baseline (original max_len).
"""
math
perplexities = {: [], : []}
text test_texts:
baseline_loss = ._compute_loss(
text, max_len=.original_max_len
)
baseline_ppl = math.exp(baseline_loss)
perplexities[].append(baseline_ppl)
extended_loss = ._compute_loss(
text, max_len=max_len, use_scaling=
)
extended_ppl = math.exp(extended_loss)
perplexities[].append(extended_ppl)
perplexities
():
tokens = .model.tokenizer.encode(text)[:max_len]
input_ids = torch.tensor([tokens], device=.model.device)
torch.no_grad():
use_scaling:
outputs = .extend_context(input_ids, max_new_len=max_len)
:
outputs = .model(input_ids)
logits = outputs.logits
shift_logits = logits[..., :-, :].contiguous()
shift_labels = input_ids[..., :].contiguous()
loss = torch.nn.functional.cross_entropy(
shift_logits.view(-, shift_logits.shape[-]),
shift_labels.view(-)
)
loss.item()
Step 5: Benchmark on Long-Context Tasks
Evaluate on retrieval, aggregation, and synthetic tasks:
def benchmark_long_context(model, max_len=24576):
"""
Benchmark diffusion LLM on various long-context tasks.
"""
benchmarks = {}
print("Evaluating NIAH...")
niah_scores = evaluate_needle_in_haystack(model, max_len=max_len)
benchmarks['niah'] = niah_scores
print("Evaluating retrieval tasks...")
retrieval_scores = evaluate_retrieval(model, max_len=max_len)
benchmarks['retrieval'] = retrieval_scores
print("Evaluating aggregation...")
aggregation_scores = evaluate_aggregation(model, max_len=max_len)
benchmarks['aggregation'] = aggregation_scores
print("Evaluating synthetic QA...")
qa_scores = evaluate_synthetic_qa(model, max_len=max_len)
benchmarks['qa'] = qa_scores
print("\nBenchmark Results:")
for task, scores in benchmarks.items():
avg_score = np.mean(list(scores.values()))
print(f" {task}: {avg_score:.2%}")
return benchmarks
def evaluate_needle_in_haystack(model, max_len, num_tests=100):
"""
Evaluate ability to find specific fact in long context.
"""
scores = {'found': 0, : }
_ (num_tests):
haystack = * (max_len // )
needle =
haystack = haystack[:max_len//] + needle + haystack[max_len//:]
query =
answer = model.generate_with_context(haystack, query)
answer:
scores[] +=
:
scores[] +=
scores
Practical Guidance
- Scaling Factor: Start with target_len / original_len; can experiment with polynomial scaling
- Position Interpolation: For diffusion LLMs, linear scaling works well due to bidirectional training
- Testing Tasks: Always evaluate on NIAH, retrieval, and aggregation to understand model behavior
- Stability Validation: Monitor perplexity across different context lengths; should remain stable
- Integration: Modify RoPE scaling at inference only; no model retraining required
- Comparison: Benchmark against Position Interpolation and other extrapolation methods
Reference
Paper: arXiv:2506.14429
Key metrics: 6x context expansion (24k tokens), stable perplexity, strong NIAH performance
Architecture differences: Bidirectional attention enables symmetric position exposure
Related work: Position interpolation, RoPE scaling, context extension methods