教程:创建、评估欺诈检测模型并对其评分

本教程介绍了 Microsoft Fabric 中 Synapse 数据科学工作流的端到端示例。 此应用场景使用基于历史数据训练的机器学习算法构建欺诈检测模型。 然后使用该模型来检测未来的欺诈交易。

本教程涵盖以下步骤:

  • 安装自定义库
  • 加载数据
  • 通过探索性数据分析了解和处理数据
  • 使用 Scikit-Learn 训练机器学习模型,并使用 MLflow 和 Fabric 自动日志记录功能来跟踪试验。
  • 保存并注册具有最佳性能的机器学习模型
  • 加载机器学习模型进行评分和进行预测

先决条件

请按照笔记本进行操作

可以选择下面其中一个选项,以在笔记本中进行操作:

  • 在数据科学体验中打开并运行内置笔记本
  • 将笔记本从 GitHub 上传到数据科学体验

打开内置笔记本

“欺诈检测”是本教程随附的示例笔记本。

要在 Synapse 数据科学体验中打开教程的内置示例笔记本,请执行以下操作:

  1. 转到 Synapse 数据科学主页。

  2. 选择“使用示例”

  3. 选择相应的示例:

    • 来自默认的“端到端工作流 (Python)”选项卡(如果示例适用于 Python 教程)。
    • 来自“端到端工作流 (R)“选项卡(如果示例适用于 R 教程)。
    • 从“快速教程”选项卡选择(如果示例适用于快速教程)。
  4. 在开始运行代码之前,将湖屋连接到笔记本

从 GitHub 导入笔记本

AIsample - Fraud Detection.ipynb 是本教程随附的笔记本。

若要打开本教程随附的笔记本,请按照让系统为数据科学做好准备教程中的说明操作,将该笔记本导入到工作区。

或者,如果要从此页面复制并粘贴代码,则可以创建新的笔记本

在开始运行代码之前,请务必将湖屋连接到笔记本

步骤 1:安装自定义库

对于机器学习模型开发或临时数据分析,可能需要为 Apache Spark 会话快速安装自定义库。 有两个选项可用于安装库。

  • 使用笔记本的内联安装功能(%pip%conda),仅在当前笔记本中安装库。
  • 也可以创建 Fabric 环境,安装来自公共来源的安装库或将自定义库上传到该环境,然后工作区管理员可以将环境附加为工作区的默认值。 然后,环境中的所有库都将可用于工作区中的任何笔记本和 Spark 作业定义。 有关环境的详细信息,请参阅在 Microsoft Fabric 中创建、配置和使用环境

在本教程中,使用 %pip install 在笔记本中安装 imblearn 库。

注意

PySpark 内核将在 %pip install 之后重启。 在运行任何其他单元之前安装所需的库。

# Use pip to install imblearn
%pip install imblearn

步骤 2:加载数据

欺诈检测数据集包含欧洲持卡人从 2013 年 9 月开始两天内进行的信用卡交易。 数据集仅包含数值特征,这是应用于原始特征的主体组件分析 (PCA) 转换的结果。 PCA 转换了除 TimeAmount 之外的所有特征。 为了保密,我们无法提供有关数据集的原始特征或更多背景信息。

这些详细信息描述了数据集:

  • 特征 V1V2V3、…、V28 是使用 PCA 获取的主体组件
  • 特征 Time 包含数据集中某次交易和首次交易之间经过的秒数
  • 特征 Amount 是交易金额。 可使用此功能进行依赖示例的成本敏感型学习
  • Class 列是响应(目标)变量。 欺诈则取值 1,否则取值 0

在总共 284,807 笔交易中,只有 492 笔属于欺诈交易。 少数类(欺诈)仅占数据的 0.172% 左右,使数据集高度不平衡。

下表显示了 creditcard.csv 数据的预览:

