WAN2.2 i2v 14B FP16 GGUF with lightning LoRA 4steps or lightx2v 6steps&Distorch Memory Manager
2025年10月8日、WAN2.2用lightning LoRAを適用したVerⅡ 4stepsを更新しました。
かなり良いです。上のコンセプトはWAN2.1ではなかなか難しい形でした。
About WAN2.2
i2v with WAN2.1 lightx2v LoRA 6steps
i2vⅡ with WAN2.2 lightning LoRA 4steps
いつものやつですね。FP16フルサイズのGGUFを使用しても尚、改造したMulitiGPUノードのDistorchによりVRAM消費量を12GB未満に抑える…動画生成のモデルが出る度に、私の定番になりつつあります。
思ったより速いです。毎度お馴染みDistorchですが、シングルタスク単体は、WAN2.1より速いですね、意外な事に。
また以下の様に、lightx2vによるステップ数削減が効くことも判っています。只、現状WAN2.1用LoRAを使用すると大量のエラーが吐き出される為、そこはスマートではないです。このエラーそのものは不可避ですが、表示自体を「何とかする」事は可能なので後述します。
Distorch改造に関しては、以下を参照して下さい。
480×480だと、処理中は以下の感じです。720×720、length121辺りまではVRAM消費が12GBが収まるようです。上jsonは640×640、length121設定にしています。

fp16 GGUF
量子化していないフルサイズのGGUFを、以下に公開しました。
Ollama-Generate V2
動作は重くなりますが、プロンプトの自動生成機能を付与する為、ollamaを使用します。vision系LLMにも対応しますが、正直動作精度が良くありません。通常のtext生成LLMの方が無難です。

その代わり、プロンプト入力は日本語に対応します。Olama Generaye V2でで自動翻訳がかかる設計にしています。

