LoginSignup
10
7

More than 1 year has passed since last update.

Kubeflow KFServingでscikit-learnの学習済みモデルを用いた推論サービスを公開する

Posted at

著者: 株式会社 日立ソリューションズ 柳村 明宏
監修: 株式会社 日立製作所

はじめに

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を利用した検証を行いました。

投稿一覧:
1. MLOpsを実現するKubeflowを前提のKubernetesも含めて構築する(前編)
2. MLOpsを実現するKubeflowを前提のKubernetesも含めて構築する(後編)
3. Kubeflow Pipelinesでscikit-learnの機械学習モデルを訓練・評価してみる
4. 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を参照してください。

model.py
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を参照してください。
setup.py
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
configmap
:(省略)
  predictors: |-
    {
:(省略)
        "sklearn": {
            "image": "[Dockerユーザ名]/[イメージ名]"
        },
:(省略)

2.3 Amazon S3アクセスに必要なAWS認証設定

Amazon S3に格納しているモデルファイルを参照するため、AWS認証用のYAMLファイル「~/kfserving/docs/samples/storage/s3/s3_secret.yaml」について、公式ドキュメントを参考に編集します。
参考: s3_secret.yamlStore 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`
s3_secret.yaml
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

sklearn.yaml
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以外のクラウド環境で利用する方にも、基礎的な情報として参考してもらえればと思います。

10
7
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
7