Amazon Web Services ブログ

Amazon SageMaker Model Monitor と Debugger を使って不正な予測を検知して分析する

この記事は、Detecting and analyzing incorrect model predictions with Amazon SageMaker Model Monitor and Debugger を翻訳したものです。

畳み込みニューラルネットワーク(CNN)は、画像分類や物体検出などのタスクにおいて、最先端の結果をもたらします。自動運転において道路上の交通標識や物体を検出したり、ヘルスケアにおいて画像から異常部位をより正確に分類したり、小売業において在庫管理をしたりと、さまざまなアプリケーションで使用されています。

しかし、CNN はブラックボックスとなります。これは、どのような理由でその予測になったのかを理解することが重要なアプリケーションでは問題となる可能性があります。また、モデルをデプロイしてから推論に使用するデータが、モデルを学習した際に使用したデータと比較して、非常に異なる分布に変化することがあります。この現象は一般的にデータドリフトと呼ばれ、誤った予測につながる可能性があります。このような状況において、誤った予測につながる原因を理解し、説明できることが重要です。

クラス活性化マップ顕著性マップなどのテクニックを使用すると、CNN モデルがどのように決定を下すかを視覚化できます。ヒートマップとして表現されたこれらのマップは、予測において画像内の重要な部分を明らかにします。次の画像例は、ドイツの交通標識データセットからのものです。左側の画像は、画像クラス 25(道路作業)を予測する、fine-tuning された ResNet モデルへの入力です。右の画像は、ヒートマップでオーバーレイされた入力画像を示しています。赤い領域はクラス 25 を予測するために関連性が最も高く、青い領域は関連性が低い画素を示しています。

CNN の出力を視覚化することは、モデルが不正確な予測を行い、その理由が明確でない場合に特に役立ちます。また、学習データセットにより多くのサンプルが必要かどうか、またはデータセットに偏りがあるかどうかを調べるのに役立ちます。たとえば、道路交通における障害物を検出するオブジェクト検出モデルがあり、学習データセットに夏に採取したサンプルのみが含まれている場合、オブジェクトが雪で覆われる可能性があることが認識されていないため、冬の間はうまく機能しない可能性があります。

この記事では、交通標識分類用のモデルをデプロイし、Amazon SageMaker Model Monitor を設定して、予測スコアが一貫して低い、特定の画像クラスばかり予測されるなどの、予期しないモデルの動作を自動的に検出します。Model Monitor が問題を検出すると、Amazon SageMaker Debugger を使用して、デプロイされたモデルの視覚的な説明情報を取得します。これは、推論中にテンソルを出力するようにエンドポイントを更新し、それらのテンソルを使用して顕著性マップを算出することで可能になります。この記事に記載されているさまざまな手順と結果を再現するには、Amazon SageMaker ノートブックインスタンスまたは Amazon SageMaker Studio 内から GitHub リポジトリを複製し、ノートブックを実行します。

SageMaker モデルの定義

この記事は、ドイツの交通標識データセット [2] を使用して 43 カテゴリの交通標識を区別するために学習した Resnet18 モデルを使用しています。入力画像が与えられると、モデルはそれぞれの画像クラスの確率を出力します。各クラスは、異なる交通標識カテゴリに対応します。私たちは、モデルをファインチューニングしてその重みをGitHubリポジトリにアップロードしました。

Amazon SageMaker にモデルをデプロイする前に、その重みをアーカイブして Amazon Simple Storage Service(Amazon S3)にアップロードする必要があります。Jupyterノートブックのセルに次のコードを入力します。

sagemaker_session.upload_data(path='model.tar.gz', key_prefix='model')

Amazon SageMaker ホスティングサービスを使用して、モデルから予測を取得するための永続的なエンドポイントを立ち上げます。そのために、モデルアーカイブの Amazon S3 パスを受ける PyTorch モデルオブジェクトを定義する必要があります。model_fn 関数transform_fn 関数を実装する entry_point ファイル pretrained_model.py を定義します。ホスト時にこれらの関数を使用して、モデルが推論コンテナ内に正しくロードされ、推論リクエストが正しく処理されていることを確認します。次のコードを参照してください。

from sagemaker.pytorch.model import PyTorchModel

