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 模型專區,然後按一下「啟用」(如果尚未啟用)。
步驟 2:啟用 Compute Engine API
前往「Compute Engine」,並選取「啟用」 (如果尚未啟用)。建立筆記本執行個體時會用到。
步驟 3:建立 AI 平台筆記本執行個體
前往 Cloud 控制台的 AI 平台筆記本專區,然後按一下「新增執行個體」。接著選取「TensorFlow 企業版 2.1」執行個體類型,但「不含 GPU」:
使用預設選項,然後點選「建立」。執行個體建立完成後,請選取「Open JupyterLab」:
開啟執行個體時,請從啟動器中選取「Python 3」筆記本:
步驟 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()
預覽畫面應如下所示:
步驟 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)
您的圖表看起來應該會與下列類似 (但略有不同):
步驟 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)
畫面應如下所示 (實際數字各不相同):
我們可以看到,我們的模型根據測試集的 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()
執行後應該會建立如下的視覺化內容:
在此範例中,帳戶交易發生前的初始餘額是詐欺的主要指標,可讓模型的預測結果從基準值增強超過 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」(停止):
如要刪除在本研究室中建立的所有資源,只要刪除筆記本執行個體即可,不必停止執行個體。
使用 Cloud 控制台中的導覽選單前往「儲存空間」,然後刪除您為了儲存模型資產而建立的兩個值區。