Stable Diffusionの追加学習で「いりこ」の画像を生成する

## モチベーション

　特に異常検知などの分野においては、異常を示す本物の画像を容易に多数用意できないことがあると聞きます。そうでなくても、数万単位で似た画像が必要になった場合、手動で集めるのは困難です。そこで、StableDiffusionの画像生成機能をデータ水増しに使えないかと考えました。

　ただし、StableDiffusionは強力な画像生成機能を持ってはいますが、任意の画像を生成しようとすると必ずしも思うような出力は得られません。

　例えば、「いりこ」の画像を作ろうと頑張ってみましたが、うまく生成できませんでした。

![grid-0022.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315871/ba9076c1-987c-9562-3308-cc6f4e8a3eea.jpeg)

　StableDiffusionが学習している大規模データには大型の魚が多く含まれているためでしょう。生成結果はそちらに引っ張られてしまうようです。そこで、追加学習をすることで求める画像に近い画像を生成できるようにすればよいのではないかと考えました。

　StableDiffusionの追加学習は、現在、人物の写真やイラストに対して盛んにおこなわれていますが、そうでないものに対してはどのくらいうまく働くか試してみたかったというのもモチベーションの一つです。

## この記事で書かないこと

　本記事では、詳細な手順については記述しません。主に大まかな手順と結果を中心に書きます。細かい手順などは参考文献をご参照ください。

## なぜ「いりこ」なのか

　手元にあるものの中で、ある程度の数を確保できて、サイズが手ごろで撮影しやすく、この手順に適していたのが「いりこ」だったからです。撮影が終わったら出汁になってもらいます。

## 追加学習の方法: LoRA(Low-Rank Adaptation)

　Stable Diffusionの追加学習方法にはいくつあるようですが、今回はLow-Rank Adaptation(以下LoRA)という方法を試してみます。LoRAは比較的簡単に実施でき、強力な追加学習ができるソリューションのようです(私はまだLoRAしか試してないので、他の方法に比べてどれくらい簡単なのかはわかりませんが)。

　参考文献にあるレポジトリと説明動画を参考に、自分のローカルコンピュータでLoRAによる追加学習を実施しました(動画は現行バージョンのUIとは細かい点で違いがありますが、概ね同じでした。2023年3月時点)。

大まかな手順は以下の通りです。

1. 画像を撮影して準備する。(20枚前後必要とのこと、今回は23枚用意した)
2. Kohya's GUIで画像にテキストのキャプションをつけ、テキストを補正する。
3. Kohya's GUIで学習パラメータを設定し、学習を行い、safetensorsファイルを出力する。
4. StableDiffusionWebUIでsafetensorsファイルを読み込んで画像生成

## 追加学習用の画像

　手順1では、白い紙の上にいりこを1つおいてスマホで撮影しました。ハイエンドのスマホではないのでカメラ性能はあまり期待できませんが、一応マクロ撮影機能はついていました。以下のような画像を23枚取得して追加学習用の画像とします。

<img width="350" alt="IMG20230314202618.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315871/9286f663-82d4-8af7-5e33-7289e7c86c90.jpeg">
<img width="350" alt="IMG20230314202608.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315871/b27bc5c0-6781-8e07-104c-58f3acbb1816.jpeg">

**ごちらは実写の写真です** 

　LoRA学習では正則化画像というオプションを利用できます。詳しくは参考文献を参照してほしいのですが、

> “正則化画像とは、前述のclass全体が、学習対象に引っ張られることを防ぐための画像”

だということです。

　だたし、今回は追加学習の目的は撮影した画像と同じような画像をたくさん生成したいだけなので、正則化画像を追加するオプションは使いませんでした。StableDiffusionで通常生成される画像の中に追加学習の内容を違和感なく混ぜたい場合は正則化画像オプションを使ったほうがよいかもしれません。また、追加学習する画像の背景には、白い背景だけではなく、もっとバリエーションを持たせる方がいいでしょう。

## 学習と結果の適用

　参考文献によると学習を行うには8GB以上のVRAMが必要だということです。私の手持ちのグラフィックボードもちょうどGeforce GTX1080 (VRAM 8GB)だったのでギリギリ可能でした。ただ、説明動画のままの設定で実行するとメモリ不足で途中で学習がエラー終了してしまいました。GUI上で学習設定の`Advanced Configuration`の中にある`Memory efficient attention`の設定をONにして学習するとエラーがおこらず学習できました。また、動画内では`Mixed precision`, `Save precision`に`bf16`を選択していましたが、これらを`fp16`に変更しました。

　Epochを2回分まで回したところで出力されたsafetensorsファイルを用いて、試しにStableDiffusionWebUIで画像生成してみたところ、それなりの結果が出てきました。以下がその画像の例です。プロンプト,ネガティブプロンプト,シード値,サンプラー,ステップ数など追加学習結果を使う以外の条件はすべて最初に例示した画像と同じです。いりこ一匹の画像1枚のピクセルサイズ512x512の画像4枚を1分足らずで生成できました。

![grid-0021.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315871/e10a5fde-b4df-af44-45be-4ff8c1142dc4.jpeg)
**ごちらは生成画像です** 

## 感想

　23枚という少ない追加画像数で学習時間もそれほどかかっていない(2epochで2時間くらいだった)のに、これくらいの性能がでるのは驚きました。

　撮影時に影が入ってしまっているのが生成画像にもずっと影響しています。もし業務応用を考えるとしたら学習に用いる画像は撮影環境をしっかり整備するか、画像編集して理想的な状態に近づけておくべきだと思います(参考文献の動画の中でもこの部分はかなり手間を掛けてしっかり準備していました。)。

　このように、人以外のものに対してもLoRAによる追加学習は簡単に適用でき、一定の効果があることがわかりました。この方法で生成できた画像が使えるかどうかは、そのプロジェクトでどの程度精度の良い画像が必要かによるところ大きいと思います。左下の画像のように、目が変なところに複数ついていたり、ここでは出していませんが、しっぽが二つあったりするいりこの画像もある程度の確率で出てきます(違和感のある画像は体感で3割くらいありました)。そのため、生成画像の精査も必要になってくるでしょう。

　結局画像の選別に手間がかかるという点はあるので、簡単に用意できる画像なら普通に集めたほうがよいかもしれません。しかし、「なかなか数が手に入らない画像にバリエーションを持たせたい」という場合は、この手法が役に立つかもしれません。

## 参考文献
- LoRA学習用のレポジトリと解説、下記のGUIの元になっている。LoRAについての日本語の解説もあり。

[https://github.com/kohya-ss/sd-scripts](https://github.com/kohya-ss/sd-scripts)

[https://github.com/kohya-ss/sd-scripts/blob/main/train_README-ja.md](https://github.com/kohya-ss/sd-scripts/blob/main/train_README-ja.md)

- LoRA学習用のGUI

[https://github.com/bmaltais/kohya_ss](https://github.com/bmaltais/kohya_ss)

- Kohya’s GUIの使用方法の紹介動画(英語)

[https://www.youtube.com/watch?v=N4_-fB62Hwk](https://www.youtube.com/watch?v=N4_-fB62Hwk)

[https://www.youtube.com/watch?v=k5imq01uvUY](https://www.youtube.com/watch?v=k5imq01uvUY)