見出し画像

Hugging FaceのZeroGPUspaceで画像生成環境を作る


はじめに

Hugging FaceはAI関連で有名なオープンソースプラットフォームです

モデルのダウンロードなどでお世話になったりするHugging Faceですが、
Zero GPU spaceがとても良さげだったので試してみました

なお、環境作成にあたっては、以下のあるふさんのQiitaを大変参考にしました

ほぼそのまま流用ともいう

Hugging Faceのアカウント作成

ここは省略していいかなあ
作成要望があれば書こうかなとか思っています

…ということでアカウントが出来ました!✨

Zero GPU Space?

Zero GPU SpaceはSpaceでHuggingFaceのProアカウント(有料)で使用できるようになる
Spaceで選べるGPUのことなのですが、、
とりあえず、Proアカウント→Space→ZeroGPUな感じで順に記載していきます

Proアカウント

HuggingFaceにログイン後、上の方に以下のようなバーがあります

ちっさ。

一番右のPricingをぽちー
すると以下のような画面になります

こんな感じ

真ん中にPro Accountの説明があり、Pro Accountの一番上に
ZeroGPUの説明があります
Pro Accountは月額$9で使用可能です

Pro Accountをぽちーするともう少し細かい(?)説明のページへ飛び
→Get Pro のボタンよりPro Account登録が可能です

こんな感じ

Space

HuggingFaceのSpaceでは様々なデモが公開されています
また、右のほうにある Create new Space ボタンで
自分のSpaceを作成することもできます

Space作成については後程環境作りとして記載するので
ここでは省略

ZeroGPU

ZeroGPUの詳細についてはPricingのProアカウントより辿れる
ページに記載があります

こんな感じ

ポイントとしては、
・(Proアカウントであれば)追加コストなしでGPUを使用可能
・つよつよGPU(A100)を使用可能

といった形になります、すごー!👀✨

コスト関係の話は、Spaceのコスト一覧を見るとわかりやすいです

スペースのコスト一覧

Proアカウントは月額$9、
A100は1時間で$4なので、2時間と少し使用できることになります

とはいえ、当然にいろいろと制限はあり、、

1)Gradioでしか使えない

ZeroGPUは今のところGradioだけ

2)1回で最大120秒しか使えない

120秒まで

といった制限があります

画像生成的には問題ない長さですが、
モデルのトレーニングとか長めの動画生成とかには使えないのかな?

環境作り

そんな感じで実際に作っていきます
まずはSpaceの Create new Spaceをぽちー

右下のボタン

すると以下の画面に飛びます
Space nameに作りたいSpaceの名前、
Licenseに何かしらをいれます(入力なしでも作成はできます)
Licenseは使用モデルなどに左右されるのかなあ

Select the Space SDKはGradioを選択します

Gradioを選択すると、以下のテンプレート選択が追加で出てきます
今回は画像生成環境を作るので、ひとまずtext-to-imageを選択します

Space hardwareを選択するといろいろ選べるので
ZeroGPU Nvidia A100 を選択します

選択前
選択後

公開用(自分以外も使えるようにする)場合はPublic、
自分用の場合はPrivate を選択し
Create Spaceを押します

すると、なんだかセットアップが入り、、
あるふさんのQiita記載の通りエラーが発生するので修正のたFilesへ移動します

files

そしてapp.pyをぽちー
以下のような画面に行きます

app.py

edit をぽちーすると編集可能になります

こんな感じ

モデルはSDXLの標準より変更したかったり
モデルのリンクを張りたかったりしたので
今回は以下の実装にしました

import gradio as gr
import numpy as np
import random
#from diffusers import DiffusionPipeline
from diffusers import StableDiffusionXLPipeline
import torch
import spaces


MAX_SEED = np.iinfo(np.int32).max
MAX_IMAGE_SIZE = 1216

