運用 Cloud AI 平台說明詐欺偵測模型

1. 總覽

在本研究室中,您將使用 AI 平台 Notebooks 建構及訓練用於識別詐欺交易的模型,並透過 Explainable AI SDK 瞭解模型的預測結果。詐欺偵測是一種金融服務專屬的異常偵測機制,因此機器學習模型有一些有趣的挑戰,像是資料集本質不平衡,而且必須說明模型的結果。

課程內容

學習重點:

  • 處理不平衡的資料集
  • AI 平台筆記本中透過 tf.keras 建構及評估詐欺偵測模型
  • 使用筆記本中的 Explainable AI SDK,瞭解模型將交易歸類為詐欺內容的原因
  • 透過說明將模型部署至 AI Platform,並取得已部署模型的預測和說明

在 Google Cloud 中執行這個研究室的總費用約為 $1 美元。

2. 為什麼要偵測詐欺行為?

異常偵測對機器學習來說是不錯的選擇,因為通常要撰寫一系列以規則為基礎的陳述來識別資料中的離群值。詐欺偵測是一種異常偵測,在機器學習技術方面帶來兩項有趣的挑戰:

  • 非常平衡的資料集:由於異常狀況好像有異常狀況,種類有限。資料集平衡時,機器學習運作效果最佳,因此如果離群值小於 1% 的資料,情況會變得很複雜。
  • 需要說明結果:如要尋找詐欺活動,建議瞭解系統為何將某種內容標示為詐欺,而非僅取證。可解釋性工具可協助您達成這個目標。

3. 設定環境

您需要已啟用計費功能的 Google Cloud Platform 專案,才能執行這個程式碼研究室。如要建立專案,請按照這裡的操作說明進行。

步驟 1:啟用 Cloud AI Platform Models API

前往 Cloud 控制台的 AI Platform 模型專區,然後按一下「啟用」(如果尚未啟用)。

d0d38662851c6af3.png

步驟 2:啟用 Compute Engine API

前往「Compute Engine」,並選取「啟用」 (如果尚未啟用)。建立筆記本執行個體時會用到。

步驟 3:建立 AI 平台筆記本執行個體

前往 Cloud 控制台的 AI 平台筆記本專區,然後按一下「新增執行個體」。接著選取「TensorFlow 企業版 2.1」執行個體類型,但「不含 GPU」

9e2b62be57fff946.png

使用預設選項,然後點選「建立」。執行個體建立完成後,請選取「Open JupyterLab」

fa67fe02f2a9ba73.png

開啟執行個體時,請從啟動器中選取「Python 3」筆記本:

4390b1614ae8eae4.png

步驟 4:匯入 Python 套件

建立新的儲存格,並匯入本程式碼研究室將要使用的程式庫:

import itertools
import numpy as np
import pandas as pd
import tensorflow as tf
import json
import matplotlib as mpl
import matplotlib.pyplot as plt
import explainable_ai_sdk

from sklearn.utils import shuffle
from sklearn.metrics import confusion_matrix
from sklearn.preprocessing import StandardScaler
from tensorflow import keras
from explainable_ai_sdk.metadata.tf.v2 import SavedModelMetadataBuilder

4. 下載及處理資料

我們會使用 Kaggle 的這個合成資料集訓練模型。原始資料集含有 630 萬列,其中 8, 000 列是詐欺交易 - 僅佔整個資料集的 0.1%!

步驟 1:下載 Kaggle 資料集並透過 Pandas 讀取

我們已在 Google Cloud Storage 中為您提供 Kaggle 資料集。如要下載該筆記本,請在 Jupyter 筆記本中執行下列 gsutil 指令:

!gsutil cp gs://financial_fraud_detection/fraud_data_kaggle.csv .

接下來,讓我們以 Pandas DataFrame 形式讀取資料集,然後預覽:

data = pd.read_csv('fraud_data_kaggle.csv')
data = data.drop(columns=['type'])
data.head()

