郭立 (leeguoo)

# Wan2.2 の全ソースコードを読み終えて:この動画生成コードはどこであなたを噛むのか

五大タスク行列、階層アーキテクチャから DiT/VAE/T5/分散/スケジューラまで、Wan2.2 のソースコードにある、文書とコードが乖離している実際の落とし穴を層ごとに分解する。

2026年7月16日 · 記事 · 公開

このページの目次
1.5つのタスク、5{通|とお}りの{入力|にゅうりょく}、5{通|とお}りの{落|お}とし{穴|あな}2.5{層|そう}アーキテクチャ、{何|なに}が{何|なに}に{貼|は}りつくのか3.{一次生成|いちじせいせい}では{何|なに}が{通|とお}り、どの{段階|だんかい}でこっそり{失敗|しっぱい}するのか4.五つの{平行世界|へいこうせかい}5.{優雅|ゆうが}な{降級|こうきゅう}を{書|か}いたが、{誰|だれ}も{使|つか}っていない6.VRAM と{計算力|けいさんりょく}をどう{分|わ}けるか7.shift {引数|ひきすう}が{二|に}{回|かい}、{二|ふた}つの{場所|ばしょ}で{効|き}いている8.{三|みっ}つの{解像度|かいぞうど}テーブル9.{共通|きょうつう}するパターン

Wan2.2 リポジトリを generate.py からスケジューラまですすめたあと、ほんとうにのこ価値かちがあるのは「このモデルをどう使つかうか」ではなく、このコードがあなたのづかないところでどうみついてくるかだ。

Wan2.2 は Alibaba の Wan チームがオープンソース公開こうかいした動画生成どうがせいせいモデルライブラリで、generate.py という1つの入口いりぐちから、--task によって5つの推論すいろんパイプラインへ分岐ぶんきする。テキストから動画どうが画像がぞうから動画どうが、テキスト・画像がぞう統一とういつから動画どうが音声おんせいから動画どうが、キャラクターアニメーションだ。表面上ひょうめんじょうは「1つのモデル、5つのあそかた」にえるが、コードをひらくと、むしろ5つの並行世界へいこうせかいが、CLI といううすそうわされているようにえる。以下いかではコードをんだ順番じゅんばん沿って一通ひととおていき、としあなはそのしるしをつけ、重要じゅうようなところはそのままソースコードをる。

この記事であつかうのは工学上こうがくじょうとしあなだ。まず動画生成どうがせいせいそのものの原理げんり、つまり VAE 圧縮あっしゅく、flow matching、CFG、MoE 二重専門家にじゅうせんもんか数学すうがく数値すうち理解りかいしたいなら、姉妹編しまいへんWan2.2 のソースコードでまな動画生成どうがせいせいんでほしい。2ほんわせてむとよい。

Wan2.2 <ruby>全体像<rt>ぜんたいぞう</rt></ruby>

5つのタスク、5とおりの入力にゅうりょく、5とおりのとしあな

Wan2.2 はタスクの行列ぎょうれつだ。t2v-A14Bi2v-A14Bti2v-5Bs2v-14Banimate-14B は、それぞれ要求ようきゅうする素材そざいがまったくちがう。純粋じゅんすいなテキスト、参照画像さんしょうがぞう参照画像さんしょうがぞう音声おんせいくわえたもの、さらには動画どうが1ぽん前処理まえしょり生成せいせいした姿勢素材しせいそざいパッケージまである。どのタスクを使つかうのか、どんな入力にゅうりょく用意よういするのか、そのこたえは generate.py引数検証ひきすうけんしょうコードにある。ドキュメントには半分はんぶんしかかれていない。

<ruby>業務理解<rt>ぎょうむりかい</rt></ruby>

まず generate.py にあるデフォルトのフォールバック処理しょりてみよう。

$ python
if args.prompt is None:
    args.prompt = EXAMPLE_PROMPT[args.task]["prompt"]
