Repository navigation
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Hi @jiaxiangc, thanks for the PR! It does not appear to link an issue it fixes. If this PR addresses an existing issue, please add a closing keyword (e.g. Please note that PRs without a linked issue are likely to be automatically closed 10 days after this notice. Once the PR links an issue (or gets the |
yiyixuxu
left a comment
There was a problem hiding this comment.
thanks for the PR! I left some initial comments,
additionally, could you remove the streaming support from this PR for now? we are working on adding first class streaming support for modular pipelines #14159 so happy to work together on a follow up to make sure your use case works with that API
| "hf-doc-builder>=0.3.0", | ||
| "huggingface-hub>=1.32.0,<3.0", | ||
| "requests-mock==1.10.0", | ||
| "imageio", |
There was a problem hiding this comment.
can you remove the changes added to setup.py
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class PixelUMMArchitecture: |
| return torch.cat((-x[..., half:], x[..., :half]), dim=-1) | ||
|
|
||
|
|
||
| def mrope_tables(arch: PixelUMMArchitecture, grid: tuple[int, int, int], device) -> tuple[torch.Tensor, torch.Tensor]: |
There was a problem hiding this comment.
| def mrope_tables(arch: PixelUMMArchitecture, grid: tuple[int, int, int], device) -> tuple[torch.Tensor, torch.Tensor]: | |
| def mrope_tables(rope_thera_t, rope_theta_hw, , rope_sections, grid: tuple[int, int, int], device) -> tuple[torch.Tensor, torch.Tensor]: |
| def mrope_tables(arch: PixelUMMArchitecture, grid: tuple[int, int, int], device) -> tuple[torch.Tensor, torch.Tensor]: | ||
| """cos/sin [1, T*H*W, 1, head_dim] for a THW-ordered grid, laid out [T | H | W].""" | ||
| positions = torch.meshgrid(*(torch.arange(n, device=device) for n in grid), indexing="ij") | ||
| thetas = (arch.rope_theta_t, arch.rope_theta_hw, arch.rope_theta_hw) |
There was a problem hiding this comment.
| thetas = (arch.rope_theta_t, arch.rope_theta_hw, arch.rope_theta_hw) | |
| thetas = (rope_theta_t, rope_theta_hw, rope_theta_hw) |
| return permutation, torch.argsort(permutation), (neighborhoods, block_mask) | ||
|
|
||
|
|
||
| def _pixelumm_flex_attention(query, key, value, block_mask, parallel_config): |
| class HyNomasMagiCameraAttnProcessor(HyNomasCameraAttnProcessor): | ||
| """Reference frame-local camera range attention.""" | ||
|
|
||
| def _compute_attention(self, query, key, value, num_frames, tokens_per_frame): | ||
| if num_frames == 1: | ||
| return dispatch_attention_fn(query, key, value, backend="flash") | ||
| starts = torch.arange(num_frames, dtype=torch.int32, device=query.device) | ||
| frame_ranges = torch.stack((starts, starts + 1), dim=-1) | ||
| return dispatch_attention_fn( | ||
| query, | ||
| key, | ||
| value, | ||
| backend="magi", | ||
| attention_kwargs={ | ||
| "query_ranges": frame_ranges * tokens_per_frame, | ||
| "key_ranges": frame_ranges, | ||
| "attn_type_map": torch.zeros(num_frames, dtype=torch.int32, device=query.device), | ||
| }, | ||
| ) |
There was a problem hiding this comment.
| class HyNomasMagiCameraAttnProcessor(HyNomasCameraAttnProcessor): | |
| """Reference frame-local camera range attention.""" | |
| def _compute_attention(self, query, key, value, num_frames, tokens_per_frame): | |
| if num_frames == 1: | |
| return dispatch_attention_fn(query, key, value, backend="flash") | |
| starts = torch.arange(num_frames, dtype=torch.int32, device=query.device) | |
| frame_ranges = torch.stack((starts, starts + 1), dim=-1) | |
| return dispatch_attention_fn( | |
| query, | |
| key, | |
| value, | |
| backend="magi", | |
| attention_kwargs={ | |
| "query_ranges": frame_ranges * tokens_per_frame, | |
| "key_ranges": frame_ranges, | |
| "attn_type_map": torch.zeros(num_frames, dtype=torch.int32, device=query.device), | |
| }, | |
| ) |
|
|
||
| class HyNomasCameraAttention(nn.Module, AttentionModuleMixin): | ||
| _default_processor_cls = HyNomasCameraAttnProcessor | ||
| _available_processors = [HyNomasCameraAttnProcessor, HyNomasMagiCameraAttnProcessor] |
There was a problem hiding this comment.
| _available_processors = [HyNomasCameraAttnProcessor, HyNomasMagiCameraAttnProcessor] | |
| _available_processors = [HyNomasCameraAttnProcessor] |
| raise ValueError("Action offsets require one entry per sample and action_frame_windows.") | ||
| if cache_action_start is not None and not 0 <= cache_action_start <= encoder_hidden_states.shape[1]: | ||
| raise ValueError("cache_action_start must be a valid text boundary or None.") | ||
| if kv_cache is not None: |
There was a problem hiding this comment.
can you give kv cache a class? we usually have a per-layer cache and a container class see example https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_qwenimage21.py#L56
| AITER_FA2_HUB = "aiter_fa2_hub" | ||
|
|
||
| # `magi_attention` rectangle-range attention | ||
| MAGI = "magi" |
There was a problem hiding this comment.
let's not add magi in this PR
|
|
||
|
|
||
| @dataclass | ||
| class HyNomasGenerationState: |
There was a problem hiding this comment.
Can we try to remove HyNomasGenerationState and support continued generation by passing outputs back as inputs? A few things on top of my mind:
-
latentsis both the initial-noise input and the denoised output; to work with this use case, you probably need to rename thelatentsinput to something else, e.g.initial_noise/initial_latents -
You can tag everything a continuation needs (
kv_cache,frame_offset...) withkwargs_type, e.g.OutputParam("kv_cache", ..., kwargs_type="world_state")on the loop's outputs andInputParam("kv_cache", ..., kwargs_type="world_state")on the prepare step's optional inputs.PipelineStategroups them, sostate.get_by_kwargs("world_state")returns them as a dict and the pipeline accepts that dict back as a single kwarg. A turn then looks like:
turn1 = pipe(prompt="walk forward", camera_extrinsics=cam1)
turn2 = pipe(world_state=turn1.get_by_kwargs("world_state"), prompt="turn right", camera_extrinsics=cam2, output="videos")let me know if you run into any other issues
What does this PR do?
Adds Hy-Nomas inference support to Diffusers through
HyNomasModularPipeline,HyNomasTransformer3DModel, andPixelUMMDecoder.I am a member of the Hy-Nomas team.
Closes #15113
The issue and this PR are being submitted together for feedback on the proposed scope and implementation. Maintainer acknowledgment is pending.
Changes
The workflow targets the distilled checkpoint with guidance scale 1.
Checkpoints
Preconverted checkpoints are available at tencent/Hy-Nomas-Diffusers.
The repository includes all components required for inference, including the text encoder and tokenizer. Users do not need to convert the original checkpoints or download a separate Qwen checkpoint. The bundled text encoder is distinct from the optional Nomas-PE prompt enhancement model.
Dependencies
The optional
hy-nomasextra includes:FlashAttention 2, FlashAttention 3, and MagiAttention are optional dependencies used only for reference-attention inference. They are not installed by the
hy-nomasextra, and the default PyTorch attention path does not require them.Local validation
make quality: passed.The following CPU test run completed with 114 passed, 52 skipped, and 39 deselected:
A real-checkpoint comparison against the original Hy-Nomas implementation used:
Both the raw RGB frames and decoded MP4 frames were pixel-identical, with maximum absolute difference 0.
This pixel-level equivalence result applies to the matched reference-attention configuration. It does not establish numerical equivalence for the default PyTorch attention path.
make repo-consistencyremains blocked by an existing checker importing the missingdiffusers.models.automodule. The checker and missing module are unchanged from the PR base; unrelated checker fixes are excluded.AI-assisted development and self-review
Codex assisted with implementation and validation. The complete contribution was reviewed using the repository's
self-reviewskill. No blocking findings or confirmed dead code were identified.Two non-blocking points are left for maintainer review:
camera_attention_maskgate is retained, although the guidance-one modular pipeline does not currently pass it.Before submitting
cc @yiyixuxu @asomoza