LongLive-Plug-Wan2.2-TI2V-5B-cfg: Our Generated Video, File List

October 2, 2026

LongLive-Plug-Wan2.2-TI2V-5B-cfg: Our Generated Video, File List

At a Glance

Item Value
Repository Efficient-Large-Model/LongLive-Plug-Wan2.2-TI2V-5B-cfg
Publisher guide Efficient Large Model (NVIDIA and MIT): models and licenses
Published 2026-09-29
License apache-2.0
Formats safetensors
Source type Primary source (the publisher itself)

Values determined by this site’s code when the information was collected. Dates are JST.

What we checked ourselves

  • We generated video on a rented cloud GPU from our own fixed prompts and show it unedited.

Details and conditions are in “Our Own Measurements” below.

Overview

LongLive-Plug-Wan2.2-TI2V-5B-cfg has been released as a LoRA adapter applying CFG (Classifier-Free Guidance) distillation for the text- and image-to-video generation model Wan2.2-TI2V-5B.

This model is an adapter that distills the standard CFG=5 guided flow of the base model into a single conditional forward pass at CFG=1 on the student model side. It eliminates the need to calculate unconditional (negative) branches at each denoising step, reducing the DiT (Diffusion Transformer) forward processing to just once per step. Note that denoising schedule distillation is not performed, making this different from DMD (Distribution Matching Distillation) checkpoints that carry out few-step generation.

Specifications

The specifications and recommended settings listed in the model card are as follows:

  • Parameter count: LoRA parameters are 161,218,560 (FP32 tensors: 600, base model is the 5-billion parameter scale Wan2.2-TI2V-5B)
  • Architecture: PEFT LoRA (rank: 64, alpha: 64, dropout: 0.0) targeting 300 Linear layers spanning all 30 transformer blocks (self-attention q/k/v/o, cross-attention q/k/v/o, and FFN 0/2 of each block)
  • Output specifications: Resolution of 1280×704 (704×1280 is also supported per base model specs), with the evaluation profile set to 121 frames at 24 FPS (5-second duration under original model specs)
  • Recommended steps and sampler: 50 steps, FlowUniPC scheduler (timestep shift: 5.0)
  • Recommended guidance and weight settings: guidance_scale is 1.0 (CFG=1), and LoRA weight scale is 1.0 (consistent scale during training)
  • License terms: Distributed under the Apache-2.0 license, allowing use under the specified terms including commercial use

Performance and Quality

The release source provides the following configuration values regarding the distillation design and inference settings of this adapter:

Setting Value
Teacher CFG scale used for distillation 5.0
Student CFG during training 1.0
Recommended inference CFG 1.0
Default LoRA weight scale 1.0
Teacher → student denoising steps 50 → 50
Scheduler / timestep shift FlowUniPC / 5.0
LoRA rank / alpha 64 / 64
Training checkpoint step 250

In this method, training is performed to minimize the relative guidance-weighted MSE against a fixed teacher model trajectory state, maintaining the native 50 steps identical to the original model. The design does not perform rollout generation by the student model itself during training, and does not include a DMD critic. While it has the advantage of reducing computational load by skipping unconditional branch evaluation, the publisher states that this is a research checkpoint documenting artifact integrity and executed training specifications, and does not guarantee general-purpose quality across all prompts or downstream models.

Regarding the performance of the base Wan2.2-TI2V-5B model itself, it employs the Wan2.2-VAE with a compression rate of 16×16×4 (4×16×16 across temporal, height, and width dimensions), achieving a total compression of 4×32×32 combined with patchify layers. According to the original model’s published information, it possesses the efficiency to generate 5 seconds of 720P video in under 9 minutes on a single consumer GPU even without optimizations.

Strengths and Use Cases

This adapter aims to improve computational efficiency during inference while maintaining the capabilities of the base model Wan2.2-TI2V-5B. Specifically, it streamlines the guidance process via CFG (Classifier-Free Guidance) for both text-to-video and image-to-video tasks.

While conventional CFG-based inference required two forward passes per denoising step—one “conditional" and one “unconditional (negative)"—applying this LoRA and running with guidance_scale: 1.0 enables operation with only a single conditional forward pass per step. This is expected to reduce the computational cost of inference while preserving general generation quality trends.

Distributed Files

Weight files published in Efficient-Large-Model/LongLive-Plug-Wan2.2-TI2V-5B-cfg, listed by this site from the Hugging Face API. Sizes are the actual file sizes.

