multimodalart HF Staff commited on
Commit
d155c16
·
verified ·
1 Parent(s): 9d3a55a

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -1,13 +1,41 @@
1
  ---
2
- title: Pixelmoe
3
- emoji: 🌍
4
- colorFrom: pink
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 6.31.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: PixelMoE
3
+ emoji: 🧩
4
+ colorFrom: green
5
+ colorTo: red
6
  sdk: gradio
7
  sdk_version: 6.31.0
 
8
  app_file: app.py
9
+ short_description: Class-conditional 512x512 pixel-space MoE diffusion
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 30m
12
  ---
13
 
14
+ # PixelMoE
15
+
16
+ Interactive demo for **PixelMoE: Scaling Pixel-Space Diffusion with LLM-Style
17
+ Mixture-of-Experts** ([arXiv 2610.18857](https://arxiv.org/abs/2610.18857)).
18
+
19
+ Pick any of the 1000 ImageNet-1K classes and the Space samples a 512×512 image
20
+ with the released **PixelMoE-B-E32A4/16** base model from
21
+ [KlingTeam/PixelMoE](https://huggingface.co/KlingTeam/PixelMoE), using the
22
+ authors' official sampling settings (Heun solver, 25 steps per hierarchical
23
+ stage, CFG 3.7 applied while t < 0.8, second-EMA weights).
24
+
25
+ PixelMoE runs diffusion entirely in pixel space: every image token activates
26
+ 4 of 32 routed experts plus a shared expert in each MoE block, and images are
27
+ generated by a two-stage hierarchical flow — coarse structure first, then
28
+ details at the target resolution.
29
+
30
+ ## Advanced settings
31
+
32
+ - **Steps per stage** — denoising steps per hierarchical stage (25 in the
33
+ official `scripts/sample.sh`; each Heun step is two model evaluations).
34
+ - **CFG scale / CFG interval max** — classifier-free guidance and the global
35
+ timestep below which it is active.
36
+ - **Sigma min** — lower bound clamped into the velocity denominator.
37
+
38
+ The model code is vendored from the MIT-licensed official repository
39
+ [KlingAIResearch/PixelMoE](https://github.com/KlingAIResearch/PixelMoE)
40
+ (`torch.compile` decorators and the deterministic-scatter toggle are adapted
41
+ for the ZeroGPU runtime, otherwise inference is unchanged).
app.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
4
+
5
+ import json
6
+ import random
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+
11
+ import spaces # MUST come before torch / any CUDA-touching import
12
+ import torch
13
+
14
+ sys.path.insert(0, str(Path(__file__).parent / "pixelmoe_src"))
15
+
16
+ from denoiser_inference import Denoiser
17
+
18
+ import gradio as gr
19
+ from huggingface_hub import hf_hub_download
20
+
21
+ CHECKPOINT_REPO = "KlingTeam/PixelMoE"
22
+ CHECKPOINT_FILE = "pixelmoe_b_e32a4_512.pth"
23
+ MODEL_NAME = "PixelMoE-B-E32A4/16"
24
+ IMG_SIZE = 512
25
+ NUM_CLASSES = 1000
26
+ # Generation defaults follow configs/pixelmoe_b_e32a4_512.sh + scripts/sample.sh
27
+ DEFAULT_SIGMA_MIN = 0.03
28
+ DEFAULT_CFG = 3.7
29
+ DEFAULT_INTERVAL_MAX = 0.8
30
+ DEFAULT_STEPS = 25 # per stage; scripts/sample.sh uses --num_sampling_steps 25 25
31
+
32
+ _imagenet_classes = json.loads(
33
+ (Path(__file__).parent / "pixelmoe_src" / "imagenet_classes.json").read_text()
34
+ )
35
+
36
+
37
+ def _build_model() -> Denoiser:
38
+ model = Denoiser(
39
+ model=MODEL_NAME,
40
+ img_size=IMG_SIZE,
41
+ num_classes=NUM_CLASSES,
42
+ label_drop_prob=0.1,
43
+ sigma_min=DEFAULT_SIGMA_MIN,
44
+ noise_scale=2.0,
45
+ hierarchical_stages=(0, 1),
46
+ hierarchical_end_timesteps=(0.75, 0.0),
47
+ hierarchical_logit_mean=0.0,
48
+ hierarchical_logit_std=1.0,
49
+ hierarchical_noise_rho=-1.0 / 3.0,
50
+ hierarchical_min_stage_sigma=0.0,
51
+ sampling_method="heun",
52
+ num_sampling_steps=(DEFAULT_STEPS, DEFAULT_STEPS),
53
+ cfg=DEFAULT_CFG,
54
+ cfg_interval=(0.0, DEFAULT_INTERVAL_MAX),
55
+ )
56
+ ckpt_path = hf_hub_download(CHECKPOINT_REPO, CHECKPOINT_FILE)
57
+ checkpoint = torch.load(ckpt_path, map_location="cpu", weights_only=False)
58
+ # sample.sh evaluates with the second EMA copy of the weights.
59
+ state_dict = checkpoint["model_ema2"]
60
+ missing, unexpected = model.load_state_dict(state_dict, strict=False)
61
+ if missing:
62
+ raise RuntimeError(f"Missing keys when loading checkpoint: {missing[:5]}...")
63
+ if unexpected:
64
+ raise RuntimeError(f"Unexpected keys when loading checkpoint: {unexpected[:5]}...")
65
+ del checkpoint
66
+ model.eval()
67
+ return model
68
+
69
+
70
+ denoiser = _build_model().to("cuda")
71
+
72
+
73
+ def _estimate_duration(class_name, num_steps, *args, **kwargs):
74
+ # Heun sampling: ~2 * steps NFE per stage on the two hierarchical stages.
75
+ return min(300, 30 + int(num_steps) * 2 * 2 * 2)
76
+
77
+
78
+ @spaces.GPU(duration=_estimate_duration)
79
+ def generate(
80
+ class_name: str,
81
+ num_steps: int = DEFAULT_STEPS,
82
+ cfg: float = DEFAULT_CFG,
83
+ interval_max: float = DEFAULT_INTERVAL_MAX,
84
+ sigma_min: float = DEFAULT_SIGMA_MIN,
85
+ seed: int = 0,
86
+ randomize_seed: bool = True,
87
+ progress=gr.Progress(track_tqdm=True),
88
+ ):
89
+ """Generate an ImageNet-1K class-conditional image with PixelMoE (512x512).
90
+
91
+ Args:
92
+ class_name: One of the 1000 ImageNet-1K class names.
93
+ num_steps: Denoising steps per hierarchical stage (25 in the paper's
94
+ sampling script; each Heun step costs two model evaluations).
95
+ cfg: Classifier-free guidance scale.
96
+ interval_max: Only apply CFG while the global timestep is below this
97
+ value (0.8 in the official config).
98
+ sigma_min: Lower bound clamped into the velocity denominator.
99
+ seed: RNG seed for reproducible sampling.
100
+ randomize_seed: Draw a fresh seed each run instead of the given one.
101
+ """
102
+ t0 = time.perf_counter()
103
+ try:
104
+ class_idx = _imagenet_classes.index(class_name)
105
+ except ValueError:
106
+ raise gr.Error(f"Unknown class name: {class_name!r}")
107
+
108
+ if randomize_seed:
109
+ seed = random.randint(0, 2**31 - 1)
110
+ seed = int(seed)
111
+
112
+ denoiser.cfg_scale = float(cfg)
113
+ denoiser.cfg_interval = (0.0, float(interval_max))
114
+ denoiser.sigma_min = float(sigma_min)
115
+ denoiser.sampling_steps = [int(num_steps), int(num_steps)]
116
+
117
+ generator_labels = torch.tensor([class_idx], dtype=torch.long, device="cuda")
118
+ g_state = torch.get_rng_state()
119
+ try:
120
+ torch.manual_seed(seed)
121
+ with torch.amp.autocast("cuda", dtype=torch.bfloat16):
122
+ sampled = denoiser.generate(generator_labels)
123
+ finally:
124
+ torch.set_rng_state(g_state)
125
+
126
+ sampled = (
127
+ ((sampled + 1.0) / 2.0)
128
+ .detach()
129
+ .float()
130
+ .mul_(255)
131
+ .round_()
132
+ .clamp_(0, 255)
133
+ .to(dtype=torch.uint8)
134
+ )
135
+ image = sampled[0].permute(1, 2, 0).cpu().numpy()
136
+ elapsed = time.perf_counter() - t0
137
+ print(f"PixelMoE generated class={class_name!r} in {elapsed:.1f}s", flush=True)
138
+ return image, seed
139
+
140
+
141
+ CSS = """
142
+ #col-container { max-width: 1100px; margin: 0 auto; }
143
+ main, .gradio-container, .fillable { max-width: 1100px !important; margin: 0 auto !important; }
144
+ .dark .gradio-container { color: var(--body-text-color); }
145
+ """
146
+
147
+ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
148
+ with gr.Column(elem_id="col-container"):
149
+ gr.HTML(
150
+ """
151
+ <div style="text-align: center; margin-bottom: 8px;">
152
+ <h1 style="margin-bottom: 4px;">🧩 PixelMoE</h1>
153
+ <p style="margin: 0; font-size: 1.05em; opacity: 0.85;">
154
+ Pixel-space diffusion with LLM-style Mixture-of-Experts — pick an
155
+ ImageNet-1K class and sample a 512×512 image.
156
+ </p>
157
+ <p style="margin: 6px 0 0; font-size: 0.9em; opacity: 0.75;">
158
+ <a href="https://arxiv.org/abs/2610.18857">Paper</a> ·
159
+ <a href="https://github.com/KlingAIResearch/PixelMoE">Code</a> ·
160
+ <a href="https://huggingface.co/KlingTeam/PixelMoE">Weights</a> ·
161
+ Base model, 512×512
162
+ </p>
163
+ </div>
164
+ """
165
+ )
166
+ with gr.Column():
167
+ class_name = gr.Dropdown(
168
+ choices=_imagenet_classes,
169
+ value="golden retriever",
170
+ label="ImageNet-1K class",
171
+ filterable=True,
172
+ )
173
+ with gr.Row():
174
+ run_button = gr.Button("Generate", variant="primary", scale=3)
175
+ seed_used = gr.Number(
176
+ label="Seed used", value=0, precision=0, interactive=True, scale=1
177
+ )
178
+
179
+ output = gr.Image(label="Generated image", height=512, show_download_button=True)
180
+
181
+ with gr.Accordion("Advanced settings", open=False):
182
+ with gr.Row():
183
+ num_steps = gr.Slider(
184
+ minimum=5, maximum=50, value=DEFAULT_STEPS, step=1,
185
+ label="Steps per stage",
186
+ info="The paper's sampling script uses 25 per stage (Heun ≈ 2 model calls each).",
187
+ )
188
+ cfg = gr.Slider(
189
+ minimum=1.0, maximum=6.0, value=DEFAULT_CFG, step=0.1,
190
+ label="CFG scale",
191
+ info="3.7 in the official 512×512 Base config.",
192
+ )
193
+ with gr.Row():
194
+ interval_max = gr.Slider(
195
+ minimum=0.1, maximum=1.0, value=DEFAULT_INTERVAL_MAX, step=0.01,
196
+ label="CFG interval max",
197
+ info="Guidance is applied only while t < this value.",
198
+ )
199
+ sigma_min = gr.Slider(
200
+ minimum=0.0, maximum=0.1, value=DEFAULT_SIGMA_MIN, step=0.005,
201
+ label="Sigma min",
202
+ info="Clamp on the velocity denominator (0.03 for the Base model).",
203
+ )
204
+ with gr.Row():
205
+ seed = gr.Number(label="Seed", value=0, precision=0)
206
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
207
+
208
+ gr.Examples(
209
+ examples=[
210
+ ["golden retriever"],
211
+ ["giant panda"],
212
+ ["yellow lady's slipper"],
213
+ ["tiger shark"],
214
+ ["school bus"],
215
+ ],
216
+ inputs=[class_name],
217
+ outputs=[output, seed_used],
218
+ fn=generate,
219
+ cache_examples=True,
220
+ cache_mode="lazy",
221
+ )
222
+
223
+ gr.Markdown(
224
+ """
225
+ PixelMoE runs diffusion directly in pixel space: each image token routes to 4 of
226
+ 32 experts (plus a shared expert) in every MoE block, and images are generated in
227
+ two hierarchical flow-matching stages — coarse structure first, then detail at the
228
+ target resolution. This Space serves the released **PixelMoE-B-E32A4/16** base
229
+ model with its second-EMA weights and the official 512×512 sampling settings
230
+ (Heun solver, 25 steps per stage, CFG 3.7 with interval max 0.8).
231
+ """
232
+ )
233
+
234
+ inputs = [
235
+ class_name,
236
+ num_steps,
237
+ cfg,
238
+ interval_max,
239
+ sigma_min,
240
+ seed,
241
+ randomize_seed,
242
+ ]
243
+ outputs = [output, seed_used]
244
+ run_button.click(
245
+ generate, inputs=inputs, outputs=outputs, api_name="generate",
246
+ queue=True,
247
+ )
248
+
249
+ if __name__ == "__main__":
250
+ demo.launch(mcp_server=True)
251
+ else:
252
+ demo.launch(mcp_server=True)
pixelmoe_src/deepseek_moe.py ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """DeepSeek-style token-choice MoE using PyTorch grouped matrix multiply.
2
+ """
3
+
4
+ import math
5
+
6
+ import torch
7
+ import torch.distributed as dist
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+
11
+
12
+ def normalize_router_bias_update(imbalance, eps=1e-6):
13
+ """RMS-normalize the expert-load imbalance vector."""
14
+ return imbalance * torch.rsqrt(imbalance.square().mean() + float(eps))
15
+
16
+
17
+ class DeepSeekMoEGate(nn.Module):
18
+ """Select top-k experts using FP32 sigmoid scores and balancing bias."""
19
+
20
+ def __init__(
21
+ self,
22
+ dim: int,
23
+ num_experts: int,
24
+ top_k: int,
25
+ route_scale: float = 1.0,
26
+ router_bias_update_rate: float = 1e-3,
27
+ ):
28
+ super().__init__()
29
+ if num_experts <= 0:
30
+ raise ValueError("moe_num_experts must be positive")
31
+ if top_k <= 0 or top_k > num_experts:
32
+ raise ValueError("moe_top_k must be in [1, moe_num_experts]")
33
+
34
+ self.num_experts = num_experts
35
+ self.top_k = top_k
36
+ self.route_scale = float(route_scale)
37
+ self.router_bias_update_rate = float(router_bias_update_rate)
38
+
39
+ self.weight = nn.Parameter(torch.empty(num_experts, dim))
40
+ # Balancing bias is checkpointed and updated without gradients.
41
+ self.register_buffer(
42
+ "router_bias",
43
+ torch.zeros(num_experts, dtype=torch.float32),
44
+ persistent=True
45
+ )
46
+ # Cache this batch's expert loads until the optimizer update is accepted.
47
+ self.load_counts = None
48
+ self.reset_parameters()
49
+
50
+ def reset_parameters(self):
51
+ nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
52
+ self.router_bias.zero_()
53
+
54
+ def forward(self, hidden_states):
55
+ # Route packed tokens [num_tokens, dim] in FP32.
56
+ with torch.autocast(device_type=hidden_states.device.type, enabled=False):
57
+ logits = F.linear(
58
+ hidden_states.type(torch.float32),
59
+ self.weight.type(torch.float32),
60
+ )
61
+
62
+ scores = logits.sigmoid()
63
+
64
+ # Bias affects expert selection; aggregating weights use the unbiased scores.
65
+ selection_scores = scores + self.router_bias.to(
66
+ device=scores.device, dtype=scores.dtype
67
+ ).detach()
68
+ indices = torch.topk(selection_scores, self.top_k, dim=-1, sorted=False).indices
69
+ weights = scores.gather(1, indices)
70
+ # Normalize each token's selected weights, then apply the routing scale.
71
+ weights = weights / weights.sum(dim=-1, keepdim=True).clamp_min(1e-20)
72
+ weights = weights * self.route_scale
73
+ if self.training:
74
+ self.record_load_counts(indices)
75
+ return weights, indices
76
+
77
+ @torch.no_grad()
78
+ def record_load_counts(self, topk_idx):
79
+ # Each selected token contributes one count to each of its top-k experts.
80
+ counts = topk_idx.new_zeros(
81
+ (topk_idx.shape[0], self.num_experts),
82
+ dtype=torch.float32,
83
+ )
84
+ counts.scatter_(1, topk_idx, 1)
85
+ self.load_counts = counts.sum(dim=0)
86
+
87
+ @torch.no_grad()
88
+ def update_bias(self, update_rate=1e-3):
89
+ # Called after optimizer.step(); skipped batches clear their counts instead.
90
+ if self.load_counts is None:
91
+ return
92
+ counts = self.load_counts
93
+ if dist.is_initialized():
94
+ # Use the same averaged expert loads on every DDP rank.
95
+ dist.all_reduce(counts, op=dist.ReduceOp.SUM)
96
+ counts /= dist.get_world_size()
97
+ # RMS-normalize the deficit: underloaded experts gain bias, overloaded lose it.
98
+ delta = normalize_router_bias_update(counts.mean() - counts)
99
+ self.router_bias.add_(
100
+ delta.to(dtype=self.router_bias.dtype),
101
+ alpha=float(update_rate),
102
+ )
103
+ self.load_counts = None
104
+
105
+ class DeepSeekTokenReorderer(nn.Module):
106
+ """Stably group token-expert assignments by expert index."""
107
+
108
+ def __init__(self, num_experts: int):
109
+ super().__init__()
110
+ self.num_experts = num_experts
111
+
112
+ def forward(self, top_scores, selected_experts_indices, token_ids):
113
+ """Group N * top_k token-expert assignments by expert.
114
+
115
+ Returns:
116
+ top_scores_experts_sorted: [N * top_k] mixing weights in expert
117
+ order, used to weight expert outputs.
118
+ token_indices_experts_sorted: [N * top_k] original token IDs in
119
+ the same order, used to gather inputs and scatter outputs.
120
+ Each token appears top_k times.
121
+ num_tokens_per_expert: [num_experts] route counts defining each
122
+ expert's contiguous input segment.
123
+ """
124
+ flat_experts = selected_experts_indices.reshape(-1)
125
+ if flat_experts.is_cuda:
126
+ num_tokens_per_expert = torch.histc(
127
+ flat_experts,
128
+ bins=self.num_experts,
129
+ min=0,
130
+ max=self.num_experts,
131
+ )
132
+ else:
133
+ # CPU histc does not support the int64 indices returned by topk.
134
+ num_tokens_per_expert = torch.bincount(
135
+ flat_experts,
136
+ minlength=self.num_experts,
137
+ )
138
+ sorted_idx = torch.argsort(flat_experts, stable=True)
139
+ top_scores_experts_sorted = top_scores.reshape(-1).index_select(0, sorted_idx)
140
+ token_indices_experts_sorted = token_ids.reshape(-1).index_select(0, sorted_idx)
141
+ return (
142
+ top_scores_experts_sorted,
143
+ token_indices_experts_sorted,
144
+ num_tokens_per_expert,
145
+ )
146
+
147
+
148
+ def _run_experts_grouped_mm(
149
+ w1: torch.Tensor,
150
+ w2: torch.Tensor,
151
+ w3: torch.Tensor,
152
+ x: torch.Tensor,
153
+ num_tokens_per_expert: torch.Tensor,
154
+ ) -> torch.Tensor:
155
+ """Run packed SwiGLU experts with grouped matrix multiplication."""
156
+ # Input rows are already grouped by expert; each row is one token-expert route.
157
+ if x.dim() != 2:
158
+ raise ValueError(f"grouped expert input must be 2D, got shape {tuple(x.shape)}")
159
+ if num_tokens_per_expert.numel() != w1.shape[0]:
160
+ raise ValueError("num_tokens_per_expert must contain one count per expert")
161
+
162
+ # Skip experts with no assigned tokens, preserving the order of active experts.
163
+ non_zero_mask = num_tokens_per_expert > 0
164
+ non_zero_indices = torch.nonzero(non_zero_mask, as_tuple=True)[0]
165
+ if len(non_zero_indices) == 0:
166
+ return x.new_empty((0, w2.shape[1]))
167
+ if len(non_zero_indices) < len(num_tokens_per_expert):
168
+ counts = num_tokens_per_expert[non_zero_mask]
169
+ active_w1 = w1[non_zero_mask]
170
+ active_w2 = w2[non_zero_mask]
171
+ active_w3 = w3[non_zero_mask]
172
+ else:
173
+ # Every expert has tokens, so use all expert weights directly.
174
+ counts = num_tokens_per_expert
175
+ active_w1 = w1
176
+ active_w2 = w2
177
+ active_w3 = w3
178
+ # Cumulative segment ends.
179
+ offsets = torch.cumsum(counts, dim=0, dtype=torch.int32)
180
+
181
+ # Compute each expert's gate (w1) and value (w3) projections in BF16.
182
+ x_bf16 = x.bfloat16()
183
+ gate = torch._grouped_mm(
184
+ x_bf16,
185
+ active_w1.bfloat16().transpose(-2, -1),
186
+ offs=offsets,
187
+ )
188
+ linear = torch._grouped_mm(
189
+ x_bf16,
190
+ active_w3.bfloat16().transpose(-2, -1),
191
+ offs=offsets,
192
+ )
193
+ # SwiGLU: multiply the SiLU-activated gate by the value projection.
194
+ activated_gate = F.silu(gate)
195
+ hidden = activated_gate * linear
196
+ # Project through w2, keeping the same expert-grouped row order.
197
+ output = torch._grouped_mm(
198
+ hidden,
199
+ active_w2.bfloat16().transpose(-2, -1),
200
+ offs=offsets,
201
+ )
202
+ return output.type_as(x)
203
+
204
+
205
+ class DeepSeekGroupedExperts(nn.Module):
206
+ """Routed experts with matrices stacked along the expert dimension."""
207
+
208
+ def __init__(
209
+ self,
210
+ dim: int,
211
+ hidden_dim: int,
212
+ output_dim: int,
213
+ num_experts: int,
214
+ num_layers: int,
215
+ ):
216
+ super().__init__()
217
+ self.w1 = nn.Parameter(torch.empty(num_experts, hidden_dim, dim))
218
+ self.w2 = nn.Parameter(torch.empty(num_experts, output_dim, hidden_dim))
219
+ self.w3 = nn.Parameter(torch.empty(num_experts, hidden_dim, dim))
220
+ self.num_layers = num_layers
221
+ self.reset_parameters()
222
+
223
+ def forward(self, x, num_tokens_per_expert):
224
+ return _run_experts_grouped_mm(
225
+ self.w1,
226
+ self.w2,
227
+ self.w3,
228
+ x,
229
+ num_tokens_per_expert,
230
+ )
231
+
232
+ def reset_parameters(self):
233
+ # Initialize expert projections with a truncated normal distribution.
234
+ nn.init.trunc_normal_(self.w1, mean=0.0, std=0.02)
235
+ nn.init.trunc_normal_(self.w2, mean=0.0, std=0.02)
236
+ nn.init.trunc_normal_(self.w3, mean=0.0, std=0.02)
237
+ # Scale the residual output projection by the transformer body depth.
238
+ self.w2.data /= (2 * self.num_layers) ** 0.5
239
+
240
+
241
+ class DeepSeekSharedExpert(nn.Module):
242
+ """Dense SwiGLU shared expert used for every token."""
243
+
244
+ def __init__(self, dim: int, hidden_dim: int):
245
+ super().__init__()
246
+ self.w1 = nn.Linear(dim, hidden_dim, bias=False)
247
+ self.w2 = nn.Linear(hidden_dim, dim, bias=False)
248
+ self.w3 = nn.Linear(dim, hidden_dim, bias=False)
249
+
250
+ def forward(self, x):
251
+ # Shared SwiGLU processes every image token independently of routed selection.
252
+ gate = self.w1(x)
253
+ linear = self.w3(x)
254
+ activated_gate = F.silu(gate)
255
+ product = activated_gate * linear
256
+ output = self.w2(product)
257
+ return output
258
+
259
+
260
+ class DeepSeekMoEFFN(nn.Module):
261
+ """Standalone DeepSeek-style routed and shared expert FFN."""
262
+
263
+ def __init__(
264
+ self,
265
+ dim: int,
266
+ routed_inter_dim: int,
267
+ shared_inter_dim: int,
268
+ num_layers: int,
269
+ num_experts: int = 8,
270
+ top_k: int = 2,
271
+ route_scale: float = 1.0,
272
+ router_bias_update_rate: float = 1e-3,
273
+ drop: float = 0.0,
274
+ ):
275
+ super().__init__()
276
+ if routed_inter_dim is None or shared_inter_dim is None:
277
+ raise ValueError("MoE requires explicit routed_inter_dim and shared_inter_dim")
278
+ expert_hidden_dim = int(routed_inter_dim)
279
+ shared_hidden_dim = int(shared_inter_dim)
280
+ self.num_experts = int(num_experts)
281
+ self.top_k = top_k
282
+ self.gate = DeepSeekMoEGate(
283
+ dim=dim,
284
+ num_experts=num_experts,
285
+ top_k=top_k,
286
+ route_scale=route_scale,
287
+ router_bias_update_rate=router_bias_update_rate,
288
+ )
289
+ self.experts = DeepSeekGroupedExperts(
290
+ dim=dim,
291
+ hidden_dim=expert_hidden_dim,
292
+ output_dim=dim,
293
+ num_experts=self.num_experts,
294
+ num_layers=num_layers,
295
+ )
296
+ self.reorderer = DeepSeekTokenReorderer(num_experts=self.num_experts)
297
+ self.shared_experts = DeepSeekSharedExpert(dim, shared_hidden_dim)
298
+ self.ffn_dropout = nn.Dropout(drop)
299
+
300
+ @torch.no_grad()
301
+ def update_router_bias_from_latest(self):
302
+ self.gate.update_bias(self.gate.router_bias_update_rate)
303
+
304
+ @torch._dynamo.disable
305
+ def forward(self, hidden_states):
306
+ # Merge packed token dimensions, then select top-k experts per token.
307
+ original_shape = hidden_states.shape
308
+ hidden_states_flat = hidden_states.reshape(-1, original_shape[-1])
309
+ topk_weight, topk_idx = self.gate(hidden_states_flat)
310
+
311
+ # A token appears once per selected expert; token_ids allow restoring the original order.
312
+ token_ids = torch.arange(
313
+ hidden_states_flat.shape[0],
314
+ device=hidden_states.device,
315
+ ).unsqueeze(1).expand(-1, self.top_k)
316
+ (
317
+ top_scores_experts_sorted,
318
+ token_indices_experts_sorted,
319
+ num_tokens_per_expert,
320
+ ) = self.reorderer(topk_weight, topk_idx, token_ids.reshape(-1))
321
+
322
+ # Repeat each token ID across its features to gather complete rows.
323
+ gather_index = token_indices_experts_sorted.reshape(-1, 1).expand(
324
+ -1,
325
+ hidden_states_flat.shape[-1],
326
+ )
327
+ routed_input = torch.gather(hidden_states_flat, dim=0, index=gather_index)
328
+ routed_output = self.experts(routed_input, num_tokens_per_expert)
329
+ # Weight routed outputs in FP32, then cast to the input dtype.
330
+ routed_output = (
331
+ routed_output.float()
332
+ * top_scores_experts_sorted.float().unsqueeze(-1)
333
+ ).to(dtype=hidden_states.dtype)
334
+
335
+ # Start with the shared expert, then add weighted routed outputs back to each token.
336
+ output = self.shared_experts(hidden_states_flat)
337
+ # Inference note: the upstream repo toggles
338
+ # torch.use_deterministic_algorithms() around this scatter_add at
339
+ # inference time; the toggling is unsafe inside a forked ZeroGPU worker
340
+ # and the exact reduction order does not matter for sampling, so we
341
+ # always take the plain scatter_add path.
342
+ output = output.scatter_add(dim=0, index=gather_index, src=routed_output)
343
+ output = self.ffn_dropout(output)
344
+ return output.reshape(original_shape)
pixelmoe_src/denoiser_inference.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Inference-only Denoiser for PixelMoE.
2
+ # Mirrors denoiser.py from https://github.com/KlingAIResearch/PixelMoE
3
+ # (MIT License, Copyright (c) 2026 KlingAI Research) with the training
4
+ # logic (loss computation, EMA bookkeeping) removed and the constructor
5
+ # taking plain values instead of argparse.Namespace.
6
+ import math
7
+ from typing import List
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+
13
+ from model_pixelmoe import PixelMoE_models
14
+ from hierarchical_image_noise import HierarchicalImageNoiseBuilder
15
+
16
+
17
+ class Denoiser(nn.Module):
18
+ def __init__(
19
+ self,
20
+ model="PixelMoE-B-E32A4/16",
21
+ img_size=512,
22
+ num_classes=1000,
23
+ label_drop_prob=0.1,
24
+ sigma_min=0.03,
25
+ noise_scale=2.0,
26
+ hierarchical_stages=(0, 1),
27
+ hierarchical_end_timesteps=(0.75, 0.0),
28
+ hierarchical_logit_mean=0.0,
29
+ hierarchical_logit_std=1.0,
30
+ hierarchical_noise_rho=-1.0 / 3.0,
31
+ hierarchical_min_stage_sigma=0.0,
32
+ sampling_method="heun",
33
+ num_sampling_steps=(25, 25),
34
+ cfg=3.0,
35
+ cfg_interval=(0.0, 0.8),
36
+ ):
37
+ super().__init__()
38
+ self.net = PixelMoE_models[model](
39
+ input_size=img_size,
40
+ in_channels=3,
41
+ num_classes=num_classes,
42
+ proj_drop=0.0,
43
+ flux_attn_proj_dropout=False,
44
+ use_flash_attn=False,
45
+ )
46
+ self.img_size = img_size
47
+ self.num_classes = num_classes
48
+
49
+ self.label_drop_prob = label_drop_prob
50
+ self.sigma_min = sigma_min
51
+ self.noise_scale = noise_scale
52
+
53
+ # Hierarchical Flow training hyperparameters.
54
+ self.hierarchical_stages = list(hierarchical_stages)
55
+ self.hierarchical_logit_mean = hierarchical_logit_mean
56
+ self.hierarchical_logit_std = hierarchical_logit_std
57
+ self.hierarchical_noise_rho = hierarchical_noise_rho
58
+ self.hierarchical_min_stage_sigma = float(hierarchical_min_stage_sigma)
59
+ self.hierarchical_noise_builder = HierarchicalImageNoiseBuilder(
60
+ stages=self.hierarchical_stages,
61
+ end_timesteps=hierarchical_end_timesteps,
62
+ noise_rho=self.hierarchical_noise_rho,
63
+ noise_scale=self.noise_scale,
64
+ logit_mean=self.hierarchical_logit_mean,
65
+ logit_std=self.hierarchical_logit_std,
66
+ stage_logit_means=None,
67
+ stage_logit_stds=None,
68
+ min_stage_sigma=self.hierarchical_min_stage_sigma,
69
+ )
70
+
71
+ # generation hyper-parameters
72
+ self.method = sampling_method
73
+ self.sampling_steps = list(num_sampling_steps)
74
+ self.cfg_scale = cfg
75
+ self.cfg_interval = tuple(cfg_interval)
76
+
77
+ @torch.no_grad()
78
+ def generate(self, labels):
79
+ return self.generate_hierarchical(labels)
80
+
81
+ @torch.no_grad()
82
+ def generate_hierarchical(self, labels):
83
+ """Generate images from low to high resolution.
84
+
85
+ Each higher-resolution stage starts from the previous output,
86
+ upsampled and re-noised to match its starting distribution.
87
+ """
88
+ device = labels.device
89
+ dtype = next(self.net.parameters()).dtype
90
+ batch_size = labels.size(0)
91
+ num_stages = len(self.hierarchical_stages)
92
+
93
+ # Downsample full-resolution noise to the first stage. Multiplying by 2
94
+ # restores the noise variance after each 2x bilinear downsampling.
95
+ z = self.noise_scale * torch.randn(
96
+ batch_size,
97
+ 3,
98
+ self.img_size,
99
+ self.img_size,
100
+ device=device,
101
+ dtype=dtype,
102
+ )
103
+ height = self.img_size
104
+ width = self.img_size
105
+ for _ in range(num_stages - 1):
106
+ height //= 2
107
+ width //= 2
108
+ z = F.interpolate(z, size=(height, width), mode="bilinear", align_corners=False) * 2
109
+
110
+ for stage_index in range(num_stages):
111
+ if stage_index > 0:
112
+ height *= 2
113
+ width *= 2
114
+ z = F.interpolate(z, size=(height, width), mode="nearest")
115
+ z = self._hierarchical_transition_renoise(z, stage_index)
116
+
117
+ z = self._sample_hierarchical_stage(
118
+ z,
119
+ stage_index,
120
+ labels,
121
+ )
122
+
123
+ return z
124
+
125
+ def _hierarchical_transition_renoise(self, latents, stage_index):
126
+ """Rescale the upsampled image and add noise for the next stage."""
127
+ previous_t_end = self.hierarchical_noise_builder.end_timesteps[stage_index - 1]
128
+ t_start = self.hierarchical_noise_builder.start_timesteps[stage_index]
129
+
130
+ rho = self.hierarchical_noise_rho
131
+ alpha = t_start / math.sqrt(1. - rho)
132
+ signal_scale = (1. - t_start) / (1. - previous_t_end)
133
+
134
+ # Rescale the previous output and add block-correlated noise with the
135
+ # same noise_scale used during training.
136
+ noise = self.sample_block_noise_like(latents) * self.noise_scale
137
+ return signal_scale * latents + alpha * noise
138
+
139
+ def sample_block_noise_like(self, latents):
140
+ """Sample correlated noise within each spatial 2x2 block.
141
+
142
+ Each block follows a 4D Gaussian with covariance:
143
+ cov = (1 - rho) * I + rho * 11^T
144
+
145
+ For rho=-1/3, the four pixels in a block are negatively correlated and
146
+ their sum has zero variance.
147
+ """
148
+ batch_size, channels, height, width = latents.shape
149
+ if height % 2 != 0 or width % 2 != 0:
150
+ raise ValueError("Hierarchical block noise requires even spatial sizes")
151
+
152
+ rho = self.hierarchical_noise_rho
153
+ covariance = (
154
+ torch.eye(4, device=latents.device, dtype=torch.float32) * (1 - rho)
155
+ + torch.ones(4, 4, device=latents.device, dtype=torch.float32) * rho
156
+ )
157
+ mean = torch.zeros(4, device=latents.device, dtype=torch.float32)
158
+ dist = torch.distributions.multivariate_normal.MultivariateNormal(mean, covariance)
159
+ block_number = batch_size * channels * (height // 2) * (width // 2)
160
+ noise = dist.sample((block_number,)).to(dtype=latents.dtype)
161
+ noise = noise.reshape(batch_size, channels, height // 2, width // 2, 2, 2)
162
+ noise = noise.permute(0, 1, 2, 4, 3, 5).reshape(batch_size, channels, height, width)
163
+ return noise
164
+
165
+ @torch.no_grad()
166
+ def _sample_hierarchical_stage(self, z, stage_index, labels):
167
+ """Denoise z through one stage using Euler or Heun steps."""
168
+ global_timesteps, sigmas = self._hierarchical_stage_timesteps(stage_index, z.device, z.dtype)
169
+
170
+ for i in range(len(sigmas) - 1):
171
+ t = global_timesteps[i].expand(z.shape[0]).to(device=z.device, dtype=z.dtype)
172
+ sigma = sigmas[i].expand(z.shape[0]).view(-1, 1, 1, 1)
173
+ sigma_next = sigmas[i + 1].expand(z.shape[0]).view(-1, 1, 1, 1)
174
+
175
+ if self.method == "euler":
176
+ z = self._hierarchical_euler_step(
177
+ z, t, sigma, sigma_next, labels
178
+ )
179
+ elif self.method == "heun":
180
+ t_next = global_timesteps[i + 1].expand(z.shape[0]).to(device=z.device, dtype=z.dtype)
181
+ z = self._hierarchical_heun_step(
182
+ z, t, t_next, sigma, sigma_next, labels
183
+ )
184
+ else:
185
+ raise NotImplementedError
186
+
187
+ return z
188
+
189
+ def _hierarchical_stage_timesteps(self, stage_index, device, dtype):
190
+ """Select this stage's grid points and return global timestep and stage-relative sigma."""
191
+ t_start = self.hierarchical_noise_builder.start_timesteps[stage_index]
192
+ t_end = self.hierarchical_noise_builder.end_timesteps[stage_index]
193
+ # A zero lower bound covers the full stage interval. A positive value
194
+ # excludes near-endpoint grid points, except for the endpoint added below.
195
+ min_sigma = self.hierarchical_min_stage_sigma
196
+ # Convert the stage-relative lower bound to global t. The true endpoint
197
+ # t_end is added below so sampling still finishes at sigma=0.
198
+ sampling_t_end = t_end + min_sigma * (t_start - t_end)
199
+ sampling_t_start = t_start
200
+ global_timesteps = self._hierarchical_global_sampling_timesteps(device, dtype)
201
+ global_timesteps = global_timesteps[
202
+ (global_timesteps <= sampling_t_start) & (global_timesteps >= sampling_t_end)
203
+ ]
204
+ global_timesteps = torch.cat([
205
+ global_timesteps,
206
+ torch.tensor([sampling_t_start, sampling_t_end, t_end], device=device, dtype=dtype),
207
+ ])
208
+ global_timesteps = torch.unique(global_timesteps, sorted=True).flip(0)
209
+ global_timesteps[0] = sampling_t_start
210
+ global_timesteps[-1] = t_end
211
+
212
+ denom = max(t_start - t_end, torch.finfo(dtype).eps)
213
+ sigmas = (global_timesteps - t_end) / denom
214
+ sigmas[-1] = 0.0
215
+ return global_timesteps, sigmas
216
+
217
+ def _hierarchical_global_sampling_timesteps(self, device, dtype):
218
+ """Build the shared global timestep grid from t=1 to t=0."""
219
+ if len(self.sampling_steps) == 1:
220
+ total_steps = self.sampling_steps[0] * len(self.hierarchical_stages)
221
+ else:
222
+ total_steps = sum(self.sampling_steps)
223
+
224
+ timesteps = torch.linspace(1.0, 0.0, total_steps + 1, device=device, dtype=dtype)
225
+ timesteps = self._logit_shift_sampling_timesteps(timesteps)
226
+ return timesteps
227
+
228
+ def _logit_shift_sampling_timesteps(self, timesteps):
229
+ """Adjust timestep spacing using the global Logit-Normal mean and std."""
230
+ eps = torch.finfo(timesteps.dtype).eps
231
+ t = timesteps.clamp(eps, 1.0 - eps)
232
+ exp_mean = math.exp(float(self.hierarchical_logit_mean))
233
+ shifted = exp_mean / (
234
+ exp_mean + torch.pow(1.0 / t - 1.0, float(self.hierarchical_logit_std))
235
+ )
236
+ shifted[0] = 1.0
237
+ shifted[-1] = 0.0
238
+ return shifted
239
+
240
+ @torch.no_grad()
241
+ def _hierarchical_euler_step(self, z, t, sigma, sigma_next, labels):
242
+ """Euler step: take one Euler step using the current velocity and change in sigma."""
243
+ velocity = self._forward_hierarchical_sample(
244
+ z,
245
+ t,
246
+ sigma,
247
+ labels,
248
+ )
249
+ return z + (sigma_next - sigma) * velocity
250
+
251
+ @torch.no_grad()
252
+ def _hierarchical_heun_step(self, z, t, t_next, sigma, sigma_next, labels):
253
+ """Heun step: average current and next velocities for a Heun step; use Euler at sigma=0."""
254
+ velocity = self._forward_hierarchical_sample(
255
+ z,
256
+ t,
257
+ sigma,
258
+ labels,
259
+ )
260
+ z_euler = z + (sigma_next - sigma) * velocity
261
+ if torch.all(sigma_next <= 0):
262
+ return z_euler
263
+
264
+ velocity_next = self._forward_hierarchical_sample(
265
+ z_euler,
266
+ t_next,
267
+ sigma_next,
268
+ labels,
269
+ )
270
+ return z + (sigma_next - sigma) * 0.5 * (velocity + velocity_next)
271
+
272
+ @torch.no_grad()
273
+ def _forward_hierarchical_sample(self, z, t, sigma, labels):
274
+ """Predict endpoint-derived velocity, applying CFG within its global-timestep interval."""
275
+ pred_cond = self.net(z, t, labels)[0]
276
+ v_cond = self._hierarchical_prediction_to_velocity(
277
+ pred_cond,
278
+ z,
279
+ sigma,
280
+ )
281
+
282
+ if self.cfg_scale == 1.0 or self.label_drop_prob <= 0.0:
283
+ return v_cond
284
+
285
+ uncond_labels = torch.full(
286
+ labels.shape,
287
+ self.num_classes,
288
+ device=labels.device,
289
+ dtype=labels.dtype,
290
+ )
291
+ pred_uncond = self.net(z, t, uncond_labels)[0]
292
+ v_uncond = self._hierarchical_prediction_to_velocity(
293
+ pred_uncond,
294
+ z,
295
+ sigma,
296
+ )
297
+
298
+ cfg_t = t.view(-1, 1, 1, 1)
299
+ low, high = self.cfg_interval
300
+ lower_ok = (low == 0) | (cfg_t > low)
301
+ upper_ok = (high == 1) | (cfg_t < high)
302
+ interval_mask = lower_ok & upper_ok
303
+ cfg_scale = torch.where(interval_mask, self.cfg_scale, 1.0)
304
+
305
+ v_guided = v_uncond + cfg_scale * (v_cond - v_uncond)
306
+ return v_guided
307
+
308
+ def _hierarchical_prediction_to_velocity(self, pred, noisy, sigma):
309
+ """Convert an endpoint prediction to velocity with a clamped sigma denominator."""
310
+ # v_theta = (x_sigma - predicted endpoint) / sigma.
311
+ dtype_eps = torch.finfo(sigma.dtype).eps
312
+ sampling_eps = float(self.sigma_min)
313
+ sigma_eps = max(sampling_eps, dtype_eps)
314
+ return (noisy - pred) / sigma.clamp_min(sigma_eps)
pixelmoe_src/hierarchical_image_noise.py ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --------------------------------------------------------
2
+ # References:
3
+ # Pyramid Flow: https://github.com/jy0205/Pyramid-Flow
4
+ # --------------------------------------------------------
5
+
6
+ import math
7
+ from typing import List, NamedTuple
8
+
9
+ import torch
10
+ import torch.nn.functional as F
11
+
12
+
13
+ class HierarchicalImageNoiseOutput(NamedTuple):
14
+ # One entry per active stage in this batch.
15
+ noisy_images_list: List[torch.Tensor]
16
+ # Stage-relative timestep sigma; shape [stage_batch_size, 1, 1, 1].
17
+ sigmas_list: List[torch.Tensor]
18
+ # Global timestep t used to condition the model; shape [stage_batch_size].
19
+ timesteps_list: List[torch.Tensor]
20
+ # Stage endpoints; intermediate-stage endpoints can contain noise.
21
+ targets_list: List[torch.Tensor]
22
+ sample_indices_list: List[torch.Tensor]
23
+
24
+
25
+ class HierarchicalImageNoiseBuilder:
26
+ """
27
+ Prepare noisy images and prediction targets for each resolution stage.
28
+
29
+ The input is a batch of images with shape [B, C, H, W]. Each image is
30
+ assigned to one stage for this training step; the number of images per
31
+ stage is determined by the fixed-density allocation. Sample a noise level
32
+ within the assigned stage using that stage's Logit-Normal distribution.
33
+
34
+ t is the global timestep and sigma is the timestep
35
+ within a stage: t = t_end + sigma * (t_start - t_end). Construct the noisy
36
+ image as x_sigma = sigma * x_start + (1 - sigma) * x_end.
37
+
38
+ Return noisy images, stage-relative sigmas, global timesteps, endpoint
39
+ targets, and original batch indices grouped by stage. Image tensors use
40
+ [stage_batch_size, C, stage_H, stage_W].
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ stages=(0, 1),
46
+ end_timesteps=(0.75, 0.0),
47
+ noise_rho=-1.0 / 3.0,
48
+ noise_scale=1.0,
49
+ logit_mean=0.0,
50
+ logit_std=1.0,
51
+ stage_logit_means=None,
52
+ stage_logit_stds=None,
53
+ min_stage_sigma=0.0,
54
+ ):
55
+ self.stages = list(stages)
56
+ self.noise_scale = float(noise_scale)
57
+ self.logit_mean = logit_mean
58
+ self.logit_std = logit_std
59
+ self.stage_logit_means = [float(value) for value in (
60
+ [0.6] * len(self.stages) if stage_logit_means is None else stage_logit_means
61
+ )]
62
+ self.stage_logit_stds = [float(value) for value in (
63
+ [0.8] * len(self.stages) if stage_logit_stds is None else stage_logit_stds
64
+ )]
65
+ self.min_stage_sigma = float(min_stage_sigma)
66
+
67
+ if not 0.0 <= self.min_stage_sigma < 1.0:
68
+ raise ValueError("min_stage_sigma must be in [0, 1)")
69
+ if len(self.stage_logit_means) != len(self.stages):
70
+ raise ValueError("stage_logit_means must contain one value per hierarchical stage")
71
+ if len(self.stage_logit_stds) != len(self.stages):
72
+ raise ValueError("stage_logit_stds must contain one value per hierarchical stage")
73
+ if any(std <= 0.0 for std in self.stage_logit_stds):
74
+ raise ValueError("stage_logit_stds values must be positive")
75
+ self.end_timesteps = self._parse_end_timesteps(end_timesteps)
76
+ self.start_timesteps = self._compute_start_timesteps(self.end_timesteps, noise_rho)
77
+ self.training_end_timesteps = [
78
+ t_end + self.min_stage_sigma * (t_start - t_end)
79
+ for t_start, t_end in zip(self.start_timesteps, self.end_timesteps)
80
+ ]
81
+ self.fixed_density_bins = self._build_fixed_density_bins()
82
+
83
+ @torch.no_grad()
84
+ def __call__(self, x):
85
+ """
86
+ Build resolution stages and prepare their noisy training inputs.
87
+
88
+ Args
89
+ x (torch.Tensor):
90
+ Clean full-resolution images [B, C, H, W].
91
+ Returns
92
+ HierarchicalImageNoiseOutput: training tensors grouped by active stage.
93
+ """
94
+ images_list = self.get_hierarchical_images(x)
95
+ return self.add_continuous_hierarchical_noise(images_list)
96
+
97
+ # Stage intervals
98
+
99
+ def _parse_end_timesteps(self, end_timesteps):
100
+ # Convert stage endpoints to floats; each stage needs one end timestep.
101
+ values = [float(v) for v in end_timesteps]
102
+ if len(values) == len(self.stages):
103
+ end_timesteps = values
104
+ else:
105
+ raise ValueError("end_timesteps must contain one end t per hierarchical stage")
106
+
107
+ # Global timesteps lie in [0, 1].
108
+ if any(s < 0.0 or s > 1.0 for s in end_timesteps):
109
+ raise ValueError("end timesteps must be in [0, 1]")
110
+ # Later stages must end at an equal or smaller timestep, e.g. [0.75, 0.0].
111
+ if any(end_timesteps[i] < end_timesteps[i + 1] for i in range(len(end_timesteps) - 1)):
112
+ raise ValueError("end timesteps must be monotonically non-increasing")
113
+ return end_timesteps
114
+
115
+ @staticmethod
116
+ def _compute_start_timesteps(end_timesteps, rho):
117
+ start_timesteps = []
118
+ for i_s, _ in enumerate(end_timesteps):
119
+ # The first stage starts from pure noise at t=1.
120
+ if i_s == 0:
121
+ start_timesteps.append(1.0)
122
+ continue
123
+ # Match the next stage's start distribution after upsampling, rescaling, and re-noising.
124
+ previous_t_end = end_timesteps[i_s - 1]
125
+ t_start = (math.sqrt(1. - rho) * previous_t_end) / (math.sqrt(1. - rho) * previous_t_end + (1. - previous_t_end) * math.sqrt(-rho))
126
+ start_timesteps.append(t_start)
127
+ return start_timesteps
128
+
129
+ # Fixed-density allocation
130
+
131
+ @staticmethod
132
+ def _normal_cdf(value):
133
+ return 0.5 * (1.0 + math.erf(value / math.sqrt(2.0)))
134
+
135
+ @staticmethod
136
+ def _logit(value):
137
+ return math.log(value / (1.0 - value))
138
+
139
+ def _timestep_cdf(self, t):
140
+ t = min(max(float(t), 0.0), 1.0)
141
+ if t <= 0.0:
142
+ return 0.0
143
+ if t >= 1.0:
144
+ return 1.0
145
+ z = (self._logit(t) - float(self.logit_mean)) / float(self.logit_std)
146
+ return self._normal_cdf(z)
147
+
148
+ def _interval_mass(self, low, high):
149
+ if high <= low:
150
+ return 0.0
151
+ return max(0.0, self._timestep_cdf(high) - self._timestep_cdf(low))
152
+
153
+ def _build_fixed_density_bins(self):
154
+ """
155
+ Build stage-allocation probabilities from the global timestep distribution.
156
+ Overlapping intervals share their probability equally between covering stages.
157
+
158
+ Returns
159
+ List[dict]: entries with a stage index and normalized probability mass.
160
+ A stage can have multiple entries; all masses sum to 1.
161
+ """
162
+ # Split [0, 1] at each stage's training interval boundaries.
163
+ boundaries = [0.0, 1.0]
164
+ boundaries.extend(float(v) for v in self.start_timesteps) # Add stage starts
165
+ boundaries.extend(float(v) for v in self.training_end_timesteps) # Add training lower bounds.
166
+ boundaries = sorted(set(min(max(v, 0.0), 1.0) for v in boundaries)) # Clamp to [0, 1], remove duplicates, and sort.
167
+
168
+ bins = [] # One entry for each interval/stage pair.
169
+ for low, high in zip(boundaries[:-1], boundaries[1:]):
170
+ if high <= low:
171
+ continue
172
+
173
+ # Stage coverage is constant within each bin; check its midpoint.
174
+ midpoint = 0.5 * (low + high) # A representative global t in this interval.
175
+ candidates = [] # Stages whose training intervals cover this bin.
176
+ for stage_index, (t_start, training_t_end) in enumerate(zip(self.start_timesteps, self.training_end_timesteps)):
177
+ stage_low = min(t_start, training_t_end)
178
+ stage_high = max(t_start, training_t_end)
179
+ if stage_low <= midpoint <= stage_high:
180
+ candidates.append(stage_index)
181
+
182
+ if not candidates:
183
+ continue
184
+
185
+ # Divide this bin's global Logit-Normal probability among covering stages.
186
+ mass = self._interval_mass(low, high) # P(low <= t <= high), not interval width.
187
+ if mass <= 0.0:
188
+ continue
189
+
190
+ split_mass = mass / len(candidates) # Each covering stage receives an equal share.
191
+ for stage_index in candidates:
192
+ bins.append(
193
+ {
194
+ "stage": stage_index,
195
+ "mass": split_mass,
196
+ }
197
+ )
198
+
199
+ # Normalize over valid training intervals; uncovered bins have no quota.
200
+ total_mass = sum(item["mass"] for item in bins)
201
+ if total_mass <= 0.0:
202
+ raise ValueError("Fixed-density stage allocation produced zero probability mass")
203
+
204
+ for item in bins:
205
+ item["mass"] /= total_mass
206
+
207
+ return bins
208
+
209
+ @staticmethod
210
+ def _apportion_counts(batch_size, masses):
211
+ """
212
+ Convert bin probabilities to integer image quotas.
213
+
214
+ Args
215
+ batch_size (int):
216
+ Total number of images to assign.
217
+ masses (List[float]):
218
+ Normalized probability per bin entry.
219
+ Returns
220
+ List[int]: quotas per bin entry, summing to batch_size.
221
+ """
222
+ # Expected image counts may be fractional; first round each down.
223
+ expected = [mass * batch_size for mass in masses]
224
+ counts = [int(math.floor(value)) for value in expected] # Initial integer quotas per bin entry.
225
+ remaining = batch_size - sum(counts) # Number of images still unassigned.
226
+
227
+ # Sort bin indices by fractional remainder, largest first.
228
+ remainders = sorted(
229
+ range(len(expected)),
230
+ key=lambda i: expected[i] - counts[i],
231
+ reverse=True,
232
+ )
233
+
234
+ # Add one image to each of the top remaining entries.
235
+ for i in remainders[:remaining]:
236
+ counts[i] += 1
237
+
238
+ return counts
239
+
240
+ def _fixed_batch_bin_counts(self, batch_size):
241
+ """
242
+ Get integer image quotas in fixed_density_bins order.
243
+
244
+ Args
245
+ batch_size (int):
246
+ Number of images in the batch.
247
+ Returns
248
+ List[int]: one quota per bin entry, summing to batch_size.
249
+ """
250
+ # Keep separate bin entries even when they belong to the same stage.
251
+ masses = [item["mass"] for item in self.fixed_density_bins] # Preserve bin order.
252
+ return self._apportion_counts(batch_size, masses)
253
+
254
+ # Stage-local sampling
255
+
256
+ def _sample_stage_local_timesteps(self, count, stage_index, device, dtype):
257
+ """
258
+ Sample noise levels within the selected stage's training interval.
259
+
260
+ Args
261
+ count (int):
262
+ Number of images assigned to this bin entry.
263
+ stage_index (int):
264
+ Stage whose interval and Logit-Normal parameters are used.
265
+ device (torch.device):
266
+ Device for sampled tensors.
267
+ dtype (torch.dtype):
268
+ Dtype for returned timesteps.
269
+ Returns
270
+ torch.Tensor: global timesteps [count], between the training lower bound and stage start.
271
+ """
272
+ low = float(self.training_end_timesteps[stage_index]) # Lower global timestep allowed during training.
273
+ high = float(self.start_timesteps[stage_index]) # Stage start in global timestep.
274
+
275
+ # Standard normal -> stage-specific normal -> sigmoid values in (0, 1).
276
+ normal = torch.randn(count, device=device, dtype=torch.float64) # [count]
277
+ # u is relative to the truncated training interval. With a lower bound,
278
+ # the full-stage sigma is min_stage_sigma + (1 - min_stage_sigma) * u.
279
+ u = torch.sigmoid(
280
+ float(self.stage_logit_means[stage_index])
281
+ + float(self.stage_logit_stds[stage_index]) * normal
282
+ )
283
+
284
+ # Map the relative position u to the stage's global-timestep training interval.
285
+ timesteps = low + (high - low) * u # [count]
286
+ return timesteps.clamp(min=low, max=high).to(dtype=dtype)
287
+
288
+ def _sample_fixed_batch_timesteps_and_stages(
289
+ self,
290
+ batch_size,
291
+ device,
292
+ dtype,
293
+ ):
294
+ """
295
+ Randomly assign images to fixed quotas, then sample their global timesteps.
296
+
297
+ Args
298
+ batch_size (int):
299
+ Number of images in the batch.
300
+ device (torch.device):
301
+ Device for sampled tensors.
302
+ dtype (torch.dtype):
303
+ Dtype for returned timesteps.
304
+ Returns
305
+ tuple: global timesteps [B] and stage indices [B], in original image order.
306
+ """
307
+ counts = self._fixed_batch_bin_counts(batch_size) # Integer quota per bin entry; sum = B.
308
+ sampled_timesteps = torch.empty(batch_size, device=device, dtype=dtype) # [B], global t per image.
309
+ chosen_stages = torch.empty(batch_size, device=device, dtype=torch.long) # [B], stage index per image.
310
+
311
+ # Shuffle image indices so stage assignment does not depend on batch order.
312
+ permutation = torch.randperm(batch_size, device=device)
313
+
314
+ offset = 0 # Number of indices already taken from permutation.
315
+ for bin_info, count in zip(self.fixed_density_bins, counts):
316
+ if count == 0:
317
+ continue
318
+ indices = permutation[offset:offset + count] # [count], original image indices for this quota.
319
+ stage_index = int(bin_info["stage"])
320
+
321
+ # Bin masses control counts; sample t over the stage's full training interval.
322
+ sampled_timesteps[indices] = self._sample_stage_local_timesteps(
323
+ count,
324
+ stage_index,
325
+ device,
326
+ dtype,
327
+ )
328
+ chosen_stages[indices] = stage_index # Write results back in original batch order.
329
+ offset += count
330
+
331
+ return sampled_timesteps, chosen_stages
332
+
333
+ # Hierarchical images and noise
334
+
335
+ @torch.no_grad()
336
+ def get_hierarchical_images(self, x):
337
+ """
338
+ Build `[low_res, ..., high_res]` image tensors from a clean image batch.
339
+
340
+ Args
341
+ x (torch.Tensor):
342
+ Clean full-resolution images [B, C, H, W].
343
+ Returns
344
+ List[torch.Tensor]: clean images [B, C, H_stage, W_stage], from low to high resolution.
345
+ """
346
+ if x.ndim != 4:
347
+ raise ValueError(f"Expected image tensor [B, C, H, W], got shape {tuple(x.shape)}")
348
+
349
+ # Collect images from high to low resolution, then reverse to stage order.
350
+ image_list = [x]
351
+ height, width = x.shape[-2], x.shape[-1]
352
+ for _ in range(len(self.stages) - 1):
353
+ height //= 2
354
+ width //= 2
355
+ # Halve the image height and width for the next lower-resolution stage.
356
+ x = F.interpolate(x, size=(height, width), mode="bilinear", align_corners=False)
357
+ image_list.append(x)
358
+
359
+ image_list = list(reversed(image_list))
360
+ return image_list
361
+
362
+ @torch.no_grad()
363
+ def add_continuous_hierarchical_noise(self, images_list):
364
+ """
365
+ Construct noisy inputs and endpoint targets for the assigned images at each stage.
366
+
367
+ Args
368
+ images_list (List[torch.Tensor]):
369
+ Clean images [B, C, H_stage, W_stage], ordered from low to high resolution.
370
+ Every stage keeps the same original batch order.
371
+ Returns
372
+ HierarchicalImageNoiseOutput: per-active-stage noisy inputs, sigmas,
373
+ global timesteps, endpoint targets, and original image indices.
374
+ """
375
+ stages = len(self.stages)
376
+ if stages != len(images_list):
377
+ raise ValueError("Number of clean hierarchical images must match len(stages)")
378
+
379
+ base_image = images_list[-1] # [B, C, H, W], the full-resolution clean images.
380
+ device = base_image.device
381
+ dtype = base_image.dtype
382
+ batch_size = base_image.shape[0]
383
+
384
+ # Scale full-resolution Gaussian noise, then downsample it for each stage.
385
+ # Multiplying by 2 after downsampling restores the noise variance.
386
+ noise = torch.randn_like(base_image) * self.noise_scale # [B, C, H, W]
387
+ height, width = noise.shape[-2], noise.shape[-1]
388
+ noise_list = [noise]
389
+ cur_noise = noise
390
+ for _ in range(stages - 1):
391
+ height //= 2
392
+ width //= 2
393
+ cur_noise = F.interpolate(
394
+ cur_noise,
395
+ size=(height, width),
396
+ mode="bilinear",
397
+ align_corners=False,
398
+ ) * 2.0
399
+ noise_list.append(cur_noise)
400
+ noise_list = list(reversed(noise_list)) # Match images_list's low-to-high resolution order.
401
+
402
+ # Sample only where the stage-relative sigma is at least min_stage_sigma.
403
+ sampled_timesteps, chosen_stages = self._sample_fixed_batch_timesteps_and_stages(
404
+ batch_size,
405
+ device,
406
+ dtype,
407
+ )
408
+
409
+ stage_to_indices = [
410
+ torch.nonzero(chosen_stages == i_s, as_tuple=False).flatten()
411
+ for i_s in range(stages)
412
+ ] # stage_to_indices[i] contains the original indices assigned to stage i.
413
+
414
+ # One tensor per active stage; stages with no assigned images are omitted.
415
+ noisy_images_list = []
416
+ sigmas_list = []
417
+ targets_list = []
418
+ timesteps_list = []
419
+ sample_indices_list = []
420
+
421
+ for i_s, indices in enumerate(stage_to_indices):
422
+ # Small batches can leave some stages empty.
423
+ if indices.numel() == 0:
424
+ continue
425
+
426
+ # Gather matching images, noise, and timesteps using original batch indices.
427
+ clean_image = images_list[i_s].index_select(0, indices) # [B_stage, C, H_stage, W_stage]
428
+ stage_noise = noise_list[i_s].index_select(0, indices) # Same shape as clean_image.
429
+ timesteps = sampled_timesteps.index_select(0, indices) # [B_stage], global t.
430
+ t_start = torch.as_tensor(self.start_timesteps[i_s], device=device, dtype=dtype)
431
+ t_end = torch.as_tensor(self.end_timesteps[i_s], device=device, dtype=dtype)
432
+
433
+ # Stage-relative sigma is 1 at t_start and 0 at t_end.
434
+ denom = (t_start - t_end).clamp_min(torch.finfo(dtype).eps)
435
+ sigma = ((timesteps - t_end) / denom).clamp(0.0, 1.0) # [B_stage]
436
+
437
+ # Construct the start image at global t_start.
438
+ if i_s == 0:
439
+ x_start = stage_noise # First stage starts from pure noise.
440
+ else:
441
+ # Use the same images at the previous resolution, upsampled to this stage.
442
+ last_clean_image = images_list[i_s - 1].index_select(0, indices)
443
+ last_clean_image = F.interpolate(
444
+ last_clean_image,
445
+ size=(last_clean_image.shape[-2] * 2, last_clean_image.shape[-1] * 2),
446
+ mode="nearest",
447
+ )
448
+ x_start = t_start * stage_noise + (1.0 - t_start) * last_clean_image
449
+
450
+ # Construct the endpoint the model must predict.
451
+ if i_s == stages - 1:
452
+ x_end = clean_image # Final endpoint is clean, at t=0 in our configuration.
453
+ else:
454
+ x_end = t_end * stage_noise + (1.0 - t_end) * clean_image # Intermediate endpoint retains noise.
455
+
456
+ # Broadcast sigma from [B_stage] to [B_stage, 1, 1, 1].
457
+ while sigma.ndim < x_start.ndim:
458
+ sigma = sigma.unsqueeze(-1)
459
+
460
+ # Interpolate from the stage's start image toward its endpoint target.
461
+ noisy_images = sigma * x_start + (1.0 - sigma) * x_end # [B_stage, C, H_stage, W_stage]
462
+
463
+ noisy_images_list.append(noisy_images.to(dtype))
464
+ sigmas_list.append(sigma.to(dtype)) # [B_stage, 1, 1, 1], used by the loss.
465
+ timesteps_list.append(timesteps.to(dtype)) # [B_stage], used to condition the model.
466
+ targets_list.append(x_end.to(dtype))
467
+ sample_indices_list.append(indices) # [B_stage], used to select matching class labels.
468
+
469
+ return HierarchicalImageNoiseOutput(
470
+ noisy_images_list=noisy_images_list,
471
+ sigmas_list=sigmas_list,
472
+ timesteps_list=timesteps_list,
473
+ targets_list=targets_list,
474
+ sample_indices_list=sample_indices_list,
475
+ )
pixelmoe_src/imagenet_classes.json ADDED
@@ -0,0 +1 @@
 
 
1
+ ["tench", "goldfish", "great white shark", "tiger shark", "hammerhead", "electric ray", "stingray", "cock", "hen", "ostrich", "brambling", "goldfinch", "house finch", "junco", "indigo bunting", "robin", "bulbul", "jay", "magpie", "chickadee", "water ouzel", "kite", "bald eagle", "vulture", "great grey owl", "European fire salamander", "common newt", "eft", "spotted salamander", "axolotl", "bullfrog", "tree frog", "tailed frog", "loggerhead", "leatherback turtle", "mud turtle", "terrapin", "box turtle", "banded gecko", "common iguana", "American chameleon", "whiptail", "agama", "frilled lizard", "alligator lizard", "Gila monster", "green lizard", "African chameleon", "Komodo dragon", "African crocodile", "American alligator", "triceratops", "thunder snake", "ringneck snake", "hognose snake", "green snake", "king snake", "garter snake", "water snake", "vine snake", "night snake", "boa constrictor", "rock python", "Indian cobra", "green mamba", "sea snake", "horned viper", "diamondback", "sidewinder", "trilobite", "harvestman", "scorpion", "black and gold garden spider", "barn spider", "garden spider", "black widow", "tarantula", "wolf spider", "tick", "centipede", "black grouse", "ptarmigan", "ruffed grouse", "prairie chicken", "peacock", "quail", "partridge", "African grey", "macaw", "sulphur-crested cockatoo", "lorikeet", "coucal", "bee eater", "hornbill", "hummingbird", "jacamar", "toucan", "drake", "red-breasted merganser", "goose", "black swan", "tusker", "echidna", "platypus", "wallaby", "koala", "wombat", "jellyfish", "sea anemone", "brain coral", "flatworm", "nematode", "conch", "snail", "slug", "sea slug", "chiton", "chambered nautilus", "Dungeness crab", "rock crab", "fiddler crab", "king crab", "American lobster", "spiny lobster", "crayfish", "hermit crab", "isopod", "white stork", "black stork", "spoonbill", "flamingo", "little blue heron", "American egret", "bittern", "crane", "limpkin", "European gallinule", "American coot", "bustard", "ruddy turnstone", "red-backed sandpiper", "redshank", "dowitcher", "oystercatcher", "pelican", "king penguin", "albatross", "grey whale", "killer whale", "dugong", "sea lion", "Chihuahua", "Japanese spaniel", "Maltese dog", "Pekinese", "Shih-Tzu", "Blenheim spaniel", "papillon", "toy terrier", "Rhodesian ridgeback", "Afghan hound", "basset", "beagle", "bloodhound", "bluetick", "black-and-tan coonhound", "Walker hound", "English foxhound", "redbone", "borzoi", "Irish wolfhound", "Italian greyhound", "whippet", "Ibizan hound", "Norwegian elkhound", "otterhound", "Saluki", "Scottish deerhound", "Weimaraner", "Staffordshire bullterrier", "American Staffordshire terrier", "Bedlington terrier", "Border terrier", "Kerry blue terrier", "Irish terrier", "Norfolk terrier", "Norwich terrier", "Yorkshire terrier", "wire-haired fox terrier", "Lakeland terrier", "Sealyham terrier", "Airedale", "cairn", "Australian terrier", "Dandie Dinmont", "Boston bull", "miniature schnauzer", "giant schnauzer", "standard schnauzer", "Scotch terrier", "Tibetan terrier", "silky terrier", "soft-coated wheaten terrier", "West Highland white terrier", "Lhasa", "flat-coated retriever", "curly-coated retriever", "golden retriever", "Labrador retriever", "Chesapeake Bay retriever", "German short-haired pointer", "vizsla", "English setter", "Irish setter", "Gordon setter", "Brittany spaniel", "clumber", "English springer", "Welsh springer spaniel", "cocker spaniel", "Sussex spaniel", "Irish water spaniel", "kuvasz", "schipperke", "groenendael", "malinois", "briard", "kelpie", "komondor", "Old English sheepdog", "Shetland sheepdog", "collie", "Border collie", "Bouvier des Flandres", "Rottweiler", "German shepherd", "Doberman", "miniature pinscher", "Greater Swiss Mountain dog", "Bernese mountain dog", "Appenzeller", "EntleBucher", "boxer", "bull mastiff", "Tibetan mastiff", "French bulldog", "Great Dane", "Saint Bernard", "Eskimo dog", "malamute", "Siberian husky", "dalmatian", "affenpinscher", "basenji", "pug", "Leonberg", "Newfoundland", "Great Pyrenees", "Samoyed", "Pomeranian", "chow", "keeshond", "Brabancon griffon", "Pembroke", "Cardigan", "toy poodle", "miniature poodle", "standard poodle", "Mexican hairless", "timber wolf", "white wolf", "red wolf", "coyote", "dingo", "dhole", "African hunting dog", "hyena", "red fox", "kit fox", "Arctic fox", "grey fox", "tabby", "tiger cat", "Persian cat", "Siamese cat", "Egyptian cat", "cougar", "lynx", "leopard", "snow leopard", "jaguar", "lion", "tiger", "cheetah", "brown bear", "American black bear", "ice bear", "sloth bear", "mongoose", "meerkat", "tiger beetle", "ladybug", "ground beetle", "long-horned beetle", "leaf beetle", "dung beetle", "rhinoceros beetle", "weevil", "fly", "bee", "ant", "grasshopper", "cricket", "walking stick", "cockroach", "mantis", "cicada", "leafhopper", "lacewing", "dragonfly", "damselfly", "admiral", "ringlet", "monarch", "cabbage butterfly", "sulphur butterfly", "lycaenid", "starfish", "sea urchin", "sea cucumber", "wood rabbit", "hare", "Angora", "hamster", "porcupine", "fox squirrel", "marmot", "beaver", "guinea pig", "sorrel", "zebra", "hog", "wild boar", "warthog", "hippopotamus", "ox", "water buffalo", "bison", "ram", "bighorn", "ibex", "hartebeest", "impala", "gazelle", "Arabian camel", "llama", "weasel", "mink", "polecat", "black-footed ferret", "otter", "skunk", "badger", "armadillo", "three-toed sloth", "orangutan", "gorilla", "chimpanzee", "gibbon", "siamang", "guenon", "patas", "baboon", "macaque", "langur", "colobus", "proboscis monkey", "marmoset", "capuchin", "howler monkey", "titi", "spider monkey", "squirrel monkey", "Madagascar cat", "indri", "Indian elephant", "African elephant", "lesser panda", "giant panda", "barracouta", "eel", "coho", "rock beauty", "anemone fish", "sturgeon", "gar", "lionfish", "puffer", "abacus", "abaya", "academic gown", "accordion", "acoustic guitar", "aircraft carrier", "airliner", "airship", "altar", "ambulance", "amphibian", "analog clock", "apiary", "apron", "ashcan", "assault rifle", "backpack", "bakery", "balance beam", "balloon", "ballpoint", "Band Aid", "banjo", "bannister", "barbell", "barber chair", "barbershop", "barn", "barometer", "barrel", "barrow", "baseball", "basketball", "bassinet", "bassoon", "bathing cap", "bath towel", "bathtub", "beach wagon", "beacon", "beaker", "bearskin", "beer bottle", "beer glass", "bell cote", "bib", "bicycle-built-for-two", "bikini", "binder", "binoculars", "birdhouse", "boathouse", "bobsled", "bolo tie", "bonnet", "bookcase", "bookshop", "bottlecap", "bow", "bow tie", "brass", "brassiere", "breakwater", "breastplate", "broom", "bucket", "buckle", "bulletproof vest", "bullet train", "butcher shop", "cab", "caldron", "candle", "cannon", "canoe", "can opener", "cardigan", "car mirror", "carousel", "carpenter's kit", "carton", "car wheel", "cash machine", "cassette", "cassette player", "castle", "catamaran", "CD player", "cello", "cellular telephone", "chain", "chainlink fence", "chain mail", "chain saw", "chest", "chiffonier", "chime", "china cabinet", "Christmas stocking", "church", "cinema", "cleaver", "cliff dwelling", "cloak", "clog", "cocktail shaker", "coffee mug", "coffeepot", "coil", "combination lock", "computer keyboard", "confectionery", "container ship", "convertible", "corkscrew", "cornet", "cowboy boot", "cowboy hat", "cradle", "crane", "crash helmet", "crate", "crib", "Crock Pot", "croquet ball", "crutch", "cuirass", "dam", "desk", "desktop computer", "dial telephone", "diaper", "digital clock", "digital watch", "dining table", "dishrag", "dishwasher", "disk brake", "dock", "dogsled", "dome", "doormat", "drilling platform", "drum", "drumstick", "dumbbell", "Dutch oven", "electric fan", "electric guitar", "electric locomotive", "entertainment center", "envelope", "espresso maker", "face powder", "feather boa", "file", "fireboat", "fire engine", "fire screen", "flagpole", "flute", "folding chair", "football helmet", "forklift", "fountain", "fountain pen", "four-poster", "freight car", "French horn", "frying pan", "fur coat", "garbage truck", "gasmask", "gas pump", "goblet", "go-kart", "golf ball", "golfcart", "gondola", "gong", "gown", "grand piano", "greenhouse", "grille", "grocery store", "guillotine", "hair slide", "hair spray", "half track", "hammer", "hamper", "hand blower", "hand-held computer", "handkerchief", "hard disc", "harmonica", "harp", "harvester", "hatchet", "holster", "home theater", "honeycomb", "hook", "hoopskirt", "horizontal bar", "horse cart", "hourglass", "iPod", "iron", "jack-o'-lantern", "jean", "jeep", "jersey", "jigsaw puzzle", "jinrikisha", "joystick", "kimono", "knee pad", "knot", "lab coat", "ladle", "lampshade", "laptop", "lawn mower", "lens cap", "letter opener", "library", "lifeboat", "lighter", "limousine", "liner", "lipstick", "Loafer", "lotion", "loudspeaker", "loupe", "lumbermill", "magnetic compass", "mailbag", "mailbox", "maillot", "maillot", "manhole cover", "maraca", "marimba", "mask", "matchstick", "maypole", "maze", "measuring cup", "medicine chest", "megalith", "microphone", "microwave", "military uniform", "milk can", "minibus", "miniskirt", "minivan", "missile", "mitten", "mixing bowl", "mobile home", "Model T", "modem", "monastery", "monitor", "moped", "mortar", "mortarboard", "mosque", "mosquito net", "motor scooter", "mountain bike", "mountain tent", "mouse", "mousetrap", "moving van", "muzzle", "nail", "neck brace", "necklace", "nipple", "notebook", "obelisk", "oboe", "ocarina", "odometer", "oil filter", "organ", "oscilloscope", "overskirt", "oxcart", "oxygen mask", "packet", "paddle", "paddlewheel", "padlock", "paintbrush", "pajama", "palace", "panpipe", "paper towel", "parachute", "parallel bars", "park bench", "parking meter", "passenger car", "patio", "pay-phone", "pedestal", "pencil box", "pencil sharpener", "perfume", "Petri dish", "photocopier", "pick", "pickelhaube", "picket fence", "pickup", "pier", "piggy bank", "pill bottle", "pillow", "ping-pong ball", "pinwheel", "pirate", "pitcher", "plane", "planetarium", "plastic bag", "plate rack", "plow", "plunger", "Polaroid camera", "pole", "police van", "poncho", "pool table", "pop bottle", "pot", "potter's wheel", "power drill", "prayer rug", "printer", "prison", "projectile", "projector", "puck", "punching bag", "purse", "quill", "quilt", "racer", "racket", "radiator", "radio", "radio telescope", "rain barrel", "recreational vehicle", "reel", "reflex camera", "refrigerator", "remote control", "restaurant", "revolver", "rifle", "rocking chair", "rotisserie", "rubber eraser", "rugby ball", "rule", "running shoe", "safe", "safety pin", "saltshaker", "sandal", "sarong", "sax", "scabbard", "scale", "school bus", "schooner", "scoreboard", "screen", "screw", "screwdriver", "seat belt", "sewing machine", "shield", "shoe shop", "shoji", "shopping basket", "shopping cart", "shovel", "shower cap", "shower curtain", "ski", "ski mask", "sleeping bag", "slide rule", "sliding door", "slot", "snorkel", "snowmobile", "snowplow", "soap dispenser", "soccer ball", "sock", "solar dish", "sombrero", "soup bowl", "space bar", "space heater", "space shuttle", "spatula", "speedboat", "spider web", "spindle", "sports car", "spotlight", "stage", "steam locomotive", "steel arch bridge", "steel drum", "stethoscope", "stole", "stone wall", "stopwatch", "stove", "strainer", "streetcar", "stretcher", "studio couch", "stupa", "submarine", "suit", "sundial", "sunglass", "sunglasses", "sunscreen", "suspension bridge", "swab", "sweatshirt", "swimming trunks", "swing", "switch", "syringe", "table lamp", "tank", "tape player", "teapot", "teddy", "television", "tennis ball", "thatch", "theater curtain", "thimble", "thresher", "throne", "tile roof", "toaster", "tobacco shop", "toilet seat", "torch", "totem pole", "tow truck", "toyshop", "tractor", "trailer truck", "tray", "trench coat", "tricycle", "trimaran", "tripod", "triumphal arch", "trolleybus", "trombone", "tub", "turnstile", "typewriter keyboard", "umbrella", "unicycle", "upright", "vacuum", "vase", "vault", "velvet", "vending machine", "vestment", "viaduct", "violin", "volleyball", "waffle iron", "wall clock", "wallet", "wardrobe", "warplane", "washbasin", "washer", "water bottle", "water jug", "water tower", "whiskey jug", "whistle", "wig", "window screen", "window shade", "Windsor tie", "wine bottle", "wing", "wok", "wooden spoon", "wool", "worm fence", "wreck", "yawl", "yurt", "web site", "comic book", "crossword puzzle", "street sign", "traffic light", "book jacket", "menu", "plate", "guacamole", "consomme", "hot pot", "trifle", "ice cream", "ice lolly", "French loaf", "bagel", "pretzel", "cheeseburger", "hotdog", "mashed potato", "head cabbage", "broccoli", "cauliflower", "zucchini", "spaghetti squash", "acorn squash", "butternut squash", "cucumber", "artichoke", "bell pepper", "cardoon", "mushroom", "Granny Smith", "strawberry", "orange", "lemon", "fig", "pineapple", "banana", "jackfruit", "custard apple", "pomegranate", "hay", "carbonara", "chocolate sauce", "dough", "meat loaf", "pizza", "potpie", "burrito", "red wine", "espresso", "cup", "eggnog", "alp", "bubble", "cliff", "coral reef", "geyser", "lakeside", "promontory", "sandbar", "seashore", "valley", "volcano", "ballplayer", "groom", "scuba diver", "rapeseed", "daisy", "yellow lady's slipper", "corn", "acorn", "hip", "buckeye", "coral fungus", "agaric", "gyromitra", "stinkhorn", "earthstar", "hen-of-the-woods", "bolete", "ear", "toilet tissue"]
pixelmoe_src/model_pixelmoe.py ADDED
@@ -0,0 +1,1222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --------------------------------------------------------
2
+ # References:
3
+ # JiT: https://github.com/LTH14/JiT
4
+ # FLUX: https://github.com/black-forest-labs/flux
5
+ # Megatron-LM (weight initialization): https://github.com/NVIDIA/Megatron-LM
6
+ # --------------------------------------------------------
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import math
11
+ import torch.nn.functional as F
12
+
13
+ from deepseek_moe import (
14
+ DeepSeekMoEFFN,
15
+ DeepSeekMoEGate,
16
+ DeepSeekSharedExpert,
17
+ )
18
+
19
+ from typing import List, Optional
20
+ from einops import rearrange
21
+ from util.model_util import get_2d_sincos_pos_embed, RMSNorm
22
+
23
+ try:
24
+ from flash_attn.flash_attn_interface import flash_attn_varlen_func
25
+ except Exception:
26
+ flash_attn_varlen_func = None
27
+
28
+ # Spaces note: torch.compile is not supported inside ZeroGPU's forked GPU
29
+ # worker; the upstream decorators were plain torch.compile which would raise
30
+ # at import time there. The forward passes stay eager.
31
+
32
+
33
+ # Helpers
34
+
35
+ def modulate(x, shift: torch.Tensor, scale: torch.Tensor):
36
+ # Apply scale and shift to packed tokens; inputs and output: [1, total_tokens, D].
37
+ return x * (1 + scale) + shift
38
+
39
+
40
+ def expand_segment_embeddings(embed: torch.Tensor, hidden_length: List[int], batch_size: int):
41
+ """
42
+ Repeat each image's conditioning over its packed tokens.
43
+
44
+ Args
45
+ embed (torch.Tensor):
46
+ Conditioning embeddings with shape [num_images, D].
47
+ hidden_length (List[int]):
48
+ Token count per image, including context tokens when present.
49
+ batch_size (int):
50
+ 1 for packed tokens.
51
+ Returns
52
+ token embeddings with shape [1, sum(hidden_length), D].
53
+ """
54
+ num_segments = len(hidden_length)
55
+ lengths = torch.as_tensor(hidden_length, device=embed.device, dtype=torch.long)
56
+ embed = embed.reshape(batch_size, num_segments, -1).transpose(0, 1) # [num_segments, 1, D]
57
+ return torch.repeat_interleave(embed, lengths, dim=0).transpose(0, 1).contiguous() # [1, sum(hidden_length), D]
58
+
59
+
60
+ @torch._dynamo.disable
61
+ def split_ragged_context_image_tokens(mlp_input, lengths, context_len):
62
+ """
63
+ Split packed tokens into context and image groups.
64
+
65
+ Args
66
+ mlp_input (torch.Tensor):
67
+ Packed tokens with shape [1, sum(lengths), D].
68
+ lengths (List[int]):
69
+ Token count per image, including context tokens.
70
+ context_len (int):
71
+ Context token count per image.
72
+ Returns
73
+ context tokens [1, num_images * context_len, D],
74
+ image tokens [1, sum(image_lengths), D],
75
+ image token counts.
76
+ """
77
+ segments = torch.split(mlp_input, lengths, dim=1) # Each segment: [1, lengths[i], D].
78
+ context_input = torch.cat(
79
+ [segment[:, :context_len] for segment in segments],
80
+ dim=1,
81
+ ) # [1, num_images * context_len, D]
82
+ image_lengths = [length - context_len for length in lengths] # Image token count per image.
83
+ image_input = torch.cat(
84
+ [segment[:, context_len:] for segment in segments],
85
+ dim=1,
86
+ ) # [1, sum(image_lengths), D]
87
+ return context_input, image_input, image_lengths
88
+
89
+
90
+ @torch._dynamo.disable
91
+ def merge_ragged_context_image_tokens(
92
+ context_output,
93
+ image_output,
94
+ image_lengths,
95
+ context_len,
96
+ ):
97
+ """
98
+ Reassemble packed tokens in each image's context-then-image order.
99
+
100
+ Args
101
+ context_output (torch.Tensor):
102
+ Context tokens with shape [1, num_images * context_len, D].
103
+ image_output (torch.Tensor):
104
+ Image tokens with shape [1, sum(image_lengths), D].
105
+ image_lengths (List[int]):
106
+ Image token count per image, excluding context tokens.
107
+ context_len (int):
108
+ Context token count per image.
109
+ Returns
110
+ packed tokens [1, num_images * context_len + sum(image_lengths), D].
111
+ """
112
+ context_segments = torch.split(
113
+ context_output,
114
+ [context_len] * len(image_lengths),
115
+ dim=1,
116
+ ) # Each segment: [1, context_len, D].
117
+ image_segments = torch.split(image_output, image_lengths, dim=1) # Each segment: [1, image_lengths[i], D].
118
+ return torch.cat(
119
+ [
120
+ torch.cat([context_segment, image_segment], dim=1) # [1, context_len + image_lengths[i], D]
121
+ for context_segment, image_segment in zip(
122
+ context_segments,
123
+ image_segments,
124
+ )
125
+ ],
126
+ dim=1,
127
+ ) # [1, num_images * context_len + sum(image_lengths), D]
128
+
129
+
130
+ # RoPE
131
+
132
+ def rope(pos: torch.Tensor, dim: int, theta: int) -> torch.Tensor:
133
+ """
134
+ Build 2x2 rotation matrices for one position axis.
135
+
136
+ Args
137
+ pos (torch.Tensor):
138
+ Positions with shape [B, L].
139
+ dim (int):
140
+ Even feature dimension for this axis.
141
+ theta (int):
142
+ Frequency base.
143
+ Returns
144
+ FP32 rotation matrices with shape [B, L, dim // 2, 2, 2].
145
+ """
146
+ assert dim % 2 == 0, "The dimension must be even."
147
+
148
+ scale = torch.arange(0, dim, 2, dtype=torch.float64, device=pos.device) / dim
149
+ omega = 1.0 / (theta**scale)
150
+
151
+ batch_size = pos.shape[0]
152
+ out = torch.einsum("...n,d->...nd", pos, omega) # [B, L, dim // 2]
153
+ cos_out = torch.cos(out)
154
+ sin_out = torch.sin(out)
155
+
156
+ # Rotation matrix: [[cos, -sin], [sin, cos]].
157
+ stacked_out = torch.stack([cos_out, -sin_out, sin_out, cos_out], dim=-1)
158
+ out = stacked_out.view(batch_size, -1, dim // 2, 2, 2)
159
+ return out.float() # [B, L, dim // 2, 2, 2]
160
+
161
+
162
+ def apply_rope(xq, xk, freqs_cis):
163
+ """
164
+ Rotate query and key channel pairs in FP32 (B=1 for packed tokens).
165
+
166
+ Args
167
+ xq (torch.Tensor):
168
+ Query with shape [B, L, heads, head_dim].
169
+ xk (torch.Tensor):
170
+ Key with shape [B, L, heads, head_dim].
171
+ freqs_cis (torch.Tensor):
172
+ Rotation matrices [B, L, 1, head_dim // 2, 2, 2], shared across heads.
173
+ Returns
174
+ rotated query and key with their original shapes and dtypes.
175
+ """
176
+ xq_ = xq.float().reshape(*xq.shape[:-1], -1, 1, 2) # [B, L, heads, head_dim // 2, 1, 2]
177
+ xk_ = xk.float().reshape(*xk.shape[:-1], -1, 1, 2)
178
+ xq_out = freqs_cis[..., 0] * xq_[..., 0] + freqs_cis[..., 1] * xq_[..., 1] # [B, L, heads, head_dim // 2, 2]
179
+ xk_out = freqs_cis[..., 0] * xk_[..., 0] + freqs_cis[..., 1] * xk_[..., 1]
180
+ return xq_out.reshape(*xq.shape).type_as(xq), xk_out.reshape(*xk.shape).type_as(xk)
181
+
182
+
183
+ class EmbedND(nn.Module):
184
+ """
185
+ Build RoPE matrices for each position axis and concatenate them.
186
+
187
+ Args
188
+ theta (int):
189
+ Frequency base.
190
+ axes_dim (List[int]):
191
+ Feature dimension per axis; height and width in this model.
192
+ """
193
+ def __init__(self, theta: int, axes_dim: List[int]):
194
+ super().__init__()
195
+ self.theta = theta
196
+ self.axes_dim = axes_dim
197
+
198
+ def forward(self, ids: torch.Tensor) -> torch.Tensor:
199
+ """
200
+ Embed each token's position axes as RoPE matrices.
201
+
202
+ Args
203
+ ids (torch.Tensor):
204
+ Positions [B, L, num_axes]; B=1 and num_axes=2 for image tokens.
205
+ Returns
206
+ torch.Tensor: FP32 matrices [B, L, 1, sum(axes_dim) // 2, 2, 2], shared across heads.
207
+ """
208
+ n_axes = ids.shape[-1]
209
+ emb = torch.cat(
210
+ [rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
211
+ dim=-3,
212
+ ) # [B, L, sum(axes_dim) // 2, 2, 2]
213
+ return emb.unsqueeze(2) # [B, L, 1, sum(axes_dim) // 2, 2, 2]
214
+
215
+
216
+ # Condition embedders
217
+
218
+ class TimestepEmbedder(nn.Module):
219
+ """
220
+ Embeds global timestep t into a vector; stage-relative sigma
221
+ is used by the loss and solver, and is not passed to this embedder.
222
+ """
223
+ def __init__(self, hidden_size, frequency_embedding_size=256):
224
+ super().__init__()
225
+ self.mlp = nn.Sequential(
226
+ nn.Linear(frequency_embedding_size, hidden_size, bias=True),
227
+ nn.SiLU(),
228
+ nn.Linear(hidden_size, hidden_size, bias=True),
229
+ )
230
+ self.frequency_embedding_size = frequency_embedding_size
231
+
232
+ @staticmethod
233
+ def timestep_embedding(
234
+ timesteps: torch.Tensor,
235
+ embedding_dim: int,
236
+ max_period: int = 10000,
237
+ ):
238
+ """
239
+ Create sinusoidal embeddings of global timesteps.
240
+
241
+ Args
242
+ timesteps (torch.Tensor):
243
+ a 1-D tensor of N global timestep values, one per image.
244
+ embedding_dim (int):
245
+ the dimension of the output.
246
+ max_period (int):
247
+ Sets the largest period in the frequency scale.
248
+ Returns
249
+ timestep embeddings with shape [N, embedding_dim].
250
+ """
251
+ assert len(timesteps.shape) == 1, "Timesteps should be a 1D tensor."
252
+
253
+ half_dim = embedding_dim // 2
254
+ exponent = -math.log(max_period) * torch.arange(
255
+ start=0, end=half_dim, dtype=torch.float32, device=timesteps.device
256
+ ) # [Dim // 2]
257
+ exponent = exponent / (half_dim - 1)
258
+
259
+ emb = torch.exp(exponent)
260
+ emb = timesteps[:, None].float() * emb[None, :] # [B, Dim // 2]
261
+
262
+ # Concatenate cosine and sine embeddings.
263
+ emb = torch.cat([torch.cos(emb), torch.sin(emb)], dim=-1) # [B, Dim]
264
+
265
+ # zero pad
266
+ if embedding_dim % 2 == 1:
267
+ emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
268
+ return emb
269
+
270
+ def forward(self, t):
271
+ t_freq = self.timestep_embedding(t, self.frequency_embedding_size)
272
+ t_freq = t_freq.to(dtype=self.mlp[0].weight.dtype)
273
+ t_emb = self.mlp(t_freq)
274
+ return t_emb
275
+
276
+
277
+ class LabelEmbedder(nn.Module):
278
+ """
279
+ Embed class labels; index num_classes represents the unconditional label.
280
+ """
281
+ def __init__(self, num_classes, hidden_size):
282
+ super().__init__()
283
+ self.embedding_table = nn.Embedding(num_classes + 1, hidden_size)
284
+
285
+ def forward(self, labels):
286
+ embeddings = self.embedding_table(labels)
287
+ return embeddings
288
+
289
+
290
+ # Attention
291
+
292
+ @torch._dynamo.disable
293
+ def build_flash_cu_seqlens(hidden_length, device):
294
+ """
295
+ Build cumulative token offsets for FlashAttention in eager mode.
296
+
297
+ Args
298
+ hidden_length (List[int]):
299
+ Token count per image in packed order.
300
+ device (torch.device):
301
+ Device for the offsets tensor.
302
+ Returns
303
+ offsets with shape [num_images + 1], starting at zero.
304
+ """
305
+ offsets = [0]
306
+ total = 0
307
+ for length in hidden_length:
308
+ length = int(length)
309
+ total += length
310
+ offsets.append(total)
311
+ return torch.tensor(offsets, device=device, dtype=torch.int32)
312
+
313
+
314
+ class VarlenFlashAttn(nn.Module):
315
+ @torch._dynamo.disable
316
+ def forward(self, query, key, value, cu_seqlens, max_seqlen, scale=None):
317
+ cu_seqlens = cu_seqlens.to(device=query.device, dtype=torch.int32).contiguous()
318
+ return flash_attn_varlen_func(
319
+ query,
320
+ key,
321
+ value,
322
+ cu_seqlens_q=cu_seqlens,
323
+ cu_seqlens_k=cu_seqlens,
324
+ max_seqlen_q=max_seqlen,
325
+ max_seqlen_k=max_seqlen,
326
+ dropout_p=0.0,
327
+ causal=False,
328
+ softmax_scale=scale,
329
+ )
330
+
331
+
332
+ class VarlenFlashSelfAttnSingle(nn.Module):
333
+ def __init__(self):
334
+ super().__init__()
335
+ self.flash_attn = VarlenFlashAttn()
336
+
337
+ def forward(
338
+ self,
339
+ query,
340
+ key,
341
+ value,
342
+ scale,
343
+ hidden_length=None,
344
+ image_rotary_emb=None,
345
+ ):
346
+ if flash_attn_varlen_func is None:
347
+ raise ImportError("flash-attn is required when use_flash_attn=True")
348
+
349
+ # Stack Q/K/V and apply RoPE to Q/K only.
350
+ qkv = torch.stack([query, key, value], dim=2)
351
+ if image_rotary_emb is not None:
352
+ qkv[:, :, 0], qkv[:, :, 1] = apply_rope(
353
+ qkv[:, :, 0],
354
+ qkv[:, :, 1],
355
+ image_rotary_emb,
356
+ )
357
+
358
+ qkv = rearrange(qkv, "b s ... -> (b s) ...") # [total_tokens, 3, heads, head_dim]
359
+ query, key, value = qkv.unbind(1)
360
+
361
+ # Token offsets keep attention separate for each image in the packed sequence.
362
+ max_seqlen_q = max(hidden_length)
363
+ cu_seqlens_q = build_flash_cu_seqlens(
364
+ hidden_length,
365
+ query.device,
366
+ )
367
+ output = self.flash_attn(query, key, value, cu_seqlens_q, max_seqlen_q, scale)
368
+ return output.reshape(1, -1, output.shape[-2], output.shape[-1]).flatten(2, 3) # [1, total_tokens, heads * head_dim]
369
+
370
+
371
+ class VarlenSelfAttnSingle:
372
+ def __init__(self):
373
+ pass
374
+
375
+ def __call__(
376
+ self,
377
+ query,
378
+ key,
379
+ value,
380
+ hidden_length=None,
381
+ image_rotary_emb=None,
382
+ ):
383
+
384
+ # Stack Q/K/V and apply RoPE to Q/K only.
385
+ qkv = torch.stack([query, key, value], dim=2) # [1, total_tokens, 3, heads, head_dim]
386
+ if image_rotary_emb is not None:
387
+ qkv[:, :, 0], qkv[:, :, 1] = apply_rope(
388
+ qkv[:, :, 0],
389
+ qkv[:, :, 1],
390
+ image_rotary_emb,
391
+ )
392
+
393
+ i_sum = 0
394
+ output_hidden_list = []
395
+ # Compute attention within each image's token segment.
396
+ for length in hidden_length:
397
+ qkv_tokens = qkv[:, i_sum:i_sum + length] # [1, length, 3, heads, head_dim]
398
+
399
+ query, key, value = qkv_tokens.unbind(2)
400
+ # SDPA expects Q/K/V in [1, heads, length, head_dim].
401
+ query = query.transpose(1, 2).contiguous()
402
+ key = key.transpose(1, 2).contiguous()
403
+ value = value.transpose(1, 2).contiguous()
404
+
405
+ stage_hidden_states = F.scaled_dot_product_attention(
406
+ query,
407
+ key,
408
+ value,
409
+ dropout_p=0.0,
410
+ is_causal=False,
411
+ )
412
+ stage_hidden_states = stage_hidden_states.transpose(1, 2).flatten(2, 3) # [1, length, heads * head_dim]
413
+ output_hidden_list.append(stage_hidden_states)
414
+ i_sum += length
415
+
416
+ return torch.cat(output_hidden_list, dim=1) # [1, total_tokens, heads * head_dim]
417
+
418
+
419
+ class FLUXAttention(nn.Module):
420
+
421
+ def __init__(
422
+ self,
423
+ query_dim: int,
424
+ heads: int = 8,
425
+ dim_head: int = 64,
426
+ dropout: float = 0.0,
427
+ eps: float = 1e-6,
428
+ use_flash_attn=False,
429
+ ):
430
+ super().__init__()
431
+ self.heads = heads
432
+ self.scale = dim_head**-0.5
433
+ inner_dim = heads * dim_head
434
+ self.norm_q = RMSNorm(dim_head, eps=eps)
435
+ self.norm_k = RMSNorm(dim_head, eps=eps)
436
+ self.to_q = nn.Linear(query_dim, inner_dim, bias=False)
437
+ self.to_k = nn.Linear(query_dim, inner_dim, bias=False)
438
+ self.to_v = nn.Linear(query_dim, inner_dim, bias=False)
439
+ self.to_out = nn.ModuleList([
440
+ nn.Linear(inner_dim, query_dim, bias=False),
441
+ nn.Dropout(dropout),
442
+ ])
443
+ self.use_flash_attn = use_flash_attn
444
+ if self.use_flash_attn:
445
+ self.varlen_flash_attn = VarlenFlashSelfAttnSingle()
446
+ else:
447
+ self.varlen_attn = VarlenSelfAttnSingle()
448
+
449
+ def forward(
450
+ self,
451
+ hidden_states: torch.Tensor,
452
+ hidden_length: List = None,
453
+ image_rotary_emb: Optional[torch.Tensor] = None,
454
+ ) -> torch.Tensor:
455
+
456
+ # Project packed tokens to Q/K/V: [1, total_tokens, inner_dim].
457
+ query = self.to_q(hidden_states)
458
+ key = self.to_k(hidden_states)
459
+ value = self.to_v(hidden_states)
460
+
461
+ inner_dim = key.shape[-1]
462
+ head_dim = inner_dim // self.heads
463
+
464
+ # Split attention heads: [1, total_tokens, heads, head_dim].
465
+ query = query.view(query.shape[0], -1, self.heads, head_dim)
466
+ key = key.view(key.shape[0], -1, self.heads, head_dim)
467
+ value = value.view(value.shape[0], -1, self.heads, head_dim)
468
+
469
+ # Apply RMSNorm to each head's Q/K features.
470
+ query = self.norm_q(query)
471
+ key = self.norm_k(key)
472
+
473
+ # Compute per-image attention and merge heads: [1, total_tokens, inner_dim].
474
+ if self.use_flash_attn:
475
+ hidden_states = self.varlen_flash_attn(
476
+ query, key, value,
477
+ self.scale, hidden_length,
478
+ image_rotary_emb,
479
+ )
480
+ else:
481
+ hidden_states = self.varlen_attn(
482
+ query, key, value,
483
+ hidden_length,
484
+ image_rotary_emb,
485
+ )
486
+
487
+ # Project back to the input width, then apply dropout.
488
+ hidden_states = self.to_out[0](hidden_states)
489
+ hidden_states = self.to_out[1](hidden_states)
490
+
491
+ return hidden_states
492
+
493
+
494
+ # Model components
495
+
496
+ class BottleneckPatchEmbed(nn.Module):
497
+ """
498
+ Image to Patch Embedding
499
+ """
500
+ def __init__(self, img_size=224, patch_size=16, in_chans=3, pca_dim=768, embed_dim=768):
501
+ super().__init__()
502
+ img_size = (img_size, img_size)
503
+ patch_size = (patch_size, patch_size)
504
+ num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0])
505
+ self.num_patches = num_patches
506
+
507
+ self.proj1 = nn.Conv2d(in_chans, pca_dim, kernel_size=patch_size, stride=patch_size, bias=False)
508
+ self.proj2 = nn.Conv2d(pca_dim, embed_dim, kernel_size=1, stride=1, bias=True)
509
+
510
+ def forward(self, x):
511
+ x = self.proj2(self.proj1(x)).flatten(2).transpose(1, 2)
512
+ return x
513
+
514
+
515
+ class SwiGLUFFN(nn.Module):
516
+ def __init__(
517
+ self,
518
+ dim: int,
519
+ hidden_dim: int,
520
+ drop=0.0,
521
+ ) -> None:
522
+ super().__init__()
523
+ hidden_dim = int(hidden_dim * 2 / 3)
524
+ self.w12 = nn.Linear(dim, 2 * hidden_dim, bias=False)
525
+ self.w3 = nn.Linear(hidden_dim, dim, bias=False)
526
+ self.ffn_dropout = nn.Dropout(drop)
527
+
528
+ def forward(self, x):
529
+ x12 = self.w12(x)
530
+ x1, x2 = x12.chunk(2, dim=-1)
531
+ hidden = F.silu(x1) * x2
532
+ return self.w3(self.ffn_dropout(hidden))
533
+
534
+
535
+ class PixelMoEBlock(nn.Module):
536
+ def __init__(
537
+ self,
538
+ hidden_size,
539
+ num_heads,
540
+ flux_attn_proj_dropout=False,
541
+ use_flash_attn=False,
542
+ mlp_ratio=4.0,
543
+ proj_drop=0.0,
544
+ use_moe=False,
545
+ moe_num_experts=8,
546
+ moe_top_k=2,
547
+ moe_routed_inter_dim=None,
548
+ moe_shared_inter_dim=None,
549
+ moe_route_scale=1.0,
550
+ context_token_count=0,
551
+ moe_router_bias_update_rate=1e-3,
552
+ moe_num_layers=None,
553
+ ):
554
+ super().__init__()
555
+ self.norm1 = RMSNorm(hidden_size, eps=1e-6)
556
+ attention_head_dim = hidden_size // num_heads
557
+ self.attn = FLUXAttention(
558
+ query_dim=hidden_size,
559
+ dim_head=attention_head_dim,
560
+ heads=num_heads,
561
+ eps=1e-6,
562
+ use_flash_attn=use_flash_attn,
563
+ dropout=proj_drop if flux_attn_proj_dropout else 0.0,
564
+ )
565
+
566
+ self.norm2 = RMSNorm(hidden_size, eps=1e-6)
567
+ mlp_hidden_dim = int(hidden_size * mlp_ratio)
568
+ if use_moe:
569
+ self.mlp = DeepSeekMoEFFN(
570
+ hidden_size,
571
+ num_experts=moe_num_experts,
572
+ top_k=moe_top_k,
573
+ routed_inter_dim=moe_routed_inter_dim,
574
+ shared_inter_dim=moe_shared_inter_dim,
575
+ route_scale=moe_route_scale,
576
+ router_bias_update_rate=moe_router_bias_update_rate,
577
+ drop=proj_drop,
578
+ num_layers=moe_num_layers,
579
+ )
580
+ else:
581
+ self.mlp = SwiGLUFFN(
582
+ hidden_size,
583
+ mlp_hidden_dim,
584
+ drop=proj_drop,
585
+ )
586
+ self.class_token_mlp = (
587
+ SwiGLUFFN(
588
+ hidden_size,
589
+ mlp_hidden_dim,
590
+ drop=proj_drop,
591
+ )
592
+ if use_moe and context_token_count > 0
593
+ else None
594
+ )
595
+ self.context_token_count = int(context_token_count)
596
+ self.adaLN_modulation = nn.Sequential(
597
+ nn.SiLU(),
598
+ nn.Linear(hidden_size, 6 * hidden_size, bias=True)
599
+ )
600
+
601
+ def _forward_mlp(
602
+ self,
603
+ mlp_input,
604
+ hidden_length=None,
605
+ ):
606
+ # Use one FFN when no separate class-token FFN is needed.
607
+ if self.class_token_mlp is None:
608
+ return self.mlp(
609
+ mlp_input,
610
+ )
611
+
612
+ context_len = self.context_token_count
613
+ # Split packed context/image tokens.
614
+ lengths = [int(length) for length in hidden_length]
615
+ if any(length <= context_len for length in lengths):
616
+ raise ValueError(
617
+ "Every ragged segment must contain class-context and image tokens; "
618
+ f"got lengths={lengths}, context_token_count={context_len}"
619
+ )
620
+ context_input, image_input, image_lengths = split_ragged_context_image_tokens(
621
+ mlp_input,
622
+ lengths,
623
+ context_len,
624
+ )
625
+
626
+ context_output = self.class_token_mlp(context_input)
627
+ image_output = self.mlp(
628
+ image_input,
629
+ )
630
+ return merge_ragged_context_image_tokens(
631
+ context_output,
632
+ image_output,
633
+ image_lengths,
634
+ context_len,
635
+ )
636
+
637
+ def forward(
638
+ self,
639
+ x,
640
+ c,
641
+ hidden_length=None,
642
+ image_rotary_emb=None,
643
+ ):
644
+ # Expand each image's AdaLN conditioning to its packed tokens.
645
+ embed = self.adaLN_modulation(c)
646
+ batch_emb = expand_segment_embeddings(embed, hidden_length, x.shape[0]) # [1, total_tokens, 6 * D]
647
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = batch_emb.chunk(6, dim=-1)
648
+
649
+ # Attention with AdaLN modulation and a gated residual.
650
+ attn_input = modulate(self.norm1(x), shift_msa, scale_msa)
651
+ attn_output = self.attn(
652
+ hidden_states=attn_input,
653
+ hidden_length=hidden_length,
654
+ image_rotary_emb=image_rotary_emb,
655
+ )
656
+ x = x + gate_msa * attn_output
657
+
658
+ # FFN with AdaLN modulation and a gated residual.
659
+ mlp_input = modulate(self.norm2(x), shift_mlp, scale_mlp)
660
+ mlp_output = self._forward_mlp(
661
+ mlp_input,
662
+ hidden_length=hidden_length,
663
+ )
664
+ x = x + gate_mlp * mlp_output
665
+ return x
666
+
667
+
668
+ class PixelMoEDDTBlock(nn.Module):
669
+ """
670
+ Wide DDT head block with token-wise AdaLN conditioning of shape [B, L, D].
671
+ """
672
+ def __init__(
673
+ self,
674
+ hidden_size,
675
+ num_heads,
676
+ use_flash_attn=False,
677
+ mlp_ratio=4.0,
678
+ proj_drop=0.0,
679
+ ):
680
+ super().__init__()
681
+ self.norm1 = RMSNorm(hidden_size, eps=1e-6)
682
+ attention_head_dim = hidden_size // num_heads
683
+ self.attn = FLUXAttention(
684
+ query_dim=hidden_size,
685
+ dim_head=attention_head_dim,
686
+ heads=num_heads,
687
+ eps=1e-6,
688
+ use_flash_attn=use_flash_attn,
689
+ )
690
+
691
+ self.norm2 = RMSNorm(hidden_size, eps=1e-6)
692
+ mlp_hidden_dim = int(hidden_size * mlp_ratio)
693
+ self.mlp = SwiGLUFFN(
694
+ hidden_size,
695
+ mlp_hidden_dim,
696
+ drop=proj_drop,
697
+ )
698
+ self.adaLN_modulation = nn.Sequential(
699
+ nn.SiLU(),
700
+ nn.Linear(hidden_size, 6 * hidden_size, bias=True)
701
+ )
702
+
703
+ def forward(
704
+ self,
705
+ x,
706
+ c,
707
+ hidden_length=None,
708
+ image_rotary_emb=None,
709
+ ):
710
+ # Per-token AdaLN shift, scale, and gates; c: [1, total_tokens, D].
711
+ embed = self.adaLN_modulation(c)
712
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = embed.chunk(6, dim=-1)
713
+
714
+ # Attention with AdaLN modulation and a gated residual.
715
+ attn_input = modulate(self.norm1(x), shift_msa, scale_msa)
716
+ attn_output = self.attn(
717
+ hidden_states=attn_input,
718
+ hidden_length=hidden_length,
719
+ image_rotary_emb=image_rotary_emb,
720
+ )
721
+ x = x + attn_output * gate_msa
722
+
723
+ # Dense SwiGLU FFN with AdaLN modulation and a gated residual.
724
+ mlp_output = self.mlp(modulate(self.norm2(x), shift_mlp, scale_mlp))
725
+ x = x + mlp_output * gate_mlp
726
+ return x
727
+
728
+
729
+ class DDTFinalLayer(nn.Module):
730
+ """
731
+ Token-wise conditioned final layer used by PixelMoE.
732
+ """
733
+ def __init__(self, hidden_size, patch_size, out_channels):
734
+ super().__init__()
735
+ self.norm_final = RMSNorm(hidden_size)
736
+ self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
737
+ self.adaLN_modulation = nn.Sequential(
738
+ nn.SiLU(),
739
+ nn.Linear(hidden_size, 2 * hidden_size, bias=True)
740
+ )
741
+
742
+ def forward(self, x, c):
743
+ # Apply per-token AdaLN conditioning before the output projection.
744
+ embed = self.adaLN_modulation(c)
745
+ shift, scale = embed.chunk(2, dim=-1)
746
+ x = modulate(self.norm_final(x), shift, scale)
747
+ x = self.linear(x) # [1, total_tokens, patch_size * patch_size * out_channels]
748
+ return x
749
+
750
+
751
+ # PixelMoE
752
+
753
+ class PixelMoE(nn.Module):
754
+ """PixelMoE body with a wide DDT head and FLUX variable-length attention."""
755
+ def __init__(
756
+ self,
757
+ input_size=256,
758
+ patch_size=16,
759
+ in_channels=3,
760
+ hidden_size=1024,
761
+ depth=24,
762
+ num_heads=16,
763
+ mlp_ratio=4.0,
764
+ proj_drop=0.0,
765
+ num_classes=1000,
766
+ bottleneck_dim=128,
767
+ in_context_len=32,
768
+ in_context_start=8,
769
+ flux_attn_proj_dropout=False,
770
+ use_flash_attn=False,
771
+ ddt_head_dim=2048,
772
+ ddt_head_depth=2,
773
+ ddt_head_num_heads=16,
774
+ moe_num_experts=8,
775
+ moe_top_k=2,
776
+ moe_routed_inter_dim=None,
777
+ moe_shared_inter_dim=None,
778
+ moe_route_scale=1.0,
779
+ moe_n_dense_layers=1,
780
+ moe_router_bias_update_rate=1e-3,
781
+ ):
782
+ super().__init__()
783
+ self.in_channels = in_channels
784
+ self.out_channels = in_channels
785
+ self.patch_size = patch_size
786
+ self.hidden_size = hidden_size
787
+ self.input_size = input_size
788
+ self.depth = depth
789
+ self.in_context_len = in_context_len
790
+ self.in_context_start = in_context_start
791
+ self.use_flash_attn = use_flash_attn
792
+ # Base initialization std is 0.02; residual outputs use body-depth scaling.
793
+ self.megatron_init_std = 0.02
794
+ self.megatron_init_num_layers = depth
795
+
796
+ # time and class embed
797
+ self.t_embedder = TimestepEmbedder(hidden_size)
798
+ self.y_embedder = LabelEmbedder(num_classes, hidden_size)
799
+
800
+ # linear embed
801
+ self.x_embedder = BottleneckPatchEmbed(input_size, patch_size, in_channels, bottleneck_dim, hidden_size)
802
+
803
+ # use fixed sin-cos embedding
804
+ num_patches = self.x_embedder.num_patches
805
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, hidden_size), requires_grad=False)
806
+
807
+ # in-context cls token
808
+ self.in_context_posemb = nn.Parameter(torch.zeros(1, self.in_context_len, hidden_size), requires_grad=True)
809
+ torch.nn.init.normal_(self.in_context_posemb, std=.02)
810
+
811
+ self.hierarchical_pos_embed_cache = {}
812
+
813
+ attention_head_dim = hidden_size // num_heads
814
+ self.hierarchical_rope_embed = EmbedND(
815
+ theta=10000,
816
+ axes_dim=[attention_head_dim // 2, attention_head_dim // 2],
817
+ )
818
+
819
+ # transformer
820
+ self.blocks = nn.ModuleList([
821
+ PixelMoEBlock(hidden_size, num_heads, mlp_ratio=mlp_ratio, use_flash_attn=use_flash_attn,
822
+ flux_attn_proj_dropout=flux_attn_proj_dropout,
823
+ proj_drop=proj_drop if (depth // 4 * 3 > i >= depth // 4) else 0.0,
824
+ use_moe=i >= moe_n_dense_layers,
825
+ context_token_count=in_context_len if i >= in_context_start else 0,
826
+ moe_num_experts=moe_num_experts,
827
+ moe_top_k=moe_top_k,
828
+ moe_routed_inter_dim=moe_routed_inter_dim,
829
+ moe_shared_inter_dim=moe_shared_inter_dim,
830
+ moe_route_scale=moe_route_scale,
831
+ moe_router_bias_update_rate=moe_router_bias_update_rate,
832
+ moe_num_layers=self.megatron_init_num_layers,
833
+ )
834
+ for i in range(depth)
835
+ ])
836
+
837
+ self.initialize_weights()
838
+
839
+ self.ddt_x_embedder = BottleneckPatchEmbed(
840
+ self.input_size,
841
+ self.patch_size,
842
+ self.in_channels,
843
+ ddt_head_dim,
844
+ ddt_head_dim,
845
+ )
846
+ self.ddt_s_projector = nn.Linear(self.hidden_size, ddt_head_dim)
847
+ self.ddt_blocks = nn.ModuleList([
848
+ PixelMoEDDTBlock(
849
+ ddt_head_dim,
850
+ ddt_head_num_heads,
851
+ mlp_ratio=4.0,
852
+ use_flash_attn=self.use_flash_attn,
853
+ )
854
+ for _ in range(ddt_head_depth)
855
+ ])
856
+ self.ddt_final_layer = DDTFinalLayer(ddt_head_dim, self.patch_size, self.out_channels)
857
+
858
+ attention_head_dim = ddt_head_dim // ddt_head_num_heads
859
+ self.ddt_hierarchical_rope_embed = EmbedND(
860
+ theta=10000,
861
+ axes_dim=[attention_head_dim // 2, attention_head_dim // 2],
862
+ )
863
+ self.initialize_ddt_weights()
864
+
865
+ def initialize_weights(self):
866
+ self._initialize_megatron_linear_modules(self)
867
+
868
+ # Initialize (and freeze) pos_embed using sinusoidal embeddings:
869
+ pos_embed = get_2d_sincos_pos_embed(self.pos_embed.shape[-1], int(self.x_embedder.num_patches ** 0.5))
870
+ self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
871
+
872
+ self._initialize_megatron_patch_embed(self.x_embedder)
873
+ nn.init.normal_(self.y_embedder.embedding_table.weight, mean=0.0, std=self.megatron_init_std)
874
+ nn.init.normal_(self.in_context_posemb, mean=0.0, std=self.megatron_init_std)
875
+ self._initialize_megatron_output_projections(self)
876
+
877
+ # Zero-out adaLN modulation layers:
878
+ for block in self.blocks:
879
+ nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
880
+ nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
881
+
882
+ def initialize_ddt_weights(self):
883
+ self._initialize_megatron_linear_modules(self.ddt_s_projector)
884
+ self._initialize_megatron_linear_modules(self.ddt_blocks)
885
+ self._initialize_megatron_patch_embed(self.ddt_x_embedder)
886
+ self._initialize_megatron_output_projections(self.ddt_blocks)
887
+
888
+ for block in self.ddt_blocks:
889
+ nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
890
+ nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
891
+
892
+ nn.init.constant_(self.ddt_final_layer.adaLN_modulation[-1].weight, 0)
893
+ nn.init.constant_(self.ddt_final_layer.adaLN_modulation[-1].bias, 0)
894
+ nn.init.constant_(self.ddt_final_layer.linear.weight, 0)
895
+ nn.init.constant_(self.ddt_final_layer.linear.bias, 0)
896
+
897
+ @property
898
+ def megatron_output_init_std(self):
899
+ return self.megatron_init_std / math.sqrt(2.0 * self.megatron_init_num_layers)
900
+
901
+ def _initialize_megatron_linear_modules(self, root):
902
+ """
903
+ Initialize ordinary projections with the Megatron base std.
904
+ """
905
+ def _init_linear(module):
906
+ if isinstance(module, nn.Linear):
907
+ nn.init.normal_(module.weight, mean=0.0, std=self.megatron_init_std)
908
+ if module.bias is not None:
909
+ nn.init.constant_(module.bias, 0)
910
+ elif isinstance(module, DeepSeekMoEGate):
911
+ nn.init.normal_(module.weight, mean=0.0, std=self.megatron_init_std)
912
+
913
+ root.apply(_init_linear)
914
+
915
+ def _initialize_megatron_patch_embed(self, patch_embed):
916
+ for projection in (patch_embed.proj1, patch_embed.proj2):
917
+ nn.init.normal_(projection.weight, mean=0.0, std=self.megatron_init_std)
918
+ if projection.bias is not None:
919
+ nn.init.constant_(projection.bias, 0)
920
+
921
+ def _initialize_megatron_output_projections(self, root):
922
+ """
923
+ Initialize attention and FFN output projections with depth-scaled std.
924
+ """
925
+ for module in root.modules():
926
+ if isinstance(module, FLUXAttention):
927
+ output_projection = module.to_out[0]
928
+ nn.init.normal_(output_projection.weight, mean=0.0, std=self.megatron_output_init_std)
929
+ elif isinstance(module, SwiGLUFFN):
930
+ nn.init.normal_(module.w3.weight, mean=0.0, std=self.megatron_output_init_std)
931
+ elif isinstance(module, DeepSeekSharedExpert):
932
+ nn.init.normal_(module.w2.weight, mean=0.0, std=self.megatron_output_init_std)
933
+
934
+ def get_hierarchical_pos_embed(self, num_patches, device=None, dtype=None):
935
+ """
936
+ Get cached sin-cos position embeddings for a square patch grid.
937
+
938
+ Args
939
+ num_patches (int):
940
+ Number of image patch tokens.
941
+ device (torch.device, optional):
942
+ Device for the returned embeddings.
943
+ dtype (torch.dtype, optional):
944
+ Dtype for the returned embeddings.
945
+ Returns
946
+ torch.Tensor: position embeddings [1, num_patches, hidden_size].
947
+ """
948
+ key = str(num_patches)
949
+ if key not in self.hierarchical_pos_embed_cache:
950
+ grid_size = int(num_patches ** 0.5)
951
+ if grid_size * grid_size != num_patches:
952
+ raise ValueError(f"Expected square patch grid, got {num_patches} patches")
953
+
954
+ pos_embed = get_2d_sincos_pos_embed(self.hidden_size, grid_size)
955
+ self.hierarchical_pos_embed_cache[key] = torch.from_numpy(pos_embed).float().unsqueeze(0)
956
+
957
+ return self.hierarchical_pos_embed_cache[key].to(device=device, dtype=dtype)
958
+
959
+ @torch.no_grad()
960
+ def _prepare_image_ids(self, height, width, device):
961
+ """
962
+ Build height/width coordinates for image-token RoPE.
963
+
964
+ Args
965
+ height (int):
966
+ Patch grid height.
967
+ width (int):
968
+ Patch grid width.
969
+ device (torch.device):
970
+ Device for the coordinates.
971
+ Returns
972
+ coordinates [1, height * width, 2], in row-major order.
973
+ """
974
+ height_pos = torch.arange(height, device=device, dtype=torch.float32)
975
+ width_pos = torch.arange(width, device=device, dtype=torch.float32)
976
+ image_ids = torch.stack(torch.meshgrid(height_pos, width_pos, indexing="ij"), dim=-1)
977
+
978
+ return rearrange(image_ids.unsqueeze(0), "b h w c -> b (h w) c")
979
+
980
+
981
+ def _ddt_condition(self, body_tokens, t_emb, hidden_length):
982
+ """
983
+ Combine body features and timestep embeddings to condition the DDT head.
984
+
985
+ Args
986
+ body_tokens (torch.Tensor):
987
+ Packed body features [1, total_tokens, hidden_size].
988
+ t_emb (torch.Tensor):
989
+ Timestep embeddings [num_images, hidden_size].
990
+ hidden_length (List[int]):
991
+ Image token count per image, excluding context tokens.
992
+ Returns
993
+ torch.Tensor: DDT conditioning [1, total_tokens, ddt_head_dim].
994
+ """
995
+ t_tokens = expand_segment_embeddings(t_emb, hidden_length, body_tokens.shape[0])
996
+ condition = F.silu(body_tokens + t_tokens)
997
+ return self.ddt_s_projector(condition)
998
+
999
+ def forward(self, sample, timesteps, labels):
1000
+ """
1001
+ Predict stage endpoints conditioned on global timesteps t.
1002
+
1003
+ Args
1004
+ sample (torch.Tensor or List[torch.Tensor]):
1005
+ Images [stage_batch_size, C, H, W] for each active stage.
1006
+ timesteps (torch.Tensor or List[torch.Tensor]):
1007
+ Global timesteps [stage_batch_size] for each active stage.
1008
+ labels (torch.Tensor):
1009
+ Class labels [num_images], concatenated in stage order.
1010
+ Returns
1011
+ List[torch.Tensor]: endpoint predictions [stage_batch_size, C, H, W] per active stage.
1012
+ """
1013
+ samples = sample if isinstance(sample, list) else [sample]
1014
+ timestep_list = timesteps if isinstance(timesteps, list) else [timesteps]
1015
+ if len(samples) != len(timestep_list):
1016
+ raise ValueError("sample and timesteps must have the same number of active stages")
1017
+
1018
+ stage_tokens = []
1019
+ stage_ddt_tokens = []
1020
+ stage_batch_sizes = []
1021
+ stage_token_counts = []
1022
+ hidden_length = []
1023
+ output_shapes = []
1024
+ image_rotary_emb = []
1025
+ concat_image_rotary_emb = []
1026
+ ddt_image_rotary_emb = []
1027
+
1028
+ for stage_sample in samples:
1029
+ batch_size, _, image_height, image_width = stage_sample.shape # [B_stage, C, H, W]
1030
+ token_height = image_height // self.patch_size # Patch grid height.
1031
+ token_width = image_width // self.patch_size # Patch grid width.
1032
+ # Embed image patches separately for the body and DDT head.
1033
+ tokens = self.x_embedder(stage_sample) # [B_stage, num_patches, hidden_size]
1034
+ tokens = tokens + self.get_hierarchical_pos_embed(tokens.shape[1], tokens.device, tokens.dtype) # Add [1, num_patches, hidden_size].
1035
+ ddt_tokens = self.ddt_x_embedder(stage_sample) # [B_stage, num_patches, ddt_head_dim]
1036
+ token_count = tokens.shape[1] # num_patches = token_height * token_width
1037
+
1038
+ # Image tokens use grid coordinates; class context tokens use (0, 0).
1039
+ base_ids = self._prepare_image_ids(token_height, token_width, tokens.device) # [1, num_patches, 2]
1040
+ label_ids = torch.zeros(
1041
+ 1,
1042
+ self.in_context_len,
1043
+ base_ids.shape[-1],
1044
+ device=tokens.device,
1045
+ dtype=base_ids.dtype,
1046
+ ) # [1, in_context_len, 2]
1047
+ context_ids = torch.cat([label_ids, base_ids], dim=1) # [1, in_context_len + num_patches, 2]
1048
+
1049
+ # Pack all images into batch_size=1; hidden_length retains image boundaries.
1050
+ stage_tokens.append(rearrange(tokens, "b s d -> 1 (b s) d"))
1051
+ stage_ddt_tokens.append(rearrange(ddt_tokens, "b s d -> 1 (b s) d"))
1052
+ stage_batch_sizes.append(batch_size)
1053
+ stage_token_counts.append(token_count)
1054
+ hidden_length.extend([token_count] * batch_size)
1055
+ output_shapes.append((token_height, token_width))
1056
+ # Images in the same stage share the same positional grid.
1057
+ image_rotary_emb.extend([self.hierarchical_rope_embed(base_ids)] * batch_size)
1058
+ concat_image_rotary_emb.extend([self.hierarchical_rope_embed(context_ids)] * batch_size)
1059
+ ddt_image_rotary_emb.extend([self.ddt_hierarchical_rope_embed(base_ids)] * batch_size)
1060
+
1061
+ # Concatenate stages in the same order for tokens, RoPE, and conditioning.
1062
+ image_rotary_emb = torch.cat(image_rotary_emb, dim=1) # [1, total_tokens, 1, body_head_dim // 2, 2, 2]
1063
+ concat_image_rotary_emb = torch.cat(concat_image_rotary_emb, dim=1) # Image + context tokens, in per-image order.
1064
+ ddt_image_rotary_emb = torch.cat(ddt_image_rotary_emb, dim=1) # [1, total_tokens, 1, ddt_head_dim_per_head // 2, 2, 2]
1065
+ hidden_states = torch.cat(stage_tokens, dim=1) # [1, total_tokens, hidden_size]
1066
+ ddt_hidden_states = torch.cat(stage_ddt_tokens, dim=1) # [1, total_tokens, ddt_head_dim]
1067
+ timesteps = torch.cat(timestep_list, dim=0) # [num_images], in stage order.
1068
+ if labels.shape[0] != timesteps.shape[0]:
1069
+ raise ValueError("labels must be concatenated in the same stage order as sample")
1070
+ t_emb = self.t_embedder(timesteps.reshape(-1)) # [num_images, hidden_size]
1071
+ y_emb = self.y_embedder(labels) # [num_images, hidden_size]
1072
+ c = t_emb + y_emb # [num_images, hidden_size]
1073
+
1074
+ has_label_context = False # Context tokens are inserted at in_context_start.
1075
+ image_hidden_length = list(hidden_length) # Keep image-only lengths for removing context later.
1076
+
1077
+ def add_label_context(states):
1078
+ # Prepend class embeddings plus learned context positions to each image.
1079
+ stage_segments = []
1080
+ token_offset = 0 # Start of the current stage in packed image tokens.
1081
+ label_offset = 0 # Start of the current stage in y_emb.
1082
+ next_hidden_length = [] # Token count per image, including context.
1083
+
1084
+ for batch_size, token_count in zip(stage_batch_sizes, stage_token_counts):
1085
+ stage_state = states[:, token_offset:token_offset + batch_size * token_count] # [1, B_stage * token_count, hidden_size]
1086
+ stage_state = stage_state.reshape(batch_size, token_count, -1) # [B_stage, token_count, hidden_size]
1087
+ context = y_emb[label_offset:label_offset + batch_size].unsqueeze(1).repeat(1, self.in_context_len, 1) # [B_stage, in_context_len, hidden_size]
1088
+ context = context + self.in_context_posemb # Add learned positions [1, in_context_len, hidden_size].
1089
+ stage_state = torch.cat([context, stage_state], dim=1) # [B_stage, in_context_len + token_count, hidden_size]
1090
+ stage_segments.append(rearrange(stage_state, "b s d -> 1 (b s) d")) # Pack [context_1, image_1, context_2, image_2, ...].
1091
+ next_hidden_length.extend([token_count + self.in_context_len] * batch_size)
1092
+ token_offset += batch_size * token_count
1093
+ label_offset += batch_size
1094
+
1095
+ return torch.cat(stage_segments, dim=1), next_hidden_length # [1, total_tokens + num_images * in_context_len, hidden_size], lengths.
1096
+
1097
+ def strip_label_context(states):
1098
+ # Remove each image's context prefix and restore image-only token lengths.
1099
+ stage_segments = []
1100
+ token_offset = 0 # Start of the current stage in packed context + image tokens.
1101
+
1102
+ for batch_size, token_count in zip(stage_batch_sizes, stage_token_counts):
1103
+ full_count = token_count + self.in_context_len # Context + image token count per image.
1104
+ stage_state = states[:, token_offset:token_offset + batch_size * full_count] # [1, B_stage * full_count, hidden_size]
1105
+ stage_state = stage_state.reshape(batch_size, full_count, -1) # [B_stage, full_count, hidden_size]
1106
+ stage_state = stage_state[:, self.in_context_len:] # [B_stage, token_count, hidden_size]
1107
+ stage_segments.append(rearrange(stage_state, "b s d -> 1 (b s) d")) # [1, B_stage * token_count, hidden_size]
1108
+ token_offset += batch_size * full_count
1109
+
1110
+ return torch.cat(stage_segments, dim=1), list(image_hidden_length) # [1, total_tokens, hidden_size], image-only lengths.
1111
+
1112
+ # Insert class context at in_context_start, then process the remaining body blocks.
1113
+ for i, block in enumerate(self.blocks):
1114
+ if i == self.in_context_start:
1115
+ hidden_states, hidden_length = add_label_context(hidden_states)
1116
+ has_label_context = True
1117
+
1118
+ hidden_states = block(
1119
+ hidden_states,
1120
+ c,
1121
+ hidden_length=hidden_length,
1122
+ image_rotary_emb=concat_image_rotary_emb if has_label_context else image_rotary_emb,
1123
+ )
1124
+
1125
+ # The DDT head receives image tokens only.
1126
+ hidden_states, hidden_length = strip_label_context(hidden_states)
1127
+
1128
+ ddt_condition = self._ddt_condition(hidden_states, t_emb, hidden_length)
1129
+ for block in self.ddt_blocks:
1130
+ ddt_hidden_states = block(
1131
+ ddt_hidden_states,
1132
+ ddt_condition,
1133
+ hidden_length=hidden_length,
1134
+ image_rotary_emb=ddt_image_rotary_emb,
1135
+ )
1136
+
1137
+ pred = self.ddt_final_layer(ddt_hidden_states, ddt_condition)
1138
+
1139
+ # Unpack each stage and reshape predicted patch pixels back to images.
1140
+ outputs = []
1141
+ token_offset = 0
1142
+ for batch_size, token_count, (height, width) in zip(stage_batch_sizes, stage_token_counts, output_shapes):
1143
+ stage_pred = pred[:, token_offset:token_offset + batch_size * token_count]
1144
+ stage_pred = stage_pred.reshape(
1145
+ shape=(batch_size, height, width, self.patch_size, self.patch_size, -1)
1146
+ )
1147
+ stage_pred = rearrange(stage_pred, "b h w p1 p2 c -> b (h p1) (w p2) c")
1148
+ outputs.append(rearrange(stage_pred, "b h w c -> b c h w"))
1149
+ token_offset += batch_size * token_count
1150
+ return outputs
1151
+
1152
+
1153
+ # Model variants
1154
+
1155
+ def PixelMoE_B_E32A4_8(**kwargs):
1156
+ return PixelMoE(depth=12, hidden_size=768, num_heads=12,
1157
+ bottleneck_dim=128, in_context_len=32, in_context_start=4,
1158
+ patch_size=8,
1159
+ ddt_head_dim=1536, ddt_head_depth=2, ddt_head_num_heads=12,
1160
+ moe_num_experts=32, moe_top_k=4,
1161
+ moe_routed_inter_dim=384, moe_shared_inter_dim=632,
1162
+ moe_route_scale=2.0, moe_n_dense_layers=4, **kwargs)
1163
+
1164
+
1165
+ def PixelMoE_B_E32A4_16(**kwargs):
1166
+ return PixelMoE(depth=12, hidden_size=768, num_heads=12,
1167
+ bottleneck_dim=128, in_context_len=32, in_context_start=4,
1168
+ patch_size=16,
1169
+ ddt_head_dim=1536, ddt_head_depth=2, ddt_head_num_heads=12,
1170
+ moe_num_experts=32, moe_top_k=4,
1171
+ moe_routed_inter_dim=384, moe_shared_inter_dim=632,
1172
+ moe_route_scale=2.0, moe_n_dense_layers=4, **kwargs)
1173
+
1174
+
1175
+ def PixelMoE_L_E32A4_8(**kwargs):
1176
+ return PixelMoE(depth=24, hidden_size=1024, num_heads=16,
1177
+ bottleneck_dim=128, in_context_len=32, in_context_start=8,
1178
+ patch_size=8,
1179
+ ddt_head_dim=2048, ddt_head_depth=2, ddt_head_num_heads=16,
1180
+ moe_num_experts=32, moe_top_k=4,
1181
+ moe_routed_inter_dim=512, moe_shared_inter_dim=836,
1182
+ moe_route_scale=2.0, moe_n_dense_layers=4, **kwargs)
1183
+
1184
+
1185
+ def PixelMoE_L_E32A4_16(**kwargs):
1186
+ return PixelMoE(depth=24, hidden_size=1024, num_heads=16,
1187
+ bottleneck_dim=128, in_context_len=32, in_context_start=8,
1188
+ patch_size=16,
1189
+ ddt_head_dim=2048, ddt_head_depth=2, ddt_head_num_heads=16,
1190
+ moe_num_experts=32, moe_top_k=4,
1191
+ moe_routed_inter_dim=512, moe_shared_inter_dim=836,
1192
+ moe_route_scale=2.0, moe_n_dense_layers=4, **kwargs)
1193
+
1194
+
1195
+ def PixelMoE_H_E32A4_8(**kwargs):
1196
+ return PixelMoE(depth=32, hidden_size=1280, num_heads=16,
1197
+ bottleneck_dim=256, in_context_len=32, in_context_start=10,
1198
+ patch_size=8,
1199
+ ddt_head_dim=2560, ddt_head_depth=4, ddt_head_num_heads=16,
1200
+ moe_num_experts=32, moe_top_k=4,
1201
+ moe_routed_inter_dim=640, moe_shared_inter_dim=1048,
1202
+ moe_route_scale=2.0, moe_n_dense_layers=4, **kwargs)
1203
+
1204
+
1205
+ def PixelMoE_H_E32A4_16(**kwargs):
1206
+ return PixelMoE(depth=32, hidden_size=1280, num_heads=16,
1207
+ bottleneck_dim=256, in_context_len=32, in_context_start=10,
1208
+ patch_size=16,
1209
+ ddt_head_dim=2560, ddt_head_depth=4, ddt_head_num_heads=16,
1210
+ moe_num_experts=32, moe_top_k=4,
1211
+ moe_routed_inter_dim=640, moe_shared_inter_dim=1048,
1212
+ moe_route_scale=2.0, moe_n_dense_layers=4, **kwargs)
1213
+
1214
+
1215
+ PixelMoE_models = {
1216
+ "PixelMoE-B-E32A4/8": PixelMoE_B_E32A4_8,
1217
+ "PixelMoE-B-E32A4/16": PixelMoE_B_E32A4_16,
1218
+ "PixelMoE-L-E32A4/8": PixelMoE_L_E32A4_8,
1219
+ "PixelMoE-L-E32A4/16": PixelMoE_L_E32A4_16,
1220
+ "PixelMoE-H-E32A4/8": PixelMoE_H_E32A4_8,
1221
+ "PixelMoE-H-E32A4/16": PixelMoE_H_E32A4_16,
1222
+ }
pixelmoe_src/util/__init__.py ADDED
File without changes
pixelmoe_src/util/model_util.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --------------------------------------------------------
2
+ # References:
3
+ # Lightning-DiT: https://github.com/hustvl/LightningDiT
4
+ # --------------------------------------------------------
5
+
6
+
7
+ import torch
8
+ from torch import nn
9
+ import numpy as np
10
+
11
+
12
+ class RMSNorm(nn.Module):
13
+ def __init__(self, hidden_size, eps=1e-6):
14
+ """
15
+ Normalize by RMS in FP32, apply a learned scale, and restore input dtype.
16
+ """
17
+ super().__init__()
18
+ self.weight = nn.Parameter(torch.ones(hidden_size))
19
+ self.variance_epsilon = eps
20
+
21
+ def forward(self, hidden_states):
22
+ input_dtype = hidden_states.dtype
23
+ hidden_states = hidden_states.to(torch.float32)
24
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
25
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
26
+ return (self.weight * hidden_states).to(input_dtype)
27
+
28
+
29
+ def get_2d_sincos_pos_embed(embed_dim, grid_size):
30
+ """
31
+ grid_size: int of the grid height and width
32
+ return:
33
+ pos_embed: [grid_size*grid_size, embed_dim]
34
+ """
35
+ grid_h = np.arange(grid_size, dtype=np.float32)
36
+ grid_w = np.arange(grid_size, dtype=np.float32)
37
+ grid = np.meshgrid(grid_w, grid_h) # here w goes first
38
+ grid = np.stack(grid, axis=0)
39
+
40
+ grid = grid.reshape([2, 1, grid_size, grid_size])
41
+ pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid)
42
+ return pos_embed
43
+
44
+
45
+ def get_2d_sincos_pos_embed_from_grid(embed_dim, grid):
46
+ assert embed_dim % 2 == 0
47
+
48
+ # Allocate half the embedding to each coordinate: width first, then height.
49
+ emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2)
50
+ emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2)
51
+
52
+ emb = np.concatenate([emb_w, emb_h], axis=1) # (H*W, D)
53
+ return emb
54
+
55
+
56
+ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
57
+ """
58
+ embed_dim: output dimension for each position
59
+ pos: a list of positions to be encoded: size (M,)
60
+ out: (M, D)
61
+ """
62
+ assert embed_dim % 2 == 0
63
+ omega = np.arange(embed_dim // 2, dtype=np.float64)
64
+ omega /= embed_dim / 2.
65
+ omega = 1. / 10000**omega # (D/2,)
66
+
67
+ pos = pos.reshape(-1) # (M,)
68
+ out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
69
+
70
+ emb_sin = np.sin(out) # (M, D/2)
71
+ emb_cos = np.cos(out) # (M, D/2)
72
+
73
+ emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D)
74
+ return emb
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ einops
2
+ numpy
3
+ https://huggingface.co/datasets/multimodalart/zerogpu-blackwell-wheels/resolve/main/wheels/pt211-cu130-cp312/flash_attn-2.8.3-cp312-cp312-linux_x86_64.whl