1. 概览
在本实验中,您将使用 AI Platform Notebooks 构建和训练用于识别欺诈性交易的模型,并使用可解释 AI SDK 了解模型的预测结果。欺诈检测是一种专门针对金融服务的异常值检测,它为机器学习模型带来了一些有趣的挑战:数据集本身不平衡,并且需要解释模型的结果。
学习内容
您将了解如何:
- 处理不平衡的数据集
- 在 AI Platform Notebooks 中使用 tf.keras 构建并评估欺诈检测模型
- 在笔记本中使用 可解释 AI SDK,了解模型将交易归类为欺诈交易的原因
- 将模型部署到 AI Platform 并附带说明,然后获取已部署模型的预测结果和说明
在 Google Cloud 上运行此实验的总费用约为 1 美元。
2. 为何要进行欺诈检测?
异常值检测非常适合使用机器学习,因为通常很难编写一系列基于规则的语句来识别数据中的离群点。欺诈检测是一种异常值检测,在机器学习方面面临着两个有趣的挑战:
- 极不平衡的数据集:由于异常值本身就是异常,因此数量不多。机器学习在数据集平衡时效果最佳,因此当离群值占数据不到 1% 时,情况可能会变得复杂。
- 需要解释结果:如果您要查找欺诈活动,很可能想知道系统为何将某项活动标记为欺诈活动,而不是仅仅相信系统的判断。可解释性工具可以帮助您解决这一问题。
3. 设置环境
您需要一个启用了结算功能的 Google Cloud Platform 项目才能运行此 Codelab。如需创建项目,请按照此处的说明操作。
第 1 步:启用 Cloud AI Platform Models API
前往 Cloud 控制台的 AI Platform 模型部分,然后点击“启用”(如果尚未启用)。

第 2 步:启用 Compute Engine API
前往 Compute Engine,然后选择启用(如果尚未启用)。您需要此权限才能创建笔记本实例。
第 3 步:创建 AI Platform Notebooks 实例
前往 Cloud 控制台的 AI Platform Notebooks 部分,然后点击新建实例。然后选择不带 GPU 的 TensorFlow 企业版 2.1 实例类型:

使用默认选项,然后点击创建。创建实例后,选择打开 JupyterLab:

打开实例后,从启动器中选择 Python 3 笔记本:

第 4 步:导入 Python 软件包
创建一个新单元,并导入我们将在本 Codelab 中使用的库:
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,一个用于欺诈交易,另一个用于非欺诈交易(我们将在 Codelab 后面的模型部署阶段使用这两个 DataFrame):
fraud = data[data['isFraud'] == 1]
not_fraud = data[data['isFraud'] == 0]
然后,对非欺诈性交易进行随机抽样。我们使用 0.005%,因为这样可以使欺诈交易与非欺诈交易的比例为 25/75。这样,您就可以将数据重新组合并随机排序。为简化操作,我们还将移除一些不会用于训练的列:
# 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:一种用于欺诈检测的金融移动货币模拟器”。In: The 28th European Modeling and Simulation Symposium-EMSS, Larnaca, Cyprus. 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 构建模型,该 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)
您应该会看到类似如下的内容(确切的数字会有所不同):