时间 V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 Amount
0 -1.3598071336738 -0.0727811733098497 2.53634673796914 1.37815522427443 -0.338320769942518 0.462387777762292 0.239598554061257 0.0986979012610507 0.363786969611213 0.0907941719789316 -0.551599533260813 -0.617800855762348 -0.991389847235408 -0.311169353699879 1.46817697209427 -0.470400525259478 0.207971241929242 0.0257905801985591 0.403992960255733 0.251412098239705 -0.018306777944153 0.277837575558899 -0.110473910188767 0.0669280749146731 0.128539358273528 -0.189114843888824 0.133558376740387 -0.0210530534538215 149.62 "0"
0 1.19185711131486 0.26615071205963 0.16648011335321 0.448154078460911 0.0600176492822243 -0.0823608088155687 -0.0788029833323113 0.0851016549148104 -0.255425128109186 -0.166974414004614 1.61272666105479 1.06523531137287 0.48909501589608 -0.143772296441519 0.635558093258208 0.463917041022171 -0.114804663102346 -0.183361270123994 -0.145783041325259 -0.0690831352230203 -0.225775248033138 -0.638671952771851 0.101288021253234 -0.339846475529127 0.167170404418143 0.125894532368176 -0.00898309914322813 0.0147241691924927 2.69 "0"

下载数据集并上传到湖屋

定义这些参数,以便可以将此笔记本与不同的数据集一起使用:

IS_CUSTOM_DATA = False  # If True, the dataset has to be uploaded manually

TARGET_COL = "Class"  # Target column name
IS_SAMPLE = False  # If True, use only <SAMPLE_ROWS> rows of data for training; otherwise, use all data
SAMPLE_ROWS = 5000  # If IS_SAMPLE is True, use only this number of rows for training

DATA_FOLDER = "Files/fraud-detection/"  # Folder with data files
DATA_FILE = "creditcard.csv"  # Data file name

EXPERIMENT_NAME = "aisample-fraud"  # MLflow experiment name

以下代码将下载数据集的公开可用版本,然后将其存储在 Fabric 湖屋中。

重要

在运行笔记本之前,请务必向笔记本添加湖屋。 否则会出错。

if not IS_CUSTOM_DATA:
    # Download data files into the lakehouse if they're not already there
    import os, requests

    remote_url = "https://synapseaisolutionsa.blob.core.windows.net/public/Credit_Card_Fraud_Detection"
    fname = "creditcard.csv"
    download_path = f"/lakehouse/default/{DATA_FOLDER}/raw"

    if not os.path.exists("/lakehouse/default"):
        raise FileNotFoundError("Default lakehouse not found, please add a lakehouse and restart the session.")
    os.makedirs(download_path, exist_ok=True)
    if not os.path.exists(f"{download_path}/{fname}"):
        r = requests.get(f"{remote_url}/{fname}", timeout=30)
        with open(f"{download_path}/{fname}", "wb") as f:
            f.write(r.content)
    print("Downloaded demo data files into lakehouse.")

设置 MLflow 试验跟踪

试验跟踪过程可保存你运行的每个试验的所有试验相关信息。 有时,在运行特定试验时,无法获得更好的结果。 在这些情况下,应停止并尝试新试验。

Microsoft Fabric 中的 Synapse 数据科学体验包含自动记录功能。 此功能减少了在训练期间自动记录机器学习模型的参数、指标和项所需的代码量。 该功能将扩展 MLflow 自动记录功能。 它在数据科学体验中深度集成。

通过自动日志记录,可以轻松跟踪和比较不同模型和试验的性能,而无需手动跟踪。 有关详细信息,请参阅 Microsoft Fabric 中的自动日志记录

若要在笔记本会话中禁用 Microsoft Fabric 自动日志记录,请调用 mlflow.autolog() 并设置 disable=True

# Set up MLflow for experiment tracking
import mlflow

mlflow.set_experiment(EXPERIMENT_NAME)
mlflow.autolog(disable=True)  # Disable MLflow autologging

从湖屋中读取原始数据

此代码从湖屋中读取原始数据:

df = (
    spark.read.format("csv")
    .option("header", "true")
    .option("inferSchema", True)
    .load(f"{DATA_FOLDER}/raw/{DATA_FILE}")
    .cache()
)

步骤 3:执行探索性数据分析

