How to Host an Image Classification Model in Datab... - Databricks Community - 136766の翻訳です。
本書は著者が手動で翻訳したものであり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。また、原文のコードではモデルサービングとの連携部分でエラーになるので、稼働するコードに関してはこちらをご覧ください。
長く続いているすべてのビジネスにおいて最も一般的なユースケースは、企業の知識の総体である検索可能なカタログを作成する能力です。このような知識ベースの多くは、PowerPoint、Word、PDFファイル、時には単純な画像のようなさまざまなタイプのファイルに含まれています。多くの洞察を得るには、これらのファイルにあるテキストをカタログ化するだけでなく、テキストに添付されている画像を分析、分類することも必要となります。
この記事の執筆時点では、Databricksはデフォルトでいかなる画像分類モデルも提供していません。この簡単な記事では、Databricksで資格的な分類モデルをホスティングするための基本的なスケルトンコードを議論、提供します。
Databricksにモデルを持ち込む
始め方
任意のベンダーのほとんどすべてのモデルをホスティングするためにDatabricksを使うことができますが、我々のデモ目的においては、トレーニングに用いられたトレーニングデータの広範さ、高速な推論時間からMicrosoftのResnet-50ビジュアルモデルを使用します。
それではノートブックをスタートしましょう!
ノートブックを開いて、以下のコードを実行しマソ油。これはシンプルにいくつかのライブラリをインポートし、必要なパッケージが利用できるようにPythonカーネルを再起動します。
# Install the necessary python packages.
%pip install transformers torch
# Restart python so the above packages are actually used.
dbutils.library.restartPython()
ここでは、transformersとPyTorchを使いますが、代わりにtorch.hubを使っても構いません。(処理する画像を容易に準備できるAutoImageProcessorのシンプルさからtransformersを選択しています。)
モデルの定義
要件を満たしたので、以下のようにビジョンモデルを定義します:
import io, base64, torch, mlflow.pyfunc
import pandas as pd
from mlflow.types.schema import ColSpec
from PIL import Image
from transformers import AutoImageProcessor, ResNetForImageClassification
class MicrosoftResnetFifty(mlflow.pyfunc.PythonModel):
# Initialize the model, setting the image processor and the classification model.
def __init__(self):
super(MicrosoftResnetFifty, self).__init__()
self.processor = AutoImageProcessor.from_pretrained('microsoft/resnet-50')
self.model = ResNetForImageClassification.from_pretrained('microsoft/resnet-50')
# Prepares the image for inference by converting any known image to a non-transparent PNG.
def prepare_image_for_inference(self, image_bytes):
img = Image.open(io.BytesIO(image_bytes))
buf = io.BytesIO()
img.convert("RGB").save(buf, format='PNG')
img_bytes = buf.getvalue()
image = Image.open(io.BytesIO(img_bytes))
return image
# Pre-processes the image, and executes inference, returning the predicted label.
def classify(self, image):
inputs = self.processor(image, return_tensors="pt")
with torch.no_grad():
logits = self.model(**inputs).logits
predicted_label = logits.argmax(-1).item()
return self.model.config.id2label[predicted_label]
# Takes any image of any known type, converts the image to PNG, and passes the image to the model for inference.
def classify_image(self, image_bytes):
image = self.prepare_image_for_inference(image_bytes)
prediction = self.classify(image)
return prediction
# This is the external interface that Databricks will use to pass the input to the classifier.
def predict(self, model_input: pd.DataFrame):
return [self.classify_image(base64.b64decode(image)) for image in model_input['image']]
上のコードに対する幾つかの注意点です:
モデルのユーザーフレンドリーさのために、特定のフォーマット、すなわち非透過のPNGに準拠する画像を引き渡すようにするためのコードを含めています。この変換処理はprepare_image_for_inference関数に含めています。
また、分類関数の中でtorch.no_grad()を呼び出していることに気づくかもしれません。これによって、ビジョンモデルのトレーニングには有効ですが、推論を行う際にスローダンさせるPyTorchのautograd機能を無効にしています。
モデルが一度の呼び出しで複数の画像に対するバッチ推論をできるようにするために、predict関数ではPandasデータフレームを受け付けています。
Transformersはモデルをロードするために活用するHugging Faceのライブラリであり、PyTorchは次で触れるGPUアクセラレーションを用いて助けになってくれます。
モデルのテスト
汎用の基盤モデルで面白いのは、それらが本当に面白いということなので実際にやってみます。モデルをテストするために、世界で最高の子犬の写真を使います。Ada (Lovelace) Mortonの写真です。この写真を使っても構いませんし、他の写真でも大丈夫です。
ノートブックと同じフォルダーに画像を配置し、以下のコードのセルを追加し、あなた特有の画像ファイル名に変更します:
# Here we'll test the class to ensure it returns proper values.
with open('best-pup-ever.webp', 'rb') as file:
file_content = base64.b64encode(file.read())
input_example = pd.DataFrame({"image": [file_content]})
MicrosoftResnetFifty().predict(input_example)
全てがうまくいくと、1つ2つの警告が出るかもしれませんが、以下のようなアウトプットを見ることになります:
['flat-coated retriever']
これは推論が動いていおり、モデルが稼働していることのシグナルとなります。
以下のようにアウトプットに警告が含まれる場合があります:
Using a slow image processor as `use_fast` is unset and a slow processor was saved with this model. `use_fast=True` will be the default behavior in v4.52, even if the model was saved with a slow processor. This will result in minor differences in outputs. You'll still be able to use a slow processor with `use_fast=False`.
この警告はHugging Faceのtransformersライブラリからのものであり、GPUではなくCPUを用いて推論を行っていると言うサインです。これを目撃し、サーバレスを使っている場合には、(「接続」と表示されている)サーバーのドロップダウンをクリックしてA10プロセッサーを使うようにサーバレス設定を編集し、環境にA10アクセラレーターを追加することで設定を変更することができます。これは推論をスピードアップすることになりますが、技術的には必須ではないので、この警告を全く無視すると言うことも選択肢となります。
モデルの登録
次に行うべきはモデルの登録です。これを行うには、モデルの利用者によって使用される入出力のスキーマをはじめに定義し、これらのスキーマをモデルのシグネチャに組み込みます。そして、Unity Catalogのモデルレジストリにモデルを登録します。
こちらが、このタスクを完遂するためのサンプルコードです:
import pandas as pd
from mlflow.types import Schema, ColSpec
from mlflow.models.signature import ModelSignature
input_schema = Schema([ColSpec("binary", "image")])
output_schema = Schema([ColSpec("string")])
signature = ModelSignature(inputs=input_schema, outputs=output_schema)
# Setting the registry, so this model will be stored in Unity Catalog.
mlflow.set_registry_uri("databricks-uc")
# Starting a run, and registering the model.
with mlflow.start_run() as run:
mlflow.pyfunc.log_model(
python_model=MicrosoftResnetFifty(),
artifact_path="infer_model",
input_example=input_example,
signature=signature
)
model_uri = f"runs:/{run.info.run_id}/infer_model"
registered_model_name = "main.default.resnet50"
mlflow.register_model(model_uri, registered_model_name)
モデルのサービング
最終ステップにおいては、モデルサービングエンドポイントでモデルがサービングされるようにセットアップを行います。このステップもオプションであり、モデルをどのように使うのかに依存しますが、Databricks外からモデルを呼び出そうとしている場合、モデルサービングエンドポイントを確立する必要があります。
このためには、サイドバーのモデルタブに移動します。
使用したいモデルを検索し、モデルの詳細を確認するために名前をクリックします。
モデルの詳細ページにアクセスすると「このモデルをサービングする」という画面右上の大きな青いボタンがあるのでこれをクリックします。
エンドポイント名を指定します。CPUコンピュートタイプかGPUコンピュートタイプを選択することができます。通常GPUは高速ですが、高価です。resnet-50モデルはCPUあるいはGPUでサービングすることができます。このページには、モデルのレート制限の設定、タグに適用、サーバレス利用ポリシー、アラートなど他にも多くのオプションがあり、これらはこの記事のスコープ外ですが、ページを一通りチェックし、どのようなオプションがあるのかを確認する価値はあります。
あなたのユースケースによりますが、検討いただきたい一つの機能はルート最適化エンドポイントです。このエンドポイントを膨大なボリュームや高速なトラフィックを持つ場合のあるデータパイプラインに組み込もうとしている場合、同時実効性を大きく向上し、秒間クエリー数の制限を劇的に改善することで推論の時間を劇的に高速化するためにこのチェックボックをオンにしたいと思うかもしれません。
選択したオプションに満足したら保存ボタンをクリックします。この時点で、エンドポイントの現在の状況が表示されるページに移動します。
モデルのデプロイメントが完了したら、モデルサービングエンドポイント経由で推論を行いましょう。
モデルサービングエンドポイントの呼び出し
モデルサービングエンドポイントが稼働しているので、サービングエンドポイントの詳細ページから呼び出しエンドポイントをコピーします。ページのトップ近くでURLとクリップボードにコピーするためのボタンが確認できるはずです。推論する画像を準備し、バックエンドでモデルサービングエンドポイントを呼び出すコードが以下となります。
メンテナンス性のためには、新規のノートブックにこのコードを配置し、モデル登録ノートブックと分離することをお勧めします。
import os
import requests
import numpy as np
import pandas as pd
import json
import base64
from PIL import Image
from io import BytesIO
# Converts a Pandas Dataframe into a JSON string that will
# be accepted by the model serving endpoint.
def to_dataframe_split_json(df):
obj = {'dataframe_split': json.loads(df.to_json(orient='split'))}
return json.dumps(obj, allow_nan=True)
# Fetches the proper authorization headers that will be needed to call the
# model serving endpoint.
def get_headers():
token = dbutils.notebook.entry_point.getDbutils().notebook().getContext().apiToken().get()
headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}
return headers
# This function calls the model serving endpoint and returns the results. Make sure you’re using a URL that fits with your cloud provider.
def infer(dataset):
endpoint_url = 'https://your-workspace-id.azuredatabricks.net/serving-endpoints/resnet50/invocations'
headers = get_headers()
post_data = to_dataframe_split_json(dataset)
response = requests.request(
method='POST',
headers=headers,
url=endpoint_url,
data=post_data)
if response.status_code != 200:
raise Exception(f'Request failed with status {response.status_code}, {response.text}')
return response.json()
# (Helper function to base64 encode an image.)
def get_image_bytes(image_path):
with open(image_path, 'rb') as f:
return base64.b64encode(f.read()).decode('utf-8')
# Put multiple images in the dataframe to test batch inference.
image_df = pd.DataFrame({"image":
[
get_image_bytes('cartoon-rabbit.jpg'),
get_image_bytes('sloth.png'),
get_image_bytes('best-pup-ever.webp')
]})
# Call the infer function.
results = infer(image_df)
# Print the results to the console.
print([[y for y in x.split(', ')] for x in results['predictions']])
上のコードはDatabricksのノートブック環境で動作しますが、これをDatabricksアプリや他の外部呼び出し元で使いたい場合には、異なる環境では異なるトークン取得手法が必要となるので、特にget_headers()関数を変更する必要があるでしょう。
また、ai_query関数を用いて、シンプルにユーザーによるAIモデルの呼び出しを行うだけでなく、Databricks宣言型パイプラインフローで画像を処理することも可能です。
まとめ
Databricksでは(この記事の執筆時点では)デフォルトでは基盤画像分類モデルを提供していませんが、Databricksで自身の画像モデルをダウンロードし、登録することは比較的分かりやすいタスクであり、この能力はシンプルな画像からPowerPointプレゼンテーション、Word文書、PDFに至る非構造化データの多くから価値を解放する鍵となり得ます。
楽しい構築を!