if args.image is None and "image" in EXAMPLE_PROMPT[args.task]:
    args.image = EXAMPLE_PROMPT[args.task]["image"]

--promptわたさなくても、コードはエラーをさない。ハードコードされた例文れいぶん、サングラスをかけた白猫しろねこがサーフィンする内容ないようだまって適用てきようする。s2v-14Bi2v-A14B は、さらにおなれい共有きょうゆうしている。prompt をわたさずにこの2つのタスクをそれぞれはしらせると、てくるものの雰囲気ふんいきおなじになる。どちらのタスクも公式こうしき demo 素材そざい使つかって空回からまわししているからだ。--image--audio--tts_* にもたようなフォールバックがある。初心者しょしんしゃ初回しょかいに demo がとおったあと、自分じぶん設定せっていいていると勘違かんちがいしがちで、これが最大さいだい誤解源ごかいげんだ。

--size意味いみはタスクごとに一致いっちしない。t2v/ti2v では解像度かいぞうどだが、i2v/s2v では実際じっさいには目標もくひょう画素面積がそめんせきで、最終画面さいしゅうがめん縦横比たてよこひ入力画像にゅうりょくがぞう追従ついじゅうする。1280*720れても、出力しゅつりょくがそのサイズになるとはかぎらない。frame_num4n+1たす必要ひつようがある。3つのタスクでデフォルトもそれぞれちがう(t2v/i2v は 81、ti2v-5B は 121、animate-14B は 77)。背後はいごには VAE の時系列圧縮率じけいれつあっしゅくりつちがいがあり、この数字すうじ気軽きがるえてはいけない。Wan-Animate は2段階だんかいながれだ。まず独立どくりつした preprocess_data.py実行じっこうして pose/mask/reference 素材そざいパッケージをつくり、それからこのディレクトリを generate.pyわたす。前処理まえしょりばして、もと動画どうがパスをそのままわたしてはしらせるのが、このコードでもっともよくある失敗しっぱいパターンだ。

5そうアーキテクチャ、なになにりつくのか

wan/ パッケージを分解ぶんかいすると、configs はどのおもみ・どの形状けいじょうでモデルをはしらせるかをめる。modules純粋じゅんすいなネットワーク定義ていぎで、「タスク」という概念がいねんらない。distributed は pipeline よう並列へいれつツールボックス。utils はサンプリング数学すうがく雑多ざったなもの。それらをわせているのが、text2video.pyimage2video.py などの pipeline クラスだ。

アーキテクチャ<ruby>全体像<rt>ぜんたいぞう</rt></ruby>

このそうには、いくつか歴史的れきしてき負債ふさいのこっている。wan/distributed/__init__.pyからファイルで、すべての pipeline はそこを迂回うかいしてモジュールから直接ちょくせつ import しており、パッケージめいは namespace の場所取ばしょとりとしての役割やくわりしかのこっていない。wan/utils/__init__.py__all__ には HuggingfaceTokenizerかれているが、このクラスは実際じっさいには wan/modules/tokenizers.py から export されているため、__all__したがって from wan.utils import HuggingfaceTokenizerくと、そのまま ImportError になる。期限切きげんぎれの宣言せんげん片付かたづけられずにのこっているわけだ。

さらに import wan という1ぎょう代償だいしょうもある。これは5つの pipeline とそれぞれの依存いぞん一気いっき起動きどうする。t2v-A14B だけをはしらせたい場合ばあいでも、speech2video.py の TTS/CosyVoice のおも依存いぞんまでれてくる。どれか1つのタスクモジュールの import が失敗しっぱいすると、無関係むかんけいな4つのタスクまでえでちる。

一次生成いちじせいせいではなにとおり、どの段階だんかいでこっそり失敗しっぱいするのか

