LoginSignup
10
3

Elixir Livebook で AI 画像識別アプリを実装、公開(本番リリース)する

Last updated at Posted at 2023-03-30

はじめに

前回の記事で Livebook から Web アプリをデプロイする方法を紹介しました

今回はその発展形で、Bumblebee を使った AI Web アプリをインターネット上に公開してみます

デプロイ先は Fly.io です

少しのアプリを動かすだけであれば無料で利用できます

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

コンテナ実装

特に必要な依存モジュール等がなければ以下の Web サイトから簡単に Livebook を Fly.io にデプロイすることが可能です

しかし、今回は Livebook が 0.9 以上であることなど、必要な条件があるため、自分でカスタマイズしたコンテナを Fly.io にデプロイします

幸い私は普段から Livebook をコンテナで起動していたため、以下のリポジトリーにある Dockerfile をそのまま使えました

FROM ghcr.io/livebook-dev/livebook:0.9.0

...

CMD ["/app/bin/livebook", "start"]

この Dockerfile は以下の目的で作られています

  • livebooks ディレクトリー配下の自作ノートブックを実行する
    • evision や Image などのモジュールがインストールできる
    • docker CLI を使える
  • export して WSL にインポートする
  • コンテナ内で Phoenix アプリのデモを行う

なので、 Bumblebee を動かすだけであれば上記 Dockerfile の全てが必要なわけではありません

重要なのは冒頭 FROM ghcr.io/livebook-dev/livebook:0.9.0 で Livebook 0.9.0 を指定していることと、末尾で CMD ["/app/bin/livebook", "start"] として Livebook を起動していることです

それ以外の箇所は目的に応じて変更してください

.dockerignore の作成

これは環境によりますが、私の場合は tmp ディレクトリー配下に AI のモデルファイル等の巨大ファイルを大量に保存していたため、そのままデプロイしようとするとかなり時間がかかってしまいました

コンテナに不要なディレクトリー、ファイルは .dockerignore ファイルに書いておきましょう

.dockerignore 記載例

tmp

Fly.io へのデプロイ

Fly.io のアカウント準備

まだ Fly.io を使ったことがない場合、以下の Speedrun に従って認証まで実行してください

macOS の場合は以下のようなコマンドになります

brew install flyctl
fly auth signup

fly.toml の作成

以下のような内容で Dockerfile と同じディレクトリーに fly.toml を作ります

# fly.toml file generated for livebook-rwakabay on 2023-03-30T09:19:15+09:00

app = "livebook-rwakabay"
kill_signal = "SIGTERM"
kill_timeout = 5
primary_region = "nrt"
processes = []

[env]

[experimental]
  allowed_public_ports = []
  auto_rollback = true

[[services]]
  http_checks = []
  internal_port = 8080
  processes = ["app"]
  protocol = "tcp"
  script_checks = []
  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "30s"
    interval = "15s"
    restart_limit = 6
    timeout = "2s"

livebook-rwakabay の部分はデプロイするアプリ毎の ID なので、各人で変更してください

アプリの作成

fly launch コマンドを実行し、質問には全てデフォルトのまま(何も入力せずに)Enter を押してください

$ fly launch
An existing fly.toml file was found for app livebook-rwakabay
? Would you like to copy its configuration to the new app? Yes
Creating app in /Users/oec/dx/elixir-learning
Scanning source code
Detected a Dockerfile app
? Choose an app name (leaving blank will default to 'livebook-rwakabay') 
automatically selected personal organization: Ryo Wakabayashi
Some regions require a paid plan (fra, maa).
See https://fly.io/plans to set up a plan.

? Choose a region for deployment: Tokyo, Japan (nrt)
Created app 'livebook-rwakabay' in organization 'personal'
Admin URL: https://fly.io/apps/livebook-rwakabay
Hostname: livebook-rwakabay.fly.dev
? Would you like to set up a Postgresql database now? No
? Would you like to set up an Upstash Redis database now? No
? Create .dockerignore from 5 .gitignore files? No
Wrote config file fly.toml

最後の ? Would you like to deploy now?y で変更すればそのままデプロイできます

デプロイ

fly deploy コマンドでデプロイできます

しばらくして deployed successfully が表示されればデプロイ成功です

$ fly deploy --ha=false
==> Verifying app config
--> Verified app config
==> Building image
Remote builder fly-builder-polished-waterfall-3907 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
[+] Building 43.6s (0/1)                                                                                                              
[+] Building 162.9s (19/19) FINISHED                                                                                                  
 => [internal] load remote build context
...
==> Monitoring deployment
Logs: https://fly.io/apps/livebook-rwakabay/monitoring

 1 desired, 1 placed, 1 healthy, 0 unhealthy [health checks: 1 total, 1 passing]
--> v0 deployed successfully

2023年06月現在、 fly deploy --ha=false と指定しないと、勝手に2台構成で起動してしまい、認証できなくなります

https://qiita.com/RyoWakabayashi/items/6b8a2b3642829b004523

リソースの変更

fly scale memory 2048 コマンドでリソースを 2GB に拡張します

デフォルトの 256 MB だと EXLA のインストール時にメモリ不足でエラーが発生します

$ fly scale memory 2048
Scaled VM Memory size to 2 GB
      CPU Cores: 1
         Memory: 2 GB

ログの表示

fly logs でコンテナのログを参照できます