model = PyTorchModel(model_data = 's3://' + sagemaker_session.default_bucket() + '/model/model.tar.gz',
                        role = role,
                        framework_version = '1.5.0',
                        source_dir='entry_point',
                        entry_point = 'pretrained_model.py',
                        py_version='py3')

Model Monitor のセットアップとモデルのデプロイ

Model Monitor は、稼働中の機械学習モデルを自動的に監視し、データ品質の問題を検出すると警告します。このソリューションでは、エンドポイントの入力と出力をキャプチャし、Model Monitor が収集したデータとモデルの予測結果を検査するために監視スケジュールを作成します。DataCaptureConfig API は、Model Monitor が Amazon S3 バケットに入力と出力を保存する際のサンプリング率を指定します。次の例では、サンプリング率を 50% に設定します。

from sagemaker.model_monitor import DataCaptureConfig

data_capture_config = DataCaptureConfig(
                   enable_capture=True,
                   sampling_percentage=50,
                   destination_s3_uri='s3://' + sagemaker_session.default_bucket() + '/endpoint/data_capture'
)

エンドポイントを ml.m5.xlarge インスタンスにデプロイするには、次のコードを入力します。

predictor = model.deploy(initial_instance_count=1,
                       instance_type='ml.m5.xlarge',
                       data_capture_config=data_capture_config)

endpoint_name = predictor.endpoint

テスト画像を使って推論

これで、シリアル化された入力画像を含むペイロードを使用してエンドポイントを呼び出すことができます。エンドポイントは transform_fn 関数を呼び出して、モデルの推論を実行する前にデータを前処理します。エンドポイントは、JSON 文字列でエンコードされた整数のリストとして、画像ストリームの予測クラスを返します。次のコードを参照してください。

#invoke payload
response = runtime.invoke_endpoint(EndpointName=endpoint_name, Body=payload)
response_body = response['Body']

#get results
result = json.loads(response_body.read().decode())

これで、いくつかのテスト画像とその予測クラスを可視化できます。次に示す画像は、エンドポイントに送信した交通標識画像で、上部に表示されたラベルはエンドポイントから受信した予測結果です。次の図は、エンドポイントがクラス 23 (Slippery road) を正しく予測したことを示しています。

次の図は、エンドポイントがクラス 25 (Road work) を正しく予測したことを示しています。

Model Monitor スケジュールの作成

次に、Model Monitor を使用してモニタリングスケジュールを設定する方法を示します。Model Monitor には、平均、分位数、標準偏差などの制約と統計情報を計算してベースラインを作成するための組み込みコンテナが用意されています。その後、監視スケジュールを起動して、定期的に処理ジョブを開始して、収集されたデータを検査し、指定された制約に対してデータを比較し、違反レポートを生成することができます。

このユースケースでは、モデルの単純な健全性チェックを実行するカスタムコンテナを作成します。このコンテナは、予測された画像クラスをカウントする評価スクリプトを実行します。モデルが特定の道路標識を他のクラスよりも頻繁に予測する場合、または信頼スコアが一貫して低い場合は、問題として表示されます。

たとえば、特定の入力画像では、モデルは信頼スコアに基づいてランク付けされた予測クラスのリストを返します。上位 3 つの予測が関連のないクラスに対応し、それぞれの信頼スコアが 50% 未満の場合 (たとえば、最初の予測として
Stop sign、2 番目の予測は Turn left、3 番目は Speed limit 180 km/h)、これらの予測を信頼したくないかもしれません。

カスタムコンテナを構築し、Amazon Elastic Container Registry (Amazon ECR) にアップロードする方法の詳細については、ノートブックを参照してください。次のコードでは、Amazon ECR 内の Docker イメージの場所と、評価スクリプトに必要な環境変数を指定する ModelMonitor オブジェクトを作成します。コンテナのエントリポイントファイルは、評価スクリプトです。

monitor = ModelMonitor(
         role=role,
         image_uri='%s.dkr.ecr.us-west-2.amazonaws.com/sagemaker-processing-container:latest' %my_account_id,
         instance_count=1,
         instance_type='ml.m5.xlarge',
         env={'THRESHOLD':'0.5'}
)

次に、Model Monitor スケジュールを定義してエンドポイントにアタッチします。このスケジュールは、カスタムコンテナを 1時間ごとに実行します。次のコードを参照してください。

from sagemaker.model_monitor import CronExpressionGenerator

from sagemaker.processing import ProcessingInput, ProcessingOutput