t2v-A14B(MoE 双専門家そうせんもんか)を主線しゅせんにすると、generate.py引数ひきすう解析かいせき → pipeline クラスを選択せんたく → テキスト符号化ふごうか拡散かくさんサンプリング → VAE デコード → mp4 として保存ほぞん、というながれになる。

<ruby>核心<rt>かくしん</rt></ruby>フロー

引数ひきすうチェックの粒度りゅうど均一きんいつではない。i2v-A14B--imageわたわすれるとそのまま assertちる。s2v-14B--audio 不足ふそくanimate-14Bvideo/pose/mask 不足ふそくには対応たいおうする断言だんげんがなく、エラーは実行時じっこうじまでばされてから露呈ろていする。ドキュメントには frame_num4n+1たす必要ひつようがあるとかれているが、コードにはその assert がない。あやまったフレームすうわたすと、DiT/VAE 内部ないぶ形状けいじょうわなくなってから爆発ばくはつし、エラー位置いち原因げんいんからかなりとおい。

サンプリングのしゅループで誤解ごかいされやすいてんがある。CFG(条件じょうけんあり + 無条件むじょうけんで DiT をそれぞれ1かいずつはしらせる)に MoE 双専門家そうせんもんかえがかさなると、各 timestep は「専門家せんもんかを1つえらび、前向まえむ計算けいさんを2かいはしらせる」になる。2つの専門家せんもんかおなじステップに登場とうじょうすることはない。えの根拠こんきょt.item() >= boundary * num_train_timesteps で、スケジューラがいた timestep のあたいをそのまま比較ひかくしている。この両者りょうしゃ独立どくりつ維持いじされる状態じょうたいだ。スケジューラをえたり、shiftわって timestep の値域ちいきがずれたりすると、専門家せんもんか位置いちもつられてかたよる。エラーはず、品質低下ひんしつていか目視もくし見るみるしかない。

つぎとしみをる。wan/utils/utils.pysave_video末尾まつびはこうだ。

$ python
        # write video
        writer = imageio.get_writer(
            cache_file, fps=fps, codec='libx264', quality=8)
        for frame in tensor.numpy():
            writer.append_data(frame)
        writer.close()
    except Exception as e:
        logging.info(f'save_video failed, error: {e}')

エンコードに失敗しっぱいしても logging.info を1ぎょうすだけで、raise しない。主処理しゅしょり保存ほぞん失敗しっぱいしたことをらないまま、merge_video_audioすすみ、ログの最後さいごには Finishedるが、ディスクには mp4 がない。merge_video_audio で ffmpeg がちた場合ばあいおなじくにぎりつぶされる。前段ぜんだん処理しょりがすべてただしくても、最後さいご一歩いっぽに、しかもだまったままだ。

五つの平行世界へいこうせかい

5つのタスククラスはたがいに基底きていクラスを共有きょうゆうしていない。_configure_model という共通きょうつう骨格こっかくが5つのファイルにそれぞれコピペされている: eval + 勾配こうばい凍結とうけつ系列けいれつ並列へいれつのとき attention にモンキーパッチ → dist.barrier() → FSDP 分割ぶんかつまたは GPU へ移動いどう

五大タスククラス<ruby>実装<rt>じっそう</rt></ruby><ruby>比較<rt>ひかく</rt></ruby>

5つもコピペすれば、細部さいぶはすでにズレている。S2V と Animate のバージョンには model.use_context_parallel = True が1ぎょうおおく、ほかの3つにはない。意図的いとてきなのかれなのか判断はんだんしづらい。CFG のデフォルト開閉かいへい統一とういつされていない: 4つのタスクはデフォルトでオン、Animate はデフォルトでオフ(guide_scale=1 は CFG を実行じっこうしないのとおなじ)。調整ちょうせい比較ひかくするときに「Animate のほうがはやい」を bug として調査ちょうさすると、徒労とろうわる。それは意図的いとてき計算量けいさんりょう節約せつやくする設計せっけいで、docstring の「表情ひょうじょう制御せいぎょのみに使つかい、ほとんどの場合ばあい不要ふよう」という一文いちぶん理由りゆう説明せつめいしているが、コードはそこをみにけとはおしえてくれない。