File Size
adapter_model.safetensors 645MB
generator_lora.pt 645MB

How to Get It

This model is available from the Hugging Face repository. The following files are provided as distribution formats:

  • adapter_model.safetensors: Safe and portable format generator LoRA
  • generator_lora.pt: LongLive native payload
  • adapter_config.json: PEFT settings and target module information
  • training_config.yaml: Training configuration details
  • inference_overrides.yaml: Inference settings (50 steps, CFG=1)
  • release_metadata.json: Metadata
  • provenance.json: Provenance information and checksums
  • SHA256SUMS: Checksums for artifacts

You can download generator_lora.pt using the Hugging Face huggingface_hub library with the following command:

from huggingface_hub import hf_hub_download

lora_path = hf_hub_download(
    repo_id="Efficient-Large-Model/LongLive-Plug-Wan2.2-TI2V-5B-cfg",
    filename="generator_lora.pt",
)
print(lora_path)

When using it as an inference path for LongLive, it is recommended to use settings such as sampling_steps: 50, guidance_scale: 1.0, and lora_weight_scale: 1.0.

Can You Run It Locally?

The publisher distributes this model as safetensors.

License — apache-2.0 (Commercial use allowed): Permits commercial use, modification and redistribution. Redistribution requires including the license and stating changes; includes a patent grant.

Compiled by this site’s code from the published formats and the license field. License summaries are not legal advice — check the publisher’s original terms before relying on them.

Our Own Measurements

Generated by us — not samples copied from the model card. We cannot run video models on our own server (no GPU), so we generated on a rented cloud GPU (conditions below). How we measure, and the results for every model: Our Measurements.

Video We Generated (Cloud GPU)

Efficient-Large-Model/LongLive-Plug-Wan2.2-TI2V-5B-cfg (apache-2.0 license) is a LoRA adapter and does not run on its own, so we merged it into the base model Wan-AI/Wan2.2-TI2V-5B-Diffusers (apache-2.0 license) in the combination the publisher recommends (LongLive-Plug-Wan2.2-TI2V-5B-few-step at weight 1.0, LongLive-Plug-Wan2.2-TI2V-5B-cfg at weight 0.5) and generated with 4 steps and guidance scale 1.0. We set the sampler to match the publisher’s inference code (LongLive-Plug inference.py): UniPC with shift 5.0 (diffusers’ UniPCMultistepScheduler). The publisher says to use this adapter together with LongLive-Plug-Wan2.2-TI2V-5B-few-step, so we merged both, and the same videos also appear in that adapter’s article. We ran diffusers’ WanPipeline (diffusers 0.40.0) and did not run any code from the publisher. The LoRA was merged by our own code: we map the original Wan layer names to diffusers’ names and add weight × (alpha / r) × BA to each weight. This mapping is our interpretation, not something the publisher documents; we only confirmed that every layer name and shape matched.

Conditions: NVIDIA L40S (VRAM 44GB) on Modal, weights in BF16 (VAE in FP32), 1280×704 pixels, 121 frames at 24 fps (about 5.04 s), seed 0, two fixed prompts without people, logos or text, generated 2026-10-02 (JST). Not selected — each clip is the first output. To fit our server’s upload limit we re-encoded each clip as H.264 (under 12.0 MB), so fine detail may be softer than the raw output.

Prompt: “a small red paper boat drifting down a clear forest stream, sunlight flickering through the leaves, gentle ripples, realistic”

Without the LoRA (base model, same 4 steps), 51 s to generate

With the LoRA, 52 s to generate

Prompt: “time-lapse of white clouds rolling over snowy mountain peaks at sunrise, golden light, wide shot”

Without the LoRA (base model, same 4 steps), 52 s to generate

With the LoRA, 52 s to generate

Item Result
VRAM after loading the model (BF16, text encoder already released) 12.0GB
Peak VRAM while generating 32.3GB

Generation times are for a rented cloud NVIDIA L40S and are not a guide to your own GPU; the text encoder runs first and is released before the video model loads, so the VRAM above does not include it. The “without the LoRA” clips use the same 4 steps for comparison only; the base model’s usual settings use many more steps with guidance, so they do not show what the base model can do.

Setup, Download and Run Steps (What We Actually Ran)