在本节中,首先探索原始数据和概要统计信息。 然后,通过将列强制转换为正确的类型并将 Spark DataFrame 转换为 Pandas DataFrame 来转换数据,以便更轻松地进行可视化。 最后,浏览并可视化数据中的类分布。

显示原始数据

  1. 使用 display 命令浏览原始数据并查看概要统计信息。 有关数据可视化的详细信息,请参阅 Microsoft Fabric 中的笔记本可视化

    display(df)
    
  2. 输出有关数据集的一些基本信息:

    # Print dataset basic information
    print("records read: " + str(df.count()))
    print("Schema: ")
    df.printSchema()
    

转换数据

  1. 将数据集列强制转换为正确类型:

    import pyspark.sql.functions as F
    
    df_columns = df.columns
    df_columns.remove(TARGET_COL)
    
    # Ensure that TARGET_COL is the last column
    df = df.select(df_columns + [TARGET_COL]).withColumn(TARGET_COL, F.col(TARGET_COL).cast("int"))
    
    if IS_SAMPLE:
        df = df.limit(SAMPLE_ROWS)
    
  2. 将 Spark DataFrame 转换为 Pandas DataFrame,以便更轻松地进行可视化和处理:

    df_pd = df.toPandas()
    

浏览数据集中的类分布

  1. 显示数据集中的类分布:

    # The distribution of classes in the dataset
    print('No Frauds', round(df_pd['Class'].value_counts()[0]/len(df_pd) * 100,2), '% of the dataset')
    print('Frauds', round(df_pd['Class'].value_counts()[1]/len(df_pd) * 100,2), '% of the dataset')
    

    该代码返回此数据集类分布:99.83% No Frauds 和 0.17% Frauds。 此类分布表明大多数交易都是非欺诈性的。 因此,需要在训练模型之前进行数据预处理,以避免过度拟合。

  2. 通过查看欺诈性交易与非欺诈性交易的分布,使用绘图来显示数据集中的类不平衡:

    import seaborn as sns
    import matplotlib.pyplot as plt
    
    colors = ["#0101DF", "#DF0101"]
    sns.countplot(x='Class', data=df_pd, palette=colors) 
    plt.title('Class Distributions \n (0: No Fraud || 1: Fraud)', fontsize=10)
    
  3. 使用箱形图显示交易金额的五个数字摘要(最低分、第一四分位数、中位数、第三四分位数、最高分):

    fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12,5))
    s = sns.boxplot(ax = ax1, x="Class", y="Amount", hue="Class",data=df_pd, palette="PRGn", showfliers=True) # Remove outliers from the plot
    s = sns.boxplot(ax = ax2, x="Class", y="Amount", hue="Class",data=df_pd, palette="PRGn", showfliers=False) # Keep outliers from the plot
    plt.show()
    

    当数据高度不平衡时,箱形图可能无法展示准确的见解。 不过可以先解决 Class 不平衡问题,然后创建相同的图来获取更准确的见解。

步骤 4:训练和评估模型

在此训练 LightGBM 模型来对欺诈性交易进行分类。 在不平衡数据集和平衡数据集上训练 LightGBM 模型。 然后,比较两个模型的性能。

准备训练和测试数据集

在训练之前,将数据拆分为训练数据集和测试数据集:

# Split the dataset into training and testing sets
from sklearn.model_selection import train_test_split

train, test = train_test_split(df_pd, test_size=0.15)
feature_cols = [c for c in df_pd.columns.tolist() if c not in [TARGET_COL]]

将 SMOTE 应用于训练数据集

imblearn 库使用合成少数类过采样技术 (SMOTE) 方法来解决不平衡分类问题。 当少数类的示例太少而导致模型无法有效了解决策边界时,就会发生不平衡分类。 SMOTE 是为少数类合成新样本的最广泛使用的方法。

仅将 SMOTE 应用于训练数据集,而不应用于测试数据集。 使用测试数据对模型进行评分时,需要模型针对生产中不可见的数据的性能近似值。 要使近似值有效,测试数据需要通过具有原始的不平衡分布来尽可能接近地表示生产数据。

