Amazon Web Services ブログ

Amazon SageMaker で疑わしい医療費請求にフラグを立てる



National Health Care Anti-Fraud Association (NHCAA) は、医療費詐欺が年間約 680 億 USD かかると推定しています。これは、医療支出の 2 兆 2600 億 USD の 3% に相当します。これは控えめな見積もりです。他の見積りでは、高くて年間の医療費の 10%、つまり 2,300 億 USD と推定されています。

医療費詐欺は、必然的に消費者の保険料と自己負担額の増加につながり、給付や補償が減少することにもなります。

請求に不正行為のラベルを付けるには、複雑で詳細な調査が必要になる場合があります。この記事では、Amazon SageMaker モデルをトレーニングすることで、異常な事後払いのメディケア入院費請求にフラグを立て、それを不正の疑いについてさらに調査するターゲットとする方法をご紹介します。ソリューションにはラベル付きデータは必要ありません。教師なし機械学習 (ML) を使用して、疑わしい請求にフラグを立てるモデルを作成します。

異常検出は、次の課題があるため困難な問題です。

  • データの正常性と異常の違いは、明確ではないことがよくあります。異常の検出方法は、アプリケーション固有のものである場合があります。たとえば、臨床データでは、わずかな偏差が外れ値になる可能性がありますが、マーケティングアプリケーションでは、外れ値を正当化するには大幅な偏差が必要です。
  • データのノイズは、属性値または欠損値の偏差として表示される場合があります。ノイズは外れ値を隠すか、逸脱を外れ値としてフラグを立てます。
  • 外れ値の明確な正当化を実現するのは難しいかもしれません。

このソリューションでは、Amazon SageMaker を使用します。これにより、開発者およびデータサイエンティストは、ML モデルを構築、トレーニング、およびデプロイすることができます。Amazon SageMaker は ML ワークフロー全体をカバーする完全マネージドサービスで、データをラベル付けして作成し、アルゴリズムを選択し、モデルをトレーニングし、デプロイのために調整と最適化を行い、予測を立て、アクションを実行します。

このソリューションのエンドツーエンドの実装は、Amazon SageMaker Jupyter Notebook として利用できます。詳細については、GitHub リポジトリを参照してください。

ソリューションの概要

この例では、Amazon SageMaker により次のことを行います。(1) データセットをダウンロードし、Jupyter ノートブックを使用して視覚化します。(2) ノートブック内でローカルにデータクリーニングを実行し、データのサンプルを確認します。(3) word2vec を使用してテキスト列の特徴エンジニアリングを行います。(4) 主成分分析 (PCA) モデルを前処理されたデータセットに適合させます。(5) データセット全体をスコアリングします。(6) スコアにしきい値を適用して、疑わしいまたは異常な請求を特定します。

データセットをダウンロードし、Jupyter ノートブックを使用して視覚化する

この記事では、2008 年のメディケア入院費請求データセットを使用しています。データセットは、CMS 2008 BSA Inpatient Claims PUF という名前の、公的に利用できる基本スタンドアローン (BSA) 入院患者公用ファイル (PUF) です。

データセットをダウンロードする手順は、記事の Jupyter Notebook で入手できます。詳細については、GitHub リポジトリを参照してください。

データセットには、レコードにインデックスを付けるプライマリクレームキーと 6 つの分析変数が含まれています。人口統計と請求に関連する変数もいくつかあります。ただし、このファイルは受益者 ID を提供していないため、同じ受益者に属する請求をリンクすることはできません。けれども、データセットには、このソリューションのモデルを構築するのに十分な情報があります

これは、特徴面での最小限のデータセットです。施設の郵便番号など、いくつかの必要な特徴がありません。さらにデータを追加して一連の特徴を構築し、このソリューションの精度を引き続き向上させることができます。

データセットのコピーをダウンロードするか、GitHub リポジトリからアクセスできます。

次のステップでは、7 つの分析変数を分析し、null 値を修正して各変数のデータを消去し、ICD 9 診断と手順コードを対応する説明に置き換えます。

列名のクリーンアップ

列をクリーンアップするには、次の手順を実行します。

  1. ファイル ColumnNames.csv を開きます
  2. 空白と二重引用符を削除します

これにより、データセットの作業を開始する前に、コード化された列の関連する名前が生成されます。次のコード例を参照してください。