TI2V は本質的ほんしつてきにはディスパッチャだ: 画像がぞうわたすと内部ないぶの i2v 分岐ぶんきへ、わたさないと t2v へすすみ、1つのモデルを共有きょうゆうする。ここにはデフォルトわながある: WanTI2V.generate() のシグネチャでは frame_num=81 なのに、転送先てんそうさきt2v()/i2v() のシグネチャでは frame_num=121。このクラスをそのまま Python ライブラリとして使つかい、フレームすう明示めいじしないと、てくるのは81フレームになる。CLI ユーザーはここをまない。generate.pyかなら設定せっていの121を明示的めいじてきわたし、このデフォルト迂回うかいするからだ。

S2V は唯一ゆいいつ自己回帰じこかいき分段ぶんだんするタスクで、音声おんせいちょうから num_repeat計算けいさんし、ループで断片だんぺん生成せいせいし、まえ断片だんぺん末尾まつび latent で接続せつぞくする。get_gen_size には「前回ぜんかい生成せいせい結果けっかつづけてく」ためのインターフェイス(pre_video_path 引数ひきすう)がのこっているが、しゅフローがぶときはつねNoneわたす。インターフェイスはかれているが、みちとおっていない。コードをむと、その能力のうりょく過大評価かだいひょうかしやすい。

優雅ゆうが降級こうきゅういたが、だれ使つかっていない

DiT 主幹しゅかんWanModel)は、反復はんぷくする Attention Block のかさねでできている。RoPE き Self-Attention、テキスト context に接続せつぞくする Cross-Attention、FFN、そして AdaLN の六路ろくろ変調へんちょう駆動くどうする。

<ruby>核心<rt>かくしん</rt></ruby>モデルモジュール

wan/modules/attention.py には、兜底とうていつきの分配ぶんぱい関数かんすう attention() がある。FlashAttention がはいっていれば転送てんそうし、はいっていなければ PyTorch の scaled_dot_product_attention退避たいひする。だが model.py の 9 ぎょうはこう導入どうにゅうしている。

$ python
from .attention import flash_attention

主幹しゅかん最下層さいかそうflash_attention()直接ちょくせつんでおり、その内部ないぶでは flash-attn がはいっていない場合ばあいassert FLASH_ATTN_2_AVAILABLE(attention.py:112)をかた実行じっこうして、そのままくずれる。兜底とうていかれているが、主幹しゅかん使つかっていない。attention カーネルはさらに q.device.type == 'cuda'要求ようきゅうするため、CPU や Mac の MPS では初回しょかいしで assert に失敗しっぱいし、案内あんないもない。

VAE のとしあなはもう一層いっそうふかかくれている。Wan2.1 ばん(16 チャンネル)と Wan2.2 ばん(48 チャンネル、patchify と双経路そうけいろダウンサンプリングが追加ついか)は構造こうぞう相互そうご非互換ひごかんで、2 系統けいとう正規化せいきか scale次元じげんことなる。checkpoint と VAE ばんちがえると、誤りあやまりはただの tensor shape mismatch で、根因こんいんえない。Wan2.2 VAE の encode/decode はさらに try/except TypeError不正ふせい入力にゅうりょく兜底とうていし、かた間違まちがえてわたすと例外れいがいみ、ログをして Noneかえす。がわはずっとはなれた場所ばしょでようやく None のせいで爆発ばくはつする。Wan2.1 ばんにはこの try/except がない。おなじリポジトリで、2 ばんの VAE は容錯ようさく気性きしょうちがう。