destination = 's3://' + sagemaker_session.default_bucket() + '/endpoint/monitoring_schedule'
processing_output = ProcessingOutput(output_name='model_outputs', source='/opt/ml/processing/outputs', destination=destination)
output = MonitoringOutput(source=processing_output.source, destination=processing_output.destination)

monitor.create_monitoring_schedule(
         output=output,
         endpoint_input=predictor.endpoint,
         schedule_cron_expression=CronExpressionGenerator.hourly()
)

前述のように、スクリプト evaluation.py はモデルの単純な健全性チェックを実行し、モデルの予測をカウントします。Model Monitor は、モデルの入力と出力を JSON-line 形式のファイルとしてAmazon S3 に保存します。これらは、/opt/ml/processing/input の中の処理コンテナにダウンロードされます。その後、['captureData'] ['endPointOutput'] ['data'] を使用して予測を読み込むことができます。次のコードを参照してください。

for file in files:
  content = open(file).read()
for entry in content.split('\n'):
  prediction = json.loads(entry)['captureData']['endpointOutput']['data']

処理ジョブのステータスは、CloudWatch および SageMaker Studio でも追跡できます。次のスクリーンショットでは、問題が見つからなかったことが SageMaker Studio に示されています。

モデルの予期しない挙動の検知

スケジュールの定義が完了し、モデルをリアルタイムで監視する準備が整いました。今までの準備によって予期しない動作をキャプチャできるようになったことを確認するために、強制的に誤った予測をさせます。そのために、AdvBox Toolkit [3] を使用します。これは、モデルが正しいクラスをまったく認識できないように画素単位でノイズを加えます。このようなノイズは adversarial attacks とも呼ばれ、通常、人間には見えません。これを使って、誤って Stop sign と予測されるようなテスト画像をいくつか作成しました。次に示す画像のセットでは、左の画像はオリジナル、中央は変換された敵対画像、右は両方の差分画像です。元の画像と変換された画像は似ていますが、敵対画像は正しく分類されていないことがわかります。

次の画像は、誤認識された別の画像です。

Model Monitor は、スケジュールされた次の処理ジョブを実行するときに、キャプチャされ Amazon S3 に格納された予測を分析します。ジョブは、予測された画像クラスをカウントします。1 つのクラスが時間の 50% を超えると問題発生となります。エンドポイントに敵対画像を送信したため、クラス 14(Stop)の異常なカウントを確認できるようになりました。SageMaker Studio では、処理ジョブの状態を追跡できます。次のスクリーンショットで、SageMaker Studio は、最後にスケジュールされたジョブで問題が見つかったことを示しています。

Amazon CloudWatch ログから詳細情報を取得できます。処理ジョブは、キーが 43 個のクラスのいずれかで、値はカウントを示す情報を辞書形式で表示します。たとえば、次の出力では、エンドポイントはクラス 9(No passing)を 2 回予測し、クラス 14(Stop)では異常な数を予測しています。合計 400 回の予測のうち 322 回をクラス 14 と予測しており、これは閾値として設定した 50% よりも多い割合です。辞書の値は CloudWatch メトリクスとしても保存されるため、CloudWatch コンソールを使用してメトリクスデータのグラフを作成できます。

Warning: Class 14 ('Stop sign') predicted more than 80 % of the time which is above the threshold
Predicted classes {9: 2, 19: 2, 25: 1, 14: 322, 13: 5, 5: 1, 8: 10, 18: 1, 31: 4, 26: 8, 33: 4, 36: 4, 29: 20, 12: 8, 22: 4, 6: 4}

処理ジョブで問題が見つかったので、今度は、さらなる洞察を得てみましょう。上記のテスト画像を見ると、元の画像と敵対画像の間に有意な違いはありません。モデルが見たものをよりよく理解するために、こちらの論文 Full-Gradient Representation for Neural Network Visualization  [1] に記載されている技術を使用することができます。ここでは、入力データと中間フィーチャマップの重要スコアを使用します。次のセクションでは、モデル自体を変更することなく、これらの変数をテンソルとして簡単に取得するように Debugger を設定する方法を示します。また、これらのテンソルを使用して顕著性マップを計算する方法についても詳しく説明します。

Debugger の hook の設定