colnames = pd.read_csv("./data/ColumnNames.csv")
colnames[colnames.columns[-1]] = colnames[colnames.columns[-1]].map(lambda x: x.replace('"','').strip())
display(colnames)

次の表は、データセットの作業を継続するために使用する列名を示しています。

列ラベル 列名
0 IP_CLM_ID  暗号化された PUF ID
1 BENE_SEX_IDENT_CD  受取人の性別コード
2 BENE_AGE_CAT_CD  受取人の年齢カテゴリーコード
3 IP_CLM_BASE_DRG_CD  ベース DRG コード
4 IP_CLM_ICD9_PRCDR_CD  ICD9 プライマリプロシージャコード
5 IP_CLM_DAYS_CD  入院日数コード
6 IP_DRG_QUINT_PMT_AVG  DRG の五分位数の平均支払い金額
7 IP_DRG_QUINT_PMT_CD  DRG の五分位数の支払い金額コード

以下は、使用するデータセットの特徴的な機能です。

  • 2008 年からのメディケア入院費請求
  • 各記録は、メディケアの受取人の 5% のサンプルが負担する入院費請求です。
  • 受取人の識別情報は提供されません
  • 患者が治療を受けた施設の郵便番号は提供されません
  • ファイルには 7 つの変数が含まれています。1 つのプライマリキーと 7 つの分析変数
  • データセット内のコードを解釈するために必要なデータ辞書が提供されています

データセットを視覚化する

下のスクリーンショットから明らかなように、異常および非異常記録は、目視検査を使用してマークするには明確ではありません。統計的手法であっても、それは難しい問題です。これは、次の課題が原因です。

  • 通常のオブジェクトと異常値の効果的なモデリング。データの正常性と異常性 (外れ値) の境界は明確ではないことがよくあります。
  • 外れ値の検出方法がアプリケーション固有である。たとえば、臨床データでは小さな偏差が外れ値になる可能性がありますが、マーケティングアプリケーションでは外れ値を正当化するために大きな偏差が必要です。
  • データのノイズが、属性値の偏差として、または欠損値としても存在する場合がある。ノイズは外れ値を隠すか、偏差を外れ値としてフラグを立てることがあります。
  • 理解できるか否かの観点から外れ値の正当化を行うことが難しいことがある。

次のスクリーンショットは、データセットのサンプルレコードを示しています。

ノートブック内でローカルにデータクリーニングを実行し、データのサンプルを確認します

データセットの列統計を生成します。

次のコマンドは、null 値を持つ列を識別します。

# check null value for each column
display(df_cms_claims_data.isnull().mean())

結果では、ICD9 プライマリプロシージャコードの「NaN」と 0.469985 の平均値が表示されます。「NaN」は「not a number」(数値ではない) という意味で、結果を数値として表現できない計算を実行すると得られる浮動小数点値です。これは、ICD9 プライマリプロシージャコードの null 値を修正する必要があることを意味します。

ICD9 診断コードの置換

null 値を置き換えるには、次のコードを実行し、タイプを float から int64 に変更します。データセットは、すべてのプロシージャコードを整数としてコード化します。

#Fill NaN with -1 for "No Procedure Performed"
procedue_na = -1
df_cms_claims_data['ICD9 primary procedure code'].fillna(procedue_na, inplace = True)

#convert procedure code from float to int64
df_cms_claims_data['ICD9 primary procedure code'] = df_cms_claims_data['ICD9 primary procedure code'].astype(np.int64)

性別および年齢データの分析

次のステップは、性別と年齢の不均衡分析を行うことです。次のプロセスを実行して、各性別および年齢フィールドの棒グラフをプロットします。

  1. 性別/年齢辞書の CSV ファイルを読み取る
  2. 受取人のカテゴリーコードを年齢グループ/性別の定義に参加させ、クレームデータセットの異なる年齢グループ間の分布を説明します
  3. 棒グラフ上のデータセット内のプロジェクトの性別/年齢分布

次のスクリーンショットは、年齢層分布の棒グラフを示しています。Under_6585_and_Older がより多くの表現を持っているため、クレームの分布にわずかな不均衡が見られます。これらの 2 つのカテゴリーは、より広範で自由な年齢層を表すため、不均衡を無視できます。

次のスクリーンショットは、性別の棒グラフを示していますが、ここでもわずかに不均衡があります。女性の請求数はわずかに高くなっています。ただし、重大な不均衡ではないため、無視できます。