預覽畫面應如下所示:

8d3d9e022fce1e7f.png

步驟 2:計算不平衡的資料

如前所述,資料集現在包含 99.9% 的非詐欺性範例。如果使用資料訓練模型,模型將猜測每筆交易,結果可能達到 99.9% 的準確率,因為其中 99.9% 的資料並非詐欺案例。

處理不平衡的資料有幾種不同的方法。這裡我們會使用向下取樣技巧。降低取樣是指在訓練時只使用一小部分類別。在本例中為「非詐欺」是多數類別,因為這個平台的資料佔了 99.9%。

為簡化資料集的取樣作業,我們會取用約 8,000 個詐欺性示例,以及約 31,000 個非詐欺案例的隨機樣本。如此一來,產生的資料集將擁有 25% 的詐欺案件,而數量則較以往的 0.1%。

首先,請將資料分割成兩個 DataFrame,一個用於詐欺,另一個則用於非詐欺 (我們稍後會在程式碼研究室中部署模型時採用此架構):

fraud = data[data['isFraud'] == 1]
not_fraud = data[data['isFraud'] == 0]

接著,隨機對非詐欺案例隨機取樣。而是 .005%,因為這可以讓我們按 .005% 的比例進行詐欺/非詐欺交易。如此一來,您便能將這些資料重新彙整在一起,並重組。為簡化作業,我們也會移除一些不會用於訓練的資料欄:

# Take a random sample of non fraud rows
not_fraud_sample = not_fraud.sample(random_state=2, frac=.005)

# Put it back together and shuffle
df = pd.concat([not_fraud_sample,fraud])
df = shuffle(df, random_state=2)

# Remove a few columns (isFraud is the label column we'll use, not isFlaggedFraud)
df = df.drop(columns=['nameOrig', 'nameDest', 'isFlaggedFraud'])

# Preview the updated dataset
df.head()

現在資料集的平衡程度也提升了。但如果我們發現模型的收斂準確率約 75%,就很有可能猜到「非詐欺」和各種類型的對話

步驟 3:將資料拆分為訓練集和測試集

建立模型前,最後一個步驟是分割資料。我們會採用 80/20 的訓練測試分配比例:

train_test_split = int(len(df) * .8)

train_set = df[:train_test_split]
test_set = df[train_test_split:]

train_labels = train_set.pop('isFraud')
test_labels = test_set.pop('isFraud')

*E.A. Lopez-Rojas 、A.Elmir 和 S.Axelsson。「PaySim:用來偵測詐欺行為的金融行動金錢模擬器。」內容:第 28 屆歐洲建模和模擬座談會、Larnaca、賽普勒斯,2016

5. 建構、訓練及評估 tf.keras 模型

我們將使用 TensorFlow 的 tf.keras API 進行建構。本節中的模型程式碼是建構於 TensorFlow 文件的此教學課程。首先,我們會將資料正規化,接著使用 class_weight 參數建構並訓練模型,避免剩餘的資料不平衡。

步驟 1:將資料正規化

使用數值資料訓練模型時,請務必將資料正規化,尤其是當各欄落在不同的範圍時。這樣有助於避免訓練期間損失爆炸。我們可以透過下列條件將資料正規化:

scaler = StandardScaler()
train_set = scaler.fit_transform(train_set) # Only normalize on the train set
test_set = scaler.transform(test_set)

# clip() ensures all values fall within the range [-5,5]
# useful if any outliers remain after normalizing
train_set = np.clip(train_set, -5, 5)
test_set = np.clip(test_set, -5, 5)

接著,我們要預覽正規化資料:

train_set

步驟 2:決定課程權重

降低取樣資料時,我們仍希望保留一部分非詐欺性交易,以免遺失這些交易的資訊,這就是我們未達到完美平衡的原因。由於資料集仍不平衡,而我們最關心的是正確識別詐欺交易,因此我們希望模型更加權重,可幫助我們掌握資料集內的詐欺性樣本。