We split the work into two containers on Modal, both built from the Debian (slim) image with Python 3.12. The downloading container fetches only the listed files (configs, safetensors weights and the tokenizer) from Hugging Face at pinned revisions; the generating container has no network access. These are the packages we installed:

# 1. Environment for downloading (no GPU)
python -m pip install huggingface_hub

# 2. Environment for generating (GPU, network blocked)
python -m pip install torch diffusers transformers accelerate safetensors sentencepiece ftfy imageio-ffmpeg numpy

We then called download(job) in the first container and run(job) in the second one from the script below (video_remote.py, written by us; full text). Files go under /models/<dir>:

"""
Modal のコンテナの中で動く、動画生成モデルの生成(2026-10-01、ユーザーの判断)。lmw/lab/gpu_modal.py の
video_download / video_run から呼ばれる。**標準ライブラリだけを先頭で読み**、huggingface_hub・torch・diffusers は
各関数の中で読む(コンテナごとに入っているものが違う)。

【配布元のコードは動かさない】diffusers・transformers の公開パッケージだけで読み込む。重みは safetensors だけで、
Hugging Face のリポジトリのコードを読み込む設定は使わない。LoRA は当サイトのコード(merge_lora)が、名前の対応表と
形を照合しながら元の重みに足し込む(配布元の読み込み処理は使わない)。

【2つのコンテナに分ける】(lmw/lab/remote_code_remote.py と同じ)
- download: GPU の無いコンテナ・ネットワークあり。Hugging Face から、版を固定して決まったファイルだけを落とす。
- run: GPU のコンテナ・**ネットワーク遮断**・Modal の機能も使えない・Volume は読み取り専用。

【VRAM を節約する順番】テキストエンコーダ(UMT5-XXL は 11GB)でプロンプトを先に全部埋め込みにしてから捨て、
その後で動画の本体(transformer)と VAE を読む。2つを同時に GPU に載せないので、安い GPU に収まる。

【結果の返し方】run は生成器で、動画のバイト列を CHUNK_BYTES ずつ(kind: "chunk")、最後に記録(kind: "record")を返す。
Modal は 2MiB を超える戻り値を自分の API で預けるが、生成のコンテナは Modal の機能を遮断している
(restrict_modal_access)ので断られる(2026-10-02、14B の4本を作り終えた後に 401 で全て失った)。1回の戻り値を小さく保つ。

【費用を抑える】同じ読み込みのまま、LoRA を足す前(元のモデル)と足した後を続けて生成する(読み込みは1回)。
動画は WordPress のアップロードの上限に収まるよう H.264 で圧縮する(MAX_BYTES を超えたら画質を下げて作り直す)。

記事執筆用の LLM は呼ばない。
"""
import hashlib
import json
import os
import platform
import subprocess
import time
from datetime import datetime, timezone

MOUNT = "/models"
PYTHON_VERSION = "3.12"
DOWNLOAD_PACKAGES = ("huggingface_hub",)
# diffusers の Wan のパイプラインが使うもの(ftfy はプロンプトの整形、imageio-ffmpeg は H.264 の書き出しに使う)
RUN_PACKAGES = ("torch", "diffusers", "transformers", "accelerate", "safetensors", "sentencepiece", "ftfy",
                "imageio-ffmpeg", "numpy")
# 落としてよいファイル(pickle の .bin / .pt / .ckpt は落とさない)
ALLOWED_EXTS = (".json", ".safetensors", ".model", ".txt")

# 元の Wan の層の名前(LoRA が使う)→ diffusers の WanTransformer3DModel の層の名前。
# diffusers の変換スクリプト(scripts/convert_wan_to_diffusers.py)と同じ対応
WAN_LORA_MODULES = {
    "self_attn.q": "attn1.to_q", "self_attn.k": "attn1.to_k", "self_attn.v": "attn1.to_v",
    "self_attn.o": "attn1.to_out.0",
    "cross_attn.q": "attn2.to_q", "cross_attn.k": "attn2.to_k", "cross_attn.v": "attn2.to_v",
    "cross_attn.o": "attn2.to_out.0",
    "ffn.0": "ffn.net.0.proj", "ffn.2": "ffn.net.2",
}
# 1回に返すバイト数(Modal が自分の API に預けずにそのまま返せる 2MiB より十分小さく)
CHUNK_BYTES = 1024 * 1024
LORA_PREFIXES = ("base_model.model.", "diffusion_model.", "model.")
# 生成に使うデバイス。Modal では常に cuda(当サイトの評価で、小さなモデルを CPU で通すときだけ cpu にする)
DEVICE = "cuda"