Livebook の場合、アクセスするためにトークンが必要なので、ログを参照してトークンを取得します

$ fly logs
2023-03-30T00:37:47Z runner[0ab58f2c] nrt [info]Starting instance
2023-03-30T00:37:47Z runner[0ab58f2c] nrt [info]Configuring virtual machine
2023-03-30T00:37:47Z runner[0ab58f2c] nrt [info]Pulling container image
2023-03-30T00:38:26Z runner[0ab58f2c] nrt [info]Unpacking image
2023-03-30T00:39:28Z runner[0ab58f2c] nrt [info]Preparing kernel init
2023-03-30T00:39:29Z runner[0ab58f2c] nrt [info]Configuring firecracker
2023-03-30T00:39:29Z runner[0ab58f2c] nrt [info]Starting virtual machine
2023-03-30T00:39:30Z app[0ab58f2c] nrt [info]Starting init (commit: 8e03fa6)...
2023-03-30T00:39:30Z app[0ab58f2c] nrt [info]Preparing to run: `/app/bin/livebook start` as root
...
2023-03-30T00:39:33Z app[0ab58f2c] nrt [info][Livebook] Application running at http://0.0.0.0:8080/?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

ログで [Livebook] Application running at http://0.0.0.0:8080/?token=<トークンの値> となっている行からトークンの値をコピーしておきます

トークンの値はアプリを再起動するたびに変化するため、最後に表示されているものを使用してください(リソースの変更時に再起動されています)

Livebook を開く

以下のコマンドを実行すると、ブラウザで Livebook が起動します

fly open

スクリーンショット 2023-03-30 13.50.20.png

取得しておいたトークンを入力します

スクリーンショット 2023-03-30 13.51.20.png

Livebook のトップページが表示されました

Web アプリの実装

今回は画像識別 Web アプリを実装します

「New notebook」をクリックして新しいノートブックを開き、セルに以下のコードを入力、実行してください

セットアップ

必要なモジュールをインストールします

Mix.install(
  [
    {:bumblebee, "~> 0.2"},
    {:nx, "~> 0.5"},
    {:exla, "~> 0.5"},
    {:kino, "~> 0.9"}
  ],
  config: [nx: [default_backend: EXLA.Backend]]
)

画像識別サービスの作成

モデルファイルをダウンロードしてロードし、Bumblebee で画像識別のサービスを作成します

cache_dir = "/tmp/bumblebee_cache"

{:ok, resnet} =
  Bumblebee.load_model({
    :hf,
    "microsoft/resnet-50",
    cache_dir: cache_dir
  })

{:ok, featurizer} =
  Bumblebee.load_featurizer({
    :hf,
    "microsoft/resnet-50",
    cache_dir: cache_dir
  })

serving = Bumblebee.Vision.image_classification(resnet, featurizer)

フォームの作成

画像アップロードと実行用のボタンを作ります

inputs = [
  image: Kino.Input.image("IMAGE", size: {224, 224})
]

form = Kino.Control.form(inputs, submit: "Classify")

スクリーンショット 2023-03-30 14.15.37.png

結果表示用フレームの作成

結果を表示するフレームを作成します

frame = Kino.Frame.new()

スクリーンショット 2023-03-30 14.18.44.png

画像識別処理の実装

入力フォームで Classify ボタンがクリックされたときの処理を定義します

Kino.listen(form, fn %{data: %{image: image}, origin: origin} ->
  image =
    image
    |> then(fn input ->
      input.data
      |> Nx.from_binary(:u8)
      |> Nx.reshape({input.height, input.width, 3})
    end)

  serving
  |> Nx.Serving.run(image)
  |> Map.get(:predictions)
  |> Kino.DataTable.new()
  |> then(&Kino.Frame.render(frame, &1, to: origin))
end)

&Kino.Frame.render(frame, &1, to: origin)to: origin を指定することで、クライアント(アプリを開いているブラウザ)毎に個別の結果を表示します

これを指定していない場合、複数人でこのアプリを使っていると全員で結果を共有することになります

Livebook 上での画像識別実行

適当な画像をアップロードして Classify ボタンをクリックすると、表示用フレームに識別結果がテーブル表示されます

スクリーンショット 2023-03-30 14.26.03.png

アプリのデプロイ

Livebook の左メニューからロケットのアイコンをクリックすると、デプロイ用のメニューが表示されます

Slug (URLの末尾部分)に適当な値(画像の例では「classifier」)を入力して「Deploy」ボタンをクリックしましょう

スクリーンショット 2023-03-30 14.27.45.png

DEPLOYMENTS の Status が Running になれば Web アプリが起動しています

スクリーンショット 2023-03-30 14.30.19.png

<Livebook の URL 先頭部分>/apps/<Slug に指定した値> (今回の例ではhttps://livebook-rwakabay.fly.dev/apps/classifier)をブラウザで開きましょう

入力フォームと結果表示用フレームだけの画面が開きます

スクリーンショット 2023-03-30 14.33.06.png

画像識別を実行してみます

deploy_ai.gif

実行できました

他の人にアプリを共有する場合、アプリの URL とデプロイ用メニューの「Password-protected」に入っている値(パスワード)を伝えてください

まとめ

ノートブックでアプリが実装できて、しかもデプロイまでできる、というのは本当に衝撃的です

しかも超短時間、超低コストで

今回のコードであれば実装からリリースまで数分でできてしまいます

Elixir の進化は計り知れないですね

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3