Instructions to use nebulette/moss-spectrum-diffusers with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use nebulette/moss-spectrum-diffusers with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("nebulette/moss-spectrum-diffusers", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
Download spectrum.py from nebulette/moss-spectrum-diffusers: direct link, hf CLI and curl.
- Browser
- Download file 17.5 kB
-
https://huggingface.co/nebulette/moss-spectrum-diffusers/resolve/main/spectrum.py
- Command line
-
hf download hf://nebulette/moss-spectrum-diffusers/spectrum.py
-
curl -L -o spectrum.py https://huggingface.co/nebulette/moss-spectrum-diffusers/resolve/main/spectrum.py
17.5 kB
| """ | |
| Spectrum acceleration for MOSS-Image-Base in diffusers. | |
| Spectrum: "Adaptive Spectral Feature Forecasting for Diffusion Sampling Acceleration" | |
| (Han et al., arXiv:2603.01623) | |
| Based on hanjq17/Spectrum | |
| Usage | |
| ----- | |
| from spectrum_moss import apply_spectrum, SpectrumConfig | |
| pipe = DiffusionPipeline.from_pretrained( | |
| "OpenMOSS-Team/MOSS-Image-Base", | |
| custom_pipeline="pipeline_moss_image", | |
| trust_remote_code=True, | |
| torch_dtype={"default": torch.bfloat16, "vae": torch.float32}, | |
| ) | |
| apply_spectrum(pipe) # defaults below | |
| pipe.enable_model_cpu_offload() | |
| image = pipe("1girl", guidance_scale=3.0).images[0] | |
| print(pipe.transformer._spectrum_state.summary()) | |
| apply_spectrum(pipe, SpectrumConfig(max_consecutive_forecasts=1)) # safer | |
| remove_spectrum(pipe) # restore exact behaviour | |
| No extra forward is ever spent: error statistics are measured at refresh steps by | |
| comparing the incoming real feature against what the forecaster had predicted for it. | |
| """ | |
| import math | |
| import types | |
| from dataclasses import dataclass, field | |
| from typing import Any, Optional | |
| import torch | |
| from diffusers.models.modeling_outputs import Transformer2DModelOutput | |
| class SpectrumConfig: | |
| warmup_steps: int = 8 # always-real steps at the start | |
| tail_actual_steps: int = 2 # always-real steps at the end | |
| window_size: float = 2.0 # initial refresh interval | |
| flex_window: float = 0.75 # interval growth per real step | |
| max_consecutive_forecasts: int = 2 | |
| history_points: int = 8 # K anchors kept | |
| chebyshev_degree: int = 4 # M | |
| ridge_lambda: float = 0.1 # lambda | |
| blend_weight: float = 0.5 # w: spectral vs linear | |
| cache_device: str = "same" # "same" | "cpu" | |
| debug: bool = False | |
| def validate(self) -> None: | |
| if self.warmup_steps < 0 or self.tail_actual_steps < 0: | |
| raise ValueError("warmup_steps / tail_actual_steps must be >= 0") | |
| if self.window_size < 1.0: | |
| raise ValueError("window_size must be >= 1.0") | |
| if self.flex_window < 0.0: | |
| raise ValueError("flex_window must be >= 0.0") | |
| if self.max_consecutive_forecasts < 0: | |
| raise ValueError("max_consecutive_forecasts must be >= 0") | |
| if self.history_points < self.chebyshev_degree + 1: | |
| raise ValueError("history_points must be >= chebyshev_degree + 1") | |
| if self.chebyshev_degree < 1: | |
| raise ValueError("chebyshev_degree must be >= 1") | |
| if self.ridge_lambda < 0.0: | |
| raise ValueError("ridge_lambda must be >= 0") | |
| if not 0.0 <= self.blend_weight <= 1.0: | |
| raise ValueError("blend_weight must be in [0, 1]") | |
| if self.cache_device not in ("same", "cpu"): | |
| raise ValueError("cache_device must be 'same' or 'cpu'") | |
| def window_cap(self) -> float: | |
| return max(1.0, self.window_size, float(self.history_points)) | |
| def cheb_basis(u, degree: int) -> torch.Tensor: | |
| u = torch.as_tensor(u, dtype=torch.float64).reshape(-1) | |
| cols = [torch.ones_like(u)] | |
| if degree >= 1: | |
| cols.append(u) | |
| for _ in range(2, degree + 1): | |
| cols.append(2.0 * u * cols[-1] - cols[-2]) | |
| return torch.stack(cols[: degree + 1], dim=-1) # [n, P] | |
| class Forecaster: | |
| """Chebyshev ridge forecaster (prediction = weighted sum of anchors)""" | |
| def __init__(self, cfg: SpectrumConfig): | |
| self.cfg = cfg | |
| self.us: list[float] = [] | |
| self.feats: list[torch.Tensor] = [] | |
| self._chol: Optional[torch.Tensor] = None | |
| self._last: Optional[tuple[float, torch.Tensor]] = None | |
| def reset(self) -> None: | |
| self.us.clear() | |
| self.feats.clear() | |
| self._chol = None | |
| self._last = None | |
| def ready(self) -> bool: | |
| return len(self.us) >= self.cfg.chebyshev_degree + 1 | |
| def update(self, u: float, feat: torch.Tensor) -> None: | |
| if self.feats and tuple(feat.shape) != tuple(self.feats[0].shape): | |
| self.reset() | |
| self.us.append(float(u)) | |
| self.feats.append(feat.detach()) | |
| if len(self.us) > self.cfg.history_points: | |
| self.us.pop(0) | |
| self.feats.pop(0) | |
| self._chol = None | |
| def _design(self) -> torch.Tensor: | |
| return cheb_basis(self.us, self.cfg.chebyshev_degree) # [K, P] | |
| def _factor(self) -> torch.Tensor: | |
| if self._chol is not None: | |
| return self._chol | |
| X = self._design() | |
| G = X.T @ X | |
| P = G.shape[0] | |
| eye = torch.eye(P, dtype=torch.float64) | |
| lhs = G + self.cfg.ridge_lambda * eye | |
| try: | |
| chol = torch.linalg.cholesky(lhs) | |
| except RuntimeError: | |
| scale = max(float(G.diagonal().abs().mean().item()), 1.0) | |
| chol = None | |
| for eps in (1e-8, 1e-7, 1e-6, 1e-5): | |
| try: | |
| chol = torch.linalg.cholesky(lhs + scale * eps * eye) | |
| break | |
| except RuntimeError: | |
| pass | |
| if chol is None: | |
| raise | |
| self._chol = chol | |
| return chol | |
| def _linear_weights(self, u: float) -> torch.Tensor: | |
| k = len(self.us) | |
| w = torch.zeros(k, dtype=torch.float64) | |
| if k == 0: | |
| return w | |
| if k == 1: | |
| w[0] = 1.0 | |
| return w | |
| spacing = self.us[-1] - self.us[-2] | |
| if abs(spacing) < 1e-12: | |
| w[-1] = 1.0 | |
| return w | |
| ratio = (float(u) - self.us[-1]) / spacing | |
| w[-2] = -ratio | |
| w[-1] = 1.0 + ratio | |
| return w | |
| def weights(self, u: float) -> torch.Tensor: | |
| phi = cheb_basis([u], self.cfg.chebyshev_degree) # [1, P] | |
| solved = torch.cholesky_solve(self._design().T, self._factor()) # [P, K] | |
| spectral = (phi @ solved).reshape(-1) | |
| bw = self.cfg.blend_weight | |
| if bw >= 1.0 - 1e-9: | |
| return spectral | |
| if bw <= 1e-9: | |
| return self._linear_weights(u) | |
| return bw * spectral + (1.0 - bw) * self._linear_weights(u) | |
| def predict(self, u: float) -> torch.Tensor: | |
| ws = self.weights(u).tolist() | |
| acc: Optional[torch.Tensor] = None | |
| for w, f in zip(ws, self.feats): | |
| if w == 0.0: | |
| continue | |
| term = f.to(torch.float32) * float(w) | |
| acc = term if acc is None else acc + term | |
| if acc is None: | |
| acc = torch.zeros_like(self.feats[-1], dtype=torch.float32) | |
| out = acc.to(self.feats[-1].dtype) | |
| self._last = (float(u), out) | |
| return out | |
| def relative_error(self, u: float, actual: torch.Tensor) -> Optional[float]: | |
| if self._last is None: | |
| return None | |
| _, pred = self._last | |
| if tuple(pred.shape) != tuple(actual.shape): | |
| return None | |
| num = (pred.float() - actual.float()).norm() | |
| den = actual.float().norm().clamp_min(1e-6) | |
| return float((num / den).item()) | |
| class Branch: | |
| """Per-branch scheduler state""" | |
| def __init__(self, cfg: SpectrumConfig): | |
| self.cfg = cfg | |
| self.window = float(cfg.window_size) | |
| self.consecutive = 0 | |
| self.actual = 0 | |
| self.forecast = 0 | |
| self.forecasters: dict[int, Forecaster] = {} | |
| self.errors: list[float] = [] | |
| def forecaster(self, sample: int) -> Forecaster: | |
| f = self.forecasters.get(sample) | |
| if f is None: | |
| f = self.forecasters[sample] = Forecaster(self.cfg) | |
| return f | |
| def decide(self, step: int, total: int) -> bool: | |
| """Return True for a real forward, False for a forecast.""" | |
| cfg = self.cfg | |
| if step < cfg.warmup_steps: | |
| return True | |
| if cfg.tail_actual_steps > 0 and step >= max(0, total - cfg.tail_actual_steps): | |
| return True | |
| if self.consecutive >= cfg.max_consecutive_forecasts: | |
| return True | |
| interval = max(1, int(math.floor(self.window))) | |
| return (self.consecutive + 1) % interval == 0 | |
| def note(self, actual: bool, step: int) -> None: | |
| if actual: | |
| self.actual += 1 | |
| self.consecutive = 0 | |
| if step >= self.cfg.warmup_steps: | |
| self.window = min(self.window + self.cfg.flex_window, self.cfg.window_cap) | |
| else: | |
| self.forecast += 1 | |
| self.consecutive += 1 | |
| class State: | |
| def __init__(self, cfg: SpectrumConfig, scheduler: Any): | |
| self.cfg = cfg | |
| self.scheduler = scheduler | |
| self.original_forward = None | |
| self.step_index = -1 | |
| self.branches: dict[Any, Branch] = {} | |
| self._sig_counts: dict[Any, int] = {} | |
| self._log: list[str] = [] | |
| def branch(self, key: Any) -> Branch: | |
| b = self.branches.get(key) | |
| if b is None: | |
| b = self.branches[key] = Branch(self.cfg) | |
| return b | |
| def reset_run(self) -> None: | |
| self.branches.clear() | |
| self._sig_counts.clear() | |
| self.step_index = -1 | |
| def summary(self) -> str: | |
| a = sum(b.actual for b in self.branches.values()) | |
| f = sum(b.forecast for b in self.branches.values()) | |
| errs = [e for b in self.branches.values() for e in b.errors] | |
| mean_err = sum(errs) / len(errs) if errs else 0.0 | |
| speed = (a + f) / a if a else 0.0 | |
| return ( | |
| f"Spectrum: actual={a} forecast={f} transformer_calls_saved=" | |
| f"{f}/{a + f} ({speed:.2f}x) mean_forecast_rel_err={mean_err:.4f}" | |
| ) | |
| def validate_skip_layers(layers, depth: int, name: str = "skip_layers") -> tuple: | |
| layers = () if layers is None else tuple(layers) | |
| if any(type(i) is not int or not 0 <= i < depth for i in layers): | |
| raise ValueError(f"{name} must contain integer block indices in [0, {depth}).") | |
| return layers | |
| def resolve_step(state: State, timestep: torch.Tensor) -> tuple[int, int]: | |
| sched = state.scheduler | |
| timesteps = getattr(sched, "timesteps", None) if sched is not None else None | |
| if timesteps is None or len(timesteps) == 0: | |
| return -1, 0 | |
| ts = timesteps if torch.is_tensor(timesteps) else torch.as_tensor(timesteps) | |
| t = float(torch.as_tensor(timestep).reshape(-1)[0]) | |
| idx = int((ts.float() - t).abs().argmin().item()) | |
| return idx, len(ts) | |
| def spectrum_forward( | |
| self, | |
| hidden_states: torch.Tensor, | |
| timestep, | |
| encoder_hidden_states: torch.Tensor, | |
| encoder_attention_mask: Optional[torch.Tensor] = None, | |
| return_dict: bool = True, | |
| skip_layers=None, | |
| ): | |
| """ | |
| License: Apache-2.0 | |
| https://huggingface.co/nebulette/moss-spectrum-diffusers | |
| """ | |
| state: Optional[State] = getattr(self, "_spectrum_state", None) | |
| if state is None or state.scheduler is None: | |
| return state.original_forward( | |
| hidden_states, timestep, encoder_hidden_states, | |
| encoder_attention_mask=encoder_attention_mask, | |
| return_dict=return_dict, skip_layers=skip_layers, | |
| ) | |
| cfg = state.cfg | |
| skip = validate_skip_layers(skip_layers, len(self.layers)) | |
| if hidden_states.ndim != 4 or hidden_states.shape[1] != self.config.latent_channels: | |
| raise ValueError("hidden_states must be [B, latent_channels, H, W].") | |
| batch, _, height, width = hidden_states.shape | |
| if encoder_hidden_states.ndim != 3 or encoder_hidden_states.shape[0] != batch: | |
| raise ValueError("encoder_hidden_states must be [B, S, text_dim].") | |
| mask = encoder_attention_mask | |
| if mask is None: | |
| mask = torch.ones(encoder_hidden_states.shape[:2], dtype=torch.bool, device=hidden_states.device) | |
| if mask.shape != encoder_hidden_states.shape[:2] or not torch.all((mask == 0) | (mask == 1)): | |
| raise ValueError("encoder_attention_mask must be a binary [B, S] mask.") | |
| # --- step bookkeeping ------------------------------------------------- # | |
| step, total = resolve_step(state, timestep) | |
| if step < 0: # no schedule available -> stay exact | |
| return state.original_forward( | |
| hidden_states, timestep, encoder_hidden_states, | |
| encoder_attention_mask=encoder_attention_mask, | |
| return_dict=return_dict, skip_layers=skip_layers, | |
| ) | |
| if step == 0 and state.step_index > 0: | |
| state.reset_run() | |
| if step != state.step_index: | |
| state.step_index = step | |
| state._sig_counts = {} | |
| sig = skip | |
| occurrence = state._sig_counts.get(sig, 0) | |
| state._sig_counts[sig] = occurrence + 1 | |
| branch = state.branch((sig, occurrence)) | |
| actual = branch.decide(step, total) | |
| u = 0.0 if total <= 1 else max(-1.0, min(1.0, 2.0 * step / (total - 1) - 1.0)) | |
| # shared timestep conditioning (identical to the original) | |
| times = torch.as_tensor(timestep, device=hidden_states.device, dtype=torch.float32).reshape(-1).expand(batch) | |
| half = self.config.freq_dim // 2 | |
| freq = 10000 ** (-torch.arange(half, device=times.device, dtype=torch.float64) / half) | |
| phase = times.double()[:, None] * freq | |
| embedding = torch.cat((phase.cos(), phase.sin()), -1).float() | |
| time = self.time_embedding(embedding.to(self.time_embedding[0].weight.dtype)).float() | |
| head = self.video_head_modulation(time.to(self.video_head_modulation.weight.dtype)).view(batch, 2, -1) | |
| mod = ( | |
| self.shared_block_modulation(time.to(self.shared_block_modulation.weight.dtype)).view(batch, 6, -1) | |
| if actual else None | |
| ) | |
| image_tokens = height * width | |
| outputs = [] | |
| for index in range(batch): | |
| context = encoder_hidden_states[index:index + 1, mask[index].bool()] | |
| if context.shape[1] == 0: | |
| raise ValueError("Each sample needs at least one valid text token.") | |
| forecaster = branch.forecaster(index) | |
| if not actual: | |
| # skip embeddings, text blocks and all layers | |
| try: | |
| feat = forecaster.predict(u) | |
| except Exception: | |
| # numerical trouble -> fall back to a real step | |
| actual = True | |
| branch.note(True, step) | |
| else: | |
| y = self.final_norm_video(feat.to(self.final_norm_video.weight.dtype)) | |
| y = y * (1 + head[index:index + 1, 1, None]) + head[index:index + 1, 0, None] | |
| y = self.final_linear_video(y.to(self.final_linear_video.weight.dtype)) | |
| outputs.append(y.transpose(1, 2).reshape(1, -1, height, width)) | |
| continue | |
| # real path | |
| video = hidden_states[index:index + 1].flatten(2).transpose(1, 2) | |
| video = self.video_embedder(video.to(self.video_embedder.weight.dtype).contiguous()) | |
| rope = self.rope(height, width, context.shape[1], hidden_states.device).to(video.dtype) | |
| text = self.text_embedder(context, rope[:, image_tokens:]) | |
| x = torch.cat((video, text), 1) | |
| for layer_index, layer in enumerate(self.layers): | |
| if layer_index not in skip: | |
| x = layer(x, rope, mod[index:index + 1]) | |
| # make the block compatible with tuple outputs | |
| if isinstance(x, tuple): | |
| x = x[0] | |
| feature = x[:, :image_tokens] | |
| # measure the error of the forecast that was made for this step | |
| err = forecaster.relative_error(u, feature) | |
| if err is not None: | |
| branch.errors.append(err) | |
| if cfg.debug: | |
| state._log.append(f"step {step + 1}/{total} branch={sig} rel_err={err:.4f}") | |
| cache = feature | |
| if cfg.cache_device == "cpu": | |
| cache = feature.detach().to("cpu", non_blocking=False) | |
| forecaster.update(u, cache) | |
| y = self.final_norm_video(feature.to(self.final_norm_video.weight.dtype)) | |
| y = y * (1 + head[index:index + 1, 1, None]) + head[index:index + 1, 0, None] | |
| y = self.final_linear_video(y.to(self.final_linear_video.weight.dtype)) | |
| outputs.append(y.transpose(1, 2).reshape(1, -1, height, width)) | |
| branch.note(actual, step) | |
| sample = torch.cat(outputs) | |
| if cfg.debug and step >= total - 1: | |
| state._log.append(state.summary()) | |
| if not return_dict: | |
| return (sample,) | |
| return Transformer2DModelOutput(sample=sample) | |
| def apply_spectrum(pipe, config: Optional[SpectrumConfig] = None): | |
| """ | |
| Patch ``pipe`` in place so the MOSS DiT runs under Spectrum scheduling. | |
| License: Apache-2.0 | |
| https://huggingface.co/nebulette/moss-spectrum-diffusers | |
| """ | |
| cfg = config or SpectrumConfig() | |
| cfg.validate() | |
| transformer = getattr(pipe, "transformer", None) | |
| if transformer is None: | |
| raise ValueError("pipe has no `transformer`.") | |
| if getattr(transformer, "_spectrum_state", None) is not None: | |
| remove_spectrum(pipe) | |
| state = State(cfg, getattr(pipe, "scheduler", None)) | |
| state.original_forward = transformer.forward | |
| transformer._spectrum_state = state | |
| transformer.forward = types.MethodType(spectrum_forward, transformer) | |
| return pipe | |
| def remove_spectrum(pipe) -> None: | |
| """Undo :func:`apply_spectrum`, restoring the untouched forward.""" | |
| transformer = getattr(pipe, "transformer", None) | |
| if transformer is None: | |
| return | |
| state = getattr(transformer, "_spectrum_state", None) | |
| if state is None: | |
| return | |
| if state.original_forward is not None: | |
| transformer.forward = state.original_forward | |
| try: | |
| del transformer._spectrum_state | |
| except AttributeError: | |
| pass | |