ファインチューニングした自前のStableDiffusionを公開するWebアプリを作ってみた

自前の画像生成AI(ファインチューニングした自前のStableDiffusionなど)を公開できる簡単なWebアプリを作ってみました。
ここでは、今流行りのStable Diffusion をGoogle Colaboratoryでバックエンドで動かし、Webサイトで画像生成の結果を表示できるようにしました。

今回作ったwebアプリはこちら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/219d7b42-bf2f-2233-65cf-36cc83cb4aae.png)

https://tex2img-colab-atworks.web.app/

Your Colab URI　にSDを実行するサーバのエンドポイントを入力して、生成したい画像のテキストを入力、そしてGenerateで生成できます。

なお、画像生成AIを実行できるサーバー(バックエンド)があれば、Colabratoryでなくても、誰でもこのWebアプリを利用可能となっています。

この記事の流れ
・Stable Diffutionのファインチューニングで自前の画像生成AIモデル作成
・作成したモデルをWebAPIで呼び出せるようにFlaskサーバ(バックエンド)を立てる
・サーバのエンドポイントを今回作成したwebアプリに入力して、画像生成をする

なお、今回作成したnotebookとwebアプリはgithubにて公開しています。

今回使うnotebookのリポジトリ↓

https://github.com/ATworks-np/text2img-colab

ダウンロー&解凍後、SDworkspeceをGoogleDriveのマイドライブ直下にアップロードしてください。

webアプリのソースコードのリポジトリ↓

# Stable Diffutionのファインチューニング
まずはオリジナル(っぽい)のStable Diffutionを作るため、ファインチューニングをします。
このサイトをすんごく参考にしました。

https://note.com/npaka/n/n1af7249cf391

今回は石川県の伝統工芸の九谷焼をStable Diffutionに学習させました。
(なぜ九谷焼なのか。。。)

>九谷焼（くたにやき）は、石川県南部の金沢市、小松市、加賀市、能美市で生産される色絵の磁器。

https://ja.wikipedia.org/wiki/%E4%B9%9D%E8%B0%B7%E7%84%BC

### ファインチューニングのための画像収集

訓練データとなる九谷焼の写真は、Googleの画像検索でヒットしたものを11枚用意。
こんな感じの写真。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/9f5c53f2-9c7d-875c-3214-a60a44115eaa.png)

画像が準備できたら、集めた画像を先ほどGoogle DriveにアップロードしたSDworkspaceの中のimagesにアップロードします。画像の名前は重複していなければなんでも良いです。
なお、imagesにはこの九谷焼の画像がサンプルで入っていますので削除してください。

```
My Drive
└─.SDworkspace
　　　　└─.images
     ├─ image1.png
     ├─ image2.png
     .
     .
     .
```

### 画像の解像度を512×512にする
:::note
先ほどGoogle Drive にアップロードしたsd_fintuningを開いてください。
:::
はじめに、次のコードを実行しGoogle Driveにマウント。

:::note alert
実行する際、ランタイムタイプは必ずGPUを選択すること
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/aa3e0978-2993-5218-ccac-da52b900c927.png)



そして、次のコードを実行。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/941b47c4-fdad-e8a1-aabd-4ea74afb88c1.png)


すると、SDworkspace/my_conceptに解像度が512×512になった画像たちが保存されます。



これで準備完了。

### ファインチューニング
今回使うフレームワークをインストールする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/0c484502-375d-e20c-845e-d3f3d68f2b86.png)

そしたら、このフレームワークを使うためのアクセストークンをHugging Faceで作成する。
作成したら次のコードを実行して、アクセストークンを入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/255ee7a0-abf4-4a82-b230-d71a60129fcc.png)

こんな感じのが出れば成功。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/e7cbfef6-e4de-c2a4-cd1e-8fb285f83789.png)

次に、必要なモジュールをimportする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/d8212b64-628b-0d66-7fcd-93897ad19a0e.png)


そして、モデルを指定する次のコードを実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/1d3000a7-e519-cff8-d5c9-1aed28cc291c.png)


次のコードのパラメータを自分がファインチューニングする対象に合わせて変更します。

:::note warn
「what_to_teach」「placeholder_token」「initializer_token」この三つを各自のファインチューニングの対象に合わせて変更します
what_to_teach : 学習内容
placeholder_token : 新しい概念を表す単語
initializer_token : 新しい概念が何であるかを要約する単語
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/d8691a28-4c95-c645-b5aa-1e630101197c.png)

