「LongLive-Plug-Wan2.1-T2V-14B-few-step」:当サイトの生成動画・配布ファイル一覧

基本情報
| 項目 | 内容 |
|---|---|
| リポジトリ | Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-few-step |
| 公開元のまとめ | Efficient Large Model(NVIDIA・MIT) のモデルとライセンスのまとめ |
| 公開日 | 2026-09-30 |
| ライセンス | apache-2.0 |
| 配布形式 | safetensors |
| 出典の種類 | 公開元の一次情報(HuggingFace公式リポジトリが一次情報として直接確認可能) |
情報を集めた時点で、当サイトのコードが確定させた値です。日付はJSTで表示しています。
当サイトで確かめたこと
- クラウドの GPU を借りて、当サイトの固定のプロンプトから生成した動画を、選別せずに載せています。
詳しい値と条件は、下の「当サイトでの実測」の節にあります。
概要
Efficient-Large-Modelは、Wan-AI/Wan2.1-T2V-14Bをベースモデルとして使用する、動画生成の高速化を目的としたLoRAアダプター「LongLive-Plug-Wan2.1-T2V-14B-few-step」を公開しました。このモデルは、テキストから動画を生成する(text-to-video)プロセスにおいて、より少ないステップ数での生成を可能にするために設計されています。本モデルはアダプターとして機能するため、動作させるには対応するベースモデルが必要となります。また、使用にあたっては、対応する「CFG LoRA」(LongLive-Plug-Wan2.1-T2V-14B-cfg)との併用が推奨されています。
スペック
本モデルは、以下の仕様を持つベースモデルを対象としています。
- ベースモデル:
Wan-AI/Wan2.1-T2V-14B - アーキテクチャ: Video Diffusion DiT (Flow Matchingフレームワーク)
- ベースモデル(14B)のアーキテクチャ詳細:
- Dimension: 5120 – Input Dimension: 16 – Output Dimension: 16 – Feedforward Dimension: 13824 – Frequency Dimension: 256 – Number of Heads: 40 – Number of Layers: 40
- 推奨設定:
few-stepとCFGの重みの比率を1 : 0.5に設定することが推奨されています。なお、これは推論時のCFGスケールではなく、アダプターの重みの比率を指します。
- ライセンス:
apache-2.0
性能・品質
本アダプターの基盤となるベースモデル「Wan2.1-T2V-14B」は、オープンソースおよび商用モデルの双方において、新たなSOTA(State-of-the-Art)性能のベンチマークを確立するモデルであると報告されています。
ベースモデルのアーキテクチャに関する詳細な構成は以下の通りです。
→ 横にスクロールできます
| Model | Dimension | Input Dimension | Output Dimension | Feedforward Dimension | Frequency Dimension | Number of Heads | Number of Layers |
|---|---|---|---|---|---|---|---|
| 14B | 5120 | 16 | 16 | 13824 | 256 | 40 | 40 |
公開元による評価では、1,035個の内部プロンプトを用いたテストにおいて、14の主要な次元および26のサブ次元にわたる多角的な検証が行われています。これらの結果は、人間の好みに基づいた重み付けを用いた計算によって算出されており、既存のモデルと比較して優れた性能を示すことが確認されています。
また、ベースモデルの品質を支える技術として、以下の特徴が挙げられています。
- 高品質なビジュアルと動きのダイナミクス: 高品質なビジュアルと、大幅な動きのダイナミクスを伴う動画の生成が可能です。
- 高度なテキスト生成: 中国語と英語の両方のテキストを動画内に生成できる、初のビデオモデルとされています。
- 高性能なビデオVAE: 独自の「
Wan-VAE」を採用しており、任意の長さの1080P動画を時間的な情報を保持したまま、効率的にエンコードおよびデコードすることが可能です。 - 解像度の柔軟性: 480Pおよび720Pの両方の解像度での動画生成をサポートしています。
本アダプター「LongLive-Plug-Wan2.1-T2V-14B-few-step」は、これらの強力なベースモデルに対して蒸留(distillation)技術を適用することで、生成ステップ数の削減による効率化を実現しています。
得意なこと・想定用途
本モデルは、テキストから動画を生成するタスク(text-to-video)において、ベースモデル「Wan-AI/Wan2.1-T2V-14B」の性能を維持しながら、より少ないステップ数で高速な動画生成を行うことを得意としています。PEFTやLoRA、蒸留(distillation)技術を活用したアダプターとして設計されており、対応する「CFG LoRA」と組み合わせて使用することを想定しています。なお、ベースモデル側が持つ中国語と英語の両方のテキスト生成能力や、高い動きのダイナミクスを持つビジュアル表現、および480Pや720Pの解像度を活かした動画生成の効率化に貢献します。
類似モデルとの違い
当サイトですでに紹介している「LongLive-Plug-Wan2.1-T2V-14B-cfg」がClassifier-Free Guidance (CFG) の条件付きのみのビデオ generation への蒸留を行い、無条件の推論ブランチの実行を低減することを目的としているのに対し、今回の「LongLive-Plug-Wan2.1-T2V-14B-few-step」は、より少ないステップ数での動画生成を実現するためのLoRAアダプターとして機能する点が異なります。両者は同じベースモデルであるWan-AI/Wan2.1-T2V-14Bを対象としており、few-step用とCFG用のアダプター重みを 1 : 0.5 の比率で併用することが推奨されています。
配布ファイル
Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-few-step で配布されている重みファイルを、Hugging FaceのAPIから当サイトが並べたものです。サイズは実ファイルの値です。
| ファイル | サイズ |
|---|---|
generator_lora_lightx2v.safetensors |
1.23GB |
training_checkpoint/model.pt |
4.91GB |
入手方法
Hugging Faceの公式リポジトリから入手可能です。PEFTライブラリに対応するLoRAアダプターウェイトとして配布されており、対応するベースモデルやCFG LoRAと組み合わせて利用します。
- リポジトリ: Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-few-step
- 配布形式:
safetensors
手元で動かせるか
公開元の配布形式は safetensors です。
ライセンス apache-2.0(商用利用可): 商用利用を含めて自由に利用・改変・再配布できます。再配布時にライセンス文と変更点の告知が必要で、特許の許諾条項があります。
配布形式とライセンス表記から当サイトのコードが組み立てた判定です。ライセンスの要約は法的助言ではないため、実際の利用にあたっては公開元の原文を確認してください。
当サイトでの実測
モデルカードの作例ではなく、当サイトが生成したものです。当サイトのサーバー(GPUなし)では動画のモデルを動かせないため、クラウドの GPU を借りて生成しました(条件は下記)。測り方と全モデルの一覧は観測データにまとめています。
当サイトで生成した動画(クラウドのGPU)
Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-few-step(apache-2.0 ライセンス)は単独では動かない LoRA なので、元のモデル Wan-AI/Wan2.1-T2V-14B-Diffusers(apache-2.0 ライセンス)に、配布元が勧める組み合わせ(LongLive-Plug-Wan2.1-T2V-14B-few-step を重み 1.0、LongLive-Plug-Wan2.1-T2V-14B-cfg を重み 0.5)で足し込み、4ステップ・ガイダンススケール 1.0 で生成しました。サンプラーの設定は配布元の推論コード(LongLive-Plug inference.py)に合わせ、UniPC 法・shift 5.0(diffusers の UniPCMultistepScheduler)にしています。配布元はこの LoRA を LongLive-Plug-Wan2.1-T2V-14B-cfg と組み合わせて使うよう案内しているため、両方を足し込んでおり、同じ動画をもう一方の記事にも載せています。動かしたのは diffusers 0.40.0 の WanPipeline で、配布元のコードは使っていません。LoRA の足し込みは当サイトのコードで行いました(元の Wan の層の名前を diffusers の層の名前に対応させ、各重みに 重み×(alpha/r)×BA を足す)。この対応のさせ方は配布元が文書にしているものではなく当サイトの解釈で、確かめたのは全ての層の名前と形が一致したことだけです。
条件: Modal の NVIDIA L40S(VRAM 44GB)・重みは BF16(VAE は FP32)・832×480ピクセル・81コマ(16fps、約5.06秒)・シード 0・人物・ロゴ・文字を含まない固定のプロンプト2つ・2026-10-02生成。選別はしておらず、いずれも1回目の出力です。ただし当サイトのサーバーのアップロードの上限に収めるため、H.264 で圧縮し直しています(1本 12.0MB 以下)。細部は元の出力より甘くなることがあります。
プロンプト: 「a small red paper boat drifting down a clear forest stream, sunlight flickering through the leaves, gentle ripples, realistic」(日本語訳: 澄んだ森の小川を流れていく小さな赤い紙の舟、木の葉の間からちらつく日の光、穏やかなさざ波、写実的)
LoRA なし(元のモデル・同じ4ステップ)・生成に50秒
LoRA あり・生成に51秒
プロンプト: 「time-lapse of white clouds rolling over snowy mountain peaks at sunrise, golden light, wide shot」(日本語訳: 日の出の雪山の峰を流れていく白い雲のタイムラプス、金色の光、引きの構図)
LoRA なし(元のモデル・同じ4ステップ)・生成に51秒
LoRA あり・生成に52秒
| 項目 | 結果 |
|---|---|
| 動画の本体を読み込んだ直後のVRAM(BF16。テキストエンコーダは解放済み) | 27.3GB |
| 生成中の最大のVRAM | 35.5GB |
生成時間はクラウドの NVIDIA L40S でのもので、手元の GPU の目安ではありません。テキストエンコーダは先に動かして解放してから動画の本体を読み込んでいるので、上の VRAM には含まれません。「LoRA なし」の動画は、比べるために同じ4ステップにそろえたもので、元のモデルの通常の設定(より多いステップ数とガイダンス)ではありません。元のモデルの実力を示すものではありません。
環境構築・ダウンロード・実行の手順(当サイトで実行したもの)
当サイトでは、ダウンロードと生成を Modal の2つのコンテナに分けました。どちらも Debian(slim)・Python 3.12 のイメージです。ダウンロードのコンテナは、Hugging Face から決まったファイル(設定・safetensors の重み・トークナイザ)だけを版を固定して取ってきます。生成のコンテナはネットワークを遮断しています。入れたパッケージは次のとおりです。
# 1. ダウンロード用の実行環境を作る(GPU なし)
python -m pip install huggingface_hub
# 2. 生成用の実行環境を作る(GPU あり・ネットワークなし)
python -m pip install torch diffusers transformers accelerate safetensors sentencepiece ftfy imageio-ffmpeg numpy
そのうえで、次のスクリプト(video_remote.py、当サイトが書いたもの。全文)の download(job) をダウンロードのコンテナで、run(job) を生成のコンテナで呼びました。ファイルの置き場所は /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))
download に渡した job(実際に使った値):
{
"dir": "video-1790914468",
"base": {
"repo": "Wan-AI/Wan2.1-T2V-14B-Diffusers",
"revision": "38ec498cb3208fb688890f8cc7e94ede2cbd7f68",
"files": [
"model_index.json",
"scheduler/scheduler_config.json",
"text_encoder/config.json",
"text_encoder/model-00001-of-00005.safetensors",
"text_encoder/model-00002-of-00005.safetensors",
"text_encoder/model-00003-of-00005.safetensors",
"text_encoder/model-00004-of-00005.safetensors",
"text_encoder/model-00005-of-00005.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-00012.safetensors",
"transformer/diffusion_pytorch_model-00002-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00003-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00004-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00005-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00006-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00007-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00008-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00009-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00010-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00011-of-00012.safetensors",
"transformer/diffusion_pytorch_model-00012-of-00012.safetensors",
"transformer/diffusion_pytorch_model.safetensors.index.json",
"vae/config.json",
"vae/diffusion_pytorch_model.safetensors"
]
},
"loras": [
{
"repo": "Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-few-step",
"revision": "f125af0533b94e2cee0163a7a42bb736d59a1d37",
"file": "generator_lora_lightx2v.safetensors"
},
{
"repo": "Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-cfg",
"revision": "32b8aa3d1db3d178a9c82f3731cc917a34fb7693",
"file": "adapter_model.safetensors"
}
]
}
run に渡した job(実際に使った値):
{
"dir": "video-1790914468",
"pipeline": "WanPipeline",
"settings": {
"height": 480,
"width": 832,
"num_frames": 81,
"fps": 16,
"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": "generator_lora_lightx2v.safetensors",
"weight": 1.0,
"alpha": 128,
"rank": 128
},
{
"file": "adapter_model.safetensors",
"weight": 0.5,
"alpha": 128,
"rank": 128
}
],
"compare_base": true,
"max_bytes": 12000000
}
生成のコンテナに入っていたパッケージの版(全文)
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
ダウンロードのコンテナに入っていたパッケージの版(全文)
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
関連記事
- 「LongLive-Plug-Wan2.1-T2V-14B-cfg」動画生成モデル:当サイトの生成動画・配布ファイル一覧
- 動画生成モデル「MiniMax-H3」向けLoRAアダプタ2種公開
- 「LongLive-Plug-Wan2.2-TI2V-5B-cfg」動画生成モデル:当サイトの生成動画・配布ファイル一覧
- 「LongLive-Plug-Wan2.2-TI2V-5B-few-step」:当サイトの生成動画・配布ファイル一覧
次に読むなら
- このモデルを入手できる形式を調べる → Safetensors形式の解説と対応モデル
- 公開元について調べる → Efficient Large Model(NVIDIA・MIT) のモデル・ライセンス・記事のまとめ
- 同じ用途の他のモデルを探す → 動画生成のモデル一覧
出典
- https://huggingface.co/Efficient-Large-Model/LongLive-Plug-Wan2.1-T2V-14B-few-step
- https://huggingface.co/Wan-AI/Wan2.1-T2V-14B
- https://localmodelwatch.tsuchitsuchi.com/2026/09/30/longlive-plug-wan21-t2v-14b-cfg/
更新履歴
- 2026-09-30: タイトルを、記事で分かる内容(必要VRAM・配布ファイル等)を示す形に変更しました。
- 2026-10-02: 当サイトでの実測(当サイトで生成した動画)を追加しました。
- 2026-10-02: タイトルを、記事で分かる内容(必要VRAM・配布ファイル等)を示す形に変更しました。

