Amazon Web Services ブログ

Amazon SageMaker Model Monitor – 機械学習モデルのためのフルマネージドな自動監視

2019年12月3日、Amazon SageMaker の新しい機能であり、本番環境にある機械学習 (ML) モデルを監視し、データの品質に問題が発生した場合には通知する、Amazon SageMaker Model Monitor を発表できることを嬉しく思います。

私がデータ関連の仕事を始めてから最初に学んだことは、データの品質に注意を払って払いすぎることは無いということでした。予期しない NULL な値を持ったデータや、特殊な文字エンコーディングがなされたデータベースに格納されていたデータによる問題の解決に数時間かかった経験がある人は挙手して下さい。

モデルは文字通り大量のデータから構築されるため、機械学習の実践者がなぜ多くの時間を、データセットの確認に時間を使うのかは容易に理解できます。特に、彼らは学習用データセット(モデルの学習に使われる)と、評価用データセット(精度を測るのに使われる)のデータサンプルが同じ統計的な特徴を持っていることを確認します。

そこに魔物がいます!実験に使われるデータセットを完全にコントロールしたとしても、構築したモデルが受け取るであろう、現実世界のデータが同じようにコントロールされるとは言えません。もちろん、現実世界のデータはきれいではないでしょうし、もっと気にかかるのは、受け取るデータの統計的特徴量が徐々に変化するような、データドリフトです。最小値、最大値、平均、分散など、これらの属性は、モデルが学習されている間の仮説や決定を形作ります。これらの値の重大な変化が、推論の精度に影響を与えることは、直感的に感じることができるでしょう。ローン審査予測において、入力データのドリフトや、たとえそれが欠損であったとしても、より多くの額を予測することを想像して下さい。

これらの条件を検知することはとても難しいことです。モデルによって受け取られるデータを捕捉する必要があり、学習用データと比較するために全ての統計解析を行い、ドリフトの発生を検知するルールを定義し、発生した場合にはそれを通知する、といったことをモデルを更新する度に行う必要があります。機械学習実践の専門家は、これらの複雑なツールを開発する方法を確かに知っているでしょうが、多くの時間と労力が必要になります。Undifferentiated heavy lifting が再び襲いかかってきます。

これらの負荷を減らし、お客様が価値創造に集中することを助けるために、我々は Amazon SageMaker Model Monitor を開発しました。詳細をお伝えさせて下さい。

Amazon SageMaker Model Monitor の紹介

典型的なモデル監視は以下のようになります。最初に 既存のもの、もしくは監視を目的に作られた新しいもの、どちらかの SageMaker エンドポイント を開始するところから始めます。Model Monitor は組み込みアルゴリズム組み込みフレームワーク独自コンテナのいずれかであっても、どのようなエンドポイントに対して使うことが出来ます。

SageMaker SDK を使うことによって、エンドポイントへ送付される設定可能なデータを捕捉する(要望に応じて推論結果についても捕捉することができます)ことができ、Amazon Simple Storage Service (S3) バケットを保存することができます。捕捉されたデータはメタデータ(コンテンツタイプやタイムスタンプなど)が付与され、他の S3 オブジェクトと同様に保護とアクセスをすることができます。

その後、エンドポイントにデプロイされているモデルを学習するときに使われるデータからベースラインを作ります(もちろん既存のベースラインを再利用することも可能です)。これは Amazon SageMaker Processing ジョブを起動します。これは Model Monitor が下記のような処理を行うものです。
入力データのスキーマ、例えばタイプやそれぞれの特徴の完全性についての情報を予想します。必要に応じて見返したり、更新することができます。

組み込みコンテナに対してのみ、Amazon (blog postresearch paper) によって開発され、使われているApach Spark ベースのオープンソースのツールである、Deequ を用いて特徴量の統計量を計算します。これらの統計量は データのストリームに対して正確な分位点を計算する先進技術であり、最近我々が Deequ に貢献したKLL sketches も含まれています。