このコードも実行。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/7231fb61-c46e-1a8c-fed5-60838c997ccf.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/01053b92-002a-68aa-01d6-c141db792a6d.png)



あとは次のコードを実行してしばらくするとファインチューニングが開始。
自分のランタイム環境(無料バージョン)だと3-4時間くらいで終わりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/b4c6f9e0-f81d-b709-cc66-3e99b6419d05.png)


これで、自前のオリジナルなStable Diffutionを準備完了です。
次の章では、このオリジナルなSDを実行するAPIサーバをColaboratoryで立てます。

今開いているコラボラトリーは閉じます。

# 自前のオリジナルSDを実行するサーバーを立てる(バックエンド側)
::: note
次にGoogle Drive にアップロードしたtext2image-colabAPIを開いてください。
:::
今回はFlaskでサーバを立てて、json形式で推論結果を返すWebAPIを作りました。
さっきとは違うColabratoryを作成します。

### Stable Diffutionを実行する準備
さっきと同じように、GoogleDriveにマウント→アクセストークン入力→ライブラリインポート→推論モデル準備が必要になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/2e4da9ec-d6d8-9d29-67e2-63609ae1a91b.png)


これでStable Diffutionを実行する準備完了。あとは、Flaskでサーバを立てるだけ。

### Flaskでサーバを立てる
Stable Diffutionを実行する準備をしたColabolatryの続きで、Flaskでサーバを立てます。
今回はngrokでサーバを公開します。

必要なパッケージをインストール。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/8ce2d5f5-6e95-b325-0825-216ec0f6e1d5.png)

インストールできたら、次を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/25d2d573-c88e-7ab6-7a03-899593c2498c.png)


実行すると以下のようにURLが出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/a205ad0b-c2da-9d0d-a6a0-8b40a5c4ce4b.png)
これが今回使うバックエンドのエンドポイントとなります。
今回は https://565b-35-223-175-181.ngrok.io がエンドポイントとなります。

:::note warn
このエンドポイントは実行するたびに変わります。
:::

あとは、Flaskでサーバを立ち上るだけでバックエンド側は完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/95d8bc80-9285-e24b-6383-46aec61f4fa7.png)

このバックエンド側の処理は、GETでパラメータpromptを受け取り、promptを入力としてStable Diffutionで画像を生成、その後base64でエンコードしてjson形式でレスポンスを返します。
こんな感じのjsonで、生成した4枚の画像を返します。

```
{images:
  [base64/image, 
   base64/image,
   base64/image,
   base64/image]
}
```

最後に、先ほどのエンドポイントを今回私が作成したWebアプリのURIに貼り付けて、生成したい画像のテキストを入力しGenerateボタンを押すだけで、生成できます。

Generateボタンを押した後、Colabolatryに以下のようなプログレスバーが出力されていれば正常に実行されています。

数分後にwebアプリの方に結果の画像が表示されます。

試しに次のテキストで生成してみた結果こんな画像が生成されました。
```
a plate drawn birds, <kutani-ware>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/a2e7c9c4-e92c-0008-ed73-35983aad7448.png)

九谷焼でファインチューニングされていない本家のSDで同じものを生成してみた結果がこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513482/d7658232-e3f4-280a-bdf9-2e482c36aecf.png)

比較すると、今回ファインチューニングしたSDの方が鮮やかな色彩で優雅な紋章(wikipedia丸コピ)が目立つようなお皿を生成してるのがわかります。

### オリジナルSDをこのサイトで公開する
公開する際には、このエンドポイントをURLのパラメータに入れておけば、自動でURIが入力された状態になります。
今回の場合、https://565b-35-223-175-181.ngrok.io がエンドポイントでしたので、https://tex2img-colab-atworks.web.app の後に/?uri=https://565b-35-223-175-181.ngrok.io を加えてURLを公開します。つまり次のようになります。

```
https://tex2img-colab-atworks.web.app/?uri=https://565b-35-223-175-181.ngrok.io
```

# Colaboratoryのセッション時間
COlaboratoryは無料版で90分しかセッションを維持できないので、今回のFlaskのAPIサーバは90分間しか公開できないです。。(セッションを維持できれば最大で12時間)
常時稼働したい場合は、自分でGPUのサーバを立てたり、GCPなりでGPUが使えるサーバを用意しましょう。