日数、支払いコード、支払い金額データの分析

入院日数コード、DRG 五分位数支払いコード、および DRG 五分位数支払い金額に関するデータについては、この段階で変換は必要ありません。データはきれいにコード化されており、不均衡なデータにはモデルが異常を検出するために使用できる信号が含まれている可能性があるため、不均衡分析をさらに行う必要はありません。

word2vec を使用してテキスト列の特徴エンジニアリングを行う

データセットには合計で 7 つの分析変数があります。7 つのうち、患者の年齢、患者の性別、入院日数、DRG の五分位数の支払いコード、および DRG の五分位数の支払い金額を、それ以上の変換なしに特徴として直接使用します。これらのフィールドで特徴エンジニアリングは必要ありません。これらのフィールドは整数としてコード化されており、数学演算を安全に適用できます。

ただし、診断と手順の説明から関連する特徴を抽出する必要があります。診断フィールドと手順フィールドは整数としてコード化されていますが、コード化された値の数学演算の結果は意味をゆがめます。たとえば、2 つの手順コードまたは診断コードの平均は、平均を計算するために使用する 2 つの手順/診断コードとまったく同等または近い 3 番目の手順/診断のコードであるコードになる場合があります。この記事では、より意味のある方法でデータセット内の手順と診断の説明フィールドをコード化する手法について説明します。この手法は、単語の埋め込みと呼ばれる手法に特定の word2vec を実装した Continuous Bag of Words (CBOW) を使用しています。

単語の埋め込みは、単語を数字に変換します。頻度カウントやホットエンコーディングなど、テキストを数値に変換する方法は多数あります。従来の方法のほとんどは、スパースマトリックスを生成し、文脈的および計算的にはあまり効果的ではありません。

Word2vec は、それ自体も単語であるターゲット変数にマッピングする浅いニューラルネットワークです。トレーニング中に、ニューラルネットワークは単語ベクトル表現として機能する重みを学習します。

この CBOW モデルは、特定のコンテキスト内の単語を予測します。これは、文のようなものです。word2vec によって学習した単語の密なベクトル表現は、意味論的な意味を持ちます。

診断および手順の説明に関するテキストの前処理

次のコードは、診断の説明でテキスト処理を実行して、単語の埋め込みで頭字語の一部をより意味のあるものにします。

  1. 小文字に変更
  2. 置換
    1. 「&」を「and」に
    2. 「non-」を「non」に
    3. 「w/o」と「without」に
    4. 「w」を「with」に
    5. 「maj」を「major」に
    6. 「proc」を「procedure」に
    7. 「o.r.」を「operating room」に
  3. 句を単語に分割
  4. 単語のベクトルを返す
# function to run pre processing on diagnosis descriptions
from nltk.tokenize import sent_tokenize, word_tokenize 

def text_preprocessing(phrase):
    phrase = phrase.lower()
    phrase = phrase.replace('&', 'and')
    #phrase = phrase.replace('non-', 'non') #This is to ensure non-critical, doesn't get handled as {'non', 'critical'}
    phrase = phrase.replace(',','')
    phrase = phrase.replace('w/o','without').replace(' w ',' with ').replace('/',' ')
    phrase = phrase.replace(' maj ',' major ')
    phrase = phrase.replace(' proc ', ' procedure ')
    phrase = phrase.replace('o.r.', 'operating room')
    sentence = phrase.split(' ')
    return sentence

診断の説明をトークン化して前処理した後、出力を word2vec にフィードして、単語の埋め込みを生成します。

個々の単語に単語埋め込みを生成する

前処理済みの手順と診断の説明で個々の単語の単語埋め込みを生成するには、次の手順を実行します。

  1. word2vec モデルをトレーニングして、前処理済みの手順と診断の説明を特徴に変換し、sns という Python 視覚化ライブラリを使用して、結果を 2D 空間で視覚化します。
  2. CBOW を使用して、前処理された診断と手順コードの説明から特徴ベクトルを抽出します。
  3. 診断と手順の説明のために、Amazon SageMaker Jupyter Notebook インスタンスで word2vec モデルをローカルでトレーニングします。
  4. モデルを使用して、手順と診断の説明で各単語の固定長の単語ベクトルを抽出します。