Keras class_weight 參數可讓我們根據這些類別在資料集中發生的頻率,明確指明要從每個類別提供樣本的權重:

weight_for_non_fraud = 1.0 / df['isFraud'].value_counts()[0]
weight_for_fraud = 1.0 / df['isFraud'].value_counts()[1]

class_weight = {0: weight_for_non_fraud, 1: weight_for_fraud}

我們會在下一個步驟中訓練模型時使用這個變數。

步驟 3:訓練及評估模型

我們會使用 Keras Sequential Model API 來建構模型,將模型定義為層層。我們會在訓練過程中追蹤幾個指標,這有助於我們瞭解模型在資料集內各類別的成效。

METRICS = [
      keras.metrics.TruePositives(name='tp'),
      keras.metrics.FalsePositives(name='fp'),
      keras.metrics.TrueNegatives(name='tn'),
      keras.metrics.FalseNegatives(name='fn'), 
      keras.metrics.BinaryAccuracy(name='accuracy'),
      keras.metrics.Precision(name='precision'),
      keras.metrics.Recall(name='recall'),
      keras.metrics.AUC(name='auc'),
]

def make_model(metrics = METRICS):
  model = keras.Sequential([
      keras.layers.Dense(
          16, activation='relu',
          input_shape=(train_set.shape[-1],)),
      keras.layers.Dropout(0.5),
      keras.layers.Dense(1, activation='sigmoid'),
  ])

  model.compile(
      optimizer=keras.optimizers.Adam(lr=1e-3),
      loss=keras.losses.BinaryCrossentropy(),
      metrics=metrics)

  return model

然後定義幾個在訓練期間使用的全域變數,以及一些提早停止參數。

EPOCHS = 100
BATCH_SIZE = 512

early_stopping = tf.keras.callbacks.EarlyStopping(
    monitor='val_auc', 
    verbose=1,
    patience=10,
    mode='max',
    restore_best_weights=True)

最後,將呼叫上述定義的函式來建立模型:

model = make_model()
model.summary()

您可以使用 fit() 方法訓練模型,並傳入上述定義的參數:

results = model.fit(
    train_set,
    train_labels,
    batch_size=BATCH_SIZE,
    epochs=EPOCHS,
    callbacks = [early_stopping],
    validation_data=(test_set, test_labels),
    class_weight=class_weight)

訓練需要幾分鐘才能執行。

步驟 4:以圖表呈現模型指標

現在模型已訓練完成,接著讓我們藉由繪製訓練週期中的各項指標來掌握模型成效:

mpl.rcParams['figure.figsize'] = (12, 10)
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

def plot_metrics(history):
  metrics =  ['loss', 'auc', 'precision', 'recall']
  for n, metric in enumerate(metrics):
    name = metric.replace("_"," ").capitalize()
    plt.subplot(2,2,n+1)
    plt.plot(history.epoch,  history.history[metric], color=colors[0], label='Train')
    plt.plot(history.epoch, history.history['val_'+metric],
             color=colors[0], linestyle="--", label='Val')
    plt.xlabel('Epoch')
    plt.ylabel(name)
    if metric == 'loss':
      plt.ylim([0, plt.ylim()[1]])
    elif metric == 'auc':
      plt.ylim([0.8,1])
    else:
      plt.ylim([0,1])

    plt.legend()

plot_metrics(results)

您的圖表看起來應該會與下列類似 (但略有不同):

f98a88e530bb341f.png

步驟 5:列印混淆矩陣

混淆矩陣能以視覺化方式呈現模型在測試資料集上的執行成效,系統會針對每個類別,顯示模型正確且錯誤預測的測試樣本百分比。Scikit Learn 提供了一些公用程式,可用來建立和繪製混淆矩陣,我們將會在這裡使用。

在筆記本的開頭,我們匯入了 confusion_matrix 公用程式。在使用之前,我們需要先建立模型的預測結果清單。這裡,我們會四捨五入模型傳回的值,讓這個清單與真值標籤清單相符:

