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
File size: 7,404 Bytes
40c6a90 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | """
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
|