def installed_packages() -> list[str]:
    from importlib import metadata
    seen = {}
    for dist in metadata.distributions():
        name = dist.metadata["Name"]
        if name and name.lower() not in seen:
            seen[name.lower()] = f"{name}=={dist.version}"
    return sorted(seen.values(), key=str.lower)

def _log(t0: float, message: str) -> None:
    print(f" {message}", flush=True)

def _sha256(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(1 << 20), b""):
            h.update(chunk)
    return h.hexdigest()

def download(job: dict) -> dict:
    """
    job: {"dir", "base": {"repo", "revision", "files"}, "loras": [{"repo", "revision", "file"}]}。
    元のモデルは <dir>/base、LoRA は <dir>/lora<i>/ に置く。戻り値: {"bytes", "seconds", "packages", "lora_sha256"}。
    """
    from huggingface_hub import hf_hub_download
    t0 = time.time()
    base_dir = os.path.join(MOUNT, job["dir"])
    total = 0
    targets = [(job["base"]["repo"], job["base"]["revision"], name, os.path.join(base_dir, "base"))
               for name in job["base"]["files"]]
    targets += [(lora["repo"], lora["revision"], lora["file"], os.path.join(base_dir, f"lora{i}"))
                for i, lora in enumerate(job.get("loras") or [])]
    for repo, revision, name, dest in targets:
        if not name.endswith(ALLOWED_EXTS) or ".." in name.split("/"):
            raise ValueError(f"落とさない種類のファイルです: {name}")
        path = hf_hub_download(repo, name, revision=revision, local_dir=dest)
        total += os.path.getsize(path)
    lora_sha = [_sha256(os.path.join(base_dir, f"lora{i}", lora["file"]))
                for i, lora in enumerate(job.get("loras") or [])]
    seconds = round(time.time() - t0)
    _log(t0, f"ダウンロード {total / 1024 ** 3:.1f}GB・{seconds}秒")
    return {"bytes": int(total), "seconds": int(seconds), "packages": installed_packages(), "lora_sha256": lora_sha}

def _lora_pairs(state: dict) -> dict:
    """{元の層の名前: {"A", "B", "alpha"?}}。名前の頭(base_model.model. 等)を外し、A と B の揃わない層は断る。"""
    pairs: dict = {}
    for key, tensor in state.items():
        name = key
        for prefix in LORA_PREFIXES:
            if name.startswith(prefix):
                name = name[len(prefix):]
                break
        if name.endswith(".lora_A.weight"):
            pairs.setdefault(name[:-len(".lora_A.weight")], {})["A"] = tensor
        elif name.endswith(".lora_B.weight"):
            pairs.setdefault(name[:-len(".lora_B.weight")], {})["B"] = tensor
        elif name.endswith(".alpha"):
            pairs.setdefault(name[:-len(".alpha")], {})["alpha"] = float(tensor.item())
        else:
            raise ValueError(f"LoRA の重みではない名前です: {key}")
    broken = [n for n, p in pairs.items() if "A" not in p or "B" not in p]
    if broken:
        raise ValueError(f"A と B が揃わない層があります: {broken[:5]}")
    return pairs

def diffusers_module_name(name: str) -> str:
    """blocks.12.self_attn.q → blocks.12.attn1.to_q。対応表に無い層は断る(推測で当てない)。"""
    parts = name.split(".")
    if len(parts) < 4 or parts[0] != "blocks" or not parts[1].isdigit():
        raise ValueError(f"対応表に無い層です: {name}")
    tail = ".".join(parts[2:])
    if tail not in WAN_LORA_MODULES:
        raise ValueError(f"対応表に無い層です: {name}")
    return f"blocks.{parts[1]}.{WAN_LORA_MODULES[tail]}"