#pipe = DiffusionPipeline.from_pretrained("stabilityai/sdxl-turbo", torch_dtype=torch.float16, variant="fp16", use_safetensors=True)
pipe = StableDiffusionXLPipeline.from_pretrained(
    #"yodayo-ai/kivotos-xl-2.0", 
    "yodayo-ai/holodayo-xl-2.1", 
    torch_dtype=torch.float16, 
    use_safetensors=True,
    custom_pipeline="lpw_stable_diffusion_xl",
    add_watermarker=False,
    variant="fp16"
)
pipe.to('cuda')

prompt = "1girl, solo, upper body, v, smile, looking at viewer, outdoors, night, masterpiece, best quality, very aesthetic, absurdres"
negative_prompt = "nsfw, (low quality, worst quality:1.2), very displeasing, 3d, watermark, signature, ugly, poorly drawn"

@spaces.GPU
def infer(prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps):

    if randomize_seed:
        seed = random.randint(0, MAX_SEED)
        
    generator = torch.Generator().manual_seed(seed)
    
    image = pipe(
        prompt = prompt+", masterpiece, best quality, very aesthetic, absurdres", 
        negative_prompt = negative_prompt,
        guidance_scale = guidance_scale, 
        num_inference_steps = num_inference_steps, 
        width = width, 
        height = height,
        generator = generator
    ).images[0] 
    
    return image

css="""
#col-container {
    margin: 0 auto;
    max-width: 520px;
}
"""

with gr.Blocks(css=css) as demo:
    
    with gr.Column(elem_id="col-container"):
        gr.Markdown(f"""
        # Text-to-Image Demo
        using [Holodayo XL 2.1](https://huggingface.co/yodayo-ai/holodayo-xl-2.1)
        """)
        
        with gr.Row():
            
            prompt = gr.Text(
                label="Prompt",
                show_label=False,
                max_lines=1,
                placeholder="Enter your prompt",
                container=False,
            )
            
            run_button = gr.Button("Run", scale=0)
        
        result = gr.Image(label="Result", show_label=False)

        with gr.Accordion("Advanced Settings", open=False):
            
            negative_prompt = gr.Text(
                label="Negative prompt",
                max_lines=1,
                placeholder="Enter a negative prompt",
                visible=False,
            )
            
            seed = gr.Slider(
                label="Seed",
                minimum=0,
                maximum=MAX_SEED,
                step=1,
                value=0,
            )
            
            randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
            
            with gr.Row():
                
                width = gr.Slider(
                    label="Width",
                    minimum=256,
                    maximum=MAX_IMAGE_SIZE,
                    step=32,
                    value=832,
                )
                
                height = gr.Slider(
                    label="Height",
                    minimum=256,
                    maximum=MAX_IMAGE_SIZE,
                    step=32,
                    value=1216,
                )
            
            with gr.Row():
                
                guidance_scale = gr.Slider(
                    label="Guidance scale",
                    minimum=0.0,
                    maximum=20.0,
                    step=0.1,
                    value=7,
                )
                
                num_inference_steps = gr.Slider(
                    label="Number of inference steps",
                    minimum=1,
                    maximum=28,
                    step=1,
                    value=28,
                )

    run_button.click(
        fn = infer,
        inputs = [prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps],
        outputs = [result]
    )

demo.queue().launch()

基本的にはあるふさんのQiita記載のコードをベースにしつつ、、

dissusers importを差し替え

from diffusers import StableDiffusionXLPipeline

モデルに関連する記述を差し替え

pipe = StableDiffusionXLPipeline.from_pretrained(
    #"yodayo-ai/kivotos-xl-2.0", 
    "yodayo-ai/holodayo-xl-2.1", 
    torch_dtype=torch.float16, 
    use_safetensors=True,
    custom_pipeline="lpw_stable_diffusion_xl",
    add_watermarker=False,
    variant="fp16"
)
pipe.to('cuda')