この記事では、gensim パッケージの一部として利用できる word2vec を使用しています。詳細については、Python Package Index ウェブサイトの genism 3.0.0 を参照してください。上記の手順の最終出力は、各単語に対する 72 個の浮動小数点数のベクトルです。これは、診断および手順の説明でトークン化された単語の特徴ベクトルとして使用します。

手順と診断の説明句から単語の埋め込みを生成する

各単語の単語ベクトルを取得したら、新しい単語の埋め込みを生成できます。

  1. 手順と診断の説明に含まれるすべての単語ベクトルの平均を使用して、診断と手順を説明する完全な句ごとに新しいベクトルを作成します。

新しいベクトルは、データセットの診断と手順の説明フィールドの特徴セットになります。次のコード例を参照してください。

# traing wordtovec model on diagnosis description tokens
model_drg = Word2Vec(tmp_diagnosis_tokenized, min_count = 1, size = 72, window = 5, iter = 30)
  1. 句内のすべての単語ベクトルの平均を取ります。

これにより、完全な診断記述句の単語埋め込みが生成されます。次のコード例を参照してください。

#iterate through list of strings in each diagnosis phrase
for i, v in pd.Series(tmp_diagnosis_tokenized).items():
    #calculate mean of all word embeddings in each diagnosis phrase
    values.append(model_drg[v].mean(axis =0))
    index.append(i)
tmp_diagnosis_phrase_vector = pd.DataFrame({'Base DRG code':index, 'DRG_VECTOR':values})
  1. 診断記述ベクトルを特徴に展開します。次のコード例を参照してください。
# expand tmp_diagnosis_phrase_vector into dataframe
# every scalar value in phrase vector will be considered a feature
diagnosis_features = tmp_diagnosis_phrase_vector['DRG_VECTOR'].apply(pd.Series)

# rename each variable in diagnosis_features use DRG_F as prefix
diagnosis_features = diagnosis_features.rename(columns = lambda x : 'DRG_F' + str(x + 1))

# view the diagnosis_features dataframe
display(diagnosis_features.head())

次のスクリーンショットは、生成された単語の埋め込みを示しています。ただし、これらは抽象的であり、視覚化には役立ちません。

  1. 手順コードの診断コードに対して、前述のプロセスを繰り返します。

最終的に、手順の説明用の特徴セットが作成されます。次のスクリーンショットを参照してください。

診断と手順の説明ベクトルを視覚化する

この記事では、t-SNE と呼ばれる手法を使用して、単語の埋め込み結果を 2D または 3D の多次元空間で視覚化します。次のスクリーンショットは、word2vec アルゴリズムが生成した単語ベクトルの 2D 投影をプロットする t-SNE グラフを示しています。

word2vec グラフと t-SNE グラフは、モデルのトレーニングに使用するパラメータが同じであっても、常に同じように見えるとは限りません。これは、すべての新しいトレーニングセッションの開始時にランダムに初期化されるためです。

t-SNE グラフが表示される理想的な形状はありません。ただし、すべての単語が互いに非常に近い 1 つのクラスターに表示されるパターンの使用は避けてください。下のグラフのスプレッドは良好です。

手順の説明については、前述のプロセスを繰り返します。次のスクリーンショットは、word2vec を処理および適用した後の 2D 投影を示しています。繰り返しますが、グラフのスプレッドは良好です。

すべての機能セットを集約し、トレーニング用の最終特徴セットを構成する

次のステップは、6 つの分析変数から抽出されたすべての特徴を集約し、最終的な特徴セットを構成することです。データサイエンスには標準の Python ライブラリを使用できます。

主成分分析 (PCA) モデルを前処理されたデータセットに適合させる

次のステップでは、PCA を使用して異常検出を行う方法を示します。主成分分類子に基づく新しい異常検出方式で説明されている手法を使用して、PCA ベースの異常検出方法を示します。

データをトレーニングとテストに分割する

PCA を適用して異常検出を行う前に、データをトレーニングとテストに分割する必要があります。このランダムな分割には、すべての規模の支払いの分布をカバーするサンプルがあることを確認してください。この記事では、DRG 五分位数の支払い金額コードで層別シャッフルスプリットを実行し、テストにデータの 30% 、トレーニングに 70% を使用します。次のコード例を参照してください。

from sklearn.model_selection import StratifiedShuffleSplit

sss = StratifiedShuffleSplit(n_splits=1, test_size=0.3, random_state=0)
splits = sss.split(X, strata)
for train_index, test_index in splits:
    X_train, X_test = X.iloc[train_index], X.iloc[test_index]