predicted = model.predict(test_set)

y_pred = []

for i in predicted.tolist():
  y_pred.append(int(round(i[0])))

現在我們已準備好將此函式和真值標籤加入 confusion_matrix 方法中:

cm = confusion_matrix(test_labels.values, y_pred)
print(cm)

這表示我們可以看到測試集模型正確與錯誤預測的絕對值。左上角的數字代表我們從測試集正確預測為非詐欺內容的樣本數量。右下方的數字代表正確預測為詐欺性的數量 (我們最關心這個數字)。您可以看到,它正確預測了每個類別的大多數樣本。

為方便視覺化呈現,我們根據 Scikit Learn 文件調整 plot_confusion_matrix 函式。請在這裡定義該函式:

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    if normalize:
        cm = np.round(cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], 3)

    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')

然後透過模型傳送資料來建立圖。我們將 normalize 設為 True,因此混淆矩陣會以百分比顯示正確和不正確的預測結果數量:

classes = ['not fraud', 'fraud']
plot_confusion_matrix(cm, classes, normalize=True)

畫面應如下所示 (實際數字各不相同):

b52ef4ccddce5d8c.png

我們可以看到,我們的模型根據測試集的 1,594 個詐欺交易,預測了大約 85% 的詐欺交易。請注意,本研究室的重點不在於模型品質。如果您使用詐欺偵測模型,則希望詐欺課程的準確率超過 85%。本研究室的目標是介紹相關工具,說明以不平衡資料集訓練的模型。

接下來,我們將使用 Explainable AI SDK,藉此瞭解模型根據哪些功能進行預測。

6. 使用 Explainable AI SDK

Explainable AI SDK 會提供實用方法,協助您取得模型的說明。已預先安裝於 Tensorflow AI 平台筆記本執行個體。請注意,研究室一開始已匯入筆記本。有了 SDK,我們就能在筆記本執行個體中從模型取得特徵歸因,因此不必將模型部署至雲端即可使用。

在本節中,我們會匯出剛剛訓練為 Tensorflow 1 的模型,然後將 SDK 指向已儲存的模型資產,以取得說明。

步驟 1:匯出訓練完成的模型

首先,請將模型儲存至筆記本執行個體的目錄:

model_dir = 'fraud_model'
tf.saved_model.save(model, model_dir)

重新整理筆記本左側欄中的資料夾檢視畫面後,應該會看到名為 fraud_model/ 的新目錄。

步驟 2:透過 SDK 取得說明中繼資料

接著,我們會指向該目錄的 Explainable AI SDK。產生後會產生取得模型說明所需的中繼資料。get_metadata() 方法會顯示 SDK 從模型推論的中繼資料,例如輸入名稱:

model_builder = SavedModelMetadataBuilder(model_dir)
metadata = model_builder.get_metadata()
print(metadata)

「可解釋性」可協助我們回答以下問題:「我們的模型為何認為這是詐欺?」

步驟 3:指定模型的基準

對於表格資料,Explainable AI 服務的運作方式是傳回各項特徵的歸因值。這些值代表特定特徵對預測結果的影響程度。假設在特定交易中,模型的預測詐欺機率增加了 0.2%,你可能會想「比什麼?」,這就是基準的概念。

模型的基準基本上就是用來比較的資料。我們會針對模型中的每個特徵選取基準值,因此基準預測因此成為模型在基準設為基準的情況下所預測的值。

請根據要解決的預測工作選擇基準。數值特徵的中位數通常會使用資料集中每個特徵的中位數做為基準。然而,就詐欺偵測而言,這並不是我們想要的。當我們的模型將交易標示為詐欺事件時,我們最關心的是,這代表我們要與非詐欺交易比較的基準案例。

為了做到這一點,我們會使用資料集中非詐欺性交易的中位數做為基準。我們可以使用上方擷取的 not_fraud_sample DataFrame 取得中位數,然後縮放,以符合模型預期的輸入內容:

