Help us understand the problem. What is going on with this article?

PHP から SageMaker の推論を実行したい

TL;DR

  • Python から SageMaker の推論を行うことが多いと思いますが、フロントエンドが PHP の場合もあると思います。EC2 で実行される PHP から SageMaker の推論を実行する方法を書いてみました。
  • PHP だと numpy を扱えないので、画像だと Base64 とかにエンコードして送ります。

EC2 と SageMaker 間のデータ形式を決める

これは PHP に限らず必要な作業です。フロントエンドの方とML Engineer の方で決めましょう。

SageMaker は基本 Python なので便利な numpy を使いたくなるかもしれません。numpy だと TensorFlow, PyTorch, MXNet など各種モデルに簡単にデータを渡すことができます。一方、送る側が PHP だと、Python のライブラリである numpy でデータを送るのは難しいです。そこで、numpy ではなくて、PHP でも扱える形式、例えば json や画像の場合は Base64 などを使ってみましょう。ここでは EC2 にある画像を Base64 にエンコードして SageMaker に送ることを考えます。

SageMaker のエンドポイントを作る

ここでは試しに GluonCV の Semantic segmentation の pretrained モデルである DeepLab のモデルをホストします。
デプロイまでの流れは Gist に上げました。
https://gist.github.com/harusametime/ac3050d25c47b23534784ce8fb09da6b

デプロイには推論用のスクリプトが必要で、ここにBase64の画像をどう扱うかも実装します。まずモデル読み込みの model_fn 関数の実装です。これは gluoncv の関数一発で呼び出せます。GPU が使えるインスタンスを想定しているので、CPU のみの場合は、mx.cpu() を ctx にしてくださいね。

def model_fn(model_dir):
    net = gluoncv.model_zoo.get_deeplab_resnet50_ade(pretrained=True, ctx=mx.gpu())
    return net

さて次は、上記のモデルにデータを渡して予測する transform_fn の実装です。最初に base64 のデコードをしたら、data には画像ファイルが入りますので、PIL Image で開いて、numpy変換、mxnet array へ変換します。DeepLab の前処理の test_transformを実行してから、predict で予測する流れです。Semantic Segmentation なので、ピクセルごとにラベルの候補を複数出してきます。ここでは一番尤もらしいラベルを出すとして、argmax で確率が最大のものを出します。最後に json 形式でレスポンスを返します。

def transform_fn(net, data, input_content_type, output_content_type):

    data = base64.b64decode(data)
    data = Image.open(BytesIO(data))
    data = np.array(data)
    data = mx.nd.array(data,ctx=mx.gpu())
    data = test_transform(data,ctx=mx.gpu())
    output = net.predict(data)
    predict = mx.nd.squeeze(mx.nd.argmax(output, 1)).asnumpy().tolist()
    response_body = json.dumps(predict)
    return response_body, output_content_type

EC2側の PHP 実装

PHPを触るのは15年ぶりくらいな気がしますがやっていきましょう。SageMaker の推論を Python から実行する際に Python SDK (Boto3) が必要なように、PHPには AWS SDK for PHP が必要です。

EC2 から SageMaker の推論ができるよう IAM Role を付与

IAM Role の新規作成画面から EC2 を選んで、EC2 で使えるロールを作ります。

そのロールから SageMaker を操作できるよう AmazonSageMakerFullAccess のポリシーを付与します。あとは名前を付けて保存しましょう。

保存したロールはEC2の画面でインスタンスに付与できます。
pic3.png

AWS SDK for PHP のインストール

公式のインストールガイドを参考にしましょう。
まず、PHPのバージョンが 5.5 以降が推奨です。私は使っている環境が PHP 5.3 だったのでバージョンを上げました。php をアンインストールして再インストールするだけです。もっとスマートなやり方があるかもしれません。php-xml はAWS SDK に必要だったのでいれました。

yum -y remove php-*
yum -y remove httpd-tools
yum clean all
yum -y install php
yum -y install php-xml

php が入ったら次は Composer のインストールです。インストールガイド に従えば良いですが、私はガイドからリンクされているHow do I install Composer programmatically? のファイルを作ってシェルから実行しました。

Composer が入ったら、いよいよ AWS SDK のインストールです。

composer require aws/aws-sdk-php

これを実行するとインストール完了です。実行したディレクトリに vendor というフォルダができて、そこには autoload.php というファイルができています。このファイルを PHP のスクリプトから呼べば、AWS SDK を全部読んでくれるようです。

PHPのスクリプトを実行

推論を実行する際は InvokeEndpoint を実行しますので、AWS SDK for PHP のドキュメントから使い方を探します。

https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-runtime.sagemaker-2017-05-13.html

$result = $client->invokeEndpoint([
    'Accept' => '<string>',
    'Body' => <string || resource || Psr\Http\Message\StreamInterface>, // REQUIRED
    'ContentType' => '<string>',
    'CustomAttributes' => '<string>',
    'EndpointName' => '<string>', // REQUIRED
    'InferenceId' => '<string>',
    'TargetModel' => '<string>',
    'TargetVariant' => '<string>',
]);

REQUIRED なのは BodyEndpointName ですね。Body には Base64画像を入れて、EndpointName には上の方で作成したエンドポイントの名前を入れます。わからない方は、SageMaker のコンソールから確認できます。



では、このあたりをいれて php のコードを書いてみるとこんな感じです。

<?php
require 'vendor/autoload.php';
use Aws\SageMakerRuntime\SageMakerRuntimeClient;

$im = file_get_contents("./image.jpg");
$base64img = base64_encode($im);
$client = new SageMakerRuntimeClient([
    'region'  => 'us-west-2',
        'version' => '2017-05-13'
]);

$result = $client->invokeEndpoint([
    'Body' => $base64img, // REQUIRED
    'EndpointName' => 'エンドポイントの名前', // REQUIRED
   ]);
print($result['Body']);
?>

実行してみるとこんな感じです。

[ec2-user@ip-10-0-0-144 ~]$ php predict.php
 [[5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0,
...(途中省略)
 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0,
3.0, 3.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]

2次元配列なので画像の縦・横に対応していて、値としてラベル 5 とか 3 とか 0 が入っていますね。
ADE20K データセットでは、5 は天井、3 は床、0 は壁なので、与えた画像は室内の画像で、最初(上の方)に天井が写っていることが予測されますね。

harusamet
この発言は個人の見解であり所属する組織の公式見解ではありません
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away