
Google Colab ではじめる LoRA
Google Colab で LoRA を試したのでまとめました。
1. LoRA
「LoRA」(Low-rank Adaptation)は、数枚の被写体画像と対応するテキストを元にファインチューニングを行うことで、Text-to-Imageモデルに新たな被写体を学習させる手法です。
特徴は、次のとおりです。
・Dreamboothより高速
・VRAM 8GBでも動作
・学習データだけ抽出して他モデルとマージできる
・学習結果のサイズが小さい (Unet のみで3MB、Unet+Clipで6MB)
・UnetとCLIPの両方をファインチューニング可能。
2. ファインチューニングの実行
Colabでのファインチューニングの実行手順は、次のとおりです。
(1) メニュー「編集→ノートブックの設定」で、「ハードウェアアクセラレータ」に「GPU」を選択。
(2) Googleドライブのマウント。
# Googleドライブのマウント
from google.colab import drive
drive.mount('/content/drive')
(3) 作業フォルダへの移動。
# 作業フォルダへの移動
import os
os.makedirs("/content/drive/My Drive/work", exist_ok=True)
%cd '/content/drive/My Drive/work'
(4) パッケージのインストール。
# パッケージのインストール
!git clone https://github.com/cloneofsimo/lora.git
!pip install accelerate bitsandbytes
%cd lora
!pip install .
# 入出力フォルダの準備
os.makedirs("./instance_data", exist_ok=True)
os.makedirs("./output", exist_ok=True)
(5) 学習画像を「instance_data」フォルダに配置。
以下の5枚の画像をGoogleドライブ経由で配置しました。

おすすめの「学習画像」の条件は、次のとおりです。
・学習画像の枚数は 10〜120枚 程度
・単体オブジェクトが写ってるもの
・多様なポーズ
・多様な背景
必要な画像枚数は、モデルにどの程度の柔軟さを求めるかによります。似たポーズ、似た背景が多いと、それも学習してしまいます。
(6) ファインチューニングの実行。
インスタンスプロンプト「sks」、学習画像フォルダ「instance_data」で学習します。
# ファインチューニングの実行
!accelerate launch \
--num_processes=1 \
--num_machines=1 \
--mixed_precision="fp16" \
--dynamo_backend="no" \
train_lora_dreambooth.py \
--pretrained_model_name_or_path="stabilityai/stable-diffusion-2-1-base" \
--instance_prompt="sks" \
--instance_data_dir="./instance_data" \
--output_dir="./output" \
--resolution=512 \
--train_batch_size=1 \
--color_jitter \
--learning_rate=1e-4 \
--learning_rate_text=5e-5 \
--lr_scheduler="constant" \
--lr_warmup_steps=0 \
--gradient_accumulation_steps=1 \
--max_train_steps=3000 \
--train_text_encoder
これで、「sks」というプロンプトに「特定の猫」が学習されます。学習結果は、「output」フォルダに出力されます。
・lora_weight.pt : Untの重み
・lora_weight.text_encoder.pt : テキストエンコーダーの重み
3. 推論の実行
Colabでの推論の実行手順は、次のとおりです。
(1) パイプラインの準備。
import torch
from lora_diffusion import monkeypatch_lora, tune_lora_scale
from diffusers import StableDiffusionPipeline, EulerDiscreteScheduler
# パイプラインの準備
model_id = "stabilityai/stable-diffusion-2-1-base"
pipe = StableDiffusionPipeline.from_pretrained(
model_id,
scheduler=EulerDiscreteScheduler.from_pretrained(
model_id,
subfolder="scheduler"
),
torch_dtype=torch.float16
).to("cuda")
# LoRAの重みの適用
monkeypatch_lora(pipe.unet,
torch.load(os.path.join("./output", "lora_weight.pt")))
monkeypatch_lora(pipe.text_encoder,
torch.load(os.path.join("./output", "lora_weight.text_encoder.pt")),
target_replace_module=["CLIPAttention"])
モデルにLoRAの重みを適用するには、monkeypatch_lora()を使います。
(2) 推論の実行。
モデルのLoRAの重みを調整するには、tune_lora_scale()を使います。
初期状態では月がでなかったので、0.8で学習結果を弱めています。
# LoRAの重みの調整
tune_lora_scale(pipe.unet, 0.8)
tune_lora_scale(pipe.text_encoder, 0.8)
# 推論の実行
image = pipe(
"sks on the moon",
num_inference_steps=50,
guidance_scale=7
).images[0]
image

4. 正則化画像を利用したファインチューニングの実行
正則化画像を利用したファインチューニングの実行手順は、次のとおりです。
(1) 正則化画像を用意し、class_dataフォルダに配置。
今回は、500枚用意しました。

