HuggingFace Diffusers 0.12 : 使用方法 : パイプライン, モデルとスケジューラのロード

HuggingFace Diffusers 0.12 : 使用方法 : ロード & ハブ – パイプライン, モデルとスケジューラのロード (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 02/10/2023 (v0.12.1)

* 本ページは、HuggingFace Diffusers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Web: www.classcat.com  ;   ClassCatJP

 

HuggingFace Diffusers 0.12 : 使用方法 : ロード & ハブ – パイプライン, モデルとスケジューラのロード

diffusers ライブラリの中心となる前提は拡散モデルを できる限りアクセスしやすくする ことです。従ってアクセス可能性は、単一行のコードで完全な拡散パイプラインと個々のコンポーネントをロードする API を提供することで実現されます。

以下では以下を簡単にロードする方法を詳細に説明します :

 

パイプラインのロード

DiffusionPipeline クラスは ハブで利用可能な 任意の拡散モデルにアクセスする最も簡単な方法です。CompVis の潜在的拡散モデル をダウンロードする方法のサンプルを見てみましょう。

from diffusers import DiffusionPipeline

repo_id = "CompVis/ldm-text2im-large-256"
ldm = DiffusionPipeline.from_pretrained(repo_id)

ここで DiffusionPipeline は正しいパイプライン (i.e. LDMTextToImagePipeline) を自動的に検出し、すべての必要な configuration と重みファイルを (まだしてないのであれば) ダウンロードしてキャッシュし、そして最後に ldm と呼ばれるパイプライン・インスタンスを返します。このパイプライン・インスタンスはテキスト-to-画像生成のために LDMTextToImagePipeline.call() を使用して呼び出すことができます (i.e., ldm(“image of a astronaut riding a horse”))。

ロードのために一般的な DiffusionPipeline を使用する代わりに、適切なパイプライン・クラスを直接ロードすることもできます。上記のコードスニペットは以下を行なうのと同じインスタンスを生成します :

from diffusers import LDMTextToImagePipeline

repo_id = "CompVis/ldm-text2im-large-256"
ldm = LDMTextToImagePipeline.from_pretrained(repo_id)

LDMTextToImagePipeline のような拡散パイプラインは複数のコンポーネントから成ることが多いです。これらのコンポーネントは “unet”, “vqvae” と “bert” のようなパラメータ化モデルとトークナイザーやスケジューラの両方であり得ます。これらのコンポーネントは推論でパイプラインを使用するとき互いに複雑な方法で相互作用できます、例えば LDMTextToImagePipelineStableDiffusionPipeline に対しては推論呼び出しは ここ で説明されています。パイプライン・クラス の目的は、後で示されるように、これらの拡散システムの複雑さをラップして、カスタマイズのための柔軟性を維持しながら、ユーザに簡単に利用できる API を提供することです。

 

アクセス要求を必要とするパイプラインのロード

極めてリアルな画像を生成する拡散モデルの能力ゆえに、例えばアダルトや暴力的な画像を生成するような、そのようなモデルが望ましくない応用のために悪用される可能性があるという特定の危険があります。そのような不適切なユースケースの可能性を最小化するために、最もパワフルな拡散モデルの幾つかはモデルを使用可能とする前にユーザがライセンスを認めることを要求しています。ユーザがライセンスに同意しない場合、パイプラインはダウンロードできません。runwayml/stable-diffusion-v1-5 を前に行ったのと同じ方法でロードしようとする場合 :

from diffusers import DiffusionPipeline

repo_id = "runwayml/stable-diffusion-v1-5"
stable_diffusion = DiffusionPipeline.from_pretrained(repo_id)

それは貴方が モデルカード でライセンスをクリックして承認し、かつ Hugging Face ハブにログインしている場合にのみ動作します。そうでないならば以下のようなエラーメッセージを得ます :

OSError: runwayml/stable-diffusion-v1-5 is not a local folder and is not a valid model identifier listed on 'https://huggingface.co/models'
If this is a private repository, make sure to pass a token having permission to this repo with `use_auth_token` or log in with `huggingface-cli login`

従って、ライセンスをクリックして承認することを確実にする必要があります。You can do this by simply visiting the model card and clicking on “Agree and access repository”:

(訳注: 画像 – リンク切れ)

 
次に、モデルをロードしようとする前に、アクセストークンでログインする必要があります :

huggingface-cli login

あるいは代わりに、use_auth_token フラグで アクセストークン を直接渡すことができます。この場合、事前に huggingface-cli login を実行する必要はありません :

from diffusers import DiffusionPipeline

repo_id = "runwayml/stable-diffusion-v1-5"
stable_diffusion = DiffusionPipeline.from_pretrained(repo_id, use_auth_token="")

Hugging Face ハブに依存する必要なく、アクセスを必要とするパイプラインを使用する最後のオプションは、次のセクションで説明されるようにパイプラインをローカルでロードすることです。

 

パイプラインをローカルでロードする

パイプラインとその対応するファイルへの完全な制御を持つことを望む場合か、前に述べたように、アクセス要求を必要とするパイプラインを Hugging Face ハブに接続することなく、使用することを望む場合、パイプラインをローカルでロードすることを勧めます。

拡散パイプラインをローカルでロードするためには、まずフォルダ構造全体をローカルディスクに手動でダウンロードしてから、ローカルパスを DiffusionPipeline.from_pretrained() に渡す必要があります。CompVis の潜在的拡散モデル のサンプルを再度見てみましょう。

最初に、git-lfs を使用して モデルレポジトリ にアップロードされたフォルダ構造全体をダウンロードする必要があります :

git lfs install
git clone https://huggingface.co/runwayml/stable-diffusion-v1-5

上記のコマンドは ./stable-diffusion-v1-5 という名前のローカルフォルダをディスクに作成します。そして、行わなければならないすべてのことは、ローカルフォルダパスを from_pretrained に単純に渡すことだけです :

from diffusers import DiffusionPipeline

repo_id = "./stable-diffusion-v1-5"
stable_diffusion = DiffusionPipeline.from_pretrained(repo_id)

ここでのケースのように、repo_id がローカルパスである場合、DiffusionPipeline.from_pretrained() はそれを自動的に検出しますので、ハブからはどのようなファイルもダウンロードしようとはしません。通常は最新の変更に確実に追随するためにハブから直接重みをロードすることを勧めますが、匿名であることや、自己完結型アプリケーション等… を望む場合にはパイプラインをローカルでロードすることが好まれるはずです。

 

カスタマイズされたパイプラインのロード

拡散パイプラインのカスタマイズされたバージョンをロードしたい上級ユーザはデフォルトコンポーネントのどれでも、例えばスケジューラを別のスケジューラ・クラスと交換することでそれを行うことができます。この機能の標準的なユースケースはスケジューラの交換です。Stable Diffusion v1-5 はデフォルトでは PNDMScheduler を使用していますが、これは一般には最も性能の高いスケジューラではありません。stable diffusion のリリース以来、複数の改良されたスケジューラが公開されました。これらを使用するため、ユーザは好きなスケジューラを手動でロードしてそれを DiffusionPipeline.from_pretrained() に渡す必要があります。

E.g. 推論についてより良い品質 vs 生成スピードのトレードオフのために EulerDiscreteScheduler or DPMSolverMultistepScheduler を使用するため、それらを以下のようにロードできるでしょう :

from diffusers import DiffusionPipeline, EulerDiscreteScheduler, DPMSolverMultistepScheduler

repo_id = "runwayml/stable-diffusion-v1-5"

scheduler = EulerDiscreteScheduler.from_pretrained(repo_id, subfolder="scheduler")
# or
# scheduler = DPMSolverMultistepScheduler.from_pretrained(repo_id, subfolder="scheduler")

stable_diffusion = DiffusionPipeline.from_pretrained(repo_id, scheduler=scheduler)

ここで注意を払う価値のある 3 点があります。

  • 最初に、スケジューラは SchedulerMixin.from_pretrained() でロードされます。

  • 2 番目に、スケジューラは subfolder=”scheduler” なる関数引数でロードされます、stable diffusion のスケジューリングの configuration は 公式パイプライン・レポジトリのサブフォルダ で定義されているからです。

  • 3 番目に、スケジューラ・インスタンスは DiffusionPipeline.from_pretrained() への scheduler キーワード引数で単純に渡すことができます。これは、StableDiffusionPipeline がそのスケジューラを scheduler 属性で定義しているために機能します。sampler=scheduler のような別の名前は使用できません、sampler は StableDiffusionPipeline.__init__() に対して定義されたキーワードではないからです。

拡散パイプラインのためにスケジューラ・コンポーネントだけがカスタマイズできるわけではありません ; 理論的には、パイプラインのすべてのコンポーネントがカスタマイズ可能です。けれども実際には、パイプラインが想定するものと 互換な 代替を持つコンポーネントに切り替えることだけが意味がある場合が多いです。多くのスケジューラクラスは ここ で見られるように互いに互換です。これは、”unet” のような他のコンポーネントに対しては常には当てはまりません。

カスタマイズ可能な一つの特殊なケースは stable diffusion の “safety_checker” です。セーフティチェッカーが上手く機能しないと確信するならば、単純に None を渡してそれを無効にできます :

from diffusers import DiffusionPipeline, EulerDiscreteScheduler, DPMSolverMultistepScheduler

stable_diffusion = DiffusionPipeline.from_pretrained(repo_id, safety_checker=None)

別の一般的なユースケースは複数のパイプラインで同じコンポーネントを再利用することです、例えば “runwayml/stable-diffusion-v1-5” の重みと設定は StableDiffusionPipelineStableDiffusionImg2ImgPipeline の両方に対して利用できて、正確に同じ重みを RAM 内で二度使用することを望まないかもしれません。この場合、すべての入力インスタンスをカスタマイズして重みを一度だけ RAM にロードするのに役立つでしょう :

from diffusers import StableDiffusionPipeline, StableDiffusionImg2ImgPipeline

model_id = "runwayml/stable-diffusion-v1-5"
stable_diffusion_txt2img = StableDiffusionPipeline.from_pretrained(model_id)

components = stable_diffusion_txt2img.components

# weights are not reloaded into RAM
stable_diffusion_img2img = StableDiffusionImg2ImgPipeline(**components)

Note how the above code snippet makes use of DiffusionPipeline.components.

 

ロードはどのように動作するのでしょう?

クラスメソッドとして、DiffusionPipeline.from_pretrained() は 2 つのことを担います :

  • diffusers で repo_id を実行するために必要なフォルダ構造の最新版をダウンロードしてそれらをキャッシュします。最新フォルダ構造がローカルキャッシュで利用可能であれば、DiffusionPipeline.from_pretrained() はキャッシュを単純に再利用して、ファイルを 再ダウンロードしません

  • キャッシュされた重みを正しいパイプラインクラス – 公式にサポートされたパイプラインクラス の一つ – にロードしてそしてクラスのインスタンスを返します。それによって正しいパイプラインクラスは model_index.json ファイルから取得されます。

拡散パイプラインの基礎的なフォルダ構造は対応するクラスインスタンスに 1対1 対応します、例えば CompVis/ldm-text2im-large-256 に対して LDMTextToImagePipeline です。例を見ればこれがより良く理解できます。定義したばかりのパイプラインクラス・インスタンスをプリントアウトしましょう :

from diffusers import DiffusionPipeline

repo_id = "CompVis/ldm-text2im-large-256"
ldm = DiffusionPipeline.from_pretrained(repo_id)
print(ldm)

Output:

LDMTextToImagePipeline {
  "bert": [
    "latent_diffusion",
    "LDMBertModel"
  ],
  "scheduler": [
    "diffusers",
    "DDIMScheduler"
  ],
  "tokenizer": [
    "transformers",
    "BertTokenizer"
  ],
  "unet": [
    "diffusers",
    "UNet2DConditionModel"
  ],
  "vqvae": [
    "diffusers",
    "AutoencoderKL"
  ]
}

まず、公式パイプラインが LDMTextToImagePipeline であることを見て、2 番目に LDMTextToImagePipeline が 5 つのコンポーネントから構成されることがわかります :

ここでパイプライン・インスタンスをモデルレポジトリ CompVis/ldm-text2im-large-256 のフォルダ構造と比較してみましょう。ハブの CompVis/ldm-text2im-large-256 のフォルダ構造を見ると、それが上記の LDMTextToImagePipeline のプリントアウトされたインスタンスと 1対1 で一致していることがわかります :

.
├── bert
│   ├── config.json
│   └── pytorch_model.bin
├── model_index.json
├── scheduler
│   └── scheduler_config.json
├── tokenizer
│   ├── special_tokens_map.json
│   ├── tokenizer_config.json
│   └── vocab.txt
├── unet
│   ├── config.json
│   └── diffusion_pytorch_model.bin
└── vqvae
    ├── config.json
    └── diffusion_pytorch_model.bin

ご覧のように、LDMTextToImagePipeline のインスタンスの各属性は、クラス属性 (“bert”, “scheduler”, “tokenizer”, “unet”, “vqvae”) のように 正確な 名前のサブフォルダで定義されたその設定とたぶん重みを持ちます。重要なことは、すべてのパイプラインは DiffusionPipeline に以下の両方を知らせる model_index.json ファイルを想定していることです :

  • どのパイプラインクラスをロードすべきか、そして
  • どのライブラリから何のサブクラスがどのサブフォルダにストアされているか

従って CompVis/ldm-text2im-large-256 の場合は model_index.json は以下のように定義されます :

{
  "_class_name": "LDMTextToImagePipeline",
  "_diffusers_version": "0.0.4",
  "bert": [
    "latent_diffusion",
    "LDMBertModel"
  ],
  "scheduler": [
    "diffusers",
    "DDIMScheduler"
  ],
  "tokenizer": [
    "transformers",
    "BertTokenizer"
  ],
  "unet": [
    "diffusers",
    "UNet2DConditionModel"
  ],
  "vqvae": [
    "diffusers",
    "AutoencoderKL"
  ]
}
  • _class_name は DiffusionPipeline にどのパイプラインクラスをロードするべきか知らせます。
  • _diffusers_version はこのモデルがどの diffusers バージョンで作成されたかを知るために有用であり得ます。
  • そしてパイプラインのすべてのコンポーネントは以下の形式で定義されます :
"name" : [
  "library",
  "class"
]
  • “name” フィールドは、設定と重みがストアされるサブフォルダの名前とパイプラインクラスの属性名の両方に対応します (ここここ で見られるように)。
  • “library” フィールドはライブラリ名に対応します、例えば diffusers や transformers でそこから “class” をロードするべきです。
  • “class” フィールドはクラス名に対応します、例えば BertTokenizerUNet2DConditionModel です。

 

モデルのロード

src/diffusers/models 下で定義されたモデルは ModelMixin.from_pretrained() 関数でロードできます。この API は DiffusionPipeline.from_pretrained() に非常に類似していて同じように動作します :

  • diffusers でモデル重みと設定の最新版をダウンロードしてそれらをキャッシュします。最新ファイルがローカルキャッシュで利用可能である場合、ModelMixin.from_pretrained() は単純にキャッシュを再利用してファイルを再ダウンロードはしません。

  • キャッシュされた重みを定義されたモデルクラス – 既存のモデルクラス の一つ – をロードしてクラスのインスタンスを返します。

DiffusionPipeline.from_pretrained() とは対照的に、モデルはフォルダ構造を通常は必要としない少ないファイル、diffusion_pytorch_model.bin と config.json ファイルだけに依存します。

Let’s look at an example:

from diffusers import UNet2DConditionModel

repo_id = "CompVis/ldm-text2im-large-256"
model = UNet2DConditionModel.from_pretrained(repo_id, subfolder="unet")

ModelMixin.from_pretrained() にモデル重みが レポジトリのサブフォルダ に位置していることを知らせるために subfolder=”unet” 引数を定義しなければならない方法に注意してください。

Loading customized pipelines で説明したように、DiffusionPipeline.from_pretrained() を通してロードされたモデルを拡散パイプラインに渡すことができます :

from diffusers import DiffusionPipeline

repo_id = "CompVis/ldm-text2im-large-256"
ldm = DiffusionPipeline.from_pretrained(repo_id, unet=model)

モデルファイルがルートレベルで直接見つけられる場合、それは通常は google/ddpm-cifar10-32 のような幾つかの非常に単純な拡散モデルに対してのみ当てはまりますが、subfolder 引数を渡す必要はありません :

from diffusers import UNet2DModel

repo_id = "google/ddpm-cifar10-32"
model = UNet2DModel.from_pretrained(repo_id)

 

スケジューラのロード

スケジューラは SchedulerMixin.from_pretrained() に依存します。スケジューラは パラメータ化も訓練もされません が、代わりに設定ファイルにより純粋に定義されます。一貫性のため、モデルやパイプラインに対して使用したのと同じメソッド名を使用しますが、この場合には重みはロードされません。

パイプラインやモデルとは対照的に、スケジューラのロードは大量のメモリを消費しませんし、様々な異なるスケジューラに対して同じ設定ファィルが利用できることが多いです。例えば、all of :

are compatible with StableDiffusionPipeline and therefore the same scheduler configuration file can be loaded in any of those classes:

from diffusers import StableDiffusionPipeline
from diffusers import (
    DDPMScheduler,
    DDIMScheduler,
    PNDMScheduler,
    LMSDiscreteScheduler,
    EulerDiscreteScheduler,
    EulerAncestralDiscreteScheduler,
    DPMSolverMultistepScheduler,
)

repo_id = "runwayml/stable-diffusion-v1-5"

ddpm = DDPMScheduler.from_pretrained(repo_id, subfolder="scheduler")
ddim = DDIMScheduler.from_pretrained(repo_id, subfolder="scheduler")
pndm = PNDMScheduler.from_pretrained(repo_id, subfolder="scheduler")
lms = LMSDiscreteScheduler.from_pretrained(repo_id, subfolder="scheduler")
euler_anc = EulerAncestralDiscreteScheduler.from_pretrained(repo_id, subfolder="scheduler")
euler = EulerDiscreteScheduler.from_pretrained(repo_id, subfolder="scheduler")
dpm = DPMSolverMultistepScheduler.from_pretrained(repo_id, subfolder="scheduler")

# replace `dpm` with any of `ddpm`, `ddim`, `pndm`, `lms`, `euler`, `euler_anc`
pipeline = StableDiffusionPipeline.from_pretrained(repo_id, scheduler=dpm)

 

以上