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

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

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

この記事で{扱|あつか}うのは{工学上|こうがくじょう}の{落|お}とし{穴|あな}だ。まず{動画生成|どうがせいせい}そのものの{原理|げんり}、つまり VAE {圧縮|あっしゅく}、flow matching、CFG、MoE {二重専門家|にじゅうせんもんか}の{数学|すうがく}と{数値|すうち}を{理解|りかい}したいなら、{姉妹編|しまいへん}の[Wan2.2 のソースコードで{学|まな}ぶ{動画生成|どうがせいせい}](https://blog.leeguoo.com/zh/posts/wan22-how-video-generation-works/)を{読|よ}んでほしい。2{本|ほん}を{合|あ}わせて{読|よ}むとよい。

![Wan2.2 {全体像|ぜんたいぞう}](https://img.leeguoo.com/media/2c77d3ee-9df8-4f63-899a-212ea30d79ff/wf-00-index.png)

## 5つのタスク、5{通|とお}りの{入力|にゅうりょく}、5{通|とお}りの{落|お}とし{穴|あな}

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

![{業務理解|ぎょうむりかい}](https://img.leeguoo.com/media/2e3f8a01-72db-421a-a1c7-1f99ed31954f/wf-01-business-understanding.png)

まず `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-14B` と `i2v-A14B` は、さらに{同|おな}じ{例|れい}を{共有|きょうゆう}している。prompt を{渡|わた}さずにこの2つのタスクをそれぞれ{走|はし}らせると、{出|で}てくるものの{雰囲気|ふんいき}が{同|おな}じになる。どちらのタスクも{公式|こうしき} demo {素材|そざい}を{使|つか}って{空回|からまわ}ししているからだ。`--image`、`--audio`、`--tts_*` にも{似|に}たようなフォールバックがある。{初心者|しょしんしゃ}は{初回|しょかい}に demo が{通|とお}ったあと、{自分|じぶん}の{設定|せってい}が{効|き}いていると{勘違|かんちが}いしがちで、これが{最大|さいだい}の{誤解源|ごかいげん}だ。

`--size` の{意味|いみ}はタスクごとに{一致|いっち}しない。`t2v`/`ti2v` では{解像度|かいぞうど}だが、`i2v`/`s2v` では{実際|じっさい}には{目標|もくひょう}の{画素面積|がそめんせき}で、{最終画面|さいしゅうがめん}の{縦横比|たてよこひ}は{入力画像|にゅうりょくがぞう}に{追従|ついじゅう}する。`1280*720` と{入|い}れても、{出力|しゅつりょく}がそのサイズになるとは{限|かぎ}らない。`frame_num` は `4n+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.py`、`image2video.py` などの pipeline クラスだ。

![アーキテクチャ{全体像|ぜんたいぞう}](https://img.leeguoo.com/media/e0a93afd-e50a-40a4-ab67-edd3fb237e54/wf-02-architecture-overview.png)

この{層|そう}には、いくつか{歴史的|れきしてき}な{負債|ふさい}が{残|のこ}っている。`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 として{保存|ほぞん}、という{流|なが}れになる。

![{核心|かくしん}フロー](https://img.leeguoo.com/media/949e06aa-5c0b-48c2-b8c5-d8f67104a551/wf-03-core-flow.png)

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

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

{次|つぎ}に{落|お}とし{込|こ}みを{見|み}る。`wan/utils/utils.py` の `save_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 へ{移動|いどう}。

![五大タスククラス{実装|じっそう}{比較|ひかく}](https://img.leeguoo.com/media/36276abd-0fb5-4450-9b42-3a8033d33d06/wf-04-task-classes.png)

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 の{六路|ろくろ}{変調|へんちょう}で{駆動|くどう}する。

![{核心|かくしん}モデルモジュール](https://img.leeguoo.com/media/ae45f413-59bf-4df6-849e-ead34fa31aab/wf-05-core-modules.png)

`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 は、すべて{同|おな}じ{並列|へいれつ}グループに{属|ぞく}するか、そうでなければ{単一|たんいつ}カードになる。

![{複数|ふくすう}カード{分散|ぶんさん}{推論|すいろん}{戦略|せんりゃく}](https://img.leeguoo.com/media/4a2fb674-a7d3-4cb4-8f5c-883d937d4e08/wf-06-distributed-inference.png)

Ulysses の{考|かんが}え{方|かた}は 2 {回|かい}の{変換|へんかん}だ。q/k/v を heads 次元に{沿|そ}って scatter し、{系列|けいれつ}次元に{沿|そ}って gather する。すると{各|かく}カードは{完全|かんぜん}な{系列|けいれつ}を{持|も}って heads の{一部|いちぶ}を{計算|けいさん}し、ローカルで FlashAttention を{走|はし}らせたあと、all-to-all で{元|もと}に{戻|もど}す。この{中|なか}には{等長|とうちょう}という{仮定|かてい}が{埋|う}まっている。`sp_dit_forward` は `torch.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 {拡張|かくちょう}](https://img.leeguoo.com/media/38393f5b-ab8b-479c-90f2-20673cd79709/wf-07-schedulers-and-prompt.png)

`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_shift` は `set_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_checkpoint` は `from_pretrained` に{渡|わた}す `subfolder` のサブディレクトリ{名|めい}だ。同じ `xxx_checkpoint` という{命名|めいめい}で、{用途|ようと}は{二|ふた}つ。モデルディレクトリを{手動|しゅどう}で{整理|せいり}したり、サブディレクトリ{名|めい}を{変|か}えたりすると、エラーは transformers レイヤーの「config.json が{見|み}つからない」になり、フィールドを{間違|まちが}えたとは{教|おし}えてくれない。

![タスク{設定|せってい}とモデルダウンロード](https://img.leeguoo.com/media/24e55e71-b72f-4f82-a657-cf0b0cdb0ad2/wf-08-configs-and-checkpoints.png)

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

## {共通|きょうつう}するパターン

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

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