从上图中可以看出,我们的模型正确预测了测试集中大约 85% 的欺诈性交易(共 1,594 笔)。请注意,本实验的重点不是模型质量。如果您要在生产环境中部署欺诈检测模型,您可能希望欺诈类的准确率高于 85%。本实验旨在向您介绍用于解释在不平衡数据集上训练的模型的工具。
接下来,我们将使用可解释 AI SDK 了解模型依赖哪些特征来进行这些预测。
6. 使用可解释 AI SDK
可解释 AI SDK 提供了用于获取模型说明的实用方法。它预安装在 TensorFlow AI Platform Notebook 实例中 - 请注意,我们在实验开始时已在笔记本中导入了它。借助该 SDK,我们可以在笔记本实例中获取模型的特征归因,这意味着我们无需将模型部署到云端即可使用它。
在本部分中,我们将刚刚训练的模型导出为 TensorFlow SavedModel,然后让 SDK 指向我们保存的模型资源以获取说明。
第 1 步:导出训练后的模型
首先,将模型保存到笔记本实例中的某个目录:
model_dir = 'fraud_model'
tf.saved_model.save(model, model_dir)
如果您刷新笔记本左侧边栏中的文件夹视图,应该会看到系统创建了一个名为 fraud_model/ 的新目录。
第 2 步:使用 SDK 获取说明元数据
接下来,我们将让可解释 AI SDK 指向该目录。这样做会生成获取模型说明所需的元数据。get_metadata() 方法会显示 SDK 从模型推断出的元数据,例如输入名称:
model_builder = SavedModelMetadataBuilder(model_dir)
metadata = model_builder.get_metadata()
print(metadata)
可解释性有助于我们回答以下问题:“为什么我们的模型认为这是欺诈交易?”
第 3 步:指定模型的基准
对于表格数据,可解释 AI 服务会返回每个特征的归因值。这些值表示特定特征对预测的影响程度。假设某笔交易的金额导致我们的模型将预测的欺诈概率提高了 0.2%。您可能会想:“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 的文件。在笔记本中,前往 fraud_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 以上。交易金额、目标账号的最终余额和步骤是接下来最重要的指标。在数据集中,“步数”表示一个时间单位(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)
)
您还可以利用此模型上的可解释 AI 执行更多操作。下面的一些想法供您参考:
- 向模型发送许多示例,并对归因值求平均值,以查看某些特征是否总体上更重要。我们可以利用这些信息来改进模型,并移除可能不重要的特征
- 查找模型标记为欺诈性交易但实际上并非欺诈性交易的假正例,并检查其归因值
- 使用其他基准,看看这对归因值有何影响
🎉 恭喜!🎉
您已了解如何处理不平衡的数据、训练 TensorFlow 模型来检测欺诈性交易,以及如何使用可解释 AI SDK 来查看模型在进行个别预测时最依赖哪些特征。如果您愿意,可以就此停止。在笔记本中使用 SDK 旨在简化模型开发流程,让您在部署模型之前就能获取说明。很可能在构建出满意的模型后,您会希望部署该模型以大规模获取预测结果。如果您属于这种情况,请继续执行可选的下一步。如果已完成,请跳至清理步骤。
7. 可选:将模型部署到 AI Platform Prediction
在此步骤中,您将学习如何将模型部署到 AI Platform Prediction。
第 1 步:将已保存的模型目录复制到 Cloud Storage 存储分区。
通过之前运行的 SDK 步骤,您已具备将模型部署到 AI Platform 所需的一切。为了准备部署,您需要将 SavedModel 资产和说明元数据放入可解释 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 Platform 指向此存储分区。
在笔记本中运行以下 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 位置。可解释 AI 目前为表格模型提供了两种不同的说明方法。这里我们使用的是抽样 Shapley。num-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 将这 5 个示例发送给模型:
!gcloud beta ai-platform explain \
--model=$MODEL \
--version=$VERSION \
--region='us-central1' \
--json-request=prediction.json
在响应 JSON 中,您会看到这些示例中每个特征的归因值。每个示例的 example_score 键都包含模型的预测结果,在本例中,该结果是指特定交易是欺诈性交易的可能性百分比。
8. 清理
如果您想继续使用此笔记本电脑,建议您在不使用时将其关闭。在 Cloud 控制台的笔记本界面中,选择笔记本,然后选择停止:

如果您想删除在本实验中创建的所有资源,只需删除笔记本实例,而不是停止它。
使用 Cloud 控制台中的导航菜单,浏览到“存储空间”,然后删除您创建的用于存储模型资产的两个存储分区。