「sks cat」というインスタンスプロンプトで「sks」に「特定の猫の柄」を学習させます。正則化画像がないと、「sks」と「cat」の両方の単語の概念が変更されるため、「sks」にどのような内容が学習されるかわかりません。そこで、「cat」という単語を正則化画像で学習させることで、結果として「sks」 に「cat」以外の概念を学習させることができます。
おすすめの「正則化画像」の条件は、次のとおりです。
・正則化画像の枚数は 学習画像枚数x100 程度
(2) ファインチューニングの実行。
クラスプロンプト「cat」、正則化画像フォルダ「class_data」で学習します。
!accelerate launch \
--num_processes=1 \
--num_machines=1 \
--mixed_precision="fp16" \
--dynamo_backend="no" \
train_lora_dreambooth.py \
--pretrained_model_name_or_path="stabilityai/stable-diffusion-2-1-base" \
--instance_prompt="sks cat" \
--instance_data_dir="./instance_data" \
--class_prompt="cat" \
--class_data_dir="./class_data" \
--num_class_images=500 \
--output_dir="./output" \
--resolution=512 \
--train_batch_size=1 \
--with_prior_preservation \
--learning_rate=1e-4 \
--learning_rate_text=5e-5 \
--lr_scheduler="constant" \
--lr_warmup_steps=0 \
--train_text_encoder \
--gradient_accumulation_steps=1 \
--max_train_steps=3000 \
--train_text_encoder
これで、「sks cat」というプロンプトに「特定の猫」、「sks」に「特定の猫の柄」が学習されます。
(3) 推論の実行。
「sks cat」で「特定の猫」、「sks dog」で「同じ柄の犬」、「sks rabbit」で「同じ柄のうさぎ」が生成されることを確認します。

見たところ「sks」に部屋の背景も学習されてしまってるようなので、多様な背景の学習画像を用意した方が良さそうです。
【おまけ】 train_lora_dreambooth.py のパラメータ
「train_lora_dreambooth.py」のパラメータは、次のとおりです。
◎ 基本
-h, --help : ヘルプ
--pretrained_model_name_or_path <学習済みモデル名>
--pretrained_vae_name_or_path <学習済みVAE名>
--revision <学習済みモデルのリビジョン>
--tokenizer_name <トークナイザー名>
--resolution <入力画像の解像度>
--train_batch_size <学習バッチサイズ>
--max_train_steps <学習ステップ数>
◎ 入出力
--instance_prompt <インスタンスプロンプト>
--instance_data_dir <学習画像フォルダのパス>
--class_prompt <クラスプロンプト>
--class_data_dir <正則化画像フォルダのパス>
--num_class_images <正則化画像数>
--output_dir <出力フォルダのパス>
◎ 学習率
--learning_rate <学習率>
--learning_rate_text <テキストエンコーダーの学習率>
--lr_scheduler {"linear", "cosine", "cosine_with_restarts", "polynomial", "constant", "constant_with_warmup"}
--lr_warmup_steps <ウォームアップステップ数>
--scale_lr : 学習率をスケーリングするか
◎ テキストエンコーダーの学習
--train_text_encoder : テキストエンコーダーを学習するか
◎ 学習再開
--resume_unet <Unetの重みのパス>
--resume_text_encoder <テキストエンコーダーの重みのパス>
◎ その他
--with_prior_preservation : prior preservation loss
の追加
--prior_loss_weight <PRIOR_LOSS_WEIGHT> : prior preservation loss
の重み
--seed <乱数シード>
--center_crop : センタークロップするか
--color_jitter : カラージッターを適用するか
--sample_batch_size <サンプルバッチサイズ>
--num_train_epochs <学習エポック数>
--save_steps <保存ステップ数>
--gradient_accumulation_steps <勾配累積ステップ数>
--gradient_checkpointing : メモリ節約のため勾配チェックポイントを使用するか
--lora_rank <LoRAランク>
--use_8bit_adam : 8-bit Adamを使用するか
--adam_beta1 <ADAM_BETA1> : Adamのパラメータ
--adam_beta2 <ADAM_BETA2> : Adamのパラメータ
--adam_weight_decay <ADAM_WEIGHT_DECAY> : Adamのパラメータ
--adam_epsilon <ADAM_EPSILON> : Adamのパラメータ
--max_grad_norm <最大勾配ノルム>
--push_to_hub : モデルをHuggingface HubにPushするか
--hub_token <HUB_TOKEN>
--logging_dir <TensorBoardのログフォルダ>
--mixed_precision {no,fp16,bf16} : 混合精度を使用するか
--local_rank <LOCAL_RANK> : 分散学習のローカルランク
【おまけ】 正則化画像の生成スクリプト
正則化画像の生成スクリプトの例は、次のとおりです。
# Googleドライブのマウント
from google.colab import drive
drive.mount('/content/drive')
# 作業フォルダへの移動
import os
os.makedirs("/content/drive/My Drive/work", exist_ok=True)
%cd '/content/drive/My Drive/work'
# パッケージのインストール
!pip install diffusers[torch] transformers safetensors accelerate
from diffusers import StableDiffusionPipeline, EulerDiscreteScheduler
import torch
# パイプラインの準備
model_id = "stabilityai/stable-diffusion-2-1-base"
pipe = StableDiffusionPipeline.from_pretrained(
model_id,
scheduler=EulerDiscreteScheduler.from_pretrained(
model_id,
subfolder="scheduler"
),
torch_dtype=torch.float16
).to("cuda")
# パラメータ
class_prompt = "cat, photo, full body shot"
num_class_images = 500
# 出力フォルダの準備
os.makedirs("./output", exist_ok=True)
# 推論の実行
num = 0
while num < num_class_images:
images = pipe(class_prompt,
width=512,
height=512,
num_inference_steps=20,
guidance_scale=7.5,
num_images_per_prompt=10).images
for image in images:
image.save("./output/image" + str(num) + ".png")
num += 1
気軽にクリエイターの支援と、記事のオススメができます!