T5 がわでは、umT5そうごとに独立どくりつした相対そうたい位置いちエンコーディングを使つかい、24 そうそれぞれが bias を計算けいさんする。設定せっていでは shared_pos=False で、bug ではなく umT5 の設計せっけいだが、この設定せっていぎょうないと共有きょうゆうだとおもいやすい。より実際的じっさいてき危険きけんは、t5.py と 2 ばんの VAE が checkpoint のみにすべてはだかtorch.load使つかっており、weights_only=Trueけていないことだ。出所でどころからないおもみファイルをむとき、ぎゃくシリアライズで任意にんいコードが実行じっこうされる危険きけんがある。

VRAM と計算力けいさんりょくをどうけるか

複数ふくすう GPU 戦略せんりゃくには 2 つのじくがある。FSDP は VRAM を担当たんとうし(パラメータの shard)、Ulysses の系列けいれつ並列へいれつ計算力けいさんりょく担当たんとうする(activation の系列けいれつ次元を別々べつべつのカードにる)。どちらも torch.distributed大域たいいき world を使つかい、独立どくりつした data parallel 次元はない。1 かい起動きどう使つかうすべての GPU は、すべておな並列へいれつグループにぞくするか、そうでなければ単一たんいつカードになる。

<ruby>複数<rt>ふくすう</rt></ruby>カード<ruby>分散<rt>ぶんさん</rt></ruby><ruby>推論<rt>すいろん</rt></ruby><ruby>戦略<rt>せんりゃく</rt></ruby>

Ulysses のかんがかたは 2 かい変換へんかんだ。q/k/v を heads 次元に沿って scatter し、系列けいれつ次元に沿って gather する。するとかくカードは完全かんぜん系列けいれつって heads の一部いちぶ計算けいさんし、ローカルで FlashAttention をはしらせたあと、all-to-all でもともどす。このなかには等長とうちょうという仮定かていまっている。sp_dit_forwardtorch.chunk系列けいれつり、pad_freqs は「かくカードの切片せっぺんおなながさ」という前提ぜんてい位置いちエンコーディングを計算けいさんする。torch.chunk ではれないと最後さいごの 1 かたまりがよりみじかくなる。系列けいれつちょうulysses_size整数せいすうばいでないと、RoPE 周波数しゅうはすうと token が対応たいおうしなくなり、しずかにあやまって、品質ひんしつ低下ていかとしてしかあらわれない。

FSDP のしょう VRAMの恩恵おんけいも、最初さいしょからられるわけではない。モデルはいったんまるごと GPU にせられ、そのあと shard_modelわたされて shard される。meta device 初期化しょきかとおっていない。shard のまえに、かくカードがまず完全かんぜんなモデルをせられなければならず、VRAMのきついカードは起動きどうした瞬間しゅんかんに OOM になる。また free_model は FSDP の private 属性ぞくせい _handle.flat_param.dataさわっており、torch のアップグレードにはこのぎょうがいつこわれてもおかしくない。

shift 引数ひきすうかいふたつの場所ばしょいている

スケジューラ(FlowUniPCMultistepScheduler / FlowDPMSolverMultistepScheduler)は、diffusers のスケジューラを flow-matching ばん改造かいぞうしたもので、ファイル冒頭ぼうとうには "Copied from diffusers" とかれている。

スケジューラと Prompt <ruby>拡張<rt>かくちょう</rt></ruby>

text2video.py で UniPC を構築こうちくするコード:

$ python
sample_scheduler = FlowUniPCMultistepScheduler(
    num_train_timesteps=self.num_train_timesteps,
    shift=1,
    use_dynamic_shifting=False)
sample_scheduler.set_timesteps(
    sampling_steps, device=self.device, shift=shift)

コンストラクタの shift=1固定値こていちで、本当ほんとう--sample_shiftset_timestepsなかく。DPM++ がわはまた経路けいろちがう。まえget_sampling_sigmas(steps, shift) で sigma れつ手計算てけいさんしてからわたす。shift のデフォルト調整ちょうせいしたり、第三だいさんの solver を追加ついかしたりしたいとき、片方かたほう真似まねしてくともう片方かたほうらしやすい。字面じづらどおりにコンストラクタ引数ひきすうえても、効果こうかはない。