次はこれらのアーティファクトを使って、Model Monitor が集めたデータや推論結果の品質を検査する、監視スケジュールを設定します。組み込みコンテナか、カスタムコンテナかによらず、既にいくつか準備されている組み込みルールが適用され、 S3 にレポートが定期的に保存されます。このレポートは最新の期間に受け取られたデータについて、統計量やスキーマの情報、品質の悪化が起こった場合にはその検知結果が記載されます。

最後ですが、重要な点として、Model Monitor は 特徴量毎の指標を Amazon CloudWatch に出力します。そこでダッシュボードを作成したり、アラートを通知するような設定が可能です。CloudWatch から出力された集約指標は Amazon SageMaker Studio で確認することができ、統計量や監視結果、収集されたデータは Jupyter notebook 上で閲覧やさらなる分析が可能です。

詳細や AWS CloudFormation を使った Model Monitor の活用例は、 開発者ガイドをご確認下さい。
では、 組み込み XGBoost アルゴリズムを用いた離脱予測モデルの学習を例に、デモを見てみましょう。

データ捕捉のための設定

最初のステップはデータ捕捉が可能になるように、エンドポイント設定を設定することです。ここでは、私は全ての入力されるデータと、モデルの推論結果のような出力を捕捉することにしました。また、CSV と JSON データのコンテンツタイプについても設定しました。

data_capture_configuration = {
    "EnableCapture": True,
    "InitialSamplingPercentage": 100,
    "DestinationS3Uri": s3_capture_upload_path,
    "CaptureOptions": [
        { "CaptureMode": "Output" },
        { "CaptureMode": "Input" }
    ],
    "CaptureContentTypeHeader": {
       "CsvContentTypes": ["text/csv"],
       "JsonContentTypes": ["application/json"]
}

次に、CreateEndpoint API を用いて、エンドポイントを作成します。

create_endpoint_config_response = sm_client.create_endpoint_config(
    EndpointConfigName = endpoint_config_name,
    ProductionVariants=[{
        'InstanceType':'ml.m5.xlarge',
        'InitialInstanceCount':1,
        'InitialVariantWeight':1,
        'ModelName':model_name,
        'VariantName':'AllTrafficVariant'
    }],
    DataCaptureConfig = data_capture_configuration)

既存のエンドポイントについては、UpdateEndpoint API でシームレスなエンドポイント設定のアップデートを行います。

何回か、推論のためにエンドポイントを呼び出したあと、S3 に捕捉されたデータを確認することができます(わかりやすさのために、出力は編集されています)。

$ aws s3 ls --recursive s3://sagemaker-us-west-2-123456789012/sagemaker/DEMO-ModelMonitor/datacapture/DEMO-xgb-churn-pred-model-monitor-2019-11-22-07-59-33/AllTrafficVariant/2019/11/22/08/24-40-519-9a9273ca-09c2-45d3-96ab-fc7be2402d43.jsonl
AllTrafficVariant/2019/11/22/08/25-42-243-3e1c653b-8809-4a6b-9d51-69ada40bc809.jsonl

これらのファイルから得られた内容の例です。

"endpointInput":{
"observedContentType":"text/csv",
"mode":"INPUT",
"data":"132,25,113.2,96,269.9,107,229.1,87,7.1,7,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,0,1,0,1,0,0,1",
"encoding":"CSV"
},
"endpointOutput":{
"observedContentType":"text/csv; charset=utf-8",
"mode":"OUTPUT",
"data":"0.01076381653547287",
"encoding":"CSV"}
},
"eventMetadata":{
"eventId":"6ece5c74-7497-43f1-a263-4833557ffd63",
"inferenceTime":"2019-11-22T08:24:40Z"},
"eventVersion":"0"}

私が期待していたものです。次に、このモデルに対するベースラインを作成しましょう。

監視ベースラインの作成

ここはとてもシンプルなステップです。ベースラインデータセットの保存先と結果の保存先をを設定します。

from processingjob_wrapper import ProcessingJob

processing_job = ProcessingJob(sm_client, role).
   create(job_name, baseline_data_uri, baseline_results_uri)

このジョブが完了すると、統計量と制約に関連する2つの新しいオブジェクトを S3 に確認することができます。

aws s3 ls s3://sagemaker-us-west-2-123456789012/sagemaker/DEMO-ModelMonitor/baselining/results/
constraints.json
statistics.json

