機械学習のために EKS クラスターで AWS Inferentia インスタンスを使用する - Amazon EKS

機械学習のために EKS クラスターで AWS Inferentia インスタンスを使用する

このトピックでは、Amazon EC2 Inf1 インスタンスを実行中のノードで Amazon EKS クラスターを作成する方法、(オプションで) サンプルアプリケーションをデプロイする方法について説明します。Amazon EC2 Inf1 インスタンスは AWS Inferentia チップを利用しています。Inf1 チップは、クラウド内で高パフォーマンスと低コストの推論を提供するために、AWS によってカスタムビルドされたものです。機械学習モデルは、AWS Neuron を使用してコンテナにデプロイされます。Neuron は、コンパイラー、ランタイム、および Inferentia チップの機械学習推論パフォーマンスを最適化するプロファイリングツールで構成される Software Development Kit (SDK) です。AWSNeuron は、TensorFlow、PyTorch、MXNet などの一般的な機械学習フレームワークをサポートしています。

注記

Neuron デバイスの論理 ID は連続している必要があります。複数の Neuron デバイスをリクエストする Pod が inf1.6xlarge または inf1.24xlarge インスタンス タイプ (複数の Neuron デバイスを持つ) でスケジュールされている場合、Kubernetes スケジューラが連続していないデバイス ID を選択すると、その Pod は起動に失敗します。詳細については、「GitHub」の「Device logical IDs must be contiguous」(デバイスロジカル ID は連続している必要があります) を参照してください。

前提条件

  • コンピュータに eksctl がインストールされている。インストールされていない場合は、eksctl ドキュメントの「インストール」を参照してください。

  • コンピュータに kubectl がインストールされている。詳細については、「kubectl と eksctl のセットアップ」を参照してください。

  • (オプション) コンピュータに python3 がインストールされている。インストールされていない場合は、「Python ダウンロード」でインストール手順を参照してください。

クラスターを作成する

  1. Inf1 Amazon EC2 インスタンスを実行するノードを持つクラスターを作成します。inf1.2xlarge を任意の Inf1 インスタンスタイプ に置き換えることができます。eksctl ユーティリティは、Inf1 インスタンスタイプでノードグループを起動していることを検出し、アクセラレーションされた Amazon EKS 最適化 Amazon Linux AMI のいずれかを使用してノードを起動します。

    注記

    TensorFlow Serving でサービスアカウント用の IAM ロールを使用することはできません。

    eksctl create cluster \ --name inferentia \ --region region-code \ --nodegroup-name ng-inf1 \ --node-type inf1.2xlarge \ --nodes 2 \ --nodes-min 1 \ --nodes-max 4 \ --ssh-access \ --ssh-public-key your-key \ --with-oidc
    注記

    出力の次の行の値をメモします。これは、後の (オプションの) ステップで使用されます。

    [9] adding identity "arn:aws:iam::111122223333:role/eksctl-inferentia-nodegroup-ng-in-NodeInstanceRole-FI7HIYS3BS09" to auth ConfigMap

    Inf1 インスタンスでノードグループを起動すると、eksctl によって AWS Neuron Kubernetes デバイスプラグインが自動的にインストールされます。このプラグインは、Neuron デバイスをシステムリソースとして Kubernetes スケジューラにアドバタイズします。このスケジューラはコンテナによってリクエストできます。デフォルトの Amazon EKS ノードの IAM ポリシーに加えて、Amazon S3 読み取り専用のアクセスポリシーが追加されます。これにより、後のステップで説明するサンプルアプリケーションで Amazon S3 からトレーニングされたモデルをロードできるようになります。

  2. すべての Pods が正しく起動していることを確認します。

    kubectl get pods -n kube-system

    簡略化された出力:

    NAME READY STATUS RESTARTS AGE [...] neuron-device-plugin-daemonset-6djhp 1/1 Running 0 5m neuron-device-plugin-daemonset-hwjsj 1/1 Running 0 5m

(オプション) TensorFlow Serving アプリケーションイメージのデプロイ

トレーニング済みモデルは、Inferentia インスタンスにデプロイする前に、Inferentia ターゲットにコンパイルする必要があります。続行するには、Amazon S3 に保存されている Neuron optimized TensorFlow モデルが必要になります。SavedModel がまだない場合は、Neuron 互換の ResNet50 モデルの作成についてのチュートリアルに従って、結果の SavedModel を S3 にアップロードします。ResNet-50 は、画像認識のタスクに使用される一般的な機械学習モデルです。Neuron モデルのコンパイルについての詳細は、「AWS Deep Learning AMI デベロッパーガイド」の「AWS Inferentia チップと DLAMI」を参照してください。

サンプルのデプロイメントマニフェストでは、AWS Deep Learning Containers によって提供される TensorFlow の事前構築済みの推論サービングコンテナが管理されます。コンテナ内には、AWS Neuron ランタイムと TensorFlow Serving アプリケーションがあります。Neuron 用に最適化された事前構築済みの深層学習コンテナの完全なリストは、GitHub で [Available Images] (利用可能なイメージ) に保持されます。起動時に、DLC は Amazon S3 からモデルを取得し、保存したモデルで Neuron TensorFlow Serving を起動して、予測リクエストのために待機します。