# Apply SMOTE to the training data
import pandas as pd
from collections import Counter
from imblearn.over_sampling import SMOTE

X = train[feature_cols]
y = train[TARGET_COL]
print("Original dataset shape %s" % Counter(y))

sm = SMOTE(random_state=42)
X_res, y_res = sm.fit_resample(X, y)
print("Resampled dataset shape %s" % Counter(y_res))

new_train = pd.concat([X_res, y_res], axis=1)

有关 SMOTE 的详细信息,请参阅资源 SMOTE 方法的 scikit-learn 参考页有关过采样的 scikit-learn 用户指南

训练机器学习模型并运行试验

Microsoft Fabric 中的 Apache Spark 支持使用大数据进行机器学习。 通过使用 Apache Spark,可以从大量结构化、非结构化和快速移动的数据中获取有价值的见解。

使用 Microsoft Fabric 中的 Apache Spark 训练机器学习模型时,有以下几个选项可供选择:Apache Spark MLlib、SynapseML 和其他的开源库。 有关详细信息,请参阅在 Microsoft Fabric 中训练机器学习模型

机器学习试验是组织和控制所有相关机器学习运行的主要单元。 一次运行对应于模型代码的单次执行。 机器学习试验跟踪涉及管理所有试验及其组件(例如参数、指标、模型和其他生成工件)

使用试验跟踪时,可以组织特定机器学习试验的所有必需组件。 还可以使用保存的试验轻松重现过去的结果。 有关机器学习试验的详细信息,请参阅 Microsoft Fabric 中的机器学习试验

  1. 要跟踪其他指标、参数和文件,请通过设置 exclusive=False 来更新 MLflow 自动日志记录配置:

    mlflow.autolog(exclusive=False)
    
  2. 使用 LightGBM 训练两个模型。 一个模型处理不平衡数据集,另一个模型处理平衡数据集(通过 SMOTE)。 然后比较这两个模型的性能。

    import lightgbm as lgb
    
    model = lgb.LGBMClassifier(objective="binary") # Imbalanced dataset
    smote_model = lgb.LGBMClassifier(objective="binary") # Balanced dataset
    
    # Train LightGBM for both imbalanced and balanced datasets and define the evaluation metrics
    print("Start training with imbalanced data:\n")
    with mlflow.start_run(run_name="raw_data") as raw_run:
        model = model.fit(
            train[feature_cols],
            train[TARGET_COL],
            eval_set=[(test[feature_cols], test[TARGET_COL])],
            eval_metric="auc",
            callbacks=[
                lgb.log_evaluation(10),
            ],
        )
    
    print(f"\n\nStart training with balanced data:\n")
    with mlflow.start_run(run_name="smote_data") as smote_run:
        smote_model = smote_model.fit(
            new_train[feature_cols],
            new_train[TARGET_COL],
            eval_set=[(test[feature_cols], test[TARGET_COL])],
            eval_metric="auc",
            callbacks=[
                lgb.log_evaluation(10),
            ],
        )
    

确定训练的特征重要性

  1. 确定基于不平衡数据集训练的模型的特征重要性。

    with mlflow.start_run(run_id=raw_run.info.run_id):
        importance = lgb.plot_importance(
            model, title="Feature importance for imbalanced data"
        )
        importance.figure.savefig("feauture_importance.png")
        mlflow.log_figure(importance.figure, "feature_importance.png")
    
  2. 确定基于平衡数据训练的模型的特征重要性。 SMOTE 生成了平衡数据:

    with mlflow.start_run(run_id=smote_run.info.run_id):
        smote_importance = lgb.plot_importance(
            smote_model, title="Feature importance for balanced (via SMOTE) data"
        )
        smote_importance.figure.savefig("feauture_importance_smote.png")
        mlflow.log_figure(smote_importance.figure, "feauture_importance_smote.png")
    

使用不平衡数据集训练模型时,与使用平衡数据集训练的模型相比,重要特征具有显著差异。

评估模型

在此评估两个已训练的模型:

  • 使用原始不平衡数据训练的 model
  • 使用平衡数据训练的 smote_model