constraints.json ファイルは学習用データに対する推定されたスキーマ(正確かどうか確認することを忘れずに)について記載されています。それぞれの特徴は型が決められ、その特徴が常にデータが存在するかどうか(1.0 であれば 100% データが存在する)についての情報を得ることができます。下記は、最初のいくつかの行です。

{
  "version" : 0.0,
  "features" : [ {
    "name" : "Churn",
    "inferred_type" : "Integral",
    "completeness" : 1.0
  }, {
    "name" : "Account Length",
    "inferred_type" : "Integral",
    "completeness" : 1.0
  }, {
    "name" : "VMail Message",
    "inferred_type" : "Integral",
    "completeness" : 1.0
  }, {
    "name" : "Day Mins",
    "inferred_type" : "Fractional",
    "completeness" : 1.0
  }, {
    "name" : "Day Calls",
    "inferred_type" : "Integral",
    "completeness" : 1.0

このファイルの最後には、 CloudWath での監視に関して、オン/オフの設定や、ドリフトの閾値などの設定といった情報を確認することができます。

"monitoring_config" : {
    "evaluate_constraints" : "Enabled",
    "emit_metrics" : "Enabled",
    "distribution_constraints" : {
      "enable_comparisons" : true,
      "min_domain_mass" : 1.0,
      "comparison_threshold" : 1.0
    }
  }

statistics.json ファイルはそれぞれの特徴に関して、平均や分位点、ユニークな値といった、エンドポイントが受け取ったデータについて異なる統計量についての記載があります。こちらが例になります。

"name" : "Day Mins",
    "inferred_type" : "Fractional",
    "numerical_statistics" : {
      "common" : {
        "num_present" : 2333,
        "num_missing" : 0
      },
      "mean" : 180.22648949849963,
      "sum" : 420468.3999999996,
      "std_dev" : 53.987178959901556,
      "min" : 0.0,
      "max" : 350.8,
      "distribution" : {
        "kll" : {
          "buckets" : [ {
            "lower_bound" : 0.0,
            "upper_bound" : 35.08,
            "count" : 14.0
          }, {
            "lower_bound" : 35.08,
            "upper_bound" : 70.16,
            "count" : 48.0
          }, {
            "lower_bound" : 70.16,
            "upper_bound" : 105.24000000000001,
            "count" : 130.0
          }, {
            "lower_bound" : 105.24000000000001,
            "upper_bound" : 140.32,
            "count" : 318.0
          }, {
            "lower_bound" : 140.32,
            "upper_bound" : 175.4,
            "count" : 565.0
          }, {
            "lower_bound" : 175.4,
            "upper_bound" : 210.48000000000002,
            "count" : 587.0
          }, {
            "lower_bound" : 210.48000000000002,
            "upper_bound" : 245.56,
            "count" : 423.0
          }, {
            "lower_bound" : 245.56,
            "upper_bound" : 280.64,
            "count" : 180.0
          }, {
            "lower_bound" : 280.64,
            "upper_bound" : 315.72,
            "count" : 58.0
          }, {
            "lower_bound" : 315.72,
            "upper_bound" : 350.8,
            "count" : 10.0
          } ],
          "sketch" : {
            "parameters" : {
              "c" : 0.64,
              "k" : 2048.0
            },
            "data" : [ [ 178.1, 160.3, 197.1, 105.2, 283.1, 113.6, 232.1, 212.7, 73.3, 176.9, 161.9, 128.6, 190.5, 223.2, 157.9, 173.1, 273.5, 275.8, 119.2, 174.6, 133.3, 145.0, 150.6, 220.2, 109.7, 155.4, 172.0, 235.6, 218.5, 92.7, 90.7, 162.3, 146.5, 210.1, 214.4, 194.4, 237.3, 255.9, 197.9, 200.2, 120, ...

では、エンドポイントの監視を始めてみましょう。

エンドポイントの監視

再び、API コールを1つ呼び出せば終わりです。私はシンプルに監視スケジュールを作成するために、ベースラインデータセットについての constraints.json ファイルと statistics.json ファイルを渡しました。オプションとして、データや推論結果の調整のために前処理や後処理のための関数を渡すこともできます。

ms = MonitoringSchedule(sm_client, role)
schedule = ms.create(
   mon_schedule_name, 
   endpoint_name, 
   s3_report_path, 
   # record_preprocessor_source_uri=s3_code_preprocessor_uri, 
   # post_analytics_source_uri=s3_code_postprocessor_uri,
   baseline_statistics_uri=baseline_results_uri + '/statistics.json',
   baseline_constraints_uri=baseline_results_uri+ '/constraints.json'
)

その後、私はエンドポイントに対して、ランダムな値で構成されたサンプルなど、本物ではないデータをエンドポイントへ送信し始めました。そして、レポートを集めるために、Model Monitor を待ちました。とても不安です!

レポートの検査

S3 にあるレポートを簡単に確認します。

mon_executions = sm_client.list_monitoring_executions(MonitoringScheduleName=mon_schedule_name, MaxResults=3)
for execution_summary in mon_executions['MonitoringExecutionSummaries']:
    print("ProcessingJob: {}".format(execution_summary['ProcessingJobArn'].split('/')[1]))
    print('MonitoringExecutionStatus: {} \n'.format(execution_summary['MonitoringExecutionStatus']))

ProcessingJob: model-monitoring-201911221050-df2c7fc4
MonitoringExecutionStatus: Completed 

ProcessingJob: model-monitoring-201911221040-3a738dd7
MonitoringExecutionStatus: Completed 

ProcessingJob: model-monitoring-201911221030-83f15fb9
MonitoringExecutionStatus: Completed 

これらの監視ジョブに関してレポートを見つけましょう。

desc_analytics_job_result=sm_client.describe_processing_job(ProcessingJobName=job_name)
report_uri=desc_analytics_job_result['ProcessingOutputConfig']['Outputs'][0]['S3Output']['S3Uri']
print('Report Uri: {}'.format(report_uri))

Report Uri: s3://sagemaker-us-west-2-123456789012/sagemaker/DEMO-ModelMonitor/reports/2019112208-2019112209

何が確認できるでしょうか?

aws s3 ls s3://sagemaker-us-west-2-123456789012/sagemaker/DEMO-ModelMonitor/reports/2019112208-2019112209/

constraint_violations.json
constraints.json
statistics.json

期待した通り、constraints.json と statistics.json は監視ジョブで処理されたデータサンプルについてのスキーマと統計情報が記載されていました。constraints_violations.json を見てみましょう!

violations" : [ {
    "feature_name" : "State_AL",
    "constraint_check_type" : "data_type_check",
    "description" : "Value: 0.8 does not meet the constraint requirement! "
  }, {
    "feature_name" : "Eve Mins",
    "constraint_check_type" : "baseline_drift_check",
    "description" : "Numerical distance: 0.2711598746081505 exceeds numerical threshold: 0"
  }, {
    "feature_name" : "CustServ Calls",
    "constraint_check_type" : "baseline_drift_check",
    "description" : "Numerical distance: 0.6470588235294117 exceeds numerical threshold: 0"
  }

整数型のデータに対して浮動小数点型のデータを与えてしまっていたようです。確かに上手く動作しないでしょう!

いくつかの特徴もドリフトが発生していることを示しています。データ投入のプロセスの中で、何かが良くないか、もしくは実際にデータの分布が変化していて、モデルを再学習させる必要がありそうです。これらの情報は CloudWatch でも確認することができるので、閾値を定義し、アラームや学習ジョブを自動的に起動するような設定を行うことが出来ます。

利用可能になりました!

これまで見てきたように、Amazon SageMaker Model Monitor は簡単に設定でき、機械学習モデルの品質問題に関して、すぐに知ることができます。

Amazon SageMaker Model Monitor は Amazon SageMaker が 利用可能なすべてのAWS リージョンで現在利用可能です。この機能は 我々の機械学習プロジェクトの開発環境である、Amazon SageMaker Studio にも統合されています。

ぜひ試して頂き、Amazon SageMaker の AWS フォーラム、または通常の AWS へのご連絡先からフィードバックをお寄せください。

翻訳は Machine Learning SA の上総が担当しました。原文は、こちら