Text-to-Video
Diffusers
Wan2.2
English
Chinese
video
video-generation
quantization
inference-optimization
wan
Instructions to use viberobin/Wan2.2-TI2V-5B-VedioQuant with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use viberobin/Wan2.2-TI2V-5B-VedioQuant with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("viberobin/Wan2.2-TI2V-5B-VedioQuant", torch_dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Wan2.2
How to use viberobin/Wan2.2-TI2V-5B-VedioQuant with Wan2.2:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
| """ | |
| VedioQuant Pipeline — Wan2.1 + TurboQuant 缓存压缩 | |
| 用法: | |
| from pipeline_vedioquant import VedioQuantPipeline | |
| pipe = VedioQuantPipeline.from_pretrained("robin-ph/Wan2.1-T2V-1.3B-VedioQuant") | |
| video = pipe("a cat sitting on a sofa", num_frames=17).frames[0] | |
| """ | |
| import torch | |
| import numpy as np | |
| from typing import Optional | |
| from diffusers import WanPipeline | |
| class PolarQuantCompressor: | |
| """TurboQuant 压缩器 (PolarQuant: 随机旋转 + 预计算码本量化)""" | |
| # 标准高斯 N(0,1) 的 Lloyd-Max 最优码本 | |
| _CODEBOOKS = { | |
| 2: np.array([-1.5104, -0.4528, 0.4528, 1.5104]), | |
| 3: np.array([-2.1520, -1.3440, -0.7560, -0.2451, 0.2451, 0.7560, 1.3440, 2.1520]), | |
| 4: np.array([-2.7326, -2.0690, -1.6180, -1.2562, -0.9423, -0.6568, -0.3881, -0.1284, | |
| 0.1284, 0.3881, 0.6568, 0.9423, 1.2562, 1.6180, 2.0690, 2.7326]), | |
| } | |
| _BOUNDARIES = { | |
| 2: np.array([-0.9816, 0.0, 0.9816]), | |
| 3: np.array([-1.7480, -1.0500, -0.5006, 0.0, 0.5006, 1.0500, 1.7480]), | |
| 4: np.array([-2.4008, -1.8435, -1.4371, -1.0993, -0.7996, -0.5224, -0.2582, 0.0, | |
| 0.2582, 0.5224, 0.7996, 1.0993, 1.4371, 1.8435, 2.4008]), | |
| } | |
| def __init__(self, dim, bits=3, seed=42): | |
| self.dim = dim | |
| self.bits = bits | |
| rng = np.random.RandomState(seed) | |
| R = rng.randn(dim, dim).astype(np.float32) | |
| Q, _ = np.linalg.qr(R) | |
| self.Pi = torch.tensor(Q, dtype=torch.float32) | |
| scale = 1.0 / np.sqrt(dim) | |
| self.levels = torch.tensor(self._CODEBOOKS[bits] * scale, dtype=torch.float32) | |
| self.boundaries = torch.tensor(self._BOUNDARIES[bits] * scale, dtype=torch.float32) | |
| def compress(self, x): | |
| if x.dim() == 1: | |
| x = x.unsqueeze(0) | |
| x = x.float().cpu() | |
| norms = torch.norm(x, dim=1) | |
| x_hat = x / norms.clamp(min=1e-10).unsqueeze(1) | |
| x_rot = x_hat @ self.Pi.T | |
| indices = torch.bucketize(x_rot, self.boundaries).to(torch.uint8) | |
| return norms, indices | |
| def decompress(self, norms, indices): | |
| x_q = self.levels[indices.long()] | |
| x_hat = x_q @ self.Pi | |
| return x_hat * norms.unsqueeze(1) | |
| class VedioQuantPipeline(WanPipeline): | |
| """ | |
| Wan2.1 + VedioQuant 缓存压缩 Pipeline | |
| 在标准 WanPipeline 基础上自动启用 TurboQuant 缓存压缩: | |
| - 3-bit 压缩, 10.6× 缓存缩减 | |
| - 余弦相似度 0.98, 质量损失 < 2% | |
| - 720P/81帧: 缓存从 886MB 降至 83MB | |
| 用法: | |
| pipe = VedioQuantPipeline.from_pretrained( | |
| "robin-ph/Wan2.1-T2V-1.3B-VedioQuant", | |
| torch_dtype=torch.float16 | |
| ) | |
| pipe.to("cuda") | |
| video = pipe("a cat on a sofa", num_frames=17).frames[0] | |
| 参数: | |
| vedioquant_bits: 量化位数 (2/3/4), 默认 3 | |
| vedioquant_threshold: 缓存复用阈值, 默认 0.05 | |
| vedioquant_enabled: 是否启用压缩, 默认 True | |
| """ | |
| vedioquant_bits: int = 3 | |
| vedioquant_threshold: float = 0.05 | |
| vedioquant_enabled: bool = True | |
| _vq_state = None | |
| _vq_compressor = None | |
| _vq_hooks = None | |
| _vq_stats = None | |
| def enable_vedioquant(self, bits=3, threshold=0.05): | |
| """手动启用 VedioQuant 缓存压缩""" | |
| self.vedioquant_bits = bits | |
| self.vedioquant_threshold = threshold | |
| self.vedioquant_enabled = True | |
| self._install_hooks() | |
| def disable_vedioquant(self): | |
| """禁用 VedioQuant""" | |
| self.vedioquant_enabled = False | |
| self._remove_hooks() | |
| def get_vedioquant_stats(self): | |
| """获取缓存统计""" | |
| if self._vq_stats is None: | |
| return {"status": "not initialized"} | |
| return dict(self._vq_stats) | |
| def _install_hooks(self): | |
| """安装压缩缓存 hooks""" | |
| self._remove_hooks() | |
| # 推断 hidden_dim | |
| cfg = self.transformer.config | |
| hidden_dim = cfg.num_attention_heads * cfg.attention_head_dim | |
| self._vq_compressor = PolarQuantCompressor( | |
| dim=hidden_dim, bits=self.vedioquant_bits | |
| ) | |
| self._vq_state = { | |
| "prev_residual": None, | |
| "compressed_cache": None, | |
| "head_output": None, | |
| } | |
| self._vq_stats = { | |
| "steps": 0, | |
| "cache_hits": 0, | |
| "bits": self.vedioquant_bits, | |
| "compression_ratio": f"{32.0 / self.vedioquant_bits:.1f}x", | |
| } | |
| self._vq_hooks = [] | |
| # 找到 transformer blocks | |
| blocks = None | |
| for name, child in self.transformer.named_children(): | |
| if name in ("blocks", "transformer_blocks", "layers"): | |
| blocks = list(child) | |
| break | |
| if blocks and len(blocks) > 1: | |
| # Hook 第一个 block (head) | |
| def head_hook(module, input, output): | |
| self._vq_stats["steps"] += 1 | |
| out = output[0] if isinstance(output, tuple) else output | |
| inp = input[0] if isinstance(input, tuple) else input | |
| residual = (out - inp).detach().cpu().float() | |
| should_compute = True | |
| if self._vq_state["prev_residual"] is not None: | |
| absmean = (residual - self._vq_state["prev_residual"]).abs().mean() | |
| prev_absmean = self._vq_state["prev_residual"].abs().mean() | |
| if prev_absmean > 1e-10: | |
| diff = (absmean / prev_absmean).item() | |
| should_compute = diff > self.vedioquant_threshold | |
| if not should_compute and self._vq_state["compressed_cache"] is not None: | |
| self._vq_stats["cache_hits"] += 1 | |
| self._vq_state["prev_residual"] = residual | |
| self._vq_state["head_output"] = out.detach() | |
| return output | |
| self._vq_hooks.append(blocks[0].register_forward_hook(head_hook)) | |
| # Hook 最后一个 block (tail) — 压缩存储 | |
| def tail_hook(module, input, output): | |
| out = output[0] if isinstance(output, tuple) else output | |
| if self._vq_state["head_output"] is not None: | |
| residual = out - self._vq_state["head_output"].to(out.device) | |
| flat = residual.detach().cpu().float().reshape( | |
| -1, residual.shape[-1] | |
| ) | |
| norms, indices = self._vq_compressor.compress(flat) | |
| self._vq_state["compressed_cache"] = (norms, indices, residual.shape) | |
| return output | |
| self._vq_hooks.append(blocks[-1].register_forward_hook(tail_hook)) | |
| def _remove_hooks(self): | |
| if self._vq_hooks: | |
| for h in self._vq_hooks: | |
| h.remove() | |
| self._vq_hooks = [] | |
| def __call__(self, *args, **kwargs): | |
| """自动在推理时启用 VedioQuant""" | |
| if self.vedioquant_enabled and not self._vq_hooks: | |
| self._install_hooks() | |
| # 重置统计 | |
| if self._vq_stats: | |
| self._vq_stats["steps"] = 0 | |
| self._vq_stats["cache_hits"] = 0 | |
| result = super().__call__(*args, **kwargs) | |
| if self._vq_stats: | |
| total = self._vq_stats["steps"] | |
| hits = self._vq_stats["cache_hits"] | |
| self._vq_stats["hit_rate"] = f"{hits/max(total,1)*100:.0f}%" | |
| return result | |