Livebook から Hugging Face に 画像生成アプリ（GPU対応）をデプロイする

## はじめに

Livebook Launch Week 1 - Day 4 では、 Jose が音声チャットのアプリを [Hugging Face](https://huggingface.co/) にデプロイしていました

https://news.livebook.dev/build-and-deploy-a-whisper-chat-app-to-hugging-face-in-15-minutes---launch-week-1---day-4-wYM0w

私は画像 AI 大好きなので、 Stable Diffusion による画像生成アプリを Hugging Face にデプロイしてみます

ただし、 Stable Diffusion は非常に重いモデルであるため、 CPU 環境で動かすと、1回の画像生成に10分以上かかってしまいます
（[以前、ローカルの CPU で実行した例はこちら](https://qiita.com/RyoWakabayashi/items/8070f6bc372015be6a8d)）

そこで、今回は Hugging Face にお金を払って GPU を使います

実装したノートブックはこちら

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/bumblebee/gpu/image_generation.livemd

## Hugging Face とは

トップページには **"The AI community building the future."** （未来を築く AI コミュニティー）と書かれています

![スクリーンショット 2023-05-23 10.53.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9d463474-3e3c-0096-ce23-88681a1b3599.png)

つまり、 AI エンジニアのためのコミュニティーサイト、というわけなのですが、その機能が非常に強力です

GitHub のようにコードや学習済モデルの管理・共有ができる上に、 Fly.io のようにアプリケーションのプラットフォームとして使うこともできます

まだアカウントを作っていない人は、トップページ右上の "Sign Up" から、まずは無料でアカウントを作りましょう

## 支払い設定

Hugging Face でコンテナを動かす場合（スペース）の料金表はこちら

https://huggingface.co/pricing#spaces

CPU Basic であれば無料で使えますが、それ以外は有料です

今回は Nvidia T4 - small を使いたいので、まず Hugging Face の支払い設定をします

ログイン後のトップページ右上の自分のアイコンをクリックし、 "Settings" を開きます

![スクリーンショット 2023-05-23 9.10.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/2fc6411a-8c3e-8845-302f-f543096e43c8.png)

設定画面が開くので、左メニューから "Billing" を開きます

まだ支払い設定をしていない場合、支払い情報を更新するように求められます

![スクリーンショット 2023-05-23 9.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7cc6700c-ce9d-e385-cd89-366e0dd8f55f.png)

"Payment information" タブを開き、 "Add a payment method" ボタンをクリックしてください

![スクリーンショット 2023-05-23 9.04.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/bbe9bc46-ed27-aeb1-500d-efdb6f94bf09.png)
 
クレジットカードの情報を入力し終えると、支払い情報が以下のように表示されます

![スクリーンショット 2023-05-23 9.10.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/537d40e8-9afd-4e4e-2928-aa8f6bf0ca9f.png)

## スペースの作成

Livebook を動かすためのスペースを作ります

トップページ右上の自分のアイコンをクリックし、 "New Space" を開きます

![スクリーンショット 2023-05-22 17.37.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/caaaf5d0-4734-b076-9cbd-d4ac7b877817.png)

Space name に適当な名前（この例では "livebook"）を入れ、 Space SDK に "Docker"、 Docker template に "Livebook" を選択します

![スクリーンショット 2023-05-22 17.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/d510deda-6d76-8730-beb4-d69c77111a3d.png)

Space Hardware で "Nvidia T4・4vCPU・15GB・$0.60 per hour" を選択します
（遅くてもいいから無料がいい場合、 CPU の最小を選択してください）

![スクリーンショット 2023-05-23 9.11.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/b8eab200-8ee7-e9fc-02c9-f16a824df88a.png)

コンテナのビルドが開始され、数分待つと以下の画像のように一番下に Livebook の URL が表示されます

![スクリーンショット 2023-05-23 9.14.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/db52f602-49ba-2f76-5a2e-29f752f07d7e.png)

URL の末尾 `?token=` の後にあるトークンをコピーしておきます

:::note info
ここで "CUDA Version 11.8.0" の文言が確認できます
:::

ログパネルの右上 "x" をクリックし、ログを閉じます（後で開き直すこともできます）

![スクリーンショット 2023-05-23 11.11.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/e83d6c81-08ea-6844-56e6-d4422ec1f547.png)

左側に Livebook の認証画面が表示されるので、コピーしておいたトークンを入力し、 "Authenticate" ボタンをクリックします

![スクリーンショット 2023-05-22 17.42.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/6c6bc352-06a8-a124-3fd0-92d3c67153e0.png)

すると、見慣れた Livebook のホーム画面が表示されます

![スクリーンショット 2023-05-22 17.42.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7ce13493-5246-657d-4282-2a967ddea656.png)


## アプリケーションの実装

Livebook ホーム画面の右上 "+ New notebook" ボタンをクリックし、新しいノートブックを開きます


![スクリーンショット 2023-05-22 17.43.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/b635e0a1-0e88-bfbc-0a0a-32a0268015fd.png)

"Notebook dependencies and setup" と書かれているセットアップセルに以下のコードを入力し、実行（セルの左上 "Setup" をクリック）します

```elixir
Mix.install(
  [
    {:kino_bumblebee, "~> 0.3.0"},
    {:exla, "~> 0.5.1"}
  ],
  system_env: [
    {"XLA_TARGET", "cuda118"}
  ],
  config: [nx: [default_backend: EXLA.Backend]]
)
```

重要なのは `{"XLA_TARGET", "cuda118"}` の部分です

起動時のログに "CUDA Version 11.8.0" とあったので、 EXLA で使う CUDA のバージョン指定を `cuda118` にしています

この指定がないと、せっかく GPU のあるハードウェアを選択しているのに、 GPU を使ってくれません

セットアップセルが実行できたら、 "Section" と次の黒いセルの間、左右中央あたりにカーソルを持っていきます

すると、 "+ Smart" というボタンが表示されます

![スクリーンショット 2023-05-23 11.19.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/8e044d1e-b065-4ea4-84f6-4ad4f42fa420.png)

"+ Smart"　をクリックするとメニューが開くので、 "Neural Network task" をクリックします

![スクリーンショット 2023-05-22 17.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/70cf8eeb-bdff-921a-edf2-b3ee6dc0e735.png)

すると、以下の画像のように Huggin Face のアイコンがついたセルが追加されます

![スクリーンショット 2023-05-22 17.46.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/56ec631e-8348-d034-1213-8289b4fe0a21.png)

TASK に "Text-to-image" を選択すると、セルの内容が以下のように変化します

![スクリーンショット 2023-05-22 17.46.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/ae4b5a20-e41d-9cde-e131-947b113d96fa.png)

この状態でセルを実行（セルの左上 "Evalutate" をクリック）します

Stable Diffusion のモデルダウンロードが始まり、しばらくすると Text の入力と Run ボタンが表示されます

![スクリーンショット 2023-05-22 17.51.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/aaef9e27-31ce-15be-9ba8-16fee89c3bb7.png)

Text に "Elixir changes the world, high quality, detailed, digital art" を入力し、 Run ボタンをクリックしてみましょう

1分程待つと、以下のように画像が生成されます

![スクリーンショット 2023-05-23 9.30.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/89f2a44a-b24c-385d-0dbe-b40a8ac40829.png)

画像生成が上手く機能しました

では、これをアプリ用に少し修正します

スマートセルの右上、鉛筆のアイコンをクリックします

![スクリーンショット 2023-05-23 11.26.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/41f3b0f0-647b-2ebe-0ba5-d5cc7a52b18f.png)

確認メッセージが表示されますが、問題ないので "Convert" ボタンをクリックします

![スクリーンショット 2023-05-23 9.34.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/d99d655e-53b4-5ed0-7dbd-1d03308e85b0.png)

すると、スマートセルがコード（二つのセル）に変換されます

一つ目のセルのセルの最下行に以下のコードを追加します

```diff_elixir
...
serving =
  Bumblebee.Diffusion.StableDiffusion.text_to_image(clip, unet, vae, tokenizer, scheduler,
    num_steps: 20,
    num_images_per_prompt: 2,
    safety_checker: safety_checker,
    safety_checker_featurizer: featurizer,
    compile: [batch_size: 1, sequence_length: 50],
    defn_options: [compiler: EXLA]
  )
+
+Kino.start_child({Nx.Serving, serving: serving, name: ImageGeneration})
```

Livebook Launch Week 1 - Day 4 の動画では batch_size を 8 に変更していましたが、 Stable Diffusion はメモリを大量に使うため、 batch_size はそのままにしておきます（大きくするとメモリ不足のエラーが発生します）

二つ目のセルを以下のように変更します

アクセスしてきた人毎に、自分のリクエストに対するレスポンスだけを受け取るためです

```diff_elixir
...
-Kino.listen(form, fn %{data: %{text: text}} ->
+Kino.async_listen(form, fn %{data: %{text: text}, origin: origin} ->
  Kino.Frame.render(frame, Kino.Text.new("Running..."))
- output = Nx.Serving.run(serving, text)
+ output = Nx.Serving.batched_run(ImageGeneration, text)

  for result <- output.results do
    Kino.Image.new(result.image)
  end
  |> Kino.Layout.grid(columns: 2)
- |> then(&Kino.Frame.render(frame, &1))
+ |> then(&Kino.Frame.render(frame, &1, to: origin))
end)
...
```

この状態で改めてセルを実行し、 Run ボタンをクリックしてみましょう

同じ画像が生成されれば OK です

もしメモリエラーなどが出た場合、セットアップセルから再実行してみてください

## アプリのデプロイ

ノートブックの左メニューからロケットのアイコンをクリックし、アプリケーション設定を表示します

Slug に "image-generation" のような任意の文字を入れます（記号は "-" しか使えません）

![スクリーンショット 2023-05-23 11.41.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/3cab5bbf-6675-2caf-899c-c5c51e3558aa.png)

Deploy ボタンをクリックし、しばらくすると DEPLOYMENTS に表示されている Status が Running になります

:::note info
もしここで Status が Error になった場合、メモリ不足になっている可能性があるので、セットアップせるだけ再実行してメモリを解放し、改めて Deploy ボタンをクリックしてみてください
:::

![スクリーンショット 2023-05-23 9.45.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a583a337-707f-78bd-2089-aa2548d63aa2.png)

表示された URL の部分を新しいタブで開くと、 "https://rwakabay-livebook.hf.space/apps/image-generation" のような URL でアプリが開きます

![スクリーンショット 2023-05-23 9.45.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a16552c1-4d8e-897d-09b3-654833268f19.png)

こちらでも同じように画像生成を実行可能です

![スクリーンショット 2023-05-23 9.46.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/35b90bea-1691-bece-ff6e-728cb9b7deb7.png)

## 公開アプリへの追加

実装したノートブックを公開アプリとしてスペースに追加し、常に自動で起動するようにします

ノートブックの方に戻って、右上（ノートブックのタイトルの右側）にあるメニューを開きます

"Export" をクリックしてください

![スクリーンショット 2023-05-23 9.47.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/1bd4914a-b288-aff9-a50f-ea9c8e9c3fe6.png)

表示されるエクスポートモーダルで、ダウンロード用のアイコン（黒いセルの右上）をクリックしてください

.livemd ファイルがダウンロードされます

![スクリーンショット 2023-05-23 9.47.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/5527700d-d4f4-927f-4372-e3e2a57f1e44.png)

Hugging Face の画面で2段目右上 "Files" をクリックしてください

スペースに保管されているファイルの一覧が表示されます

![スクリーンショット 2023-05-23 9.47.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/80c638b1-c901-e0ab-25a6-8212001c279d.png)

ファイルの一覧から "public-apps" のディレクトリーを開いてください

"public-apps" 内の ".gitkeep" と "welcome.livemd" のファイルが表示されます

![スクリーンショット 2023-05-23 9.48.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/1ac5cf3f-ad13-cd86-cb11-d7c4a182fb1d.png)

ファイル一覧の右上 "+ Add file" をクリックしてください

![スクリーンショット 2023-05-23 9.48.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/77111bff-b6be-8026-0820-d17db68a6416.png)

アップロード画面が開くので、上部の "Drag files/folders..." と書いてあるエリアに、先ほどダウンロードしておいた .livemd ファイルをドラッグ&ドロップします

コミットメッセージは適当に入れておきます

左下 "Commit changes to main" をクリックしてください

![スクリーンショット 2023-05-23 9.49.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/50e22e4d-b9f0-6e10-c668-4b6496dbdcdb.png)

ノートブックの追加コミットを実行すると、自動的にコンテナのビルドが開始されます

![スクリーンショット 2023-05-23 9.49.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/c1448f19-77b2-3722-8dc3-862a96f72693.png)

またしばらく待つと Livebook の URL が表示されるので、トークンを使って認証し、ホーム画面に移動します

ホーム画面の左メニューから "Apps" を開くと、先ほど追加したノートブックがアプリとして実行されています
（最初開いた時点では Status が Booting ですが、しばらく待てば Running になります）

![スクリーンショット 2023-05-23 10.20.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/b97d01b3-9ba0-bc29-a476-54d5bd3f53f9.png)

改めて画像生成アプリの URL を開けば、先ほどと同じ画面が開き、同じように画像生成することができます

![スクリーンショット 2023-05-23 10.41.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/fff49238-b990-26d8-c0d0-dba0f380e099.png)

## スペースの削除

一通り画像生成を堪能したらスペースを削除しましょう

そのままだとコストがどんどん追加されます

Hugging Face の上から2段目のメニュー右端 "Settings" からスペースの設定画面を開きます

![スクリーンショット 2023-05-23 12.06.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/2e89174a-ce03-987e-6231-5b338488f6c3.png)

スペースの設定画面最下段にある "Dlete this space" でスペース名を入力し、 "I understand, delete this space" をクリックします

![スクリーンショット 2023-05-23 12.06.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/67d864b7-12c7-415d-7811-fc56e20d88cd.png)

これで削除完了です

## 実際にかかった費用

Billing のページで実際にかかった費用を確認できます

![スクリーンショット 2023-05-23 12.16.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/1d27283a-b77b-2f98-c871-74e50ca38ff4.png)

私の場合、２時間52分使って $1.72 でした

2023/05/23 のレート（138.50）で 238.22 円ですね

AWS の SageMaker とコストを比較してみましょう
（SageMaker は us-west-2 オレゴンリージョン、オンデマンドの場合の料金です）

| サービス       | インスタンス        | vCPU | Memory | 料金 ($/hour) |
|:--------------|:------------------|-----:|-------:|-------------:|
| Hugging Face  | Nvidia T4 - small |    4 |   15GB |          0.6 |
| AWS SageMaker | ml.g4dn.xlarge    |    4 |  16GiB |        0.736 |

メモリは GB (ギガバイト) 単位と GiB (ギビバイト) 単位なので単純比較しにくいですが、ほぼ同等です

SageMaker の GPU も Hugging Face と同様 T4 です

ほぼ同じ構成ですが、料金は Hugging Face の方が2割ほど安いですね

もちろん、 AWS の方は Saving Plans を使うなどすればもっと安く（最大 64% 削減）することが可能です

## まとめ

はじめて Hugging Face で GPU インスタンスを動かしてみましたが、 特に問題なくアプリをデプロイすることができました

料金も比較的お手頃です

ノートブックをコミットすると、自動で再デプロイされる = CD (Continuous Delivery) が組み込まれているのが素晴らしいですね

今後もちょくちょく使ってみたいと思います