次のステップは、データを標準化して、高スケール変数による優位性を回避することです。

トレーニングサンプルに基づきデータを標準化する

後でトレーニングに使用する PCA アルゴリズムはデータの直交分散を最大化するため、PCA を実行する前にトレーニングデータを標準化して平均分散と単位分散を設定します。これにより、PCA アルゴリズムがそのような再スケーリングに対して冪等性があることを確認し、大規模な変数が PCA 投影を支配しないようにします。次のコード例を参照してください。

from sklearn.preprocessing import StandardScaler
n_obs, n_features = X_train.shape
scaler = StandardScaler()
scaler.fit(X_train)
X_stndrd_train = scaler.transform(X_train)

これで、データセットから特徴を抽出して標準化しました。Amazon SageMaker PCA を使用して、異常検出を行うことができます。Amazon SageMaker PCA を使用して、変数の数を減らし、変数が互いに独立していることを確認します。

Amazon SageMaker PCA は教師なし ML アルゴリズムで、可能な限り多くの情報を保持しながら、データセット内の次元 (特徴の数) を削減します。これは、コンポーネントと呼ばれる新しい特徴セットを見つけることでこれを行います。これは、相互に関連性のない元の特徴の複合体です。また、最初のコンポーネントがデータの最大の変動性を考慮し、2 番目のコンポーネントが 2 番目に大きい変動性を考慮するなどの制約もあります。

Amazon SageMaker PCA を使用してデータでトレーニングされた出力モデルは、各変数の相互関係 (共分散行列)、データの分散方向 (固有ベクトル)、およびこれらの異なる方向の相対的な重要度 (固有値) を計算します。

データをバイナリストリームに変換し、Amazon S3 にアップロードする

Amazon SageMaker トレーニングジョブを開始する前に、データをバイナリストリームに変換し、Amazon S3 にアップロードします。次のコード例を参照してください。

# Convert data to binary stream.
matrx_train = X_stndrd_train.as_matrix().astype('float32')
import io
import sagemaker.amazon.common as smac
buf_train = io.BytesIO()
smac.write_numpy_to_dense_tensor(buf_train, matrx_train)
buf_train.seek(0)

Amazon SageMaker fit 関数を呼び出してトレーニングジョブを開始する

次のステップは、Amazon SageMaker fit 関数を呼び出してトレーニングジョブを開始することです。次のコード例を参照してください。

#Initiate an Amazon SageMaker Session
sess = sagemaker.Session()
#Create an Amazon SageMaker Estimator for Amazon SageMaker PCA.
#Container parameter has the image of Amazon SageMaker PCA algorithm #embedded in it.
pca = sagemaker.estimator.Estimator(container,
                                    role,
                                    train_instance_count=num_instances,
                                    train_instance_type=instance_type,
                                    output_path=output_location,
                                    sagemaker_session=sess)
#Specify hyperparameter
pca.set_hyperparameters(feature_dim=feature_dim,
                        num_components=num_components,
                        subtract_mean=False,
                        algorithm_mode='regular',
                        mini_batch_size=200)

#Start training by calling fit function
pca.fit({'train': s3_train_data})

pca.fit 関数呼び出しは、個別のトレーニングインスタンスの作成をトリガーします。これにより、トレーニング用およびビルドとテスト用に異なるインスタンスタイプを選択できます。

データセット全体をスコアリングする

トレーニング済み PCA モデルをダウンロードして展開する

トレーニングジョブが完了したら、Amazon SageMaker はモデルアーティファクトを指定された S3 出力場所に書き込みます。次元削減のために、返された PCA モデルアーティファクトをダウンロードして解凍できます。

Amazon SageMaker PCA アーティファクトには、�、固有ベクトル主成分、� の昇順、その特異値が含まれています。コンポーネントの特異値は、コンポーネントが説明する標準偏差に等しくなります。たとえば、特異成分の 2 乗値は、その成分が説明する分散に等しくなります。したがって、各コンポーネントが説明するデータの分散の割合を計算するには、特異値の 2 乗を取り、それをすべての特異値の 2 乗の合計で除算します。

最も多くの分散を説明するコンポーネントを最初に表示するには、この返された順序を逆にします。

次元をさらに削減するために PCA コンポーネントをプロットする

