StreamlitからDatabricksでサービングしている機械学習モデルを呼び出す

本書は、以下の記事で説明されているStreamlitアプリケーションを動作させる際の手順を説明するものです。この方法を用いることで、DatabricksでサービングしているモデルをGUIから呼び出すことが簡単にできるようになります。

https://qiita.com/taka_yayoi/items/173d5228c1d08c5130ef

:::note warn
本書で説明するアプリケーションはデモを目的としたものです。プロダクション環境で利用される際には、適切なセキュリティ対策を講じるようにしてください。
:::

# モデルサービング

上の記事からリンクされているノートブックを実行し、類似画像レコメンデーションのモデルを構築します。Cmd79まで実行しモデルをMLflowで記録します。そして、記録されたモデルをモデルレジストリに登録します。今回の例では`sim_model_taka`という名前でモデルを登録しています。モデルレジストリの詳細については、[DatabricksにおけるMLflowモデルレジストリ](https://qiita.com/taka_yayoi/items/e7a4bec6420eb7069995)をご覧ください。
![スクリーンショット 2022-03-04 17.08.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/169fee82-3ff2-672a-76a3-b9b1535a3cc4.png)

モデルレジストリに登録すると以下のような状態になります。こちらの画面にはサイドバーのペルソナスイッチャーで**Machine Leaning**を選択し、**Models > sim_model_taka**をクリックすることでアクセスできます。
![スクリーンショット 2022-03-04 17.08.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/c2136973-c588-8f12-98b4-2c53c67e497b.png)

**Serving**タブをクリックし、**Enable Serving**をクリックします。これでモデルサービング用のクラスターが起動し、モデルがデプロイされます。モデルサービングの詳細に関しては、[Databricksにおけるモデルサービング](https://qiita.com/taka_yayoi/items/b5a5f83beb4c532cf921)をご覧ください。

**Status**(モデルサービング用クラスターのステータス)が**Pending**から**Running**になり、**Model Versions**に表示されるモデルバージョン(デプロイされる機械学習モデル)も**Pending**から**Running**になるまで待ちます。

両方**Running**になったら、画面上の**Model URL**をコピーしておきます。後のステップで使用します。
![スクリーンショット 2022-03-04 17.19.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/c3a8e339-24ec-33b7-bea6-b84533854cea.png)


:::note warn
モデルサービング用のクラスターを稼働させている際にも課金が発生します。モデルを使用しない場合には、**Status**の右にある**Stop**をクリックして、モデルサービング用のクラスターを停止してください。
:::

# パーソナルアクセストークンの取得

モデルサービングされているモデルを呼び出す際には、Databricksのパーソナルアクセストークンが必要となります。

サイドメニューの**設定 > ユーザー設定 > アクセストークン**に移動し、**新規トークンの作成**をクリックして、トークンの名前、存続期間を指定します。**作成**をクリックするとトークンが表示されるのでコピーしておきます。
![Screen Shot 2022-02-27 at 8.38.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/86988492-c6be-9018-1eeb-7af4df1573f2.png)

:::note warn
**注意** パーソナルアクセストークンは厳重に管理してください。第三者に教えたりしないでください。
:::

# Streamlitアプリケーションの設定

以下で公開されているファイルをローカルにダウンロードします。このサンプルアプリケーションはDockerで稼働するので、ローカルにDockerがインストールされていることを確認してください。インストールされていない場合にはインストールしてください。

https://github.com/avisoori-databricks/Databricks_image_recommender

:::note warn
上のリポジトリに含まれている`app.py`ではエラーになりますので、以下のapp.pyに差し替えてください。`<Model URL>`には上でコピーしたものを貼り付けてください。
:::

```py:recommender_app/app.py
import streamlit as st 
import numpy as np 
from PIL import Image
import base64
import io

import os
import requests
import numpy as np
import pandas as pd

st.header('類似画像レコメンデーションシステム')
st.write('[eコマース向け類似画像レコメンデーションシステムの構築 \- Qiita](https://qiita.com/taka_yayoi/items/173d5228c1d08c5130ef)') # markdown

# Copy and paste this code from the MLflow real-time inference UI. Make sure to save Bearer token from 
def create_tf_serving_json(data):
  return {'inputs': {name: data[name].tolist() for name in data.keys()} if isinstance(data, dict) else data.tolist()}

def score_model(dataset):
  token = os.environ.get("DATABRICKS_TOKEN")
  url = '<Model URL>'
  headers = {'Authorization': f'Bearer {os.environ.get("DATABRICKS_TOKEN")}'}
  
  data_json = dataset.to_dict(orient='split') if isinstance(dataset, pd.DataFrame) else create_tf_serving_json(dataset)
  response = requests.request(method='POST', headers=headers, url=url, json=data_json)
  if response.status_code != 200:
    raise Exception(f'Request failed with status {response.status_code}, {response.text}')
  return response.json()

def render_response_image(i):
  """response is the returned JSON object. We can loop through this object and return the reshaped numpy array for each recommended image which can then be rendered"""

  single_image_string = response[i]["0"]
  image_array = np.frombuffer(bytes.fromhex(single_image_string), dtype=np.float32)
  image_reshaped = np.reshape(image_array, (28,28))
  st.image(image_reshaped, width=100)
  st.image("images/heading.png")

# Source: https://discuss.streamlit.io/t/png-bytes-io-numpy-conversion-using-file-uploader/1409
img_file_buffer = st.file_uploader('Upload a PNG image', type='png')

if img_file_buffer is not None:
  image = Image.open(img_file_buffer)
  img_array = np.array(image)
  st.write("Uploaded Image: ")
  st.image(image, width = 100)
  # To convert to a string based IO:
  byteIO = io.BytesIO()
  image.save(byteIO, format='PNG')
  img_str = base64.b64encode(byteIO.getvalue())

  model_input  =  img_str.decode("utf-8")

  df = pd.DataFrame.from_dict({"input": [model_input] })

  response = score_model(df) 

  st.write("Recommended Items")

  col0, col1, col2, col3, col4 = st.columns(5)

  with col0:
    render_response_image(0)
  with col1:
    render_response_image(1)
  with col2:
    render_response_image(2)
  with col3:
    render_response_image(3)
  with col4:
    render_response_image(4)
```

:::note info
以下ではDockerでアプリケーションを稼働させていますが、[Share Streamlit](https://share.streamlit.io/)でアプリケーションをホスティングすることも可能です。
:::

# Dockerイメージのビルド・実行

本例では、上記パーソナルアクセストークンを環境変数としてDockerに引き渡します。以下の記事を参考にさせていただきました。

https://qiita.com/KEINOS/items/518610bc2fdf5999acf2

```bash:Bash
export DATABRICKS_TOKEN=<パーソナルアクセストークン>
```

```
cd recommender_app
docker build -f Dockerfile -t app:latest . 
docker run -p 8501:8501 app:latest --env DATABRICKS_TOKEN
```

以下のようなメッセージが表示されたら、ブラウザで`http://localhost:8501/`にアクセスします。
![スクリーンショット 2022-03-04 17.32.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/fb6aff04-a6b8-e04e-218d-22d5d58017e1.png)

このように画像をアップロードするGUIが表示されます。
![スクリーンショット 2022-03-04 17.33.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cfae4790-e57d-841f-daa0-ea0f1945e76d.png)

アップロードする画像は28x28ピクセルである必要があります。
![スクリーンショット 2022-03-04 11.28.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/497283f8-e4ba-1870-b3f9-4ccd87cae96b.png)

なお、こちらから画像を取得することができます。

https://github.com/zalandoresearch/fashion-mnist

画像をアップロードすると類似している画像が表示されます。このように、お手軽に機械学習モデルを活用できるようになります。是非ご活用ください。

![スクリーンショット 2022-03-04 17.37.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b29b64c5-9ff2-55eb-a4dd-f29f46440e3e.png)

しかし、Streamlitいいですね。お手軽にWebアプリが組めて。もっと勉強しようと思います。以下の記事を参考にさせていただきました。

https://qiita.com/kunishou/items/68d676410f4878c568aa

https://qiita.com/irisu-inwl/items/9d49a14c1c67391565f8


### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)