著者: 株式会社 日立ソリューションズ 柳村 明宏
監修: 株式会社 日立製作所
はじめに
KubeflowはKubernetes上でMLOpsを実現するためのOSSのツールキットです。2020/3にバージョン1.0がリリースされ、2021/4現在も活発に開発が行われており、機械学習を利用したシステムの開発/運用のライフサイクルを回すための有効な手段の1つとして注目されています。
本連載では、Kubeflowのバージョン1.2(連載開始時点の最新版、2020年11月リリース)について、構築手順、Kubeflow Pipelines、KFServingの基礎的な利用手順についての情報を紹介いたします。
第4回の本稿では、第3回⽬で作成した学習済みモデルを利⽤した推論サービスを、Kubeflowのサービングコンポーネントの1つであるKFServingを利用して公開する⼿順を紹介いたします。
本連載では、Kubernetesの前提バージョンを1.20としています。
ステータスがStableのSeldon Core Servingは、前提とするKubernetesのバージョンが1.12から1.17であったため、ステータスがBetaであるKFservingを利用した検証を行いました。
投稿一覧:
- MLOpsを実現するKubeflowを前提のKubernetesも含めて構築する(前編)
- MLOpsを実現するKubeflowを前提のKubernetesも含めて構築する(後編)
- Kubeflow Pipelinesでscikit-learnの機械学習モデルを訓練・評価してみる
- Kubeflow KFServingでscikit-learnの学習済みモデルを用いた推論サービスを公開する (本投稿)
KFServingの概要
KFServingは、Kubernetes上でのサーバレス推論を実現するコンポーネントで、TensorFlow、XGBoost、Scikit-learn、PyTorch、ONNXなどの一般的な機械学習(ML)フレームワークを抽象度の高いYAMLファイルに記述することで構築することができます。
KFServingの詳細は、Kubeflow公式サイトのKFServingのページを参照してください。
KFServingの利用手順
今回のKFServingの利用手順を次に示します。
今回のKubeflow Pipelinesの利用手順
# | 手順 | 概要 |
---|---|---|
1 | KFServingの開発環境を準備 | 第1回目、および、第2回目でkubeflowを構築したマシンにログインし、KFServingのリポジトリをクローンします。 |
2 | 推論サービスのデプロイ | scikit-learnのsampleを編集して、第3回目でAmazon S3にアップロードしたscikit-learnの学習済みモデルの読込み、および、推論処理を行えるようにします。編集したコードのDockerイメージをビルドし、推論サービスのデプロイを行います。 |
3 | 推論サービスの利用 | デプロイした推論サービスを利用し、推論を実行します。 |
1. KFServingの開発環境を準備
1.1 SSHでログイン
第1回目、および、第2回目で、Kubeflowを構築したマシンにSSHでログインします。
1.2 KFServingのバージョン確認
本稿では、Kubeflow構築済みの環境を前提とするため、KFServingのインストールは不要です。
また、ネームスペースはkubeflowとなります。
KFServingのバージョンは、「2.3 Amazon S3アクセスに必要なAWS認証設定」で編集する、KFServingのAWS認証用の設定ファイルの内容に影響します。
# KFServingのバージョン確認
kubectl get po -n kubeflow -o yaml | grep image.*kfserving-controller:
# --- 実行結果例 ここから -------------------------------------------------
# image: gcr.io/kfserving/kfserving-controller:v0.4.1
# image: gcr.io/kfserving/kfserving-controller:v0.4.1
# --- 実行結果例 ここまで -------------------------------------------------
1.3 KFServingのリポジトリのクローン
KFServingのGitHubのリポジトリをクローンします。
# KFServingのリポジトリをクローン
git clone https://github.com/kubeflow/kfserving.git
scikit-learnのsampleは、次のサンプルを利用します。
YAMLファイルのsample
コードのsample
編集するファイルは次のファイルです。
# | 記載箇所 | ファイルパス |
---|---|---|
1 | 2.1 scikit-learnの学習済みモデルを用いた推論処理を作成 | ~/kfserving/python/sklearnserver/sklearnserver/model.py |
2 | 2.2 Dockerイメージのビルド | ~/kfserving/python/sklearnserver/setup.py |
3 | 2.3 Amazon S3アクセスに必要なAWS認証設定 | ~/kfserving/docs/samples/storage/s3/s3_secret.yaml |
4 | 2.4 推論サービスのデプロイ | ~/kfserving/docs/samples/v1alpha2/sklearn/sklearn.yaml |
2.推論サービスのデプロイ
2.1 scikit-learnの学習済みモデルを用いた推論処理を作成
第3回目でAmazon S3にアップロードしたscikit-learnの学習済みモデルを読込み、KFServingを利用して推論を実行できるように「~/kfserving/python/sklearnserver/sklearnserver/model.py」を編集します。
model.pyの編集後のコードを次に示します。編集前のコード(KFServingのscikit-learnサンプルのmodel.py)については、model.pyのsampleを参照してください。
import kfserving
import os
import pickle
import pandas as pd
import sys
import json
from typing import Dict
sys.path.append('/sklearnserver/sklearnserver')
from preprocessor import PreProcessor
MODEL_BASENAME = "ml_pipeline"
MODEL_EXTENSIONS = [".pickle"]
class SKLearnModel(kfserving.KFModel): # pylint:disable=c-extension-no-member
def __init__(self, name: str, model_dir: str):
super().__init__(name)
self.name = name
self.model_dir = model_dir
self.ready = False
# モデルファイルの読込み処理
def load(self) -> bool:
model_path = kfserving.Storage.download(self.model_dir)
paths = [os.path.join(model_path, MODEL_BASENAME + model_extension)
for model_extension in MODEL_EXTENSIONS]
for path in paths:
if os.path.exists(path):
self._model = pickle.load(open(path, 'rb'))
self.ready = True
break
return self.ready
# 推論処理
def predict(self, request: Dict) -> Dict:
# リクエストから値を取得
instances = request["instances"]
try:
# DataFrame化
X_df = pd.DataFrame(instances)
# 予測を実行
pred = self._model.predict(X_df)
# 予測結果をJson化
result = {"Survived": int(pred[0])}
# 予測結果を返信
return json.dumps(result)
except Exception as e:
raise Exception("Failed to predict %s" % e)
MODEL_BASENAME、MODEL_EXTENSIONSは、第3回目で作成した学習済みモデルを指定します。
model.pyでは、第3回目で作成したpreprocessor.pyをインポートするため、preprocessor.pyを「~/kfserving/python/sklearnserver/sklearnserver/」に配置してください。
2.2 Dockerイメージのビルド
「2.1 scikit-learnの学習済みモデルを用いた推論処理を作成」で編集したコードをビルドしてDockerイメージを作成するために、「~/kfserving/python/sklearnserver/setup.py」を編集します。
setup.pyの編集後のコードを次に示します。ここでは次の編集を実施します。
- 「install_requires=」のscikit-learnのバージョンを、学習済モデルを作成したときに利用したscikit-learnのバージョンに合わせます。
- 追加したいライブラリがあれば追加で記載します。今回は「pandas >= 1.0.0"」を記載しています。
編集前のコード(ライブラリ配布用のpythonサンプルのsetup.py)については、setup.pyのsampleを参照してください。
from setuptools import setup, find_packages
tests_require = [
'pytest',
'pytest-asyncio',
'pytest-tornasync',
'mypy'
]
setup(
name='sklearnserver',
version='0.5.0',
author_email='singhan@us.ibm.com',
license='https://github.com/kubeflow/kfserving/LICENSE',
url='https://github.com/kubeflow/kfserving/python/sklearnserver',
description='Model Server implementation for scikit-learn. \
Not intended for use outside KFServing Frameworks Images',
long_description=open('README.md').read(),
python_requires='>3.4',
packages=find_packages("sklearnserver"),
install_requires=[
"kfserving>=0.5.0",
"scikit-learn == 0.24.1",
"pandas >= 1.0.0"
],
tests_require=tests_require,
extras_require={'test': tests_require}
)
インストールするscikit-learnのバージョンは、第3回目で指定したバージョンと同一のバージョンを指定します。
自身のDockerレジストリにログインします。
# docker loginの実行
sudo docker login
Dockerイメージを作成します。
参考: Building your own scikit-Learn Server Docker Image
# Dockerイメージの作成
sudo docker build -t [Dockerユーザ名]/[イメージ名] -f ~/kfserving/python/sklearn.Dockerfile ~/kfserving/python
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# Successfully built 81d6efc54e84
# Successfully tagged [Dockerユーザ名]/[イメージ名]:latest
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
# 作成したイメージの確認
sudo docker images --digests [Dockerユーザ名]/[イメージ名]:latest
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
# [Dockerユーザ名]/[イメージ名] latest <none> 41909b0f94e0 42 minutes ago 1.36GB
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
# 作成したイメージのアップロード
sudo docker push [Dockerユーザ名]/[イメージ名]:latest
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# The push refers to repository [docker.io/[Dockerユーザ名]/[イメージ名]]
# 9ef05367b9dc: Pushed
# b84a57dacdd2: Pushed
# 1add26dfd9ff: Pushed
# 9df36e5bd672: Pushed
# 16be529e8612: Pushed
# 9ab20c4343be: Layer already exists
# 7ebccfde7bb7: Layer already exists
# d20676915161: Layer already exists
# 852a1bb5386c: Layer already exists
# 9437609235f0: Layer already exists
# bee1c15bf7e8: Layer already exists
# 423d63eb4a27: Layer already exists
# 7f9bf938b053: Layer already exists
# f2b4f0674ba3: Layer already exists
# latest: digest: sha256:66dbf7d11e89d824b8e48f9e568ecd0049f21e2b76f694f5cb3c68365afa5ad0 size: 3272
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
Inference Service用のconfigmapに、作成したscikit-learn のDockerイメージを指定します。
参考: Run SKLearn InferenceService with your own image
# Inference Service用のconfigmapの編集
kubectl -n kubeflow edit configmap inferenceservice-config
:(省略)
predictors: |-
{
:(省略)
"sklearn": {
"image": "[Dockerユーザ名]/[イメージ名]"
},
:(省略)
2.3 Amazon S3アクセスに必要なAWS認証設定
Amazon S3に格納しているモデルファイルを参照するため、AWS認証用のYAMLファイル「~/kfserving/docs/samples/storage/s3/s3_secret.yaml」について、公式ドキュメントを参考に編集します。
参考: s3_secret.yaml、Store models in S3 bucket
今回、KFServingは0.41を利用しているため、0.4以降のバージョンを利用する場合の例を記載します。
0.3以前のバージョンを利用する場合は、dataタグに指定するAWS_ACCESS_KEY_ID、および、AWS_SECRET_ACCESS_KEYの変数名をそれぞれ、awsAccessKeyID、および、awsSecretAccessKeyと指定します。
AWS_ACCESS_KEY_ID、および、AWS_SECRET_ACCESS_KEYの値をBASE64エンコードして設定
<…の値>部分は環境に合わせて指定します。
export AWS_ACCESS_KEY_ID_BASE64=`echo -n "<AWS_ACCESS_KEY_IDの値>" | base64`
export AWS_SECRET_ACCESS_KEY_BASE64=`echo -n "<AWS_SECRET_ACCESS_KEYの値>" | base64`
apiVersion: v1
kind: Secret
metadata:
name: mysecret
annotations:
serving.kubeflow.org/s3-endpoint: s3.ap-northeast-1.amazonaws.com
serving.kubeflow.org/s3-usehttps: "1"
serving.kubeflow.org/s3-verifyssl: "1"
serving.kubeflow.org/s3-region: ap-northeast-1
type: Opaque
data:
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID_BASE64}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY_BASE64}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa
secrets:
- name: mysecret
s3-endpoint、および、s3-regionは環境に合わせて設定します。
AWS認証情報をKubernetesへ設定します。
# AWS認証情報のKubernetesへの設定
kubectl apply -f s3_secret.yaml
# --- 実行結果例 ここから -----------------
# secret/mysecret created
# serviceaccount/sa created
# --- 実行結果例 ここまで -----------------
2.4 推論サービスのデプロイ
scikit-learn Inference Service用のYAML「~/kfserving/docs/samples/v1alpha2/sklearn/sklearn.yaml」を編集し、推論サービスをデプロイします。
sampleでは、storageUriにGCPのパスが記載されていますが、今回Amazon S3を利用するため、Amazon S3のパスを記載します。
参考: Run SKLearn InferenceService with your own image
apiVersion: "serving.kubeflow.org/v1alpha2"
kind: "InferenceService"
metadata:
name: "sklearn-iris"
spec:
default:
predictor:
serviceAccountName: sa
sklearn:
storageUri: "s3://kubeflow-pl"
serviceAccountNameには、「2.3 Amazon S3アクセスに必要なAWS認証設定」で編集したYAMLファイルのServiceAccountのnameを指定します。
storageUriには、第3回目で学習済みモデルをアップロードしたAmazon S3のUriを指定します。
scikit-learn Inference Serviceをデプロイします。
kubectl apply -f sklearn.yaml
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# inferenceservice.serving.kubeflow.org/sklearn-iris created
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
# Podの状態確認
kubectl get pods -l model=sklearn-iris
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# NAME READY STATUS RESTARTS AGE
# sklearn-iris-predictor-default-6lvmc-deployment-856956bb95sgd5r 2/2 Running 0 89s
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
# ログの確認
kubectl logs sklearn-iris-predictor-default-6lvmc-deployment-856956bb95sgd5r kfserving-container
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# [I 210310 16:28:10 storage:45] Copying contents of /mnt/models to local
# [I 210310 16:28:10 kfserver:115] Registering model: sklearn-iris
# [I 210310 16:28:10 kfserver:96] Listening on port 8080
# [I 210310 16:28:10 kfserver:98] Will fork 1 workers
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
3. 推論サービスの利用
推論したいデータをJsonファイルで作成して、推論を実行します。
参考: Run a prediction
正常に動作した場合、「2.1 scikit-learnの学習済みモデルを用いた推論処理を作成」で実装した通り、「{"Survived": 0}または{"Survived": 1}」が返ってきます。
3.1 入力データの準備
推論したいデータをJsonファイルで作成します。
# 推論データの作成
cat <<EOF> ml-input.json
{
"instances": [
{
"PassengerId": 294,
"Pclass": 1,
"Name": "John, Mr. Smith",
"Sex": "male",
"Age": 28,
"SibSp": 0,
"Parch": 0,
"Ticket": 312453,
"Fare": 18.2500,
"Cabin": "NaN",
"Embarked": "Q"
}
]
}
EOF
3.2 推論を実行
推論実行時に必要な環境変数を設定します。
MODEL_NAMEは、「2.4 推論サービスのデプロイ」で設定したmetadataタグのnameを指定してします。
MODEL_NAME=sklearn-iris
INPUT_PATH=ml-input.json
SERVICE_HOSTNAME=$(kubectl get inferenceservice sklearn-iris -o jsonpath='{.status.url}' | cut -d "/" -f 3)
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
KFServingのエンドポイントにHTTPリクエストを送信して、推論サービスを呼び出します。
# 推論の実行
curl -v -H "Host: ${SERVICE_HOSTNAME}" http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/$MODEL_NAME:predict -d $INPUT_PATH
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# * Trying [INGRESS_HOSTの値]...
# * TCP_NODELAY set
# * Connected to [INGRESS_HOSTの値] ([INGRESS_HOSTの値]) port [INGRESS_PORTの値] (#0)
# > POST /v1/models/sklearn-iris:predict HTTP/1.1
# > Host: sklearn-iris.default.example.com
# > User-Agent: curl/7.58.0
# > Accept: */*
# > Content-Length: 263
# > Content-Type: application/x-www-form-urlencoded
# >
# * upload completely sent off: 263 out of 263 bytes
# < HTTP/1.1 200 OK
# < content-length: 15
# < content-type: text/html; charset=UTF-8
# < date: Wed, 10 Mar 2021 16:36:26 GMT
# < server: istio-envoy
# < x-envoy-upstream-service-time: 26
# <
# * Connection #0 to host [INGRESS_HOSTの値] left intact
# {"Survived": 0}
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
Podには下記のようにHTTPリクエストの結果が出力されます。
# ログの確認
kubectl logs sklearn-iris-predictor-default-6lvmc-deployment-856956bb95sgd5r kfserving-container
# --- 実行結果例 ここから -------------------------------------------------------------------------------
# [I 210310 16:36:26 web:2243] 200 POST /v1/models/sklearn-iris:predict (127.0.0.1) 22.87ms
# --- 実行結果例 ここまで -------------------------------------------------------------------------------
おわりに
本稿では、Kubeflow KFServingでscikit-learnの学習済みモデルを用いた推論サービスを公開する手順を紹介しました。
Kubeflowの公式サイトなどに掲載されている情報やサンプルの多くは、Google Cloud Platform前提です。本投稿ではオンプレミスやGoogle Cloud Platform以外のクラウド環境で利用する方にも、基礎的な情報として参考してもらえればと思います。