見出し画像

Diffusersで作るFlux1のggufモデルを動かすシンプル画像生成サーバ


はじめに

訂正:GPUが有効に活用されtiなかったようです。25行目、pipe定義の後ろに.to( "cuda")を追加。その下の25行目をコメントアウトします。大きなVRAMが必要で、Q2でも16Gで動かないかもしれません(ギリギリか)。ここのサンプルコードだと20Gは最低必要です。

Diffusersが0.32.0にバージョンアップしました。最新のモデルであるFluxやそのggufも動かすことができるようになったので画像生成サーバ化してアプリからapi経由で使えるようにしました。専用サーバで、まずはT2Iで最小限のパラメータを指定し、生成速度などが確認できるシンプルサーバです。

環境

Githubから最新版をクローン

Diffusersの指示に従います。cudaとpytorchがインストールされているものとします。VENVなどの仮想環境を作成すると失敗してもやり直しができます。

pip install --upgrade diffusers accelerate transformers accelerate bitsandbytes -U
pip install -U gguf
pip install fastapi

サーバの構成

FastAPIを使用してAPIサーバを動かします。簡単なコードです。

import torch
from diffusers import FluxPipeline, FluxTransformer2DModel, GGUFQuantizationConfig
import pickle
# ===================================     FastAPI  ==============================
from fastapi import FastAPI, Form
from fastapi.responses import Response
import time

app = FastAPI()

ckpt_path = (
    "https://huggingface.co/city96/FLUX.1-dev-gguf/blob/main/flux1-dev-Q4_K_S.gguf"
    )
transformer = FluxTransformer2DModel.from_single_file(
    ckpt_path,
    quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16),
    torch_dtype=torch.bfloat16,
    )
pipe = FluxPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    transformer=transformer,
    torch_dtype=torch.bfloat16,
    ).to( "cuda")
pipe.enable_model_cpu_offload()#上記.to( "cuda")を付けてここをコメントアウトするとGPUで動きます。

#-------------------- FastAPI エンドポイント--------------------------
@app.post("/generate/")
def generate(
    prompt:str = Form(None),
    seed:int= Form(0),
    auto_seed:bool = Form(True),
    height :int= Form(None),
    width:int = Form(None),
    num_inference_steps:int =Form(28),
    guidance_scale:float = Form(0.1),

    ):
    start_time = time.time()
    #−−−−−Seed値を取得 auto_seedがTrueならtorch.initial_seed( )
    if seed == None:    
        if auto_seed: #seedが無くてauto_seedが有効(True)の時
            seedx= torch.initial_seed( )
            generator = torch.manual_seed(seedx )
        else:                   #seedが無くてauto_seedが無効(False)の時
            generator = torch.manual_seed(1)
    else: #seedが指定された場合
        generator = torch.manual_seed(seed )
    #−−−−−こから画像の生成
    image = pipe(
        prompt,
        guidance_scale=guidance_scale,
        height=height,
        width=width,
        num_inference_steps= num_inference_steps,
        max_sequence_length=256,
        generator=generator
        )

    image_list=image.images[0]
    print("Generation time = ",(time.time() - start_time)*1000,"mS")
    #−−−−−生成画像を返信
    images_data = pickle.dumps(image_list, 5)  # tx_dataはpklデータ
    return Response(content= images_data, media_type="application/octet-stream")
 
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8001)

クライアント側

こちらはもっと簡単です。FastAPIのgenerateエンドポイントを呼ぶだけです。最低限、プロンプトのみで動くはず。

import requests
from PIL import Image
from io import BytesIO
import pickle
# t2i TEST
data= {
    "prompt":"masterpiece, best quality, 1girl, solo, flower, long hair, outdoors, letterboxed, school uniform, day, sky, looking up, short sleeves, parted lips, shirt, cloud, black hair, sunlight, white shirt, serafuku, upper body, from side, pink flower, blurry, brown hair, blue sky, depth of field",
    "auto_seed":False,
    "height":1024,
    "width":1024,
    "seed":4,
    "num_inference_steps":20,
    "guidance_scal":4.0,
  }
url = 'http://0.0.0.0:8001/generate/'
response = requests.post(url, data=data ) # POSTリクエストを送信
print(response)
# レスポンスを表示 
if response.status_code == 200:
    print("get data OK")
    image_data = response.content
    image =(pickle.loads(image_data))#元の形式にpickle.loadsで復元
    image.show() 
else:
    print("リクエストが失敗しました。ステータスコード:", response.status_code)

生成時間

4060ti
1024x1024サイズ出力時:99秒
512x512サイズ出力時:45秒
とても遅いです。GPUで正しく動いてないのかと疑いますが、GPUはしっかり働いてます。
num_inference_stepsは標準で28、good-qualityを期待するなら50と記載がありますが、上記の生成時間は28のときで50だと実用的ではないと想います。

生成した画像(1024x1024) num_inference_steps=28

生成した画像(512x512) num_inference_steps=50

まとめ

動かすのは簡単ですが、色々とパラメータを設定した方が良さそうです。生成時間はすごく長いです。