def merge_lora(transformer, path: str, weight: float, alpha: float | None, rank: int | None) -> dict:
    """
    LoRA を元の重みに足し込む: W ← W + weight × (alpha / r) × (B @ A)。alpha と r は配布元の adapter_config.json の値を
    ホスト側が渡す(重みのファイルに alpha があればそちらを使う)。足し込みは float32 で計算してから元の型に戻す。
    全ての層が対応表で見つかり、形が一致することを確かめる(1つでも外れたら断る)。
    """
    import torch
    from safetensors.torch import load_file
    pairs = _lora_pairs(load_file(path))
    merged = 0
    for name, p in sorted(pairs.items()):
        module = transformer.get_submodule(diffusers_module_name(name))
        a, b = p["A"].float(), p["B"].float()
        r = a.shape[0]
        if rank and r != rank:
            raise ValueError(f"{name}: ランク {r} が adapter_config.json の {rank} と違います")
        scale = weight * ((p.get("alpha") or alpha or r) / r)
        w = module.weight
        delta = (b.to(w.device) @ a.to(w.device)) * scale
        if tuple(delta.shape) != tuple(w.shape):
            raise ValueError(f"{name}: 形 {tuple(delta.shape)} が元の重み {tuple(w.shape)} と違います")
        with torch.no_grad():
            w.copy_((w.float() + delta).to(w.dtype))
        merged += 1
    return {"modules": merged, "weight": weight, "scale": round(weight * ((alpha or rank or 1) / (rank or 1)), 6)}

def encode_mp4(frames, fps: int, path: str, max_bytes: int) -> dict:
    """
    0〜1 の RGB のコマ(F, H, W, 3)を H.264(yuv420p)の mp4 にする。max_bytes を超えたら、ビットレートの上限を
    下げて作り直す(3回まで)。戻り値: 実際に使った ffmpeg の引数と大きさ。
    """
    import imageio_ffmpeg
    import numpy as np
    pixels = (np.clip(frames, 0.0, 1.0) * 255).round().astype(np.uint8)
    count, height, width = pixels.shape[0], pixels.shape[1], pixels.shape[2]
    duration = count / fps
    # 上限の大きさの 85% に収まるビットレート(音声なし)
    kbps = int(max_bytes * 8 * 0.85 / duration / 1000)
    for attempt, crf in enumerate((18, 22, 26)):
        rate = int(kbps * (0.8 ** attempt))
        args = ["-y", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{width}x{height}", "-r", str(fps), "-i", "-",
                "-an", "-c:v", "libx264", "-preset", "slow", "-crf", str(crf), "-maxrate", f"{rate}k",
                "-bufsize", f"{rate * 2}k", "-pix_fmt", "yuv420p", "-movflags", "+faststart", path]
        proc = subprocess.run([imageio_ffmpeg.get_ffmpeg_exe(), *args], input=pixels.tobytes(),
                              capture_output=True, timeout=600)
        if proc.returncode != 0:
            raise RuntimeError(f"ffmpeg が失敗しました: {proc.stderr.decode(errors='replace')[-400:]}")
        size = os.path.getsize(path)
        if size <= max_bytes:
            return {"ffmpeg_args": ["ffmpeg", *args[:-1], os.path.basename(path)], "bytes": size,
                    "frames": int(count), "width": int(width), "height": int(height), "fps": int(fps),
                    "duration": round(duration, 2)}
    raise ValueError(f"動画を {max_bytes} バイト以下に圧縮できませんでした")

