Instructions to use teawhite/ActionRoPE with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Wan2.2
How to use teawhite/ActionRoPE 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
Download code/actionrope/build_text_table.py from teawhite/ActionRoPE: direct link, hf CLI and curl.
- Browser
- Download file 5.16 kB
-
https://huggingface.co/teawhite/ActionRoPE/resolve/main/code/actionrope/build_text_table.py
- Command line
-
hf download hf://teawhite/ActionRoPE/code/actionrope/build_text_table.py
-
curl -L -o build_text_table.py https://huggingface.co/teawhite/ActionRoPE/resolve/main/code/actionrope/build_text_table.py
5.16 kB
| #!/usr/bin/env python3 | |
| """建 ActionRoPE 的 UMT5 文本表(动作从句已剥离)。 | |
| 数据集自带的 text_table_eybx.pt 是 1,253 键,每一条都含 "the player is {动作}" | |
| —— 那是动作走文本的老方案。ActionRoPE 的动作走 RoPE,文本只留场景,剥完 | |
| 只剩 360 条不同的串(18 纯场景 + 342 转场),所以要重建一张表。 | |
| 编码走 DiffSynth 自己的 tokenizer + WanTextEncoder,与训练/推理时 | |
| WanVideoUnit_PromptEmbedder 的路径逐字一致: | |
| · HuggingfaceTokenizer(seq_len=512, clean='whitespace') | |
| · prompt_emb[:, seq_len:] = 0 (尾部 pad 清零) | |
| 产物与老表同构:dict[str, bf16[512, 4096]],训练时直接查表,不必挂 11 GB 的 UMT5。 | |
| python -m actionrope.build_text_table --out data/latent/text_table_actionrope.pt | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import glob | |
| import json | |
| import os | |
| import sys | |
| import time | |
| import torch | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from actionrope.prompts import NULL_PROMPT, strip_action # noqa: E402 | |
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| DEFAULT_CKPT = os.path.join(ROOT, "models", "Wan2.2-TI2V-5B") | |
| def collect_keys(meta_glob: str, null_prompt: str) -> list[str]: | |
| """从 meta/*.jsonl 收全部逐 cell prompt,剥掉动作从句后去重。 | |
| 走 meta 而不是 22,782 个 .pt:meta 只有 65 MB,.pt 要读 72 GB, | |
| 两者的 prompts 字段是同一份(encode_latents.py 直接从 meta 抄进 sidecar)。 | |
| """ | |
| keys, n_rows, n_cells = set(), 0, 0 | |
| files = sorted(glob.glob(meta_glob)) | |
| if not files: | |
| raise FileNotFoundError(f"没有 meta 文件: {meta_glob}") | |
| for f in files: | |
| with open(f, encoding="utf-8") as fh: | |
| for line in fh: | |
| r = json.loads(line) | |
| n_rows += 1 | |
| for p in r["prompts"]: | |
| n_cells += 1 | |
| keys.add(strip_action(p)) | |
| print(f"扫过 {n_rows:,} 个 clip / {n_cells:,} 个 cell -> {len(keys):,} 个不同串") | |
| # 场景 dropout / CFG 用的空条件串。剥离后 prompts_bossdrop 的 21 条会全部 | |
| # 塌成同一个「没有场景」的串,这里显式加进表里,训练侧才有得查。 | |
| if null_prompt not in keys: | |
| keys.add(null_prompt) | |
| print(f"追加空条件串: {null_prompt!r}") | |
| return sorted(keys) | |
| def main(): | |
| ap = argparse.ArgumentParser(description="ActionRoPE UMT5 文本表") | |
| ap.add_argument("--meta", default=os.path.join(ROOT, "data/meta/*.jsonl")) | |
| ap.add_argument("--out", default=os.path.join(ROOT, "data/latent/text_table_actionrope.pt")) | |
| ap.add_argument("--ckpt", default=DEFAULT_CKPT, help="Wan2.2-TI2V-5B 本地目录") | |
| ap.add_argument("--null_prompt", default=NULL_PROMPT, | |
| help="场景 dropout / CFG 的空条件串") | |
| ap.add_argument("--batch", type=int, default=32) | |
| ap.add_argument("--device", default="cuda") | |
| ap.add_argument("--dry_run", action="store_true", help="只收串、不加载 UMT5") | |
| args = ap.parse_args() | |
| keys = collect_keys(args.meta, args.null_prompt) | |
| plain = [k for k in keys if "burning away into" not in k and "cutting to" not in k] | |
| print(f" 纯场景 {len(plain)} 条 / 转场 {len(keys) - len(plain)} 条") | |
| if args.dry_run: | |
| for k in plain: | |
| print(" ", k) | |
| return | |
| from diffsynth.pipelines.wan_video import WanVideoPipeline, ModelConfig | |
| # 只加载 text_encoder:DiT 和 VAE 这一步用不上,省 15 GB 显存。 | |
| # redirect_common_files=False —— 本地已有 .pth,不要去 ModelScope 拉 safetensors。 | |
| pipe = WanVideoPipeline.from_pretrained( | |
| torch_dtype=torch.bfloat16, | |
| device=args.device, | |
| model_configs=[ModelConfig(path=os.path.join(args.ckpt, "models_t5_umt5-xxl-enc-bf16.pth"))], | |
| tokenizer_config=ModelConfig(path=os.path.join(args.ckpt, "google/umt5-xxl")), | |
| redirect_common_files=False, | |
| ) | |
| pipe.load_models_to_device(["text_encoder"]) | |
| table, t0 = {}, time.time() | |
| for i in range(0, len(keys), args.batch): | |
| chunk = keys[i:i + args.batch] | |
| ids, mask = pipe.tokenizer(chunk, return_mask=True, add_special_tokens=True) | |
| ids, mask = ids.to(args.device), mask.to(args.device) | |
| seq_lens = mask.gt(0).sum(dim=1).long() | |
| with torch.no_grad(): | |
| emb = pipe.text_encoder(ids, mask) | |
| # 与 WanVideoUnit_PromptEmbedder 一致:有效长度之后一律清零 | |
| for j, v in enumerate(seq_lens): | |
| emb[j, v:] = 0 | |
| for k, e, v in zip(chunk, emb, seq_lens): | |
| table[k] = e.to(torch.bfloat16).cpu().clone() | |
| print(f" {min(i + args.batch, len(keys)):4d}/{len(keys)} · {time.time() - t0:.0f}s", flush=True) | |
| shape = next(iter(table.values())).shape | |
| os.makedirs(os.path.dirname(args.out), exist_ok=True) | |
| torch.save(table, args.out) | |
| print(f"DONE {len(table):,} 键 · 每键 {tuple(shape)} bf16 · " | |
| f"{os.path.getsize(args.out) / 1e6:.0f} MB -> {args.out}") | |
| if __name__ == "__main__": | |
| main() | |