PCA を使用して、問題の次元を減らすことができます。� 特徴と �−1 コンポーネントがありますが、次のグラフを見ると、コンポーネントの多くが説明されたデータの分散にあまり寄与していないことがわかります。データの分散の 95% を説明する主要なコンポーネント � のみを保持します。

13 個のコンポーネントがデータの分散の 95.08% を説明しています。次のグラフの赤い点線は、データの分散の 95% に必要なカットオフをハイライトしています。

マハラノビス距離を計算して、請求ごとに異常を記録する

この記事では、各ポイントのマハラノビス距離を異常スコアとして使用しています。これらのポイントの上位 �% を外れ値と見なします。� は、検出の感度に応じて異なります。この投稿は上位 1% 、𝛼=0.01 を取ります。したがって、分布 � の (1−𝛼) 分位数を、データポイントの異常を考慮するためのしきい値として計算します。

次のグラフは、Amazon SageMaker PCA アルゴリズムの出力である特徴セットから導出されたマハラノビス距離に基づいて生成されたものです。赤い線は、� で定義された感度に基づく異常検出のしきい値を示しています。

 

マハラノビス距離と感度から導出された異常スコアを使用して、請求に「is anomalyTRUE/FALSE のラベルを付けることができます。「anomalousTRUE のレコードは、異常のしきい値をクリアし、疑わしいと見なされる必要があります。「anomalousFALSE レコードはしきい値をクリアせず、疑わしいとは見なされません。これにより、異常な請求と標準的な請求が分離されます。

スコアにしきい値を適用して、疑わしいまたは異常な請求を特定します

異常なレコードをプロットして分析する

CMS 請求データセットで実行した一連のアクションを使用して、純粋な数学的手法に基づいて、ラベルのないデータなしで異常請求レコードにタグを付けることができます。

次のスクリーンショットは、標準的なレコードの例を示しています。

次のスクリーンショットは、異常なレコードの例を示しています。

標準データと異常データを分離したので、「anomalous」の TRUE とマークされたデータポイントを疑わしいと見なし、さらに調査するためにそれらを渡すことができます。

専門家の調査により、請求が本当に異常であるかどうかを確認できます。興味があり、独自の説明、仮説、またはパターンを考えたい場合は、年齢、性別、入院日数、五分位コード、五分位支払い、手順、診断コードなどのさまざまな変数間でペアプロットを行うことができます。

基本的な分析のために、seaborn ライブラリを使用してペアプロットを行うことができます。次のスクリーンショットは、標準グラフ (青) と異常グラフ (オレンジ) のペアプロットを 1 つのグラフに重ねて示しています。青い点と非対称のオレンジ色の点、または近くに青い点がない孤立したオレンジ色の点を特定できます。

赤で強調表示されたペアプロットは、非対称パターンを示しています。青とオレンジの間には、オレンジのドットが存在するが青いドットが存在しない孤立した領域があります。これらのプロットをより深く掘り下げ、強調表示されたプロットの背後にあるデータを分析して、パターンを見つけたり、仮説を立てたりすることができます。この記事ではラベル付きデータが提供されないため、仮説をテストすることは困難です。ただし、時間が経つにつれて、仮説をテストし、モデルの精度を向上させるためのラベル付きデータが増える可能性があります。

 

結論

この記事では、疑わしい請求にフラグを立てるモデルを作成する方法を示しました。モデルを開始点として使用して、支払いの整合性をサポートするプロセスを構築できます。既存のソースからより多くのデータを取り込むか、データソースを追加することで、モデルをさらに拡張できます。この記事のモデルはスケーリングされ、より多くのデータを吸収して結果とパフォーマンスを向上させることができます。

このモデルを使用すると、詐欺案件を最小限に抑えることができます。フラグを立てられることを恐れて、不正請求を思いとどまらせ、加入者の医療保険料のコストを引き下げられるかもしれません。独自の Amazon SageMaker Jupyter Notebook を使用して、ここで説明した手法を試してみてはいかがでしょうか。手順とアーティファクトについては、GitHub リポジトリでご覧いただけます。


著者について

Vikrant Kahlir、戦略的ソリューションアーキテクト、AWS ソリューションアーキテクチャ

Elena Ehrlich、シニアデータサイエンティスト、AWS プロフェッショナルサービス

Hanif Mahboobi、シニアデータサイエンティスト、AWS プロフェッショナルサービス