Fixed lora.py
\ComfyUI\comfy\lora.py"
動作には影響しませんが、本来はWAN2.1用のlightx2v LoRAをWAN2.2に適用すると、大量の
lora key not loaded: diffusion_model.blocks.0.attn1.to_q.weight
lora key not loaded: diffusion_model.blocks.0.attn1.to_k.weight的なエラーが出ます。気分は良くないので、「WAN2.2にWAN2.1用のLoRAを適用した場合」に限り、これを以下の様に変換する修正を施しましたので、上に公開します。
Suppressed 290 WAN2.1 LoRA key warnings (WAN2.1 LoRA on WAN2.2 model - expected behavior)解説
WAN2.2モデルにWAN2.1 LoRA適用時のみ未ロード警告を集約抑制する実装ノート(ComfyUI `lora.py`)
目的
WAN2.2にWAN2.1 LoRAを適用したときだけ、大量に出る「想定内の未ロードキー警告」を集約ログにまとめる。
それ以外(WAN2.1モデルや他モデル)では、警告を抑制せず通常どおり出す。
新規でエラー表示が出た全系列(`double_blocks`/`single_blocks` の img/text ヘッド群)も抑制対象に含める。
対象ファイル
`ComfyUI/comfy/lora.py`
変更前(初期状態)
抑制ロジックはなく、未ロードキーはすべて警告として出力。
if log_missing:
for x in lora.keys():
if x not in loaded_keys:
logging.warning("lora key not loaded: {}".format(x))問題:
WAN2.1 LoRAをWAN2.2に適用すると、構造差由来の未ロードキーが大量に出続け、ログがノイズ化。
変更後(完成形)
変更位置: `def load_lora(...):` 内の `if log_missing:` ブロック
実装の骨子:
LoRA側にWAN2.1特有の「キー断片」が1つでもあれば「WAN2.1 LoRA」と判定。
モデル側(`to_load.values()`)にWAN2.1特有断片が1つも無ければ「WAN2.2」と推定。
両方が真(WAN2.1 LoRA × WAN2.2モデル)かつ、該当キーがインジケータに合致する場合のみ抑制し、件数を集約ログで出力。
それ以外は通常通り、個別の警告を出す。
実コード(該当部のみ抜粋):
if log_missing:
# WAN2.1 on WAN2.2 warning suppression (expected missing keys)
# Detect WAN2.1-style LoRA by presence of characteristic key fragments in the LoRA file
wan21_lora_indicators = [
# classic image-attn/text-attn specific
"k_img.diff_b", "k_img.lora_down.weight", "k_img.lora_up.weight",
"v_img.diff_b", "v_img.lora_down.weight", "v_img.lora_up.weight",
"norm_k_img.diff",
# img_emb variants (WAN2.1)
"img_emb.proj.",
# cross_attn variants (WAN2.1)
".cross_attn.k_img.", ".cross_attn.v_img.",
# trainers: double_blocks/single_blocks heads (img/text)
".double_blocks.", ".single_blocks.",
".img_attn_proj.", ".img_attn_qkv.",
".img_mlp.fc1.", ".img_mlp.fc2.",
".img_mod.linear.",
".txt_attn_proj.", ".txt_attn_qkv.",
".txt_mlp.fc1.", ".txt_mlp.fc2.",
".txt_mod.linear.",
# common single block linear heads
".linear1.lora_", ".linear2.lora_", ".modulation.linear."
]
wan21_keys_found = [key for key in lora.keys() if any(indicator in key for indicator in wan21_lora_indicators)]
is_wan21_lora = len(wan21_keys_found) > 0
# Apply suppression ONLY when applying WAN2.1 LoRA onto WAN2.2 model.
# Heuristic for model: if model (to_load values) contains WAN2.1-only keys, it's not WAN2.2.
try:
model_key_samples = [str(v) for v in to_load.values()]
except Exception:
model_key_samples = []
model_has_wan21_like_keys = any(
(".cross_attn.k_img." in mk) or (".cross_attn.v_img." in mk) or
("img_emb.proj" in mk) or ("norm_k_img" in mk)
for mk in model_key_samples
)
is_wan22_model_target = not model_has_wan21_like_keys
suppressed_count = 0
actual_warnings = []
for x in lora.keys():
if x not in loaded_keys:
# Suppress only expected WAN2.1-on-WAN2.2 missing keys
if (is_wan21_lora and is_wan22_model_target and
any(indicator in x for indicator in wan21_lora_indicators)):
suppressed_count += 1
continue
actual_warnings.append(x)
for x in actual_warnings:
logging.warning("lora key not loaded: {}".format(x))
if suppressed_count > 0:
logging.info(
"Suppressed {} WAN2.1 LoRA key warnings (WAN2.1 LoRA on WAN2.2 model - expected behavior)".format(
suppressed_count
)
)何を抑制対象にしたか(部分一致インジケータ)
k_img/v_img 系:
"k_img.diff_b", "k_img.lora_down.weight", "k_img.lora_up.weight"
"v_img.diff_b", "v_img.lora_down.weight", "v_img.lora_up.weight"
"norm_k_img.diff"
img_emb 系:
"img_emb.proj."
cross_attn 系:
".cross_attn.k_img.", ".cross_attn.v_img."
新たに提示された系列も網羅:
".double_blocks.", ".single_blocks."
".img_attn_proj.", ".img_attn_qkv."
".img_mlp.fc1.", ".img_mlp.fc2."
".img_mod.linear."
".txt_attn_proj.", ".txt_attn_qkv."
".txt_mlp.fc1.", ".txt_mlp.fc2."
".txt_mod.linear."
".linear1.lora_", ".linear2.lora_", ".modulation.linear."
注意:
末尾の `.weight` までの完全一致にしない(トレーナー差により末端が揺れるため)。部分一致で十分かつ安全。
再現手順
対象ファイル: `ComfyUI/comfy/lora.py`
`load_lora` の `if log_missing:` ブロックを上記の完成形に差し替える
ComfyUIを再起動(または該当モジュール再ロード)
ログレベルを info 以上に設定(集約ログは info)
動作検証の観点
WAN2.2 × WAN2.1 LoRA:
個別の未ロード警告は抑制され、末尾に集約ログのみ表示
例: `Suppressed 290 WAN2.1 LoRA key warnings (WAN2.1 LoRA on WAN2.2 model - expected behavior)`
WAN2.2 × WAN2.2 LoRA:
抑制は発動しない(通常は未ロード警告自体が出ない想定)
WAN2.1 × WAN2.1 LoRA(または他モデル):
抑制は発動しない(個別警告が出る)
トラブルシューティング
抑制されないキーがあった場合:
そのキー名の一部を `wan21_lora_indicators` に1行追加(部分一致でOK)
抑制が効きすぎる場合(本来出すべき環境で消える):
モデル側判定(`model_has_wan21_like_keys`)の条件をより厳格化
ログが出ない:
ログレベルを info 以上に設定
変更のインパクト
WAN2.2 × WAN2.1適用時のログノイズを大幅に削減
WAN2.2以外の組み合わせでは従来通りの警告表示を維持(安全側)
このノートの内容で、第三者が後から読んでも同じ実装・同じ挙動を再現できます。必要になったら `wan21_lora_indicators` に断片を1行足すだけで拡張可能です。
About WAN2.1 LoRA
WAn2.1用LoRAの適用に関しては一概に総括できませんが、以下はHjgh側でstrength3.0、Low側で1.5で想定通りの動作をした例です。360度回転モーションLoRAを使用しています。
WAN2.2はプロンプト再現能力が非常に高く、実はこの程度の事はプロンプトだけでやれてしまいますが。
Fixed GGUF Loader for WAN2.2
この先はともかく、少なくとも現時点で肝心のGGUFローダーがWAN2.2に対応していません。
故に改造しています。以下、特に難しくありません。厳密に言えば、「GGUFローダーの記述を改造する事により、MultiGPUのGGUFローダーがWAN2.2を認識する」という仕組みになっています。
## 修正箇所
**ファイル**: `ComfyUI/custom_nodes/ComfyUI-GGUF/loader.py`
### 修正前のコード
IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan"}### 修正後のコード
IMG_ARCH_LIST = {"flux", "sd1", "sdxl", "sd3", "aura", "hidream", "cosmos", "ltxv", "hyvid", "wan", "wan2.2"}```
Distorch Memory Manager
About a node
30GBクラスのベースモデル2個に対してdistorch処理をかける工程は、何か影響が出る予感がしていましたが、案の定結果RTX4070 12GBクラスではx4 UpscaleがOOMの為完走しないという問題を引き起こしました。
但し、私は既に独自の改造により解決しています。まずは下図のように、Distorchによるメモリ使用をアンロードする為のオリジナルノードを作成しました。

independently Distorch Memory Cleaner
ノードは、以下記事で公開しています。ダウンロードして解凍し、custom_nodesフォルダに置くだけで動作します。
custom_nodes\ComfyUI-MultiGPU_init_.py
難易度が高くなるので推奨はしませんが、同機能を以下の様にMultiGPUに統合する事も可能です。但し、この方法はファイル構造やコードの内容を理解できるユーザーでないと、使い続ける事が難しいでしょう。理由は、上記事にて記述しています。
DisTorch Memory Managerの改造詳細をコード付きで解説します。
## 📋 DisTorch Memory Manager 改造総括
### 1. **DisTorchMemoryCleaner** - 基本的なメモリクリア
class DisTorchMemoryCleaner:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"image": ("IMAGE",),
}}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "clean_memory"
CATEGORY = "DisTorch"
def clean_memory(self, image):
import torch
import gc
# GPUメモリのクリア
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.synchronize()
# Pythonのガベージコレクション
gc.collect()
# DisTorchの仮想メモリを解放
try:
import comfy.model_management
# 仮想メモリの割り当てをリセット
if hasattr(comfy.model_management, 'free_memory'):
comfy.model_management.free_memory(0, 'cuda:0')
comfy.model_management.free_memory(0, 'cpu')
except:
pass
print("DisTorch memory cleaned")
return (image,)**機能**:
- GPUメモリのクリア(`torch.cuda.empty_cache()`)
- Pythonガベージコレクション(`gc.collect()`)
- DisTorch仮想メモリのリセット
- シンプルで安全なメモリ管理
### 2. **DisTorchMemoryManager** - 包括的なメモリ管理(UI破損対策済み)
class DisTorchMemoryManager:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"image": ("IMAGE",),
"clean_gpu": ("BOOLEAN", {"default": True}),
"clean_cpu": ("BOOLEAN", {"default": False, "tooltip": "CPU memory cleanup (use with caution)"}),
"force_gc": ("BOOLEAN", {"default": True}),
"reset_virtual_memory": ("BOOLEAN", {"default": True}),
"restore_original_functions": ("BOOLEAN", {"default": False, "tooltip": "Restore original model_management functions"}),
}}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "manage_memory"
CATEGORY = "DisTorch"
def manage_memory(self, image, clean_gpu, clean_cpu, force_gc, reset_virtual_memory, restore_original_functions):
import torch
import gc
import psutil
import pydantic
print("=== DisTorch Memory Management ===")
# メモリ使用量の表示(前)
if torch.cuda.is_available():
gpu_memory_before = torch.cuda.memory_allocated() / 1024**3
print(f"GPU Memory before: {gpu_memory_before:.2f} GB")
cpu_memory_before = psutil.virtual_memory().used / 1024**3
print(f"CPU Memory before: {cpu_memory_before:.2f} GB")
# GPUメモリのクリア
if clean_gpu and torch.cuda.is_available():
try:
torch.cuda.empty_cache()
torch.cuda.synchronize()
print("GPU cache cleared")
except Exception as e:
print(f"GPU cache clear failed: {e}")
# CPUメモリのクリア(UI破損対策済み)
if clean_cpu:
# 基本的なガベージコレクションのみ実行
# オブジェクトの詳細検査は行わない(UIに影響する可能性があるため)
try:
# 明示的にガベージコレクションを実行
collected = gc.collect()
print(f"CPU memory cleanup: {collected} objects collected")
except Exception as e:
print(f"CPU memory cleanup failed: {e}")
# ガベージコレクション
if force_gc:
try:
collected = gc.collect()
print(f"Garbage collected: {collected} objects")
except Exception as e:
print(f"Garbage collection failed: {e}")
# DisTorchの仮想メモリをリセット
if reset_virtual_memory:
try:
import comfy.model_management
if hasattr(comfy.model_management, 'free_memory'):
comfy.model_management.free_memory(0, 'cuda:0')
comfy.model_management.free_memory(0, 'cpu')
print("Virtual memory reset")
except Exception as e:
print(f"Virtual memory reset failed: {e}")
# メモリ使用量の表示(後)
if torch.cuda.is_available():
try:
gpu_memory_after = torch.cuda.memory_allocated() / 1024**3
gpu_freed = gpu_memory_before - gpu_memory_after
print(f"GPU Memory after: {gpu_memory_after:.2f} GB (freed: {gpu_freed:.2f} GB)")
except Exception as e:
print(f"GPU memory measurement failed: {e}")
try:
cpu_memory_after = psutil.virtual_memory().used / 1024**3
cpu_freed = cpu_memory_before - cpu_memory_after
print(f"CPU Memory after: {cpu_memory_after:.2f} GB (freed: {cpu_freed:.2f} GB)")
except Exception as e:
print(f"CPU memory measurement failed: {e}")
# 元の関数を復元(オプション)
if restore_original_functions:
try:
mm.get_torch_device = original_get_torch_device
mm.text_encoder_device = original_text_encoder_device
print("Original model_management functions restored")
except Exception as e:
print(f"Failed to restore original functions: {e}")
print("=== Memory Management Complete ===")
return (image,)**主要な改善点**:
1. **UI破損対策**:
# CPUメモリクリアを安全に実行
if clean_cpu:
# オブジェクトの詳細検査は行わない
try:
collected = gc.collect()
print(f"CPU memory cleanup: {collected} objects collected")
except Exception as e:
print(f"CPU memory cleanup failed: {e}")2. **詳細なメモリ監視**:
# メモリ使用量の前後比較
gpu_memory_before = torch.cuda.memory_allocated() / 1024**3
cpu_memory_before = psutil.virtual_memory().used / 1024**3
# 処理後
gpu_freed = gpu_memory_before - gpu_memory_after
cpu_freed = cpu_memory_before - cpu_memory_after3. **エラーハンドリング**:
try:
torch.cuda.empty_cache()
torch.cuda.synchronize()
print("GPU cache cleared")
except Exception as e:
print(f"GPU cache clear failed: {e}")### 3. **DisTorchSafeMemoryManager** - 安全なメモリ管理(推奨)
class DisTorchSafeMemoryManager:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"image": ("IMAGE",),
"clean_gpu": ("BOOLEAN", {"default": True}),
"force_gc": ("BOOLEAN", {"default": True}),
"reset_virtual_memory": ("BOOLEAN", {"default": True}),
}}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "safe_manage_memory"
CATEGORY = "DisTorch"
def safe_manage_memory(self, image, clean_gpu, force_gc, reset_virtual_memory):
import torch
import gc
print("=== DisTorch Safe Memory Management ===")
# メモリ使用量の表示
if torch.cuda.is_available():
gpu_memory_before = torch.cuda.memory_allocated() / 1024**3
print(f"GPU Memory before: {gpu_memory_before:.2f} GB")
# GPUメモリのクリア(UIに影響しない)
if clean_gpu and torch.cuda.is_available():
try:
torch.cuda.empty_cache()
torch.cuda.synchronize()
print("GPU cache cleared")
except Exception as e:
print(f"GPU cache clear failed: {e}")
# ガベージコレクション(UIに影響しない)
if force_gc:
try:
collected = gc.collect()
print(f"Garbage collected: {collected} objects")
except Exception as e:
print(f"Garbage collection failed: {e}")
# DisTorchの仮想メモリをリセット
if reset_virtual_memory:
try:
import comfy.model_management
if hasattr(comfy.model_management, 'free_memory'):
comfy.model_management.free_memory(0, 'cuda:0')
comfy.model_management.free_memory(0, 'cpu')
print("Virtual memory reset")
except Exception as e:
print(f"Virtual memory reset failed: {e}")
# メモリ使用量の表示(後)
if torch.cuda.is_available():
try:
gpu_memory_after = torch.cuda.memory_allocated() / 1024**3
gpu_freed = gpu_memory_before - gpu_memory_after
print(f"GPU Memory after: {gpu_memory_after:.2f} GB (freed: {gpu_freed:.2f} GB)")
except Exception as e:
print(f"GPU memory measurement failed: {e}")
print("=== Safe Memory Management Complete ===")
return (image,)**特徴**:
- **CPUメモリクリア機能を完全に除外**(UI破損を防ぐ)
- **GPUメモリとガベージコレクションのみ**実行
- **最も安全で安定した**メモリ管理
## �� **改造の核心ポイント**
### 1. **UI破損問題の解決**
# 問題のあったコード(削除済み)
# for obj in gc.get_objects():
# if isinstance(obj, pydantic.BaseModel):
# # この部分がUI破損の原因
# 解決策
if clean_cpu:
# 基本的なガベージコレクションのみ実行
collected = gc.collect()
print(f"CPU memory cleanup: {collected} objects collected")### 2. **メモリ使用量の可視化**
# メモリ解放量の詳細表示
gpu_freed = gpu_memory_before - gpu_memory_after
cpu_freed = cpu_memory_before - cpu_memory_after
print(f"GPU Memory freed: {gpu_freed:.2f} GB")
print(f"CPU Memory freed: {cpu_freed:.2f} GB")### 3. **エラーハンドリングの強化**
try:
torch.cuda.empty_cache()
torch.cuda.synchronize()
print("GPU cache cleared")
except Exception as e:
print(f"GPU cache clear failed: {e}")## �� **使用方法**
# DisTorchSafeMemoryManager(最も安全)
clean_gpu = True
force_gc = True
reset_virtual_memory = True
# DisTorchMemoryManager(上級者向け)
clean_gpu = True
clean_cpu = False # UI破損を防ぐためFalse推奨
force_gc = True
reset_virtual_memory = True### **効果**:
- **GPUメモリ**: 最大数GBの解放
- **CPUメモリ**: 数百MB〜数GBの解放
- **安定性**: UI破損なし
- **パフォーマンス**: アップスケール・ビデオ処理の安定化
これらの改造により、ComfyUIでのメモリ管理が大幅に改善され、アップスケールやビデオ処理でのメモリエラーが大幅に減少します。
Paging file
このノードを使用しても尚、upscalingでOOM(この場合、VRAMではなくシステムメモリ不足で落ちる)エラーを起こす場合、ページングファイルのサイズを拡張して対応してください。

高速化ノード組み合わせと、テンソル不一致エラーについて
初期状態での組み合わせは、Skip Layer+Torch Compileですが、この組み合わせはどうも、720×720生成時にテンソル不一致エラーの原因になるようです。稀に640×640でも発生しますが、条件がどうも不明です。

Skip Layerをバイパスして、代わりにApply First Block Cacheを使用すると解決する場合が多いです。何れにせよ、組み合わせを選択できるようにしておきました 。
Torch.compileは無いと、特に640×640以上で相当生成速度が落ちるので、なるべく使用したい処です。
