Anima-Turbo INT8ConvRot量子化版をM1 MaxのComfyUIで動かす
目次
CivitAIに Anima-Turbo v1.0 INT8ConvRot Quantized が上がっていた。早速チェックしてみると、UNet・テキストエンコーダ(qwen_3_06b_base)・VAE(hdrVAEAnimaKrea2QWEN)の3点をINT8ConvRotという手法で量子化し、モデルカードには「RTX 20シリーズ以上で推論50%以上高速化」「VRAM大幅削減」「画質劣化は軽微」と書かれている。
ただしこの加速効果の説明はCUDA環境(RTX 20シリーズ以上でINT8ConvRot加速、RTX 10シリーズは加速非対応だが高速推論は維持)が前提で、Apple Silicon / MPSでの挙動には触れられていない。M1 Max上のComfyUIで実際にロードできるか、速度・VRAM・画質がどう変わるかを実測してみた。
検証の範囲
画像生成は普通はほとんどComfyUI経由なので、検証もまずそのワークフローにINT8ConvRot版を組み込み、モデルのロードからテキストエンコーダ、10ステップのサンプリング、VAEデコードまで通るか、速度・VRAM・画質がどう変わるかを確かめた。
Apple GPU上で量子化行列積そのものを実行でき、fp16より速くなるかは、ComfyUIを外してPyTorchの最小コード、MLX、直接書いたMetalカーネルだけで測る別記事の単体ベンチへ分けた。
ここで使うComfyUI APIは、ローカルの/promptへワークフローを1回送信し、完了するまで/historyを1秒ごとに確認しているだけである。テンソルや各レイヤーの演算をHTTP経由で往復させているわけではなく、実際の推論はComfyUIサーバープロセス内のPyTorch/MPSで進む。APIを挟むことで変わるのは、最大1秒程度の計測粒度だけになる。
検証環境
| 項目 | 内容 |
|---|---|
| マシン | Mac (M1 Max, 64GB RAM) |
| UI | ComfyUI 0.24.1 |
| 比較対象A(基準) | anima-base-v1.0.safetensors + anima-turbo-lora-v0.2.safetensors(fp16) |
| 比較対象B(INT8ConvRot・フル) | animaTurboV10Int8convrotQuantized_v10.safetensors + 付属int8 TE/VAE |
| 比較対象C(INT8ConvRot・混合) | UNetのみint8、TE/VAEは既存fp16(qwen_3_06b_base / qwen_image_vae) |
| 共通条件 | 同一prompt・seed(123456789)、832×1024、steps10・cfg1・er_sde/simple |
モデル構成
| コンポーネント | 基準(fp16) | INT8ConvRot版 |
|---|---|---|
| UNet | anima-base-v1.0.safetensors + Turbo LoRA v0.2 | animaTurboV10Int8convrotQuantized_v10.safetensors(2.1GB) |
| Text Encoder | qwen_3_06b_base.safetensors | animaTurboV10Int8convrotQuantized_v10_txt.safetensors(718MB) |
| VAE | qwen_image_vae.safetensors | hdrVAEAnimaKrea2QWEN_int8.safetensors(484MB) |
ロードすら通らなかった
3パターンとも同一プロンプト・同一シードでComfyUI APIから流した。結果、INT8ConvRot版は「フル」「混合(UNetのみint8)」の両方ともロード段階でエラーになり、画像生成まで到達しなかった。
| ケース | 結果 | 所要時間 | VRAM使用量 |
|---|---|---|---|
| A: 基準(fp16 + Turbo LoRA) | 成功 | 58.6秒 | 約15.0GB |
| B: INT8ConvRot フル | 失敗(ロードエラー) | — | — |
| C: INT8ConvRot 混合(UNetのみint8) | 失敗(ロードエラー) | — | — |
エラーは両ケースとも同じ箇所で発生した。
KeyError: 'int8_tensorwise'
File "comfy/ops.py", line 1074, in _load_quantized_module
qconfig = QUANT_ALGOS[module.quant_format]
BはCLIPLoader(テキストエンコーダのロード)、CはUNETLoaderでそれぞれ同じKeyErrorが出た。量子化済みsafetensorsの内部メタデータに書かれたquant_formatがint8_tensorwiseだが、ComfyUI側の量子化フォーマット登録テーブルにこのキーが存在しない。
ComfyUIが対応している量子化フォーマットにint8_tensorwiseが無い
comfy/quant_ops.pyのQUANT_ALGOSを直接確認すると、ComfyUI 0.24.1(2026-06-04時点のコード、gitコミットba9ffa0)がネイティブ対応する量子化フォーマットは次の4つだけだった。
| フォーマット | 対応レイアウト |
|---|---|
| float8_e4m3fn | TensorCoreFP8E4M3Layout |
| float8_e5m2 | TensorCoreFP8E5M2Layout |
| nvfp4 | TensorCoreNVFP4Layout |
| mxfp8(CK利用可能時のみ) | TensorCoreMXFP8Layout |
いずれもNVIDIAのTensor Core向けFP8/FP4系フォーマットで、int8_tensorwiseという名前のキーはComfyUIのコア・comfy_extras・インストール済みcustom_nodesのどこにも存在していない(grep -rl int8_tensorwiseで0件)。
_load_quantized_moduleはロード対象のstate_dictに埋め込まれたquant_formatメタデータを見てレジストリを引くだけの処理で、GPU種別やバックエンドを見ていない。同じComfyUI 0.24.1であればCUDA環境でも同じKeyErrorが出るはずで、モデルカードが謳う「RTX 20シリーズ以上でINT8ConvRot加速」は、少なくとも素のComfyUI 0.24.1上では検証以前にそもそも動かせる環境ではなかった。
ComfyUIのint8対応はv0.27.0から
safetensorsのヘッダーを直接確認すると、量子化された各レイヤーにはweight(I8)、weight_scale(F32)に加えてcomfy_quantというU8[72]のサイドカーテンソルが付いている。これはComfyUI自身の量子化スキームが書き込む管理用テンソルで、外部の汎用量子化ツールの出力ではなく、ComfyUI向けに量子化されたモデルであることが分かる。
ComfyUIのGitHubリポジトリを直接確認したところ、検証機のv0.24.1(2026-06-04時点のコミット)は最新v0.30.1から325コミット遅れていた。git log -S"int8_tensorwise"で該当コミットを追うと、int8量子化モデル対応は Support int8 models. (#14636) で追加され、初出タグはv0.27.0(2026-06-25マージ、検証機のコミットの3週間後)。さらに Support int8 convrot embedding lookup (#15035) というConvRotそのものを指すコミットも存在する。
v0.30.1のcomfy/quant_ops.pyを確認すると、QUANT_ALGOSにint8_tensorwise(TensorWiseINT8Layout)とconvrot_w4a4(TensorCoreConvRotW4A4Layout)の両方が登録済みだった。
というわけでロード失敗の原因は、検証機のComfyUIがint8量子化モデル対応より前のバージョンだったのが理由。そう言えば前もバージョン違いで本実験前に動かないみたいなことを言ってた気がするので、メイン使用のComfyUIをついでに引き上げておいた方がいいかと思った。
v0.30.1にアップグレードした
comfy/model_management.pyのMPS/float8まわりと、Anima用のsupported_inference_dtypesはv0.24.1→v0.30.1間で変更はなかった(過去にComfyUIアプデでQwen Image Editの推論dtypeが変わり生成が10分かかるようになった記事があるため、同種の回帰がないか差分を先に確認した)ので、この2点に限れば今回のアップグレードで新規に壊れるリスクは低いと判断した。
local/v0.24.1ブランチはロールバック用にそのまま残し、local/v0.30.1ブランチ(v0.30.1タグ)を新規に切って切り替えた。venv内の依存関係をrequirements.txt基準で更新すると、PyTorchが2.10.0から2.13.0に上がった。
起動するとcomfyui_version: 0.30.1を確認でき、UNETLoader/CLIPLoaderのロードエラーは解消した。フル・混合の両方とも量子化レイヤーの読み込み自体は通った。
ロードは通ったが、推論の最初の行列積で別のエラーが出た
KSamplerの最初のブロック(comfy/ldm/cosmos/predict2.pyのadaln_modulation_self_attn)で新しいエラーが出た。
NotImplementedError: The operator 'aten::_int_mm' is not currently implemented for the MPS device.
If you want this op to be considered for addition please comment on
https://github.com/pytorch/pytorch/issues/141287 ...
As a temporary fix, you can set the environment variable
`PYTORCH_ENABLE_MPS_FALLBACK=1` to use the CPU as a fallback for this op.
トレースを辿ってみると、ComfyUIの量子化バックエンドcomfy_kitchenのint8線形層実装(comfy_kitchen/backends/eager/quantization.pyのint8_linear → fast_int8_mm)が、int8×int8行列積にtorch._int_mmを呼んでいた。このオペレータ自体がPyTorch本体でMPSカーネル未実装(PyTorch issue #141287)で、フル・混合どちらのケースも同じ箇所で同じ例外になった。この例外は、ComfyUIを通さない最小コードでも再現した。
ComfyUI側の対応(v0.27.0でのint8サポート追加)とPyTorch側のMPSカーネル実装は別の話で、今回はComfyUIが対応してもPyTorch側が対応してなかった。
PYTORCH_ENABLE_MPS_FALLBACK=1で試したが、Turboの意味がなくなるほど遅かった
エラーメッセージが提示するCPUフォールバックを試した。PYTORCH_ENABLE_MPS_FALLBACK=1を付けてComfyUIを再起動し、int8フル構成を再実行した。
基準(fp16 + Turbo LoRA)は10ステップ・49.3秒で完了する設定だが、CPUフォールバック版のint8フル構成は27分経過してもKSamplerの10ステップが終わらなかった。ブロックごと・ステップごとに_int_mmを呼ぶたびにMPS↔CPU間のテンソル転送とCPU側の演算が入るため、実行を中断した。
INT8ConvRot版が謳う「50%以上の高速化」どころか、素のfp16+Turbo LoRA構成の何十倍も遅い状態で、Turbo(少ステップ高速生成)としての意味が失われている。
int8×int8のPyTorch MPS経路が存在しない
検証機のComfyUIはorigin/masterから1コミット差(フロントエンドパッケージのバージョン更新のみで無関係)まで上げており、ComfyUIの更新を待って解決するようなものではない。
ComfyUIの量子化バックエンドcomfy_kitchenのパッケージ構成を見ると、backends/配下にはcuda・triton・hip(AMD ROCm)と汎用のeagerがあり、mps向けのバックエンドはディレクトリごと存在しない。MPSではeager実装が使われる。その中身が前述のfast_int8_mmで、CUDA向けのパディング処理(if a.is_cuda)は持っているが、MPSを想定した分岐はない。hasattr(torch, "int8_mm")という将来のAPIを見込んだ分岐もあるが、PyTorch 2.13.0にはまだ存在せず、結局torch._int_mm(MPS未実装)が呼ばれる。
通常のINT8ConvRot経路では、comfy_kitchenのint8_linearがtorch._int_mmを呼び、そのPyTorch演算にはMPSカーネルがない。今回止まった直接の原因は、MPS上でint8の活性とint8の重みを掛ける経路が両者の組み合わせに存在しないことだった。
comfy_kitchenにMPS用の逆量子化経路を足してみた
comfy_kitchen/tensor/int8.pyを確認してみると、量子化されていない重みが来た場合や特定条件では、すでに「逆量子化してから通常のlinear/mm/addmmを呼ぶ」フォールバック経路が実装の中に存在していた。量子化された重みを使う通常経路だけがtorch._int_mmを要求する高速パスに直結していた。
TensorWiseINT8Layout.dequantizeが呼ぶdequantize_int8_simple_dtype/dequantize_int8_convrot_weight_dtypeのeager実装は、int8とスケールの単純な要素積で、CUDA固有の命令に依存しない。そこでMPSデバイスの場合だけ、既存の量子化済み重み(QuantizedTensor)をweight.dequantize()で通常のfp16/bf16テンソルへ変換してから、素のtorch.nn.functional.linear(mm/addmmも同様)に渡すよう、_handle_int8_linear_tensorwise・_handle_int8_mm_tensorwise・_handle_int8_addmm_tensorwiseの3箇所に分岐を足した。
if input_tensor.device.type == "mps":
weight_fp = weight.dequantize()
return torch.nn.functional.linear(input_tensor, weight_fp, bias)
この方式は量子化されたint8×int8の高速演算そのもの(torch._int_mm)を使わず、レイヤーごとに一旦fp16/bf16へ戻してから通常の行列積を実行するだけなので、MPSに存在する演算だけで実行できる。
パッチ適用後、3ケースとも生成できた
ComfyUIを再起動してベンチを再実行した。
| ケース | 結果 | 所要時間 | VRAM使用量(参考値) |
|---|---|---|---|
| A: 基準(fp16 + Turbo LoRA) | 成功 | 46.3秒 | 約9.1GB |
| B: INT8ConvRot フル(UNet+TE+VAE) | 成功 | 43.3秒 | 測定ノイズあり(後述) |
| C: INT8ConvRot 混合(UNetのみint8) | 成功 | 41.2秒 | 約4.0GB |
3枚とも破綻のない画像が出力された。



B・Cはほぼ同じ絵柄で出た(同じUNetを使っているので当然)。基準(A)とは背景・制服の配色が異なるが、これはanima-base-v1.0 + Turbo LoRAという別構成と、Anima-Turboという別チェックポイントを比較しているためで、同一シードでも構図が一致するわけがない。量子化による劣化を見るならB・C同士の比較の方が妥当で、この2枚の間に目視で分かる破綻や劣化は見当たらなかった。
VRAM使用量は測定値がちょっとおかしかった。ComfyUIはケースの切り替え時に前のモデルをアンロードするため、Bの「使用量」がマイナス(空きメモリが増えた)になった。ケース間でモデルのロード・アンロードのタイミングが揃っていないのが原因で、この簡易計測では正確なピークメモリを比較できていない。
速度は基準46.3秒に対しB43.3秒・C41.2秒と、わずかに速いが誤差の範囲に近い。モデルカードが謳う「50%以上の高速化」を裏付ける結果にはならなかった。
比較対象が揃っていなかった問題を直す
ここまでの基準(A)はanima-base-v1.0 + Turbo LoRA v0.2で、これはAnima-Turbo本体が未公開だった時期の代替構成。INT8ConvRot版が量子化した元の「Anima-Turbo v1.0」チェックポイントそのものとは系統が違うため、量子化だけを変数にした比較になっていなかった。
CivitAIの公式Animaモデルページ(circlestone_labs、modelId 2458426)に無量子化のanima-turbo-v1.0(anima_turboV10.safetensors、bf16、3.99GB)があったので、これを取得して同じAnima-Turbo系統で揃え直した。Turboはこのチェックポイントに焼き込み済みで、別LoRAは不要。
| ケース | UNet | 状態 | 所要時間 |
|---|---|---|---|
| 公式Anima-Turbo v1.0(無量子化bf16) | anima_turboV10.safetensors | パッチ不要、ネイティブに動作 | 42.2秒 |
| INT8ConvRot フル | animaTurboV10Int8convrotQuantized_v10.safetensors+int8 TE/VAE | 前述のMPS用逆量子化パッチが無いと動かない | 47.2秒 |
| INT8ConvRot 混合(UNetのみint8) | 同上+fp16 TE/VAE | 同上 | 45.2秒 |
無量子化の公式版の方が、パッチを当てたINT8ConvRot版2種より速かった。 逆量子化パッチはレイヤーごとにint8→スケール適用→fp16変換という追加処理が入るため、量子化前の重みをそのまま使うより遅くなる。量子化によって速くなるどころか、Mac上では余分な変換コストが乗る分だけ遅くなっている。


画質面では、同じチェックポイント系統だけあって構図・髪型・服装はほぼ一致した。色調の差は瞳だけではなく、int8版は瞳が赤紫寄りで目の縁にも赤みが乗り、背景や襟元の青みも強い。ただ構造的な破綻や劣化は見当たらず、モデルカードの「画質劣化は軽微」という主張通りのようだった。
速度は公式無量子化版より遅く、画質は同等なので、この逆量子化パッチでMac上のINT8ConvRot版を使うメリットはダウンロード容量が小さい(2.1GB vs 3.99GB)点だけになる。別のMPS経路を使った場合については、別記事の単体ベンチで切り分ける。
逆量子化パッチで確認できた範囲
このパッチはMPSに存在しないtorch._int_mm(int8×int8の高速行列積)を一切使わず、レイヤーごとにfp16へ逆量子化してから通常の行列積を実行しているだけで、実行時の計算そのものは実質fp16相当になる。INT8ConvRotが本来売りにしている量子化テンソル演算による高速化は今回検証できていない。
モデル自体の重みは壊れておらず、逆量子化すれば正しく機能する。Mac(MPS)でもこの経路を通すと生成自体はできたが、体感速度はfp16構成とほぼ同等で、量子化による高速化は今回の方法では再現できなかった。ダウンロード容量(本体2.1GB、TE 718MB、VAE 484MB)が小さい点だけは、ディスク使用量の面でそのままメリットとして残るが、それだけなので、有用だとは言い難い。
動く段階と動かない段階
Macでint8量子化テンソル演算がどの段階まで動くかを切り分けるとこんな感じ。
| 段階 | 結果 |
|---|---|
| 量子化済みテンソルのロード | 可能(ComfyUI v0.27.0以降でint8_tensorwiseに対応済み) |
CPU上でのtorch._int_mm実行 | 可能(検証機のComfyUI venvで最小コードでも成功を確認) |
MPS上でのtorch._int_mm実行 | 不可(aten::_int_mm is not currently implemented for the MPS device) |
PYTORCH_ENABLE_MPS_FALLBACK=1 | 動くが、警告通りCPUフォールバックであり、Mac GPU(MPS)上のint8高速化ではない |
今回のパッチ(comfy_kitchen/tensor/int8.pyでMPSだけweight.dequantize()してfp系のlinear/mm/addmmに逃がす) | 動くが、これもint8×int8の速度検証ではない |
正確に言うなら、現状のComfyUI + comfy_kitchen + PyTorch MPSでは、MPS上でINT8ConvRotのint8×int8 GEMMを実行する経路がない。CPUフォールバックか逆量子化経由なら動くが、どちらもMac GPU上のint8高速化検証にはならない。
一応、PyTorchのMPS対応演算は追跡が続いており(PyTorch issue #141287)、AppleもMPS向けカスタムMetalカーネルが書けることを公式に案内はしている。したがって今回未実装なのは「このソフトウェアスタックの通常経路」の話で、Macでは物理的に不可能という話ではない、と思う。
そもそも設計自体がCUDA前提で作られている。ComfyUIのint8_tensorwiseのドキュメントでは、対応GPUの条件がSM 7.5以上と明記されている。これはNVIDIAのCompute Capability世代を指す条件で、モデルカードが謳う「RTX 20シリーズ以上で加速」もこの設計をそのまま反映したもの。Apple SiliconのMPSは端から対象に入っていない。
NVIDIAのGPU Performance Background User’s Guideによると、Turing世代はINT8演算をTensor CoreまたはCUDA coreで実行できる。元の実装は、そのハードウェアへ最適化されたint8行列積を使えることが前提である。今回の基本的なMetalカーネルとは、入力型が同じでも実行される命令と最適化の蓄積が異なる。
そのため、NVIDIA環境でfp16より速くなること自体は技術的には理解できる。ただしモデルカードが掲げる「50%以上高速化」は、今回のM1 Maxの結果からその数値の正誤は判断できない。今回の検証で言えるのは、CUDA向けに成立する高速経路がMPSにはそのまま存在してなかったということくらいかと思う。
量子化行列積をPyTorch、Metal、MPSMatrix、MLXから直接測った結果は、M1 Max上のINT8ConvRot単体ベンチへ続く。