计算模型指标

  1. 定义一个函数 prediction_to_spark,该函数用于执行预测并将预测结果转换为 Spark DataFrame。 稍后可以使用 SynapseML 计算预测结果的模型统计信息。

    from pyspark.sql.functions import col
    from pyspark.sql.types import IntegerType, DoubleType
    
    def prediction_to_spark(model, test):
        predictions = model.predict(test[feature_cols], num_iteration=model.best_iteration_)
        predictions = tuple(zip(test[TARGET_COL].tolist(), predictions.tolist()))
        dataColumns = [TARGET_COL, "prediction"]
        predictions = (
            spark.createDataFrame(data=predictions, schema=dataColumns)
            .withColumn(TARGET_COL, col(TARGET_COL).cast(IntegerType()))
            .withColumn("prediction", col("prediction").cast(DoubleType()))
        )
    
        return predictions
    
  2. 使用 prediction_to_spark 函数通过 modelsmote_model 这两个模型执行预测:

    predictions = prediction_to_spark(model, test)
    smote_predictions = prediction_to_spark(smote_model, test)
    predictions.limit(10).toPandas()
    
  3. 计算两个模型的指标:

    from synapse.ml.train import ComputeModelStatistics
    
    metrics = ComputeModelStatistics(
        evaluationMetric="classification", labelCol=TARGET_COL, scoredLabelsCol="prediction"
    ).transform(predictions)
    
    smote_metrics = ComputeModelStatistics(
        evaluationMetric="classification", labelCol=TARGET_COL, scoredLabelsCol="prediction"
    ).transform(smote_predictions)
    display(metrics)
    

使用混淆矩阵评估模型性能

混淆矩阵显示模型在使用测试数据进行评分时产生的

  • 真正 (TP)
  • 真负 (TN)
  • 假正 (FP)
  • 假负 (FN)

的数量。 对于二元分类,模型将返回一个 2x2 混淆矩阵。 对于多类分类,模型将返回一个 nxn 混淆矩阵,其中 n 是类的数量。

  1. 使用混淆矩阵总结已训练的机器学习模型对测试数据的性能:

    # Collect confusion matrix values
    cm = metrics.select("confusion_matrix").collect()[0][0].toArray()
    smote_cm = smote_metrics.select("confusion_matrix").collect()[0][0].toArray()
    print(cm)
    
  2. 绘制 smote_model(使用平衡数据训练)预测结果的混淆矩阵图:

    # Plot the confusion matrix
    import seaborn as sns
    
    def plot(cm):
        """
        Plot the confusion matrix.
        """
        sns.set(rc={"figure.figsize": (5, 3.5)})
        ax = sns.heatmap(cm, annot=True, fmt=".20g")
        ax.set_title("Confusion Matrix")
        ax.set_xlabel("Predicted label")
        ax.set_ylabel("True label")
        return ax
    
    with mlflow.start_run(run_id=smote_run.info.run_id):
        ax = plot(smote_cm)
        mlflow.log_figure(ax.figure, "ConfusionMatrix.png")
    
  3. 绘制 model(使用原始不平衡数据训练)预测结果的混淆矩阵图:

    with mlflow.start_run(run_id=raw_run.info.run_id):
        ax = plot(cm)
        mlflow.log_figure(ax.figure, "ConfusionMatrix.png")
    

使用 AUC-ROC 和 AUPRC 度量值评估模型性能

“接收者操作特性曲线下面积 (AUC-ROC)”度量值用于评估二元分类器的性能。 AUC-ROC 图表直观显示了真正率 (TPR) 和假正率 (FPR) 之间的权衡。

在某些情况下,根据“精准率-召回率曲线下面积 (AUPRC)”度量值来评估分类器更为合适。 AUPRC 曲线用于合并这些速率:

  • 精准率,也称为正预测值 (PPV)
  • 召回,也称为 TPR