def run(job: dict):
    """
    job: {"dir", "pipeline", "settings": {height, width, num_frames, fps, steps, guidance, flow_shift?},
          "seed", "prompts": [{"id", "en"}], "loras": [{"file", "weight", "alpha", "rank"}], "compare_base",
          "max_bytes"}。
    生成器: {"kind": "chunk", "id", "index", "data": bytes} を動画ごとに順に返し、最後に
    {"kind": "record", ...記録の一部(秒数・VRAM・版。動画の中身は含まない)} を返す。
    """
    import diffusers
    import numpy as np
    import torch
    import transformers
    os.environ["HF_HUB_OFFLINE"] = "1"
    os.environ["TRANSFORMERS_OFFLINE"] = "1"
    os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
    t0 = time.time()
    base_dir = os.path.join(MOUNT, job["dir"])
    model_dir = os.path.join(base_dir, "base")
    settings = job["settings"]
    guidance = float(settings["guidance"])
    cuda = DEVICE == "cuda"
    gpu = torch.cuda.get_device_name(0) if cuda else "cpu"
    _log(t0, f"GPU: {gpu}・diffusers {diffusers.__version__}・torch {torch.__version__}")
    record = {"gpu": gpu, "vram_total": int(torch.cuda.get_device_properties(0).total_memory) if cuda else 0,
              "diffusers": str(diffusers.__version__), "transformers": str(transformers.__version__),
              "torch": str(torch.__version__), "dtype": "bfloat16", "vae_dtype": "float32",
              "packages": installed_packages(), "python": platform.python_version(),
              "measured_at": datetime.now(timezone.utc).isoformat()}
    pipeline_cls = getattr(diffusers, job["pipeline"])

    # 1. プロンプトを埋め込みにする(テキストエンコーダだけを GPU に載せ、終わったら捨てる)
    with open(os.path.join(model_dir, "model_index.json"), encoding="utf-8") as f:
        index = json.load(f)
    skip_models = {name: None for name, spec in index.items()
                   if isinstance(spec, list) and name not in ("text_encoder", "tokenizer", "scheduler")}
    text_pipe = pipeline_cls.from_pretrained(model_dir, torch_dtype=torch.bfloat16, use_safetensors=True,
                                             local_files_only=True, **skip_models)
    text_pipe.text_encoder.to(DEVICE)
    embeds = {}
    with torch.no_grad():
        for p in job["prompts"]:
            # max_sequence_length はパイプラインの __call__ の既定(512。元の Wan も 512)に合わせる。
            # encode_prompt の既定は 226 で、そのままだと標準の呼び出しと違う埋め込みになる
            pos, neg = text_pipe.encode_prompt(prompt=p["en"], negative_prompt="", device=DEVICE,
                                               do_classifier_free_guidance=guidance > 1.0,
                                               max_sequence_length=int(settings.get("max_sequence_length", 512)))
            embeds[p["id"]] = (pos.cpu(), None if neg is None else neg.cpu())
    del text_pipe
    if cuda:
        torch.cuda.empty_cache()
    record["encode_sec"] = round(time.time() - t0)

    # 2. 本体と VAE を読む(VAE は float32。diffusers の Wan の例と同じ)
    vae_cls = getattr(diffusers, index["vae"][1])
    vae = vae_cls.from_pretrained(model_dir, subfolder="vae", torch_dtype=torch.float32, use_safetensors=True,
                                  local_files_only=True)
    pipe = pipeline_cls.from_pretrained(model_dir, vae=vae, text_encoder=None, tokenizer=None,
                                        torch_dtype=torch.bfloat16, use_safetensors=True, local_files_only=True)
    if settings.get("flow_shift") is not None:
        pipe.scheduler = type(pipe.scheduler).from_config(pipe.scheduler.config, flow_shift=float(settings["flow_shift"]))
    record["scheduler"] = {"class": type(pipe.scheduler).__name__,
                           **{k: v for k, v in dict(pipe.scheduler.config).items()
                              if k in ("flow_shift", "use_flow_sigmas", "prediction_type", "solver_order")}}
    pipe.to(DEVICE)
    if cuda:
        torch.cuda.synchronize()
    record["vram_loaded"] = int(torch.cuda.memory_allocated()) if cuda else 0
    record["load_sec"] = round(time.time() - t0)
    _log(t0, f"読み込み: VRAM {record['vram_loaded'] / 1024 ** 3:.1f}GB・{record['load_sec']}秒")
    if cuda:
        torch.cuda.reset_peak_memory_stats()

    def generate(group: str) -> list[dict]:
        out = []
        for p in job["prompts"]:
            pos, neg = embeds[p["id"]]
            started = time.time()
            generator = torch.Generator("cpu").manual_seed(int(job["seed"]))
            frames = pipe(prompt_embeds=pos.to(DEVICE),
                          negative_prompt_embeds=None if neg is None else neg.to(DEVICE),
                          height=int(settings["height"]), width=int(settings["width"]),
                          num_frames=int(settings["num_frames"]), num_inference_steps=int(settings["steps"]),
                          guidance_scale=guidance, generator=generator, output_type="np").frames[0]
            if cuda:
                torch.cuda.synchronize()
            seconds = time.time() - started
            frames = np.asarray(frames, dtype=np.float32)
            if not np.isfinite(frames).all():
                raise FloatingPointError("生成したコマに数でない値があります")
            # 処理の誤り(真っ黒・一色の動画)を取り込み役の機械の検査(lmw/lab/sanity.py)が見つけるための値
            stats = {"pixel_mean": round(float(frames.mean()) * 255, 2), "pixel_std": round(float(frames.std()) * 255, 2)}
            path = os.path.join("/tmp", f"{group}-{p['id']}.mp4")
            encoded = encode_mp4(frames, int(settings["fps"]), path, int(job["max_bytes"]))
            with open(path, "rb") as f:
                raw = f.read()
            blobs[f"{group}-{p['id']}"] = raw
            out.append({"id": f"{group}-{p['id']}", "prompt_id": p["id"], "group": group,
                        "seconds": round(seconds, 1), **encoded, **stats,
                        "sha256": hashlib.sha256(raw).hexdigest()})
            _log(t0, f"{group}/{p['id']}: {encoded['frames']}コマ・生成 {seconds:.0f}秒・{encoded['bytes'] / 1e6:.2f}MB")
        return out

    videos = []
    blobs: dict[str, bytes] = {}
    loras = job.get("loras") or []
    # 3. LoRA を足す前の元のモデル(比べるため。同じプロンプト・シード・ステップ数)
    if loras and job.get("compare_base"):
        videos += generate("base")
    # 4. LoRA を足し込んでから生成する
    merges = []
    for i, lora in enumerate(loras):
        path = os.path.join(base_dir, f"lora{i}", lora["file"])
        merges.append(merge_lora(pipe.transformer, path, float(lora["weight"]), lora.get("alpha"), lora.get("rank")))
        _log(t0, f"LoRA {i}: {merges[-1]['modules']}層を足し込みました(重み {lora['weight']})")
    if merges:
        record["merges"] = merges
    videos += generate("lora" if loras else "model")
    record["vram_peak"] = int(torch.cuda.max_memory_allocated()) if cuda else 0
    record["videos"] = videos
    for vid, raw in blobs.items():
        for index in range(0, len(raw), CHUNK_BYTES):
            yield {"kind": "chunk", "id": vid, "index": index // CHUNK_BYTES, "data": raw[index:index + CHUNK_BYTES]}
    yield {"kind": "record", **plain(record)}

def plain(value):
    """JSON に通して、標準の型だけにする(ホストには torch も diffusers も無い)。"""
    return json.loads(json.dumps(value, default=str))

The job passed to download (as used):

{
  "dir": "video-1790915190",
  "base": {
    "repo": "Wan-AI/Wan2.2-TI2V-5B-Diffusers",
    "revision": "b8fff7315c768468a5333511427288870b2e9635",
    "files": [
      "model_index.json",
      "scheduler/scheduler_config.json",
      "text_encoder/config.json",
      "text_encoder/model-00001-of-00003.safetensors",
      "text_encoder/model-00002-of-00003.safetensors",
      "text_encoder/model-00003-of-00003.safetensors",
      "text_encoder/model.safetensors.index.json",
      "tokenizer/special_tokens_map.json",
      "tokenizer/spiece.model",
      "tokenizer/tokenizer.json",
      "tokenizer/tokenizer_config.json",
      "transformer/config.json",
      "transformer/diffusion_pytorch_model-00001-of-00005.safetensors",
      "transformer/diffusion_pytorch_model-00002-of-00005.safetensors",
      "transformer/diffusion_pytorch_model-00003-of-00005.safetensors",
      "transformer/diffusion_pytorch_model-00004-of-00005.safetensors",
      "transformer/diffusion_pytorch_model-00005-of-00005.safetensors",
      "transformer/diffusion_pytorch_model.safetensors.index.json",
      "vae/config.json",
      "vae/diffusion_pytorch_model.safetensors"
    ]
  },
  "loras": [
    {
      "repo": "Efficient-Large-Model/LongLive-Plug-Wan2.2-TI2V-5B-few-step",
      "revision": "38a6ec4e8f5b644749d3072bfe1ac383d00951c0",
      "file": "adapter_model.safetensors"
    },
    {
      "repo": "Efficient-Large-Model/LongLive-Plug-Wan2.2-TI2V-5B-cfg",
      "revision": "fa1f9280778aa27c8038fe06e29fb5f2ed6be451",
      "file": "adapter_model.safetensors"
    }
  ]
}

The job passed to run (as used):

{
  "dir": "video-1790915190",
  "pipeline": "WanPipeline",
  "settings": {
    "height": 704,
    "width": 1280,
    "num_frames": 121,
    "fps": 24,
    "steps": 4,
    "guidance": 1.0,
    "flow_shift": 5.0
  },
  "seed": 0,
  "prompts": [
    {
      "id": "boat",
      "en": "a small red paper boat drifting down a clear forest stream, sunlight flickering through the leaves, gentle ripples, realistic"
    },
    {
      "id": "clouds",
      "en": "time-lapse of white clouds rolling over snowy mountain peaks at sunrise, golden light, wide shot"
    }
  ],
  "loras": [
    {
      "file": "adapter_model.safetensors",
      "weight": 1.0,
      "alpha": 128,
      "rank": 128
    },
    {
      "file": "adapter_model.safetensors",
      "weight": 0.5,
      "alpha": 64,
      "rank": 64
    }
  ],
  "compare_base": true,
  "max_bytes": 12000000
}
Package versions in the generating container (full list)
accelerate==1.15.0 aiohappyeyeballs==2.6.1 aiohttp==3.12.7 aiosignal==1.3.2 annotated-doc==0.0.5 anyio==4.15.1 attrs==25.3.0 cbor2==5.7.0 certifi==2026.7.22 charset-normalizer==3.5.2 click==8.5.0 cuda-bindings==13.4.3 cuda-pathfinder==1.8.2 cuda-toolkit==13.0.3.0 diffusers==0.40.0 filelock==4.0.9 frozenlist==1.6.0 fsspec==2026.9.0 ftfy==6.3.1 grpclib==0.4.8 h11==0.16.0 h2==4.2.0 hf-xet==1.6.0 hpack==4.1.0 httpcore==1.0.9 httpx==0.28.1 huggingface_hub==1.33.0 hyperframe==6.1.0 idna==3.20 imageio-ffmpeg==0.6.0 importlib_metadata==9.0.1 Jinja2==3.1.6 markdown-it-py==4.2.0 MarkupSafe==3.0.3 mdurl==0.1.2 mpmath==1.3.0 multidict==6.4.4 networkx==3.7 numpy==2.5.3 nvidia-cublas==13.1.1.3 nvidia-cuda-cupti==13.0.85 nvidia-cuda-nvrtc==13.0.88 nvidia-cuda-runtime==13.0.96 nvidia-cudnn-cu13==9.24.0.43 nvidia-cufft==12.0.0.61 nvidia-cufile==1.15.1.6 nvidia-curand==10.4.0.35 nvidia-cusolver==12.0.4.66 nvidia-cusparse==12.6.3.3 nvidia-cusparselt-cu13==0.8.1 nvidia-nccl-cu13==2.30.7 nvidia-nvjitlink==13.4.92 nvidia-nvshmem-cu13==3.4.5 nvidia-nvtx==13.0.85 packaging==26.3 pillow==12.3.0 pip==25.1.1 propcache==0.3.1 protobuf==6.31.1 psutil==7.2.2 Pygments==2.21.0 PyYAML==6.0.3 regex==2026.9.29 requests==2.34.2 rich==15.0.0 safetensors==0.8.0 sentencepiece==0.2.2 setuptools==84.0.0 shellingham==1.5.4 sympy==1.14.0 tokenizers==0.23.2 torch==2.14.1 tqdm==4.70.1 transformers==5.18.0 triton==3.8.0 typer==0.27.2 typing_extensions==4.16.0 urllib3==2.8.0 uv==0.7.19 wcwidth==0.9.1 wheel==0.45.1 yarl==1.20.0 zipp==4.1.0
Package versions in the downloading container (full list)
aiohappyeyeballs==2.6.1 aiohttp==3.12.7 aiosignal==1.3.2 anyio==4.15.1 attrs==25.3.0 cbor2==5.7.0 certifi==2025.4.26 click==8.5.0 filelock==4.0.7 frozenlist==1.6.0 fsspec==2026.9.0 grpclib==0.4.8 h11==0.16.0 h2==4.2.0 hf-xet==1.6.0 hpack==4.1.0 httpcore2==2.13.1 httpx2==2.13.1 huggingface_hub==2.0.0 hyperframe==6.1.0 idna==3.20 multidict==6.4.4 packaging==26.3 pip==25.1.1 propcache==0.3.1 protobuf==6.31.1 PyYAML==6.0.3 tqdm==4.70.1 truststore==0.10.4 typing_extensions==4.16.0 uv==0.7.19 wheel==0.45.1 yarl==1.20.0

Related Articles

What to Read Next

Sources

Update History

  • 2026-09-30: Changed the title to show what the article covers (VRAM requirements, file list, etc.).
  • 2026-10-02: Added our own measurements: video we generated.
  • 2026-10-02: Changed the title to show what the article covers (VRAM requirements, file list, etc.).