prompt = "1girl, solo, upper body, v, smile, looking at viewer, outdoors, night, masterpiece, best quality, very aesthetic, absurdres"
negative_prompt = "nsfw, (low quality, worst quality:1.2), very displeasing, 3d, watermark, signature, ugly, poorly drawn"

モデル設定とGradio表示関連設定を差し替え

css="""
#col-container {
    margin: 0 auto;
    max-width: 520px;
}
"""

with gr.Blocks(css=css) as demo:
    
    with gr.Column(elem_id="col-container"):
        gr.Markdown(f"""
        # Text-to-Image Demo
        using [Holodayo XL 2.1](https://huggingface.co/yodayo-ai/holodayo-xl-2.1)
        """)
        
        with gr.Row():
            
            prompt = gr.Text(
                label="Prompt",
                show_label=False,
                max_lines=1,
                placeholder="Enter your prompt",
                container=False,
            )
            
            run_button = gr.Button("Run", scale=0)
        
        result = gr.Image(label="Result", show_label=False)

        with gr.Accordion("Advanced Settings", open=False):
            
            negative_prompt = gr.Text(
                label="Negative prompt",
                max_lines=1,
                placeholder="Enter a negative prompt",
                visible=False,
            )
            
            seed = gr.Slider(
                label="Seed",
                minimum=0,
                maximum=MAX_SEED,
                step=1,
                value=0,
            )
            
            randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
            
            with gr.Row():
                
                width = gr.Slider(
                    label="Width",
                    minimum=256,
                    maximum=MAX_IMAGE_SIZE,
                    step=32,
                    value=832,
                )
                
                height = gr.Slider(
                    label="Height",
                    minimum=256,
                    maximum=MAX_IMAGE_SIZE,
                    step=32,
                    value=1216,
                )
            
            with gr.Row():
                
                guidance_scale = gr.Slider(
                    label="Guidance scale",
                    minimum=0.0,
                    maximum=20.0,
                    step=0.1,
                    value=7,
                )
                
                num_inference_steps = gr.Slider(
                    label="Number of inference steps",
                    minimum=1,
                    maximum=28,
                    step=1,
                    value=28,
                )

    run_button.click(
        fn = infer,
        inputs = [prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps],
        outputs = [result]
    )

したりしています

あれ?? あらためて見ると結構変わってる…??
ベースとは??

…といった感じで変更し終わったあとは
以下のCommit changes to main をぽちーして
変更を確定させます

Commit changes(薄っすらと Update app.pyと記載の箇所)は
変更コメントを残す機能みたいなので
入れたい人は入れてcommitしましょう

Commitした後は以下の感じで再設定が入ります(青いところ)

青→緑になると、アプリが何かしら動いている状態になります

動いた!

エラーの際は赤くなるので、頑張って直します。。

結果

できました!!

そして結果として出来たデモはこちら✨

公開設定で作っているので誰でも使用可能になっているはずです(多分きっと)

まあ、Proアカウント解除したら維持は無理なので使えなくなってるかもですが。。

試してみてわかったこと(デメリット?)

画像生成だと割と
・生成→プロンプトちょっと変えて、、→生成→
ってやると思うのですが、割とすぐ上限がきます💦

これはユーザーごとに設定されているらしく
作成者(=Proアカウント登録者)でもユーザーでも変わらない(?)みたいです

ほぼ連続で60秒くらい使用すると5分程度またされる感じ?なのかな??
最初見たエラー表示は1:30だった気がするので
エラーにかかるほどWaitが伸びていくとか…?
ちょっと詳細は確認しないとわかりませんが。。

この、思った以上に上限が短い仕様は使い始めるまでわからなかったので
注意かなあと思いました
主に自分で使うように作っているひとは特に

とはいえ、公開用にも自分用にも使えて
月額固定でGPU(しかもつよつよ)が使えるのは素敵ですね…!!

いいなと思ったら応援しよう!