若要使用 AUC-ROC 和 AUPRC 度量值来评估性能:

  1. 定义一个返回 AUC-ROC 和 AUPRC 度量值的函数:

    from pyspark.ml.evaluation import BinaryClassificationEvaluator
    
    def evaluate(predictions):
        """
        Evaluate the model by computing AUROC and AUPRC with the predictions.
        """
    
        # Initialize the binary evaluator
        evaluator = BinaryClassificationEvaluator(rawPredictionCol="prediction", labelCol=TARGET_COL)
    
        _evaluator = lambda metric: evaluator.setMetricName(metric).evaluate(predictions)
    
        # Calculate AUROC, baseline 0.5
        auroc = _evaluator("areaUnderROC")
        print(f"The AUROC is: {auroc:.4f}")
    
        # Calculate AUPRC, baseline positive rate (0.172% in the data)
        auprc = _evaluator("areaUnderPR")
        print(f"The AUPRC is: {auprc:.4f}")
    
        return auroc, auprc    
    
  2. 记录根据不平衡数据训练的模型的 AUC-ROC 和 AUPRC 指标:

    with mlflow.start_run(run_id=raw_run.info.run_id):
        auroc, auprc = evaluate(predictions)
        mlflow.log_metrics({"AUPRC": auprc, "AUROC": auroc})
        mlflow.log_params({"Data_Enhancement": "None", "DATA_FILE": DATA_FILE})
    
  3. 记录根据平衡数据训练的模型的 AUC-ROC 和 AUPRC 指标:

    with mlflow.start_run(run_id=smote_run.info.run_id):
        auroc, auprc = evaluate(smote_predictions)
        mlflow.log_metrics({"AUPRC": auprc, "AUROC": auroc})
        mlflow.log_params({"Data_Enhancement": "SMOTE", "DATA_FILE": DATA_FILE})
    

与使用不平衡数据训练的模型相比,使用平衡数据训练的模型返回的 AUC-ROC 和 AUPRC 值更高。 基于这些度量值,SMOTE 似乎是用于在处理高度不平衡数据时增强模型性能的有效技术。

如下图所示,将记录任何试验及其各自的名称。 可以在工作区中跟踪试验的参数和性能指标。

Screenshot of the tracked experiment.

此图显示了使用平衡数据集训练的模型的性能指标(在版本 2 中)。

Screenshot of logged model performance metrics and model parameters.

可以选择“版本 1”,以查看使用不平衡数据集训练的模型的指标。 在比较指标时,使用均衡数据集训练的模型 AUROC 会更高。 这些结果表明,此模型更好地正确将 0 类预测为 0,并将 1 类预测为 1

步骤 5:注册模型

使用 MLflow 注册这两个模型:

# Register the model
registered_model_name = f"{EXPERIMENT_NAME}-lightgbm"

raw_model_uri = "runs:/{}/model".format(raw_run.info.run_id)
mlflow.register_model(raw_model_uri, registered_model_name)

smote_model_uri = "runs:/{}/model".format(smote_run.info.run_id)
mlflow.register_model(smote_model_uri, registered_model_name)

步骤 6:保存预测结果

Microsoft Fabric 允许用户通过 PREDICT 可缩放函数来操作机器学习模型。 此函数支持任何计算引擎中的批处理评分(或批处理推理)。

可以直接从 Microsoft Fabric 笔记本或模型的项页生成批量预测。 有关 PREDICT 的详细信息,请参阅在 Microsoft Fabric 中使用 PREDICT 进行模型评分

  1. 加载性能更好的模型(版本 2)进行批量评分并生成预测结果

    from synapse.ml.predict import MLFlowTransformer
    
    spark.conf.set("spark.synapse.ml.predict.enabled", "true")
    
    model = MLFlowTransformer(
        inputCols=feature_cols,
        outputCol="prediction",
        modelName=f"{EXPERIMENT_NAME}-lightgbm",
        modelVersion=2,
    )
    
    test_spark = spark.createDataFrame(data=test, schema=test.columns.to_list())
    
    batch_predictions = model.transform(test_spark)
    
  2. 将预测保存到湖屋:

    # Save the predictions to the lakehouse
    batch_predictions.write.format("delta").mode("overwrite").save(f"{DATA_FOLDER}/predictions/batch_predictions")