テンソルを取得するには、学習済みモデルの Python スクリプト pretrained_model.py を更新する必要があります。このスクリプトは、先ほど Amazon SageMaker PyTorch モデルを設定する際に使用したものです。model_fn で Debugger hook 設定を作成し、hook はカスタマイズした文字列をパラメータ include_regex に設定します。ここでは、収集したいテンソルの名前の完全または部分的な正規表現を渡します。次のセクションでは、顕著性マップを計算する方法について詳しく説明します。計算には、BatchNorm、ダウンサンプリングレイヤ、モデル入力などの中間レイヤからのバイアスと勾配が必要です。テンソルを取得するには、次の正規表現を指定します。

'.*bn|.*bias|.*downsample|.*ResNet_input|.*image'

テンソルを Amazon SageMaker のデフォルトバケットに保存します。次のコードを参照してください。

def model_fn(model_dir):

#load model
model = resnet.resnet18()
model.load_state_dict(torch.load(model_dir))
model.eval()

#hook configuration
save_config = smd.SaveConfig(mode_save_configs={
        smd.modes.PREDICT: smd.SaveConfigMode(save_interval=1)
})

hook = Hook("s3://" + sagemaker_session.default_bucket() + "tensors",
            save_config=save_config,
            include_regex='.*bn|.*bias|.*downsample|.*ResNet_input|.*image' )

#register hook
hook.register_module(model)

#set mode
hook.set_mode(modes.PREDICT)

return model

新しいエントリポイントスクリプト pretrained_model_with_debugger_hook.py を使用して、新しい PyTorch モデルを作成します。

model = PyTorchModel(model_data = 's3://' + sagemaker_session.default_bucket() + '/model/model.tar.gz',
                   role = role,
                   framework_version = '1.3.1',
                   source_dir='code',
                   entry_point = 'pretrained_model_with_debugger_hook.py',
                   py_version='py3')

Debugger hook が追加されたモデルスクリプトを使用した新しい PyTorch model オブジェクトを使用して、既存のエンドポイントを更新します。

predictor = model.deploy(
            instance_type = 'ml.m5.xlarge',
            initial_instance_count=1,
            endpoint_name=endpoint_name,
            data_capture_config=data_capture_config,
            update_endpoint=True)

これで、推論リクエストが行われるたびに、エンドポイントはテンソルを記録して Amazon S3 にアップロードします。これで、顕著性マップを計算して、モデルから視覚的な説明を得ることができます。

Debugger で不正な予測を分析

分類モデルは、通常、0 から 1 の確率の配列を出力します。各エントリはデータセット内のラベルに対応します。たとえば、MNIST (10 クラス) の場合、モデルは 8 が書かれた入力画像に対して [0.08, 0, 0, 0, 0, 0, 0.12, 0, 0.5, 0.3] という予測を行います。つまり、画像は 8% の確率で 0、12% の確率で 6、50% の確率で 8、30% の確率で 9 と予測されます。顕著性マップを生成するには、最も確率の高いクラス(このユースケースではクラス 8)を取り、スコアをネットワークの前のレイヤにマッピングして、この予測の重要なニューロンを特定します。CNN は多くのレイヤで構成されており、それぞれの中間値が予測にどのように影響したかを示す重要度スコアが算出されます。

入力に対するモデルからの予測結果の勾配を使用して、重要度スコアを決定します。勾配は、入力が変更されたときの出力の変化量を示します。それらを記録するには、レイヤ出力に backward hook を登録し、推論中に backward call をトリガします。関連するテンソルをキャプチャするように Debugger hook を設定しました。

エンドポイントを更新し、いくつかの推論要求を実行した後、trial object を作成できます。これにより、Debugger が保存したデータへのアクセス、クエリ、およびフィルタ処理を行うことができます。次のコードを参照してください。

from smdebug.trials import create_trial

trial = create_trial('s3://' + sagemaker_session.default_bucket() + '/endpoint/tensors')

Debugger を使用すると、trial.tensor().value() を介してデータにアクセスできます。たとえば、最初の推論リクエストの最初の BatchNorm 層のバイアステンソルを取得するには、次のコードを入力します。

trial.tensor('ResNet_bn1.bias').value(step_num=0, mode=modes.PREDICT)

関数 trial.steps(mode=modes.PREDICT) は、記録された推論リクエストの数に対応する利用可能なステップ数を返します。