not_fraud_sample = not_fraud_sample.drop(columns=['nameOrig', 'nameDest', 'isFlaggedFraud', 'isFraud'])

baseline = scaler.transform(not_fraud_sample.values)
baseline = np.clip(baseline, -5, 5)
baseline_values = np.median(baseline, axis=0)

請注意,我們不需要指定基準,如果不指定,SDK 會使用 0 做為模型預期每個輸入值的基準。針對詐欺偵測的使用案例,指定一個基準,我們會遵循以下指示:

input_name = list(metadata['inputs'])[0]
model_builder.set_numeric_metadata(input_name, input_baselines=[baseline_values.tolist()], index_feature_mapping=df.columns.tolist()[:6])
model_builder.save_metadata(model_dir)

執行上述 save_metadata() 方法,在模型的目錄中建立名為 explanation_metadata.json 的檔案。在您的筆記本中前往 press_model/ 目錄,確認檔案已建立。其中包含 SDK 用於產生功能歸因的中繼資料。

步驟 4:取得模型解釋

現在我們準備好為個別範例提供特徵歸因了。為此,我們先使用 SDK 為模型建立本機參照:

local_model = explainable_ai_sdk.load_model_from_local_path(
    model_dir, 
    explainable_ai_sdk.SampledShapleyConfig()
)

接著,我們要從應歸類為詐欺的範例交易中,取得模型的預測結果和說明:

fraud_example = [0.722,0.139,-0.114,-0.258,-0.271,-0.305]
response = local_model.explain([{input_name: fraud_example}])
response[0].visualize_attributions()

執行後應該會建立如下的視覺化內容:

67211d9396197146.png

在此範例中,帳戶交易發生前的初始餘額是詐欺的主要指標,可讓模型的預測結果從基準值增強超過 0.5。交易金額、目標帳戶最終的餘額,以及步驟,是下一個主要指標。資料集中的「step」代表時間單位 (1 步代表 1 小時)。歸因值也可以是負值。

「概略錯誤」透過圖表上方連結,您就能瞭解這項說明的可信度。一般來說,錯誤超過 5% 表示您可能無法依賴特徵歸因。請注意,您的說明品質只能取決於您使用的訓練資料和模型。您可以改善訓練資料、模型或嘗試其他模型基準,藉此減少近似誤差。

您也可以增加說明方法中使用的步數,藉此減少這個錯誤。您可以透過 SDK 變更此設定,方法是在說明設定中加入 path_count 參數 (如未指定,則預設值為 10):

local_model = explainable_ai_sdk.load_model_from_local_path(
    model_dir, 
    explainable_ai_sdk.SampledShapleyConfig(path_count=20)
)

這個模型還有許多 Explainable AI 供您使用。以下列舉幾個做法:

  • 將大量樣本傳送至模型並平均計算歸因價值,看看某些特徵是否更重要。我們就能運用這些資料改善模型,可能會移除不重要的功能
  • 找出 Google 模型標示為詐欺但非詐欺交易的誤判情形,並檢查歸因價值
  • 使用不同的基準,觀察這對歸因價值的影響

🎉 恭喜!🎉

您已經瞭解如何考量不平衡的資料、訓練 TensorFlow 模型偵測詐欺交易,以及使用 Explainable AI SDK 來查看模型最常使用哪些特徵來進行個別預測。您可以視需要在這裡停止。在筆記本中使用 SDK,可讓您在部署模型前存取說明,藉此簡化模型開發程序。建立滿意的模型後,您可能會想進行部署,大規模取得預測結果。如果您有這種情況,請繼續選擇下一個步驟。如果已完成設定,請跳到清除步驟。

7. 選用:將模型部署至 AI 平台預測

在這個步驟中,您將瞭解如何將模型部署至 AI 平台預測

步驟 1:將已儲存的模型目錄複製到 Cloud Storage 值區。