サービングアプリケーションに割り当てられる Neuron デバイスの数は、デプロイメント yaml で aws.amazon.com/neuron リソースを変更することで調整できます。TensorFlow Serving と Neuron ランタイム間の通信は GRPC 経由で行われることに注意してください。GRPC では、IPC_LOCK 機能をコンテナに追加する必要があります。

  1. クラスターを作成する」のステップ 1 で作成したノードのインスタンスロールに AmazonS3ReadOnlyAccess IAM ポリシーを追加します。これは、サンプルアプリケーションが Amazon S3 からトレーニングされたモデルをロードできるようにするために必要です。

    aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \ --role-name eksctl-inferentia-nodegroup-ng-in-NodeInstanceRole-FI7HIYS3BS09
  2. 次の内容で、rn50_deployment.yaml という名前のファイルを作成します。希望の設定に合わせて、リージョンコードとモデルパスを更新します。モデル名は、クライアントが TensorFlow サーバーにリクエストを行う際の識別を目的としています。この例では、モデル名を使用してサンプルの ResNet50 のクライアントスクリプトと照合します。このスクリプトは、後のステップで予測リクエストを送信する際に使用されます。

    aws ecr list-images --repository-name neuron-rtd --registry-id 790709498068 --region us-west-2
    kind: Deployment apiVersion: apps/v1 metadata: name: eks-neuron-test labels: app: eks-neuron-test role: master spec: replicas: 2 selector: matchLabels: app: eks-neuron-test role: master template: metadata: labels: app: eks-neuron-test role: master spec: containers: - name: eks-neuron-test image: 763104351884.dkr.ecr.us-east-1.amazonaws.com/tensorflow-inference-neuron:1.15.4-neuron-py37-ubuntu18.04 command: - /usr/local/bin/entrypoint.sh args: - --port=8500 - --rest_api_port=9000 - --model_name=resnet50_neuron - --model_base_path=s3://your-bucket-of-models/resnet50_neuron/ ports: - containerPort: 8500 - containerPort: 9000 imagePullPolicy: IfNotPresent env: - name: AWS_REGION value: "us-east-1" - name: S3_USE_HTTPS value: "1" - name: S3_VERIFY_SSL value: "0" - name: S3_ENDPOINT value: s3.us-east-1.amazonaws.com - name: AWS_LOG_LEVEL value: "3" resources: limits: cpu: 4 memory: 4Gi aws.amazon.com/neuron: 1 requests: cpu: "1" memory: 1Gi securityContext: capabilities: add: - IPC_LOCK
  3. モデルをデプロイします。

    kubectl apply -f rn50_deployment.yaml
  4. 次の内容で、rn50_service.yaml というファイルを作成します。予測リクエストを受け入れるために HTTP ポートと gRPC ポートが開かれます。

    kind: Service apiVersion: v1 metadata: name: eks-neuron-test labels: app: eks-neuron-test spec: type: ClusterIP ports: - name: http-tf-serving port: 8500 targetPort: 8500 - name: grpc-tf-serving port: 9000 targetPort: 9000 selector: app: eks-neuron-test role: master
  5. TensorFlow モデルサービスアプリケーション用の Kubernetes サービスを作成します。

    kubectl apply -f rn50_service.yaml

(オプション) TensorFlow Serving サービスに対する予測を行う

  1. ローカルでテストするには、gRPC ポートを eks-neuron-test サービスに転送します。

    kubectl port-forward service/eks-neuron-test 8500:8500 &
  2. 次の内容で tensorflow-model-server-infer.py という Python スクリプトを作成します。このスクリプトは、サービスフレームワークである gRPC を介して推論を実行します。

    import numpy as np import grpc import tensorflow as tf from tensorflow.keras.preprocessing import image from tensorflow.keras.applications.resnet50 import preprocess_input from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2_grpc from tensorflow.keras.applications.resnet50 import decode_predictions if __name__ == '__main__': channel = grpc.insecure_channel('localhost:8500') stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) img_file = tf.keras.utils.get_file( "./kitten_small.jpg", "https://raw.githubusercontent.com/awslabs/mxnet-model-server/master/docs/images/kitten_small.jpg") img = image.load_img(img_file, target_size=(224, 224)) img_array = preprocess_input(image.img_to_array(img)[None, ...]) request = predict_pb2.PredictRequest() request.model_spec.name = 'resnet50_inf1' request.inputs['input'].CopyFrom( tf.make_tensor_proto(img_array, shape=img_array.shape)) result = stub.Predict(request) prediction = tf.make_ndarray(result.outputs['output']) print(decode_predictions(prediction))
  3. スクリプトを実行して、予測をサービスに送信します。

    python3 tensorflow-model-server-infer.py

    出力例は次のとおりです。

    [[(u'n02123045', u'tabby', 0.68817204), (u'n02127052', u'lynx', 0.12701613), (u'n02123159', u'tiger_cat', 0.08736559), (u'n02124075', u'Egyptian_cat', 0.063844085), (u'n02128757', u'snow_leopard', 0.009240591)]]