次の手順では、入力された勾配とフィーチャレベルのバイアス勾配を集約する FullGrad メソッドに基づいて顕著性マップを計算します。

暗黙的なバイアスを算出

FullGrad メソッドでは、ResNet18 の BatchNorm レイヤが暗黙的なバイアスを導入します。実行中のレイヤの平均、分散、および重みを取得することにより、暗黙的なバイアスを計算できます。次のコードを参照してください。

weight = trial.tensor(weight_name).value(step_num=step, mode=modes.PREDICT)
running_var = trial.tensor(running_var_name).value(step_num=step, mode=modes.PREDICT)
running_mean = trial.tensor(running_mean_name).value(step_num=step, mode=modes.PREDICT)
implicit_bias = - running_mean / np.sqrt(running_var) * weight

勾配とバイアスを積算

バイアスは、明示的バイアスと暗黙的なバイアスの合計です。フィーチャマップに対する出力の勾配を取得し、バイアスと勾配の積を計算することができます。次のコードを参照してください。

gradient = trial.tensor(gradient_name).value(step_num=step, mode=modes.PREDICT)
bias = trial.tensor(bias_name).value(step_num=step, mode=modes.PREDICT)
bias = bias + implicit_bias
bias_gradient = normalize(np.abs(bias * gradient))

補間と集約

中間レイヤは通常、入力画像と同じ寸法を持たないため、補間する必要があります。これは、すべてのバイアスの勾配に対して実行し、結果を集約します。全体を統合すると、元の入力画像にヒートマップとしてオーバーレイする顕著性マップが得られます。次のコードを参照してください。

for channel in range(bias_gradient.shape[1]):
interpolated = scipy.ndimage.zoom(bias_gradient[0,channel,:,:], image_size/bias_gradient.shape[2], order=1)
saliency_map += interpolated

結果

このセクションでは、モデルが Stop sign として分類した敵対画像の例をいくつか紹介します。右の図は、入力画像に顕著性マップをオーバーレイしたものです。赤い領域は、モデル予測で最大の影響を与えた領域を示し、adversarial attacks によって画素値が変換された位置を示します。たとえば、関連するオブジェクトの特徴がモデルによって考慮されなくなり、ほとんどの場合、信頼スコアが低くなっていることがわかります。

比較のために、我々はまた、元の(非敵対的な)画像で推論を行います。次に示す画像では、左側の画像は敵対画像と、予測された画像クラス Stop sign に対応する顕著性マップです。右側の画像は、元の入力画像(非敵対的)と、予測された画像クラス(Ground Truth ラベルと一致)に対応する顕著性マップを示しています。非敵対画像の場合、モデルは関連するオブジェクトの特徴にのみ焦点を当てるため、高い確率で正しい画像クラスを予測します。敵対画像の場合、モデルは関連するオブジェクトの外にある他の多くの特徴を考慮に入れます。これは、ランダムな画素値の変換によって引き起こされます。

まとめ

この記事では、Amazon SageMaker Model Monitor と Amazon SageMaker Debugger を使用して、予期しないモデルの挙動を自動的に検出し、CNN から視覚的な説明情報を取得する方法を紹介しました。詳細については、GitHub リポジトリを参照してください。

References

[1] Suraj Srinivas, Francois Fleuret, Full-gradient representation for neural network visualization, Advances in Neural Information Processing Systems (NeurIPS), 2019
[2] Johannes Stallkamp, Marc Schlipsing, Jan Salmen, Christian Igel, The German traffic sign recognition benchmark: A multi-class classification competition, The 2011 International Joint Conference on Neural Networks, 2011
[3] Dou Goodman, Hao Xin, Wang Yang, Wu Yuesheng, Xiong Junfeng, Zhang Huan, Advbox: a toolbox to generate adversarial examples that fool neural networks


About the Authors

Nathalie Rauschmayr is an Applied Scientist at AWS, where she helps customers develop deep learning applications.

 

 

 

Vikas Kumar is Senior Software Engineer for AWS Deep Learning, focusing on building scalable deep learning systems and providing insights into deep learning models. Prior to this Vikas has worked on building distributed databases and service discovery software. In his spare time he enjoys reading and music.

 

 

Satadal Bhattacharjee is Principal Product Manager at AWS AI. He leads the machine learning engine PM team on projects such as SageMaker and optimizes machine learning frameworks such as TensorFlow, PyTorch, and MXNet.