たねやつの木

Photographs, Keyboards and Programming

Qwen-Image軽量版(DFloat11)はVRAM 12GBで動くのか?RTX3060で挑戦した結果無理でした

ComfyUIがPF8で量子化したモデルの公開を行っています。詳細はこちらの記事から↓

www.taneyats.com

こんにちは、たねやつです。

驚異的な性能を持つとご紹介した画像生成AI「Qwen-Image」。その大きな課題は、要求されるハードウェアスペックの高さでした。

そんな中、Hugging Faceを眺めていると、有志の方が作成した軽量化モデル「DFloat11/Qwen-Image-DF11」が公開されているのを発見!「これなら私の環境でも動かせるかもしれない!」と、淡い期待を抱いて早速試してみることにしました。

この記事では、その挑戦の結果をありのままにレポートします。

前の記事

www.taneyats.com

この記事でわかること

  • Qwen-Imageの軽量化モデルの存在
  • RTX 3060(VRAM 12GB)で軽量化モデルを動かそうとした際の具体的な手順と結果

挑戦した環境

試した導入手順

Hugging Faceのページに記載されている手順に沿って、導入を進めました。ちなみにページによるとギリギリ16GBのVRAMがあれば生成可能かもしれないとのことです。

Model Model Size Peak GPU Memory (1328x1328 image generation) Generation Time (A100 GPU)
Qwen-Image (BFloat16) ~41 GB OOM -
Qwen-Image (DFloat11) 28.42 GB 29.74 GB 100 seconds
Qwen-Image (DFloat11 + GPU Offloading) 28.42 GB 16.68 GB 260 seconds

1. 必要なライブラリのインストール

まず、モデルの実行に必要となるライブラリをインストールします。CUDA 12.x系に対応したdfloat11と、Hugging Faceのdiffusersライブラリです。

# CUDAカーネルをインストール
pip install -U dfloat11[cu12]

# diffusersライブラリをインストール
pip install git+https://github.com/huggingface/diffusers

2. 実行スクリプトの準備

次に、Hugging Faceで提供されているPythonスクリプトを参考に、画像生成のためのコードを準備します。コードの主要な部分は以下のようになっています。

from diffusers import DiffusionPipeline, QwenImageTransformer2DModel
import torch
from transformers.modeling_utils import no_init_weights
from dfloat11 import DFloat11Model
import argparse

def parse_args():
    parser = argparse.ArgumentParser(description='Generate images using Qwen-Image model')
    parser.add_argument('--cpu_offload', action='store_true', help='Enable CPU offloading')
    parser.add_argument('--no_pin_memory', action='store_true', help='Disable memory pinning')
    parser.add_argument('--prompt', type=str, default='A coffee shop entrance features a chalkboard sign reading "Qwen Coffee 😊 $2 per cup," with a neon light beside it displaying "通义千问". Next to it hangs a poster showing a beautiful Chinese woman, and beneath the poster is written "π≈3.1415926-53589793-23846264-33832795-02384197".',
                        help='Text prompt for image generation')
    parser.add_argument('--negative_prompt', type=str, default=' ',
                        help='Negative prompt for image generation')
    parser.add_argument('--aspect_ratio', type=str, default='16:9', choices=['1:1', '16:9', '9:16', '4:3', '3:4'],
                        help='Aspect ratio of generated image')
    parser.add_argument('--num_inference_steps', type=int, default=50,
                        help='Number of denoising steps')
    parser.add_argument('--true_cfg_scale', type=float, default=4.0,
                        help='Classifier free guidance scale')
    parser.add_argument('--seed', type=int, default=42,
                        help='Random seed for generation')
    parser.add_argument('--output', type=str, default='example.png',
                        help='Output image path')
    parser.add_argument('--language', type=str, default='en', choices=['en', 'zh'],
                        help='Language for positive magic prompt')
    return parser.parse_args()

args = parse_args()

model_name = "Qwen/Qwen-Image"

with no_init_weights():
    transformer = QwenImageTransformer2DModel.from_config(
        QwenImageTransformer2DModel.load_config(
            model_name, subfolder="transformer",
        ),
    ).to(torch.bfloat16)

DFloat11Model.from_pretrained(
    "DFloat11/Qwen-Image-DF11",
    device="cpu",
    cpu_offload=args.cpu_offload,
    pin_memory=not args.no_pin_memory,
    bfloat16_model=transformer,
)

pipe = DiffusionPipeline.from_pretrained(
    model_name,
    transformer=transformer,
    torch_dtype=torch.bfloat16,
)
pipe.enable_model_cpu_offload()

positive_magic = {
    "en": "Ultra HD, 4K, cinematic composition.", # for english prompt,
    "zh": "超清,4K,电影级构图" # for chinese prompt,
}

# Generate with different aspect ratios
aspect_ratios = {
    "1:1": (1328, 1328),
    "16:9": (1664, 928),
    "9:16": (928, 1664),
    "4:3": (1472, 1140),
    "3:4": (1140, 1472),
}

width, height = aspect_ratios[args.aspect_ratio]

image = pipe(
    prompt=args.prompt + positive_magic[args.language],
    negative_prompt=args.negative_prompt,
    width=width,
    height=height,
    num_inference_steps=args.num_inference_steps,
    true_cfg_scale=args.true_cfg_scale,
    generator=torch.Generator(device="cuda").manual_seed(args.seed)
).images[0]

image.save(args.output)

max_memory = torch.cuda.max_memory_allocated()
print(f"Max memory: {max_memory / (1000 ** 3):.2f} GB")

以下で実行

python qwen_image.py --cpu_offload

モデルページによると、--cpu_offloadオプションを有効にすることで、16GBのVRAMで動作する可能性があるとのこと。私の環境は12GBなので厳しい戦いになるとは思いつつも、この設定で実行に臨みました。

いざ、実行!しかし…

準備したスクリプトを実行!しかし、モデルをメモリにロードしようとしたまさにその瞬間、プロセスが強制終了してしまいました。コンソールに表示されたのは、無慈悲な「SIGKILL」の文字...。

これは、多くの場合、OSがシステムの安定性を保つために、メモリを使いすぎたプロセスを強制的に停止させるシグナルです。つまり、VRAM 12GBでは、CPUオフロードを有効にしたこの軽量化モデルですらメモリが足りなかった、ということになります。

**SIGKILLとは?**
LinuxやmacOSなどのUnix系OSで使われるシグナルの一つ。プロセスを問答無用で強制終了させる、最も強力なシグナルです。メモリ不足(Out of Memory)の際に、OSの機能(OOM Killer)によって送られることがよくあります。

結論:RTX 3060 (12GB) ではまだ厳しい

残念ながら、今回の挑戦は「VRAM不足により失敗」という結果に終わりました。

軽量化モデルとはいえ、Qwen-Imageのパワフルさを維持するには、まだかなりのVRAMが必要とされるようです。RTX 3060の12GBというVRAMは、一昔前なら十分な容量でしたが、最新のAIモデルを動かすには少し心許なくなってきたのかもしれません。

最後に

というわけで、今回はほろ苦い結果報告となりました。

しかし、こうして有志の方がすぐに軽量化に取り組んでくれるのは、非常にありがたいことです。今後、さらに圧縮や量子化が進んだモデルが登場し、より多くの人がQwen-Imageの素晴らしい性能を体験できるようになることを心から期待しています。

私も、新しいモデルが登場したら、めげずにまた挑戦してみたいと思います!

次の記事

www.taneyats.com

参考・引用