Prompt 拡写かくしゃにはふたつのバックエンド、DashScope オンライン API とローカル Qwen があり、rank 0 で一度いちどだけ実行じっこうしてからブロードキャストし、複数ふくすうカードでの重複ちょうふくしをけている。ローカル Qwen はしのたびにモデル全体ぜんたいを CPU↔GPU かん往復おうふくさせる。メインモデルの显存けんそんけるためのトレードオフだが、その代償だいしょうとして毎回まいかい拡写かくしゃ目立めだ遅延ちえんがある。DEFAULT_SYS_PROMPTS 辞書じしょみっつのタスクでネスト構造こうぞうがそれぞれことなり、decide_system_prompt文字列もじれつマッチの固定こていブランチにたよっている。あたらしいタスクを追加ついかするにはあらたなブランチを手書てがきする必要ひつようがあり、ネストを間違まちがえると実行時じっこうじKeyError になる。

みっつの解像度かいぞうどテーブル

設定せっていレイヤー最大さいだい命名めいめいトラップ: t5_checkpoint/vae_checkpointてるのはファイルパスだが、low_noise_checkpoint/high_noise_checkpointfrom_pretrainedわたsubfolder のサブディレクトリめいだ。同じ xxx_checkpoint という命名めいめいで、用途ようとふたつ。モデルディレクトリを手動しゅどう整理せいりしたり、サブディレクトリめいえたりすると、エラーは transformers レイヤーの「config.json がつからない」になり、フィールドを間違まちがえたとはおしえてくれない。

タスク<ruby>設定<rt>せってい</rt></ruby>とモデルダウンロード

解像度かいぞうどのホワイトリストは SIZE_CONFIGSMAX_AREA_CONFIGSSUPPORTED_SIZES というみっつの独立どくりつ保守ほしゅテーブルにかれている。ti2v-5B704*1280使つかい、ほかのタスクでは 720*1280 がよく使つかわれる。このふたつの数字すうじがかなりていて、README やコミュニティの投稿とうこうでもよく混同こんどうされる。わた間違まちがえても「解像度かいぞうどはサポートされていません」とはず、みっつのテーブルで key がつからないというエラーになる。あたらしい解像度かいぞうど追加ついかするにはみっつのテーブルを同時どうじえる必要ひつようがあり、ひとつでもれても静的せいてきチェックはなに警告けいこくしてくれない。

共通きょうつうするパターン

ここのつの分析ぶんせきならべてみると、パターンがかびがる。このコードのとしあなおおくはロジックエラーではなく、安全あんぜんなフォールバックはかれているのに、主経路しゅけいろ使つかわれていないことだ。attention()降格こうかく_validate_args のばらついたアサーション、frame_num のドキュメント要件ようけんとコード検証けんしょうのずれ、shiftふたつの有効化ゆうこうかルートは、すべておなじパターンの変種へんしゅだ。ドキュメント、デフォルト実際じっさいのコードパスの三者さんしゃはなされ、チェーンのふかいところへすすむほど、代償だいしょうえにくくなる。CLI のエラーが、ランタイムエラーへ劣化れっかし、さらにエラーはないが品質ひんしつだけがこっそりわるくなる状態じょうたい劣化れっかしていく。

このコードをいで二次開発にじかいはつするなら、経験則けいけんそくひとつ。docstring やフィールドめい字面じづらしんじず、コードのなか実際じっさいいているパスをしんじること。おなじフィールド、おな関数かんすうでもタスクによって意味いみちがうところは、個別こべつ確認かくにんし、タスクかん共通きょうつうだと仮定かていしない。

次の記事 →
Claude Code で Codex の Computer Use を使う方法

コメント

コメントは即時公開されますが、ポリシー違反時は非表示になる場合があります。

最大 1000 文字。