依據我們先前執行的 SDK 步驟,取得將模型部署至 AI 平台所需的各項資源。如要準備部署,您必須將 AES 資產和說明中繼資料放入 Explainable AI 服務可讀取的 Cloud Storage 值區。

為此,我們會定義一些環境變數。將下列值填入您的 Google Cloud 專案名稱,以及您要建立的值區名稱 (必須是全域不重複的名稱)。

# Update these to your own GCP project and model
GCP_PROJECT = 'your-gcp-project'
MODEL_BUCKET = 'gs://storage_bucket_name'

現在我們準備建立儲存空間值區,以儲存匯出的 TensorFlow 模型資產。部署模型時,我們會把 AI 平台指向這個值區。

在筆記本中執行這個 gsutil 指令來建立值區:

!gsutil mb -l 'us-central1' $MODEL_BUCKET

然後將本機模型目錄複製到該值區:

!gsutil -m cp -r ./$model_dir/* $MODEL_BUCKET/explanations

步驟 2:部署模型

接下來,我們會定義一些會在部署指令中使用的變數:

MODEL = 'fraud_detection'
VERSION = 'v1'
model_path = MODEL_BUCKET + '/explanations'

我們可以使用下列 gcloud 指令建立模型:

!gcloud ai-platform models create $MODEL --region=us-central1

現在,我們已準備好使用 gcloud 部署這個模型的第一個版本。版本的部署時間約需 5 到 10 分鐘:

!gcloud beta ai-platform versions create $VERSION \
--model $MODEL \
--origin $model_path \
--runtime-version 2.1 \
--framework TENSORFLOW \
--python-version 3.7 \
--machine-type n1-standard-4 \
--explanation-method 'sampled-shapley' \
--num-paths 10 \
--region=us-central1

origin 旗標中,我們會傳入已儲存模型和中繼資料檔案的 Cloud Storage 位置。Explainable AI 目前針對表格模型提供兩種不同的解釋方法。我們使用 Sampled Shapleynum-paths 參數代表針對每個輸入特徵取樣的路徑數量。一般來說,模型越複雜,需要的近似步驟才能達到合理收斂。

如要確認模型已正確部署,請執行下列 gcloud 指令:

!gcloud ai-platform versions describe $VERSION --model $MODEL --region=us-central1

狀態應為 READY

步驟 3:取得已部署模型的預測和說明

為求可解釋性,我們最著重在說明模型預測詐欺行為的情況。我們會給模型 5 個屬於詐欺交易的測試範例。

我們會使用 Google Cloud CLI 來取得預測結果。執行下列指令,從測試集取得所有詐欺範例的索引:

fraud_indices = []

for i,val in enumerate(test_labels):
    if val == 1:
        fraud_indices.append(i)

接下來,我們要以模型預期的格式儲存 5 個範例,並將範例寫入檔案:

num_test_examples = 5

instances = []
for i in range(num_test_examples):
    ex = test_set[fraud_indices[i]]
    instances.append({input_name: ex.tolist()})

with open('prediction.json', 'a') as outputfile:
    json.dump({"instances": instances}, outputfile)

我們可以使用 gcloud 將這五個範例傳送至模型:

!gcloud beta ai-platform explain \
--model=$MODEL \
--version=$VERSION \
--region='us-central1' \
--json-request=prediction.json

在回應 JSON 中,您會看到這些範例中各項特徵的歸因值。每個範例的 example_score 鍵都含有模型的預測結果,在本例中為詐欺性交易的可能性百分比。

8. 清除

如要繼續使用這個筆記本,建議您在未使用時將其關閉。在 Cloud 控制台的「Notebooks」(筆記本) UI 中,依序選取筆記本和「Stop」(停止)

879147427150b6c7.png

如要刪除在本研究室中建立的所有資源,只要刪除筆記本執行個體即可,不必停止執行個體。

使用 Cloud 控制台中的導覽選單前往「儲存空間」,然後刪除您為了儲存模型資產而建立的兩個值區。