Instructions to use Perflow-Shuai/MiniMax-H3-3Step-LoRA with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use Perflow-Shuai/MiniMax-H3-3Step-LoRA with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("Perflow-Shuai/MiniMax-H3-3Step-LoRA", 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 h3_inference/runtime.py from Perflow-Shuai/MiniMax-H3-3Step-LoRA: direct link, hf CLI and curl.
- Browser
- Download file 7.42 kB
-
https://huggingface.co/Perflow-Shuai/MiniMax-H3-3Step-LoRA/resolve/main/h3_inference/runtime.py
- Command line
-
hf download hf://Perflow-Shuai/MiniMax-H3-3Step-LoRA/h3_inference/runtime.py
-
curl -L -o runtime.py https://huggingface.co/Perflow-Shuai/MiniMax-H3-3Step-LoRA/resolve/main/h3_inference/runtime.py
7.42 kB
| import torch | |
| from torch import nn | |
| from torch.nn import functional | |
| from torch.nn.attention import SDPBackend, sdpa_kernel | |
| from torch.utils.checkpoint import checkpoint | |
| CAUSAL_CLOCK_POLICY = 'paired_av_clocks_v1' | |
| CAUSAL_BATCH_NUMERICS_POLICY = 'chronological_kv_v1' | |
| def embed_plan_timesteps(model, plan): | |
| if plan.bidirectional and plan.clock_groups: | |
| parts = [model.time_embedder(model.time_proj(clocks).to(model.time_embedder.linear_1.weight.dtype)) for clocks in plan.clock_groups] | |
| clocks = torch.cat(plan.clock_groups) | |
| values = torch.cat(parts) | |
| owners = torch.stack([torch.where(clocks == time_input)[0][0] for time_input in plan.timesteps]) | |
| return values.index_select(0, owners) | |
| if plan.bidirectional: | |
| return model.time_embedder(model.time_proj(plan.timesteps).to(model.time_embedder.linear_1.weight.dtype)) | |
| parts = [model.time_embedder(model.time_proj(clocks).to(model.time_embedder.linear_1.weight.dtype)) for clocks in plan.clock_groups] | |
| clocks = torch.cat(plan.clock_groups) | |
| order = clocks.argsort() | |
| if not torch.equal(clocks[order], plan.timesteps): | |
| raise ValueError('Native paired clocks no longer cover the fixed inventory') | |
| return torch.cat(parts).index_select(0, order) | |
| def shared_kv_gradient_source(value): | |
| if torch.is_grad_enabled() and value.requires_grad and (value.dtype in (torch.bfloat16, torch.float16)): | |
| return value.float() | |
| return value | |
| def _attention(attn, hidden, rotary, plan): | |
| from diffusers.models.transformers.transformer_minimax_h3 import _apply_rotary_emb | |
| if attn.fused_projections: | |
| raise ValueError('Chunked runtime requires unfused base projections and a shared LoRA owner') | |
| query, key, value = [getattr(attn, name)(hidden).unflatten(-1, (attn.heads, -1)) for name in ('to_q', 'to_k', 'to_v')] | |
| query, key = (attn.norm_q(query), attn.norm_k(key)) | |
| query = torch.cat([_apply_rotary_emb(query[batch_index:batch_index + 1], *rotary[batch_index]) for batch_index in range(query.shape[0])]) | |
| key = torch.cat([_apply_rotary_emb(key[batch_index:batch_index + 1], *rotary[batch_index]) for batch_index in range(key.shape[0])]) | |
| query, key, value = (values.transpose(1, 2) for values in (query, key, value)) | |
| if query.shape[2] > 4096 and query.device.type != 'cuda': | |
| raise ValueError('Full-resolution attention requires GPU execution') | |
| backends = [SDPBackend.FLASH_ATTENTION, SDPBackend.EFFICIENT_ATTENTION] | |
| if query.shape[2] <= 4096: | |
| backends.append(SDPBackend.MATH) | |
| outputs = [] | |
| for batch_index, groups in enumerate(plan.groups): | |
| out = torch.zeros_like(query[batch_index:batch_index + 1]) | |
| key_source = shared_kv_gradient_source(key[batch_index:batch_index + 1]) | |
| value_source = shared_kv_gradient_source(value[batch_index:batch_index + 1]) | |
| for queries, keys in groups: | |
| with sdpa_kernel(backends): | |
| value = functional.scaled_dot_product_attention(query[batch_index:batch_index + 1].index_select(2, queries), key_source.index_select(2, keys).to(key.dtype), value_source.index_select(2, keys).to(value.dtype), dropout_p=0.0) | |
| out = out.index_copy(2, queries, value) | |
| outputs.append(out) | |
| merged = torch.cat(outputs).transpose(1, 2).flatten(2, 3).type_as(query) | |
| return attn.to_out[1](attn.to_out[0](merged)) | |
| def _block_forward(block, hidden, temb, indices, rotary, plan): | |
| shift_a, scale_a, gate_a, shift_f, scale_f, gate_f = block.adaln_proj(temb) | |
| normalized = block.norm1(hidden) * (1 + scale_a[indices]) + shift_a[indices] | |
| hidden = hidden + gate_a[indices] * _attention(block.attn, normalized, rotary, plan) | |
| normalized = block.norm2(hidden) * (1 + scale_f[indices]) + shift_f[indices] | |
| return hidden + gate_f[indices] * block.ff(normalized) | |
| class ChunkedH3Runtime(nn.Module): | |
| def __init__(self, model, *, checkpoint_blocks=True): | |
| super().__init__() | |
| self.model = model | |
| self.checkpoint_blocks = checkpoint_blocks | |
| self.attention_contract = 'explicit_causal_rows_stage_text_native_clocks_kv_order_v4' | |
| self.causal_clock_policy = CAUSAL_CLOCK_POLICY | |
| self.causal_batch_numerics_policy = CAUSAL_BATCH_NUMERICS_POLICY | |
| def forward(self, video_rows, audio_rows, prompt_embeds, plan, *, return_hidden=False): | |
| from diffusers.models.transformers.transformer_minimax_h3 import MiniMaxH3TransformerOutput | |
| model = self.model | |
| batch, row_count = plan.routes.valid_rows.shape | |
| if video_rows.shape[:2] != (batch, plan.video_indices.numel()): | |
| raise ValueError('Video input/plan shape differs') | |
| if audio_rows.shape[:2] != (batch, plan.audio_indices.numel()): | |
| raise ValueError('Audio input/plan shape differs') | |
| if prompt_embeds.shape[:2] != (batch, plan.max_text_length): | |
| raise ValueError('Text input/plan shape differs') | |
| if len({video_rows.device, audio_rows.device, prompt_embeds.device, plan.routes.token_tags.device}) != 1: | |
| raise ValueError('Inputs and plan must share a device') | |
| video = model.proj_in(video_rows.to(model.proj_in.weight.dtype)) | |
| audio = model.audio_proj_in(audio_rows.to(model.audio_proj_in.weight.dtype)) | |
| refined = [] | |
| for batch_index, length in enumerate(plan.text_lengths): | |
| one = model.context_embedder(prompt_embeds[batch_index:batch_index + 1, :length].to(model.context_embedder.weight.dtype)) | |
| one = model.token_refiner(one) | |
| refined.append(functional.pad(one, (0, 0, 0, plan.max_text_length - length))) | |
| text = torch.cat(refined).repeat(1, plan.copies, 1) | |
| hidden = text.new_zeros((batch, row_count, text.shape[-1])) | |
| hidden = hidden.index_copy(1, plan.text_indices, text) | |
| hidden = hidden.index_copy(1, plan.video_indices, video.to(text.dtype)) | |
| hidden = hidden.index_copy(1, plan.audio_indices, audio.to(text.dtype)) | |
| temb = embed_plan_timesteps(model, plan) | |
| indices = plan.timestep_indices * 3 + plan.routes.token_tags[None].clamp(min=0) | |
| rotary = tuple((model.rope(pos) for pos in plan.position_ids)) | |
| for block in model.transformer_blocks: | |
| def call(hidden_input, time_input, block=block): | |
| return _block_forward(block, hidden_input, time_input, indices, rotary, plan) | |
| if self.checkpoint_blocks and torch.is_grad_enabled(): | |
| hidden = checkpoint(call, hidden, temb, use_reentrant=False) | |
| else: | |
| hidden = call(hidden, temb) | |
| shift, scale = model.norm_out.linear(functional.silu(temb).to(model.norm_out.linear.weight.dtype)).chunk(2, dim=-1) | |
| hidden = model.norm_out.norm(hidden) * (1 + scale[plan.timestep_indices]) + shift[plan.timestep_indices] | |
| video_hidden = hidden.index_select(1, plan.video_indices).to(model.proj_out.weight.dtype) | |
| audio_hidden = hidden.index_select(1, plan.audio_indices).to(model.audio_proj_out.weight.dtype) | |
| video = torch.cat([model.proj_out(video_hidden[batch_index:batch_index + 1]) for batch_index in range(batch)]) | |
| audio = torch.cat([model.audio_proj_out(audio_hidden[batch_index:batch_index + 1]) for batch_index in range(batch)]) | |
| output = MiniMaxH3TransformerOutput(sample=video, audio_sample=audio) | |
| return (output, hidden) if return_hidden else output | |