はじめに
ここでは、これまでLambdaで行なっていた推論をSageMakerに置き換えた話を書きます。
置換え前は、推論を行うLambdaをデプロイするための準備作業(デプロイ先環境に合わせたコード整理など)が煩雑で、データ分析者とデプロイ担当のエンジニア間での役割分担がうまくできていないという課題がありました。 そこで、全体の開発フローも含めて見直そうということでSageMakerへの置換えを検討しました。
結論
役割を完全に分けることは難しいが分析担当者とエンジニアがお互い歩み寄るための機能がSageMakerに備わっていることが分かりました。 またそれらを活用すべく、SageMakerへ置換え可能な箇所に関しては置換えを行いました。
対象読者
機械学習案件における役割分担を検討したい方にとっての一例として参考になればと思います。 主にSageMakerを検討する際に何を考えどう判断したかを記しています。 それなりの規模の組織で既に役割が明確に別れている場合や、SageMakerの使い方を詳しく知りたいといった方にとってはあまり参考にならないかと思います。
前提条件
以降、下記前提で話を進めます。
- 外部システム上のデータを使用し、学習・推論を行う調和技研のシステムを提供する
- 外部システムとは独立したシステムであり、HTTP経由で通信する
- 本推論ではリアルタイム性を問われないため、定時バッチで推論を行う
- 登場人物
移行前の構成
上図が移行前の構成をシンプルに表した図です。各サービスの役割や処理の流れは以下の通りです。
- 予め分析担当者がモデルをJupyter上などで生成しS3へ配置
- Lambda
- 予め取得したデータをS3へ格納し、定時バッチによる推論
- 推論結果をS3へ格納
- 外部サービスからのリクエストを受け、推論結果を返戻
移行後の構成
基本的には移行前と同じですが、それを以下のように変更しました。
- 特定の作業をSageMakerの組込み機能を用いて行う
- 学習実行
- 学習済みモデルのデプロイ
- 推論用エンドポイントの作成
これまで手動や自前のスクリプトで行なっていた作業をSageMaker組込み機能により行うことができるようになります。これによって、よりシンプルな操作で作業することが可能になります。特に、SageMaker上で分析用コードを作成していれば、そのままデプロイまで行うことができます。また、他プロジェクトでもSageMakerを採用していれば、作業を統一でき属人化を防ぐことができます。
「移行前の構成」にした理由
APIを構築するにあたりLambdaを採用した理由は運用コストを減らすためです。 死活監視などの手間をできるだけ減らし分析に集中するため、EC2ではなくLambdaを選択しました。 また選択当時、機能としてLambdaで十分だったためSageMakerはあえて選択しませんでした。
この時の開発の流れは以下の通りでした。
- Jupyter上で分析、モデル作成
- モデルをS3へ配置
- 分析用コードをプロダクト用コードとして整理
- 整理したコードをLambdaへデプロイ
移行理由
その後、機能や人員が増える一方で、分析担当者皆がLambdaを用いた開発が得意という訳ではない状況になってきました。 特に、分析担当者が書いたコードをLambda用に書き換える作業のコストが高く、この作業を減らしたいという要望がありました。
この工程としては以下の通りです。
- Jupyter上で書かれたコードをPythonスクリプトへ転記
- 継続してメンテナンス可能なよう必要に応じてクラス化など行う
- 学習・推論などのテストコード作成
- モデルなどの参照パスをデプロイ先のファイルを参照するよう変更
- FlaskによるAPIインターフェイスの作成
- APIリクエスト含むテストコード作成
- Lambdaへデプロイ
- 動作確認できるまで修正加え上記作業繰り返し
また、1回のデプロイ処理に20分前後かかっていたため、うまくいくまでの試行錯誤に非常に時間がかかりました。
まとめると
- 問題: デプロイするための準備(コード整理)が高コスト
- 原因: 分析時のコードとデプロイ時のコードに乖離があること
といったところです。 そこで、SageMakerを導入し
- 分析担当者が書いたコードをそのままデプロイできないか
- 各々の役割の範囲を明確に分けることができないか
ということを検討しました。
結論としては、完全に分けることは難しいが分析担当者とエンジニアがお互い歩み寄るための機能が備わっていることが分かりました。 その機能というのは前にも述べていますが、SageMaker上で分析、学習、モデルデプロイ、推論用エンドポイントの作成を比較的簡単に行うことができるというものです。
またメリットであると感じた点は、分析時からデプロイ時の環境(モデルの配置先など)などを意識してコーディングすることになるため、その修正作業が最小限になるといった点です。Lambdaでも同じことが言えるのではないかと考えると思いますが、分析からデプロイまでSageMaker上で行うことを想定されてつくられているため、Lambda使用時よりシームレスにコーディング可能だと感じました。
これらを活用し、分析担当者が分析からモデルのデプロイまで担当可能な環境を整えることを目指しました。
SageMaker上での開発フロー
ここでは、SageMakerを用いた具体的な開発について記します。 対象としている作業の範囲は以下の通りです。
- ノートブックにおける下準備
- 分析ステップ
- 学習ステップ
- モデルデプロイステップ
主にSageMakerの機能について触れ、上記前後に必要であろうデータ準備や外部サービスとの連携については割愛します。 ここまでの作業を分析担当者が行うことで、その後のエンジニア担当作業への連携を円滑に行うことができる想定です。
基本的には以下を参考に進めると良いです。本稿ではすぐに使えるよう要点のみ記します。 まずは、ステップ 2: Amazon SageMaker ノートブックインスタンスの作成 - Amazon SageMakerまで進めて、ノートブックを作成します。
ノートブックにおける下準備
ノートブックを作成したので、最初に下準備を行います。 これ以降はノートブック上での作業となります。
モジュールインポート
import sagemaker from sagemaker import get_execution_role
Session
初期化
sess = sagemaker.Session()
AWSログインユーザアカウント情報やリージョン名の取得
role = get_execution_role() account = sess.boto_session.client("sts").get_caller_identity()["Account"] region = sess.boto_session.region_name
データ格納先の指定
S3へデータをアップロードします。 ここでは、ノートブックインスタンス上のデータ格納先ディレクトリを指定し、S3へアップロードしています。
data_location = sess.upload_data("notebook_instance_dirname")
分析ステップ
分析担当者が思い思いの実験を行います。ここで有効だと判断した手法を次の学習ステップで使用しましょう。 また、このステップは必ずしもSageMakerを介して行う必要はありません。各自の環境で実験し、学習はSageMakerのリソースを使用するといったことも可能です。 ただしその場合は、SageMaker組込みの機能を使用せず、自前のDockerイメージをECRへ登録することになります。結構はまりどころが多く長くなってしまうのでこの話は別の機会に。
(いきなりECRに触れましたが、自前のDockerイメージを使わない限りは特別ECRの存在を意識せず済むよう隠蔽されたつくりとなっています。この後のステップで出てくる「イメージ」というのはSageMakerで使用するアルゴリズムが登録されたもの程度の認識でも問題ないと思います。重要なのはそういったことを意識せず済むということなので。)
学習ステップ
イメージ指定
まず、学習に使用するアルゴリズムのコンテナイメージを指定します。ここではSageMaker組込みのアルゴリズムの XGBoost
を使用します。
from sagemaker.amazon.amazon_estimator import get_image_uri image = get_image_uri(region, 'xgboost')
独自の学習・推論コンテナイメージを使用することも可能で、その場合はECRに登録したイメージを参照することになります。 ただ、推論用にWebサーバを含めたりとデータ分析範囲外の作業が発生するためエンジニアの協力が必要です。基本的には組込みのイメージで済ませる方がシンプルで分かりやすいシステムを構築できると思います。
Estimator
インスタンス作成
XGBoost
を指定した推定器 Estimator
のインスタンスを作成します。
output_path
は、SageMakerが学習結果を保存するのに使用します。
train_instance_type
や train_instance_count
は学習タスクの重さに応じて変更してください。
est = sagemaker.estimator.Estimator( image_name=image, role=role, train_instance_count=1, train_instance_type="ml.m4.xlarge", output_path="s3://hoge/output", sagemaker_session=sess )
ハイパーパラメータの設定
est.set_hyperparameters( max_depth = 5, eta = .2, gamma = 4, min_child_weight = 6, silent = 0, objective = "multi:softmax", num_class = 10, num_round = 10 )
学習開始
実行すると学習が始まるので終わるまで待ちます。
est.fit(data_location + "data.csv")
モデルデプロイステップ
モデルをデプロイすることで、Lambdaからのリクエストを受け推論を行うことができるようになります。
デプロイ
ここではCSVの入力を想定しています。そのため serializer=csv_serializer
を指定します。
initial_instance_count
や instance_type
は適宜変更してください。
実行するとデプロイが始まるので終わるまで待ちます。
from sagemaker.predictor import csv_serializer predictor = est.deploy( initial_instance_count=1, instance_type="ml.t2.medium", serializer=csv_serializer, content_type="text/csv" )
デプロイしたモデルで推論可能か確認
最後に、デプロイしたモデルで実際に推論を行うことができるか確認します。 推論動作の確認なので、学習に使用したデータをそのまま使用します。
import pandas as pd df = pd.read_csv(data_location + "/data.csv") predictor.predict(df.to_csv())
predict()
を実行すると結果が返ってくることが確認できるでしょう。
以上でノートブックインスタンス上での作業は終了です。
まとめ
これまでLambdaで開発を行なっていましたが、分析用コードをプロダクトに適用する作業のコストが高いため、SageMakerによりそのコストを下げることができないか検討しました。 その結果、コストを下げるために分析担当者とエンジニアがお互い歩み寄る必要はあるが、SageMakerにはそのための機能が備わっていることがわかりました。後半に記したコードをテンプレート化すれば、分析担当者、エンジニアともに同じ作業が可能であり、属人化を防ぐことができます。
この部分は各社試行錯誤をしており、これといった解決策があるわけではありません。完全に役割を分けてしまうというよりは、分析担当者とエンジニアがお互いに協力する必要がある作業をいかに効率良く行うかというのが重要なのではないかと思います。
参照
- Amazon SageMaker とは (Amazon SageMaker 開発者ガイド)
- Amazon SageMaker Python SDK
- Iris Training and Prediction with Sagemaker Scikit-learn
- Building your own algorithm container
- SageMaker Scikit-learn Container
- Amazon SageMakerで独自の学習/推論用コンテナイメージを作ってみる
- Start Your Machine Learning on AWS SageMaker
- AWS Lambda とは (AWS Lambda 開発者ガイド