机器学习的特征存储-二-

80 阅读1小时+

机器学习的特征存储(二)

原文:annas-archive.org/md5/b415bac641e475b2a74b5ca99453c99f

译者:飞龙

协议:CC BY-NC-SA 4.0

第六章:模型训练和推理

在上一章中,我们讨论了在 AWS 云中的Feast 部署,并将 S3 设置为离线存储,将 DynamoDB 设置为在线存储模型。我们还回顾了使用第一章机器学习生命周期概述中构建的客户终身价值LTV/CLTV)模型所经历的几个 ML 生命周期阶段。在模型开发处理过程中,我们进行了数据清洗和特征工程,并生成了特征集,为这些特征定义创建了并应用于 Feast。最后,我们成功地将特征导入 Feast,并且也能够查询导入的数据。

在本章中,我们将继续 ML 生命周期的其余部分,这将涉及使用特征存储进行模型训练、打包、批量推理和在线模型推理。本章的目标是继续使用上一章创建的特征存储基础设施,并完成 ML 生命周期的其余部分。在这个过程中,我们将有机会了解如何在 ML 开发中使用特征存储来提高模型的上市时间,解耦 ML 生命周期的不同阶段,并有助于协作。我们还将回顾第一章机器学习生命周期概述,并在执行这些步骤时比较不同阶段。本章将帮助您了解如何使用特征存储进行模型训练,然后进行模型推理。我们还将学习在线存储库所服务的用例以及离线存储库所服务的用例。

我们将按以下顺序讨论以下主题:

  • 使用特征存储进行模型训练

  • 模型打包

  • 使用 Feast 进行批量模型推理

  • 使用 Feast 进行在线模型推理

  • 处理开发过程中功能集的变化

前提条件

为了运行示例并更好地理解本章内容,需要使用在第四章将特征存储添加到机器学习模型中创建的资源。在本章中,我们将使用上一章创建的资源,并使用本章创建的特征存储库。以下 GitHub 链接指向我创建的特征存储库:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/tree/main/customer_segmentation

技术要求

要跟随章节中的代码示例,你需要熟悉 Python 和任何笔记本环境,这可能是一个本地的设置,如 Jupyter,或者是一个在线笔记本环境,如 Google Colab、Kaggle 或 SageMaker。你还需要一个 AWS 账户,可以完全访问 Redshift、S3、Glue、DynamoDB、IAM 控制台等资源。你可以在试用期间创建一个新账户并免费使用所有服务。在最后一部分,你需要一个 IDE 环境来开发在线模型的 REST 端点。你可以在以下 GitHub 链接中找到本书的代码示例:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/tree/main/Chapter05

使用特征存储进行模型训练

第一章机器学习生命周期概述中,在特征工程之后,我们直接在同一笔记本中开始了模型训练。而相比之下,在第四章将特征存储添加到机器学习模型中,生成的特征被摄入到特征存储中。这是特征存储在机器学习生命周期中帮助实现的标准化的一个方面。通过将特征摄入到特征存储中,创建了一个可发现、可共享、可重用和版本化的数据集/特征集。

现在假设有两个数据科学家,拉姆和迪,他们正在同一个模型上工作。他们都可以使用这个特征集,而无需做任何额外的工作。不仅如此,如果背景数据每天都会更新,那么所有需要做的就是在数据科学家到来时每天运行一次特征工程笔记本,最新的特征就会可供使用。更好的做法是使用如AirflowAWS Step Functions或甚至GitHub工作流等编排框架来安排特征工程笔记本。一旦完成,拉姆和迪在来工作时都可以使用最新的特征进行实验。

正如我们一直在讨论的,数据工程师和科学家从特征存储中获得的最大优势之一是协作。让我们尝试看看我们的两位数据科学家 Dee 和 Ram 在模型构建中如何协作/竞争。每天 Dee 和 Ram 来上班时,假设计划中的特征工程已经成功运行,他们就开始进行模型训练。这里需要注意的另一件重要事情是,对于模型训练,数据源是特征存储。数据科学家不需要进入原始数据源来生成特征,除非他们对现有特征的模型不满意。在这种情况下,数据科学家将再次进行数据探索,生成额外的特征集,并将它们导入特征存储。导入的特征再次可供每个人使用。这将一直持续到团队/数据科学家对模型的性能满意为止。

在我们将 Dee 和 Ram 两位数据科学家的工作流程分开之前,让我们回顾一下他们模型训练笔记本中的共同步骤。让我们打开一个新的 Python 笔记本,命名为 model-training.ipynb,并生成训练数据。离线存储将用于生成训练数据集,因为它存储历史数据,并使用时间戳对数据进行版本控制。在 Feast 中,数据存储的接口是通过 API 实现的,正如我们在 第三章 特征存储基础、术语和用法第四章 将特征存储添加到机器学习模型 中所看到的。因此,为了生成训练数据集,我们将使用 get_historical_featuresget_historical_features API 的一个输入是实体 ID。通常,在企业中,实体 ID 可以从原始数据源中获取。典型的原始数据源包括数据库、数据仓库、对象存储等。获取实体的查询可能像 select unique {entity_id} from {table}; 这样简单。让我们在这里做类似的事情。我们的原始数据源是 CSV 文件。让我们使用它来获取实体 ID。在我们继续之前,让我们安装所需的包:

  1. 以下代码块安装了模型训练所需的包:

    !pip install feast[aws]==0.19.3 pandas xgboost
    
  2. 安装完所需的包后,如果您还没有克隆特征仓库,请克隆它,因为我们需要连接到特征存储来生成训练数据集。以下代码克隆了仓库:

    !git clone <repo_url>
    
  3. 现在我们有了特征仓库,让我们连接到 Feast/特征存储,确保一切按预期工作,然后再继续:

    # change directory
    %cd customer_segmentation
    """import feast and load feature store object with the path to the directory which contains feature_story.yaml."""
    from feast import FeatureStore
    store = FeatureStore(repo_path=".")
    for entity in store.list_entities():
      print(f"entity: {entity}")
    

前面的代码块连接到 Feast 功能仓库。repo_path="." 参数表示 feature_store.yaml 文件位于当前工作目录中。它还列出了 customer_segmentation 功能仓库中可用的实体。

现在我们能够连接到特征仓库,让我们创建训练模型所需的实体 ID 列表。为了获取实体 ID 列表,在这种情况下,CustomerId,让我们使用原始数据集并从中过滤出实体 ID。

重要提示

我们使用与第四章中使用的相同原始数据集,将特征存储添加到机器学习模型中。以下是数据集的 URL:www.kaggle.com/datasets/vijayuv/onlineretail

  1. 以下代码块加载了原始数据:

    import pandas as pd
    ##Read the OnlineRetail.csv
    retail_data = pd.read_csv('/content/OnlineRetail.csv',
                              encoding= 'unicode_escape')
    retail_data['InvoiceDate'] = pd.to_datetime(
      retail_data['InvoiceDate'], errors = 'coerce')
    

    重要提示

    您可能会质疑为什么在这里需要原始数据。Feast 允许对实体进行查询。因此,我们需要需要特征的实体 ID。

  2. 让我们过滤掉感兴趣的顾客 ID,类似于在特征创建过程中所做的过滤。以下代码块选择不属于英国的数据库集,以及存在于三个月数据集中的顾客 ID(选择三个月数据集中的顾客的原因是,在生成 RFM 特征后,我们在特征工程笔记本中的数据集上执行了左连接)。

以下代码块执行了所描述的过滤操作:

## filter data for United Kingdom
uk_data = retail_data.query("Country=='United Kingdom'").reset_index(drop=True)
t1 = pd.Timestamp("2011-06-01 00:00:00.054000")
t2 = pd.Timestamp("2011-03-01 00:00:00.054000")
uk_data_3m = uk_data[(uk_data.InvoiceDate < t1) & (uk_data.InvoiceDate >= t2)].reset_index(drop=True)

uk_data_3m中,我们需要获取唯一的CustomerId。实体数据中还需要额外的列是时间戳,以执行点时间连接。现在,我将使用所有实体 ID 的最新时间戳。

  1. 以下代码块创建了查询历史商店所需的实体 DataFrame:

    from datetime import datetime
    entity_df = pd.DataFrame(data = {
        "customerid": [str(item) for item in uk_data_3m.CustomerID.unique().tolist()],
        "event_timestamp": datetime.now()
    })
    entity_df.head()
    

前一个代码块生成了以下输出:

图 5.1 – 用于生成训练数据集的实体 DataFrame

图 5.1 – 用于生成训练数据集的实体 DataFrame

图 5.1所示,实体 DataFrame 包含两列:

  • CustomerID:需要获取特征的客户列表。

  • event_timestamp

现在 Dee 和 Ram 的模型训练笔记本中的共同步骤已完成,让我们分割他们的工作流程,看看他们如何协作。

Dee 的模型训练实验

从上一步骤继续(您可以随意复制代码块并在不同的笔记本中运行它们,并将其命名为dee-model-training.ipynb),现在是时候选择训练模型所需的特征集了:

  1. 为了选择特征,Dee 将运行以下命令来查看现有特征视图中可用的特征:

    feature_view = store.get_feature_view("customer_rfm_features")
    print(feature_view.to_proto())
    

前一个命令输出了特征视图。以下块显示了输出的一部分,包括特征和实体,它们是特征视图的一部分:

  name: "customer_rfm_features"
  entities: "customer"
  features {
    name: "recency"
    value_type: INT32
  }
  features {
    name: "frequency"
    value_type: INT32
  }
  features {
    name: "monetaryvalue"
    value_type: DOUBLE
  }
  …

meta {
  created_timestamp {
    seconds: 1647301293
    nanos: 70471000
  }
  last_updated_timestamp {
    seconds: 1647301293
    nanos: 70471000
  }
}

从特征集中,假设 Dee 想要排除与频率相关的特征,看看这会对模型的性能产生什么影响。因此,她选择了所有其他特征进行查询,并排除了频率F,其中F表示频率组。

  1. 以下代码块查询历史/离线存储,使用图 5.1中显示的实体 DataFrame 获取所需特征:

    import os
    from datetime import datetime
    os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key_id>"
    os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
    os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
    job = store.get_historical_features(
        entity_df=entity_df,
        features=[
                  "customer_rfm_features:recency", 
                  "customer_rfm_features:monetaryvalue", 
                  "customer_rfm_features:r", 
                  "customer_rfm_features:m",
                  "customer_rfm_features:rfmscore",
                  "customer_rfm_features:segmenthighvalue",
                  "customer_rfm_features:segmentlowvalue"
                  "customer_rfm_features:segmentmidvalue",
                  "customer_rfm_features:ltvcluster"
                  ]
        )
    feature_data = job.to_df()
    feature_data = feature_data.dropna()
    feature_data.head()
    

前面的代码块输出以下 DataFrame:

图 5.2 – Dee 模型的训练数据集

图 5.2 – Dee 模型的训练数据集

重要提示

在前面的代码块中,将<aws_key_id><aws_secret>替换为在第四章将特征存储添加到机器学习模型中创建的用户凭据。

  1. 现在,Dee 已经生成了训练数据集,下一步是模型训练。让我们使用与第一章机器学习生命周期概述中使用的相同参数构建 XGBoost 模型。以下代码块将数据集分为培训和测试:

    from sklearn.metrics import classification_report,confusion_matrix
    import xgboost as xgb
    from sklearn.model_selection import KFold, cross_val_score, train_test_split
    #Drop prediction column along with event time and customerId columns from X
    X = feature_data.drop(['ltvcluster''customerid', 
                           'event_timestamp'], axis=1)
    y = feature_data['ltvcluster']
    X_train, X_test, y_train, y_test = \ 
    train_test_split(X, y, test_size=0.1)
    
  2. 以下代码块使用前一个示例中创建的培训和测试数据集,训练一个XGBClassifier模型:

    xgb_classifier = xgb.XGBClassifier(max_depth=5, objective='multi:softprob')
    #model training
    xgb_model = xgb_classifier.fit(X_train, y_train)
    #Model scoring
    acc = xgb_model.score(X_test,y_test)
    print(f"Model accuracy: {acc}")
    

前面的代码块打印出模型的准确率:

Model accuracy: 0.8840579710144928
  1. 以下代码块在测试数据集上运行predict函数并打印出分类报告:

    #Run prediction on the test dataset
    y_pred = xgb_model.predict(X_test)
    print(classification_report(y_test, y_pred))
    

前面的代码块产生以下输出:

图 5.3 – Dee 模型的分类报告

图 5.3 – Dee 模型的分类报告

不仅于此,Dee 还可以尝试不同的特征集和算法。目前,我们假设 Dee 对她自己的模型感到满意。让我们继续看看 Ram 会做什么。

Ram 的模型训练实验

再次强调,我们将从图 5.1之后的步骤继续在笔记本中操作(您可以自由复制代码块,在另一个笔记本中运行它们,并将其命名为ram-model-training.ipynb)。现在是选择训练模型所需特征集的时候了。为了选择特征,Ram 将遵循与 Dee 类似的步骤。让我们假设 Ram 有不同的想法——他不是删除一个特定的类别,而是删除具有实际值的特征,只使用 R、F 和 M 分类特征以及分类特征段。根据 Ram 的说法,这些分类变量是实际值的一些转换:

  1. 以下代码块产生 Ram 训练模型所需的特征集:

    import os
    from datetime import datetime
    os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key_id>"
    os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
    os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
    job = store.get_historical_features(
        entity_df=entity_df,
        features=[
                 "customer_rfm_features:r", 
                 "customer_rfm_features:m",
                 "customer_rfm_features:f",
                 "customer_rfm_features:segmenthighvalue",
                 "customer_rfm_features:segmentlowvalue",
                 "customer_rfm_features:segmentmidvalue",
                 "customer_rfm_features:ltvcluster"
                 ]
        )
    feature_data = job.to_df()
    feature_data = feature_data.dropna()
    feature_data.head()
    

    重要提示

    在前面的代码块中,将<aws_key_id><aws_secret>替换为在第四章将特征存储添加到机器学习模型中创建的用户凭据。

前面的代码块产生以下输出:

图 5.4 – Ram 模型的训练数据集

图 5.4 – Ram 模型的训练数据集

  1. 下一步与 Dee 执行的操作类似,即训练模型并查看其分类报告。让我们来做这件事。

以下代码块在图 5.4中的特征集上训练模型:

from sklearn.metrics import classification_report,confusion_matrix
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold, cross_val_score, train_test_split
X = feature_data.drop(['ltvcluster''customerid',
                       'event_timestamp'], axis=1)
y = feature_data['ltvcluster']
X_train, X_test, y_train, y_test = \ 
train_test_split(X, y, test_size=0.1)
model =  (random_state=0).fit(X_train, y_train)
acc = model.score(X_test,y_test)
print(f"Model accuracy: {acc}")

上述代码块在训练后打印了模型在测试集上的准确率。代码与 Dee 所使用的类似,但使用的是 LogisticRegression 而不是 XGBClassifier。代码块生成了以下输出:

Model accuracy: 0.8623188405797102
  1. 让我们打印测试数据集上的分类报告,以便我们可以比较 Ram 和 Dee 的模型。以下代码块生成了该模型的分类报告:

    y_pred = model.predict(X_test)
    print(classification_report(y_test, y_pred))
    

上述代码块生成了以下输出:

![图 5.5 – Ram 模型的分类报告]

![图片 B18024_05_005.jpg]

图 5.5 – Ram 模型的分类报告

现在,Ram 和 Dee 可以通过查看他们各自运行的实验来比较彼此的工作。不仅这些两个实验,他们还可以运行多个实验,在所有比较之后得出最佳模型。不仅如此,他们还可以通过编写代码尝试所有特征集的组合来自动化实验,在运行这些实验的同时查看和探索更多数据或专注于其他方面的工作。

我在这里建议的另一件事是使用实验跟踪工具/软件之一。市场上有很多这样的工具。其中一些提供了您使用的笔记本基础设施。例如,Databricks 提供 MLflowSageMaker 有自己的,还有第三方实验跟踪工具,如 NeptuneClearML 等。更多实验跟踪和比较的工具可以在以下博客中找到:neptune.ai/blog/best-ml-experiment-tracking-tools

假设 Dee 和 Ram 在所有实验之后得出结论,认为 XGBClassifier 表现得更好,并决定使用该模型。接下来,让我们看看下一节中的模型打包。

模型打包

在上一节中,我们构建了两个模型版本。在本节中,我们将打包其中一个模型并保存它以供模型评分和部署。正如上一节所述,我们将打包 XGBClassifier 模型。再次强调,对于打包,有不同解决方案和工具可用。为了避免设置另一个工具,我将使用 joblib 库来打包模型:

  1. 在生成 XGBClassifier 模型的同一笔记本中,以下代码块安装了 joblib 库:

    #install job lib library for model packaging
    !pip install joblib
    
  2. 安装 joblib 库后,下一步是使用它来打包模型对象。以下代码块打包了模型并将其写入文件系统上的特定位置:

    import joblib
    joblib.dump(xgb_model, '/content/customer_segment-v0.0')
    

上述代码块在 /content 文件夹中创建了一个文件。为了验证这一点,运行一个 ls 命令并检查文件是否存在。让我们也验证模型是否可以被加载,并且我们是否可以在其上运行 predict 函数。

  1. 以下代码块从 /content/customer_segment-v0.0 位置加载模型并在样本数据集上运行预测:

    loaded_model = joblib.load('/content/customer_segment-v0.0')
    prediction = loaded_model.predict(X_test.head())
    prediction.tolist()
    

前面的代码块应该没有错误地运行,并打印以下预测输出:

[0.0, 0.0, 0.0, 2.0, 0.0]
  1. 现在我们有了打包好的模型,下一步是将它注册到模型仓库中。同样,有许多工具可供使用来管理模型,例如 MLflow、SageMaker 以及其他工具。我强烈建议使用其中之一,因为它们可以处理许多用于共享、部署、标准版本控制等方面的用例。为了简化,我将在这里使用 S3 存储桶作为模型注册处,并将训练好的模型上传到那里。

下面的代码将打包好的模型上传到 S3 存储桶:

import boto3
s3_client = boto3.client('s3')
s3_client.upload_file(
  '/content/customer_segment-v0.0', 
  "feast-demo-mar-2022", 
  "model-repo/customer_segment-v0.0")

前面的代码块将文件 S3 bucket, feast-demo-mar-2022 上传到以下前缀:model-repo/customer_segment-v0.0。请通过访问 AWS 控制台来验证这一点,以确保模型已上传到指定的位置。

到目前为止,我们已经完成了模型训练和实验,并在模型仓库(S3 存储桶)中注册了一个候选模型。让我们在下一节创建一个用于批量模型用例的模型预测笔记本。

使用 Feast 进行批量模型推理

在本节中,让我们看看如何运行批量模型的预测。为了对批量模型进行预测,我们需要两样东西:一个是模型,另一个是用于预测的客户及其特征集列表。在上一节中,我们在模型注册处(即 S3)创建并注册了一个模型。同时,所需的特征在特征存储中也是可用的。我们需要的只是需要运行预测的客户列表。客户列表可以从我们之前在模型训练期间使用的原始数据集中生成。然而,为了这个练习的目的,我们将取一小部分客户并对其运行预测。

让我们创建一个模型预测笔记本并加载在模型仓库中注册的模型:

  1. 下面的代码块安装了预测笔记本所需的依赖项:

    !pip install feast[aws]==0.19.3 pandas xgboost joblib
    
  2. 在安装了依赖项之后,其他所需的步骤是如果尚未完成,则获取特征仓库。这是所有使用 Feast 的笔记本中常见的需求之一。然而,在其他特征存储中,这个过程可能并不相同。其中一个原因是 Feast 是以 SDK/CLI 为导向的。其他特征存储,如 SageMaker 和 Databricks,可能只需要凭证来访问它。我们将在下一章中查看一个示例。

  3. 假设你已经克隆了上一章中创建的 Feast 仓库(该仓库也用于模型创建),下一步是从模型注册处的 S3 获取模型。

下面的代码块从 S3 位置(即模型上传到的位置)下载模型:

import boto3
import os
#aws Credentials
os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key_id>"
os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
#Download model from s3
model_name = "customer_segment-v0.0"
s3 = boto3.client('s3')
s3.download_file("feast-demo-mar-2022", 
                 f"model-repo/{model_name}", 
                 model_name)

在执行前面的代码块之后,你应该在当前工作目录中看到一个名为 customer_segment-v0.0 的文件。你可以使用 ls 命令或通过文件夹浏览器来验证它。

重要提示

将前一个代码块中的 <aws_key_id><aws_secret> 替换为在 第四章 中创建的用户凭据,添加特征存储到机器学习模型

  1. 下一步是获取需要评分的客户列表。如前所述,这可以从原始数据源中获取,但为了练习的目的,我将硬编码一个客户样本列表。为了模拟从原始数据源获取客户,我将调用一个返回客户列表的函数。

下面的代码块显示了从原始数据源获取客户的模拟函数:

def fetch_customers_from_raw_data():
  ## todo: code to fetch customers from raw data
  return ["12747.0""12841.0""12849.0", 
          "12854.0""12863.0"]
customer_to_be_scored=fetch_customers_from_raw_data()
  1. 现在我们有了要评分的客户列表,下一步是获取这些客户的特征。有几种不同的方法可以做到这一点。一种方法是使用在线存储,另一种方法是使用离线存储。对于批处理模型,由于延迟不是必需的,最经济的方法是使用离线存储;只是离线存储需要查询最新的特征。这可以通过使用 event_timestamp 列来完成。让我们使用离线存储并查询给定客户列表所需的特征。为此,我们需要实体 DataFrame。让我们接下来创建它。

  2. 下面的代码块创建所需的实体 DataFrame 以获取最新的特征:

    import pandas as pd
    from datetime import datetime
    entity_df = pd.DataFrame(data={
        "customerid": customer_to_be_scored,
        "event_timestamp": datetime.now()
    })
    entity_df.head()
    

前面的代码块输出以下实体 DataFrame:

![Figure 5.6 – 预测用实体 DataFrame

![img/B18024_05_006.jpg]

图 5.6 – 预测用实体 DataFrame

要获取任何客户的最新特征,您需要将 event_timestamp 设置为 datetime.now()。让我们使用 图 5.4 中的实体 DataFrame 来查询离线存储。

  1. 下面的代码块获取给定实体 DataFrame 的特征:

    %cd customer_segmentation
    from feast import FeatureStore
    store = FeatureStore(repo_path=".")
    job = store.get_historical_features(
        entity_df=entity_df,
        features=[
                  "customer_rfm_features:recency", 
                  "customer_rfm_features:monetaryvalue", 
                  "customer_rfm_features:r", 
                  "customer_rfm_features:m",
                  "customer_rfm_features:rfmscore",
                  "customer_rfm_features:segmenthighvalue",
                  "customer_rfm_features:segmentlowvalue",
                  "customer_rfm_features:segmentmidvalue"
              ]
        )
    pred_feature_data = job.to_df()
    pred_feature_data = pred_feature_data.dropna()
    pred_feature_data.head()
    

前面的代码块产生以下输出:

![Figure 5.7 – 预测用特征

![img/B18024_05_007.jpg]

图 5.7 – 预测用特征

  1. 现在我们有了用于预测的特征,下一步是加载下载的模型,并使用 图 5.5 中的特征为客户运行预测。下面的代码块正是这样做的:

    import joblib
    ## Drop unwanted columns
    features = pred_feature_data.drop(
        ['customerid''event_timestamp'], axis=1)
    loaded_model = joblib.load('/content/customer_segment-v0.0')
    prediction = loaded_model.predict(features)
    
  2. 运行预测的最后一步是将预测结果存储在数据库或对象存储中,以便以后使用。在这个练习中,我将把预测结果写入 S3 桶。您可以将结果沉入其他数据存储。

  3. 下面的代码块将预测结果以及特征保存到 S3 位置:

    file_name = f"customer_ltv_pred_results_{datetime.now()}.parquet"
    pred_feature_data["predicted_ltvcluster"] = prediction.tolist()
    s3_url = f's3://feast-demo-mar-2022/prediction_results/{file_name}'
    pred_feature_data.to_parquet(s3_url)
    

通过最后一个代码块,我们完成了批量模型的实现。你心中的疑问可能是 特征存储的引入是如何改变到目前为止的机器学习生命周期的?。它的早期采用解耦了特征工程、模型训练和模型评分的步骤。它们中的任何一个都可以独立运行,而无需干扰管道的其他部分。这是一个巨大的好处。另一部分是部署。我们在第一步中创建的笔记本是具体的,执行特定的任务,如特征工程、模型训练和模型评分。

现在,为了将模型投入生产,我们只需要使用编排框架安排特征工程笔记本和模型评分笔记本,模型就会以全规模运行。我们将在下一章中探讨模型的投入生产。

在下一节中,我们将看看在线模型使用案例需要做些什么。

使用 Feast 进行在线模型推理

在上一节中,我们讨论了如何在批量模型推理中使用 Feast。现在,是时候看看在线模型的使用案例了。在线模型推理的一个要求是它应该以低延迟返回结果,并且可以从任何地方调用。其中一种常见的范式是将模型作为 REST API 端点公开。在 模型打包 部分,我们使用 joblib 库记录了模型。该模型需要用 RESTful 框架包装,以便作为 REST 端点部署。不仅如此,当推理端点被调用时,特征也需要实时获取。与 第一章 中讨论的,在 机器学习生命周期概述 中,我们没有实时服务特征的架构不同,这里,我们已经有了 Feast 的支持。然而,我们需要运行命令,使用 Feast 库将离线特征同步到在线商店。让我们先做这个。稍后,我们将探讨打包。

同步最新特征从离线到在线商店

要将特征从离线存储加载到在线商店,我们需要 Feast 库:

  1. 让我们打开一个笔记本并安装所需的依赖项:

    !pip install feast[aws]==0.19.3
    
  2. 在安装所需的依赖项后,克隆特征存储仓库。如前所述,这是所有笔记本的要求。假设您已经将仓库克隆到当前工作目录中,以下命令将从离线存储加载最新特征到在线商店:

    %cd customer_segmentation/
    from datetime import datetime
    import os
    #aws Credentials
    os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key_id>"
    os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
    os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
    # Command to sync offline features into online.
    !feast materialize-incremental {datetime.now().isoformat()}
    

前面的命令会输出如下截图所示的进度:

图 5.8 – 同步离线数据到在线商店

图 5.8 – 同步离线数据到在线商店

  1. 在将离线数据加载到在线商店后,让我们在在线商店上运行一个查询,并确保它按预期工作。要查询在线商店,初始化特征商店对象并调用 get_online_features API,如下面的代码块所示:

    import pandas as pd
    from feast import FeatureStore
    store = FeatureStore(repo_path=".")
    feature_vector = store.get_online_features(
        features=[
            "customer_rfm_features:recency", 
            "customer_rfm_features:monetaryvalue", 
            "customer_rfm_features:r", 
            "customer_rfm_features:m",
        ],
        entity_rows=[
            {"customer""12747.0"},
            {"customer""12841.0"},
    {"customer""abcdef"},
        ],
    ).to_dict()
    df = pd.DataFrame(feature_vector)
    df.head()
    

上述代码块以低延迟从在线商店(DynamoDB)获取数据。当你运行上述代码块时,你会注意到它响应的速度有多快,与历史存储查询相比。代码块的输出如下所示:

图 5.9 – 查询在线商店

图 5.9 – 查询在线商店

图 5.7 的最后一行包含 NaN 值。这是 Feast 如果给定的任何实体 ID 都不存在于在线商店中时的响应示例。在这个例子中,具有 ID abcdef 的客户不存在于特征商店中,因此它为相应的行返回 NaN 值。

现在在线商店已经准备好最新的特征,让我们看看如何将模型打包成 RESTful API。

使用 Feast 代码将在线模型打包成 REST 端点

这一部分更多地关于软件工程,而不是数据工程或数据科学技能。Python 有许多 REST API 框架可供选择,例如 POST 方法端点,它将接受客户 ID 列表作为输入并返回预测列表:

  1. 下面的代码块显示了将要实现的 API 协议:

    POST /invocations
    {
       "customer_list": ["id1", "id2", …]
    }
    Response: status 200
    {
    "predictions": [0, 1, …]
    }
    
  2. 现在我们有了 API 协议,下一步是选择我们将要使用的 REST 框架。在现有的 REST 框架中选择一个框架与其他框架相比有不同的权衡。由于这超出了本书的范围,我将使用 fastapi (fastapi.tiangolo.com/),因为它是一个异步框架。如果你熟悉其他框架,如 flaskdjango,请随意使用。无论你使用哪个框架,预测结果都将相同。无论你选择哪个框架,请记住,在部署之前,我们将对 REST API 进行 Docker 化。

要构建 API,我将使用 PyCharm IDE。如果你有其他喜欢的 IDE,请随意使用。此外,为了开发 API 和运行 API,我们需要以下库:feast[aws]uvicorn[standard]fastapijoblibxgboost。你可以使用 pip install 命令安装这些库。我将由你来决定,因为安装步骤取决于你使用的 IDE、平台以及个人偏好。然而,我将使用 virtualenv 来管理我的 Python 环境。

我的项目文件夹结构如下所示。如果你还没有注意到,特征仓库也被复制到了同一个文件夹中,因为我们需要初始化特征商店对象以及特征在线商店:

![图 5.10 – 在 IDE 中的在线模型文件夹结构img/B18024_05_010.jpg

图 5.10 – IDE 中的在线模型文件夹结构

  1. main.py 文件中,让我们定义我们将要实现的 API。复制以下代码并将其粘贴到 main.py 文件中:

    from fastapi import FastAPI
    app = FastAPI()
    @app.get("/ping")
    def ping():
        return {"ping""ok"}
    @app.post("/invocations")
    def inference(customers: dict):
        return customers
    

如前一个代码块所示,有两个 API:pinginference

  • pingping API 是一个健康检查端点,在部署应用程序时将需要它。ping URL 将由基础设施,如 ECS 或 Kubernetes,用于检查应用程序是否健康。

  • inference:另一方面,inference API 将包含从特征存储中获取给定客户的特征、对模型进行评分并返回结果的逻辑。

  1. 一旦你复制了前面的代码并将其粘贴到 main.py 文件中并保存,请转到终端并运行以下命令:

    cd <project_folder>
    uvicorn main:app --reload
    
  2. 前面的命令将在本地服务器上运行 FastAPI 服务器并打印类似于以下代码块的输出:

    $ uvicorn main:app --reload
    INFO:     Will watch for changes in these directories: ['<folder path>']
    INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
    INFO:     Started reloader process [24664] using watchgod
    WARNING:  The --reload flag should not be used in production on Windows.
    INFO:     Started server process [908]
    INFO:     Waiting for application startup.
    INFO:     Application startup complete.
    

    重要提示

    确保在运行命令之前已在终端中激活了虚拟环境。

  3. 应用程序运行后,访问 URL 127.0.0.1:8000/docs。你应该会看到一个 Swagger UI,如下面的截图所示:

Figure 5.11 – API 的 Swagger UI

img/B18024_05_011.jpg

图 5.11 – API 的 Swagger UI

我们将在 图 5.9 中使用 Swagger UI 来稍后调用 API。现在,请随意玩耍,探索可用的功能,并调用 API。

  1. 现在我们已经设置了 API 结构,接下来让我们实现 inference API。如前所述,inference API 将从特征存储中读取特征并运行预测。

  2. 我们还需要从模型仓库中加载模型。在我们的案例中,仓库是 S3。因此,我们需要代码从 S3 位置下载模型并将其加载到内存中。以下代码块从 S3 下载模型并将其加载到内存中。请注意,这是在应用程序初始加载期间的一次性活动。因此,让我们在 main.py 文件中的函数外部添加以下代码:

    import boto3
    Import joblib
    model_name = "customer_segment-v0.0"
    s3 = boto3.client('s3')
    ## download file from s3
    s3.download_file(
        "feast-demo-mar-2022",
        f"model-repo/{model_name}",
        model_name)
    ## Load the model into memory.
    loaded_model = joblib.load('customer_segment-v0.0')
    
  3. 现在模型已加载到内存中,下一步是初始化特征存储对象。初始化也可以在方法外部进行,因为它是一次性活动:

    #initialize the feature store object.
    store = FeatureStore(repo_path=os.path.join(os.getcwd(), "customer_segmentation"))
    
  4. 由于 customer_segmentation 功能仓库与 main.py 文件处于同一级别,如图 5.8 所示,我已经适当地设置了 repo_path。从在线商店获取特征、运行预测和返回结果的剩余逻辑将放入 inference 方法定义中。以下代码块包含相同的内容。复制该方法并将其替换到 main.py 文件中:

    @app.post("/invocations")
    def inference(customers: dict):
        ##Step1: list of features required for scoring the model
        required_features = [
            "customer_rfm_features:recency",
            "customer_rfm_features:monetaryvalue",
            "customer_rfm_features:r",
            "customer_rfm_features:m",
            "customer_rfm_features:rfmscore",
            "customer_rfm_features:segmenthighvalue",
            "customer_rfm_features:segmentlowvalue",
            "customer_rfm_features:segmentmidvalue"
        ]
        ##step 2: get entity rows from the input
        entity_rows = [{"customer": cust_id} for cust_id in customers["customer_list"]]
        ##Step 3: query online store
        feature_vector = store.get_online_features(
            features=required_features,
            entity_rows=entity_rows,
        ).to_dict()
        ##Step 4: convert features to dataframe and reorder the feature columns in the same order that model expects.
        features_in_order = ['recency''monetaryvalue', 
                             'r''m''rfmscore', 
                             'segmenthighvalue', 
                             'segmentlowvalue', 
                             'segmentmidvalue']
        df = pd.DataFrame(feature_vector)
        features = df.drop(['customerid'], axis=1)
        features = features.dropna()
        features = features[features_in_order]
        ##Step 5: run prediction and return the list
        prediction = loaded_model.predict(features)
        return {"predictions": prediction.tolist()}
    
  5. 现在预测逻辑已完成,让我们运行应用程序并尝试运行预测。要运行应用程序,命令与之前使用的一样:

    <aws_key_id> and <aws_secret> in the preceding code block with the user credentials created in *Chapter 4*, *Adding Feature Store to ML Models*.
    
  6. 一旦应用程序成功加载,请访问 Swagger UI 网址 (localhost:8000/docs)。在 Swagger UI 中,展开 invocations API 并点击 尝试。你应该会看到一个类似于 图 5.12 的屏幕。

图 5.12 – Swagger UI 调用 API

图 5.12 – Swagger UI 调用 API

  1. 在请求体中,提供如图 图 5.12 所示的输入(以下代码块中的那个):

    {"customer_list":["12747.0", "12841.0"]}
    
  2. 使用这个输入,通过点击 执行 提交请求。API 应该在毫秒内响应,当你在屏幕上向下滚动时,输出将可见。以下图显示了示例输出:

图:5.13 – 在线模型响应

图:5.13 – 在线模型响应

这样就完成了为在线模型构建 REST API 的步骤,并附有从 Feast 获取特征的代码。现在我们既有在线模型也有批量模型,在下一章中,我们将探讨如何将这些模型投入生产,以及如何通过早期采用特征存储和 MLOps,将开发到生产的过渡变得简单。

我们还没有探讨的是如何更改/更新或添加额外的特征。在我们继续之前,让我们简要地看看这个问题。

在开发过程中处理特征集的变化

模型开发是一个不断演变的过程。模型也是如此——它们会随着时间的推移而演变。今天,我们可能只使用几个特征来构建特定的模型,但随着我们不断发现和尝试新的特征,如果新特征的性能优于当前模型,我们可能会在模型训练和评分中包含这些新特征。因此,特征集可能会随时间而变化。这意味着在特征存储中,我们在 第四章 中执行的一些步骤,即 将特征存储添加到机器学习模型中,可能需要重新审视。让我们看看这些步骤是什么。

重要提示

这里的假设是在模型开发过程中特征定义发生变化,而不是在生产之后。我们将在后面的章节中探讨模型投入生产后如何处理特征集的变化。

第 1 步 – 更改特征定义

如果在模型开发过程中特征或实体发生变化,第一步是更新特征存储库中的特征定义。如果你记得正确,当特征最终确定时,我们首先做的事情是创建特征定义。在特征存储库中,文件 rfm_features.py 包含了定义。在做出更改后,运行 feast apply 命令以更新资源中的特征定义。如果你创建或删除了新的实体或视图,相应的在线存储资源(DynamoDB 表)将被创建或删除。你可以在控制台中验证这一点。如果只有一些小的更改,例如更改数据类型或特征名称,这些更改将被保存在特征存储库注册表中。

第 2 步 – 在 Glue/Lake Formation 控制台中添加/更新模式

第二步是定义我们在 Glue/Lake Formation 数据库中创建的新表。如果旧表不再需要,您可以删除它们以避免以后产生混淆。在模式变更的情况下(如果特征名称或数据类型发生变化),您需要更新现有模式以反映这些更改。如果模式没有随着更改而更新,那么在查询历史存储或尝试从离线存储加载最新特征到在线存储时将出现错误。在此处还需要注意的另一件事是,在定义模式时,我们为特征视图设置了 S3 位置。现在这个位置包含旧数据,它仅适用于旧模式,因此您需要定义一个新的路径,以便符合新模式的数据将被写入。

另一种方法是定义一个新的表,包含新的模式定义和新的 S3 路径,以及更新特征存储库中的 Redshift 源定义以新的表名。如果您这样做,您可以查询旧定义和新定义中的数据。然而,请记住,您可能需要管理两个版本的特征集,一个具有旧模式,另一个具有新模式。此外,将有两个 DynamoDB 表。

第 3 步 – 更新笔记本中的更改

最后一步很简单,就是更新所有受影响的笔记本。在特征工程笔记本中,更新将写入新位置的数据,而在模型训练和评分笔记本中,则分别是在训练和评分期间更新特征名称或获取额外的特征。

这些是每次特征定义更新时都需要执行的三个步骤。有了这些,让我们总结一下本章学到的内容,在下一章中,我们将探讨如何将本章构建的在线和批量模型投入生产,以及生产之外面临的挑战。

摘要

在本章中,我们的目标是探讨模型训练和评分如何随着特征存储的变化而变化。为了通过 ML 生命周期的训练和评分阶段,我们使用了上一章中创建的资源。在模型训练阶段,我们探讨了数据工程师和数据科学家如何协作并共同努力构建更好的模型。在模型预测中,我们讨论了批量模型评分,以及使用离线存储作为运行批量模型的成本效益方式。我们还为在线模型构建了一个 REST 包装器,并添加了 Feast 代码,以便在运行时获取预测所需的特征。在本章结束时,我们探讨了在开发过程中如果特征有更新时所需的更改。

在下一章中,我们将继续使用本章构建的批量模型和在线模型,将它们投入生产,并探讨模型投入生产后面临的挑战。

进一步阅读

您可以在以下参考资料中找到更多关于 Feast 的信息:

第七章:模型到生产及之后

在上一章中,我们讨论了使用 Feast 进行在线和批量模型的模型训练和预测。对于练习,我们使用了在 第四章 添加特征存储到机器学习模型 练习期间部署到 AWS 云的 Feast 基础设施。在这些练习中,我们探讨了 Feast 如何将特征工程与模型训练和模型预测解耦。我们还学习了如何在批量预测和在线预测期间使用离线和在线存储。

在本章中,我们将重用 第四章 添加特征存储到机器学习模型第五章 模型训练和推理 中构建的特征工程管道和模型,以投入生产机器学习(ML)管道。本章的目标是重用我们在前几章中构建的一切,例如 AWS 上的 Feast 基础设施、特征工程、模型训练和模型评分笔记本,以投入生产 ML 模型。随着我们进行练习,这将给我们一个机会来了解 Feast 的早期采用不仅解耦了 ML 管道阶段,还加速了 ML 模型的生产准备。一旦我们将批量和在线 ML 管道投入生产,我们将探讨 Feast 的采用如何为 ML 生命周期的其他方面开辟机会,例如特征监控、自动模型重新训练,以及它如何加速未来 ML 模型的开发。本章将帮助您了解如何投入生产使用 Feast 的批量和在线模型,以及如何使用 Feast 进行特征漂移监控和模型重新训练。

我们将按以下顺序讨论以下主题:

  • 设置 Airflow 进行编排

  • 将批量模型管道投入生产

  • 将在线模型管道投入生产

  • 超越模型生产

技术要求

为了跟随章节中的代码示例,需要使用在 第四章 添加特征存储到机器学习模型第五章 模型训练和推理 中创建的资源。您需要熟悉 Docker 和任何笔记本环境,这可能是一个本地设置,例如 Jupyter,或者一个在线笔记本环境,例如 Google Colab、Kaggle 或 SageMaker。您还需要一个 AWS 账户,可以完全访问一些资源,例如 Redshift、S3、Glue、DynamoDB 和 IAM 控制台。您可以在试用期间创建一个新账户并免费使用所有服务。您可以在以下 GitHub 链接中找到本书的代码示例和特征存储库:

设置 Airflow 以进行编排

为了将在线和批量模型投入生产,我们需要一个工作流程编排工具,它可以按计划为我们运行 ML 管道。有多个工具可供选择,例如 Apache Airflow、AWS Step Functions 和 SageMaker Pipelines。如果您更喜欢,也可以将其作为 GitHub 工作流程运行。根据您熟悉或组织提供的工具,编排可能会有所不同。对于这个练习,我们将使用 Amazon Managed Workflows for Apache AirflowMWAA)。正如其名所示,这是 AWS 提供的由 Apache Airflow 管理的服务。让我们在 AWS 中创建一个 Amazon MWAA 环境。

重要提示

Amazon MWAA 没有免费试用。您可以通过此网址查看使用价格:aws.amazon.com/managed-workflows-for-apache-airflow/pricing/. 或者,您可以选择在本地或 EC2 实例上运行 Airflow(EC2 提供免费层资源)。您可以在以下位置找到运行 Airflow 本地或 EC2 的设置说明:

Airflow 本地设置:towardsdatascience.com/getting-started-with-airflow-locally-and-remotely-d068df7fcb4

Airflow 在 EC2 上:christo-lagali.medium.com/getting-airflow-up-and-running-on-an-ec2-instance-ae4f3a69441

S3 存储桶用于 Airflow 元数据

在我们创建环境之前,我们需要一个 S3 存储桶来存储 Airflow 依赖项,airflow-for-ml-mar-2022。在 S3 存储桶中,创建一个名为 dags 的文件夹。我们将使用此文件夹来存储所有 Airflow DAG。

Amazon MWAA 提供了多种不同的方式来配置要安装到 Airflow 环境中的附加插件和 Python 依赖项。由于我们需要安装一些 Python 依赖项来运行我们的项目,我们需要告诉 Airflow 安装这些必需的依赖项。一种方法是通过使用 requirements.txt 文件。以下代码块显示了文件的内容:

papermill==2.3.4
boto3==1.21.41
ipython==8.2.0
ipykernel==6.13.0
apache-airflow-providers-papermill==2.2.3

将前面代码块的内容保存到 requirements.txt 文件中。我们将使用 papermill (papermill.readthedocs.io/en/latest/) 来运行 Python 笔记本。您还可以使用 Airflow 中可用的 bashpython 操作提取代码并运行 Python 脚本。

重要提示

如果您在本地运行 Airflow,请确保库版本与 Airflow 版本兼容。撰写本文时,Amazon MWAA 的 Airflow 版本是 2.2.2。

一旦创建了requirement.txt文件,将其上传到我们创建的 S3 存储桶中。我们将在创建环境时下一节使用它。

Amazon MWAA 环境用于编排

现在我们已经拥有了创建 Amazon MWAA 环境所需的资源,让我们按照以下步骤创建环境:

  1. 要创建一个新环境,登录到您的 AWS 账户,并使用 AWS 控制台中的搜索栏导航到 Amazon MWAA 控制台。或者,访问us-east-1.console.aws.amazon.com/mwaa/home?region=us-east-1#environments。以下网页将显示:

![图 6.1 – Amazon MWAA 环境控制台

![图片 B18024_06_001.jpg]

图 6.1 – Amazon MWAA 环境控制台

  1. 图 6.1显示的页面上,点击创建环境按钮,随后将显示以下页面:

![图 6.2 – Amazon MWAA 环境详情

![图片 B18024_06_002.jpg]

图 6.2 – Amazon MWAA 环境详情

  1. 图 6.2显示的页面上为 Amazon MWAA 环境提供一个名称。向下滚动到Amazon S3 中的 DAG 代码部分;你应该在屏幕上看到以下参数:

![图 6.3 – Amazon MWAA – S3 中的 DAG 代码部分

![图片 B18024_06_003.jpg]

图 6.3 – Amazon MWAA – S3 中的 DAG 代码部分

  1. 图 6.3显示的屏幕上,在文本框中输入 S3 存储桶或使用我们上传的requirements.txt文件,或输入文件的路径。由于我们不需要任何插件来运行项目,我们可以将可选的插件文件字段留空。点击下一步按钮:

![图 6.4 – Amazon MWAA 高级设置

![图片 B18024_06_004.jpg]

图 6.4 – Amazon MWAA 高级设置

  1. 下一个显示的页面如图 6.4 所示。对于虚拟专用云(VPC),从下拉列表中选择可用的默认 VPC。这里有一个注意事项,所选 VPC 应至少有两个私有子网。如果没有私有子网,当你尝试选择子网 1子网 2时,你会注意到所有选项都变灰了。如果你遇到这种情况,点击创建 MWAA VPC。它将带你进入 CloudFormation 控制台;一旦你填写了所有参数的表格,继续操作并点击创建堆栈。它将创建一个 Amazon MWAA 可以使用的 VPC。一旦 VPC 创建完成,返回此窗口并选择新的 VPC 和子网,然后继续。

  2. 在选择 VPC 后,对于Web 服务器访问,选择公共网络;将其他所有选项保留为默认设置,并将滚动条拉至最底部。在权限部分,你会注意到它将创建一个新的角色用于 Amazon MWAA。记下角色名称。我们稍后需要向此角色添加权限。之后,点击下一步

  3. 在下一页,查看所有提供的输入,滚动到最底部,然后点击创建环境。创建环境可能需要几分钟。

  4. 环境创建完成后,你应该能够在 Amazon MWAA 环境页面上看到处于可用状态的环境。选择我们刚刚创建的环境,然后点击打开 Airflow UI链接。将显示一个 Airflow 主页,类似于以下图所示:图 6.5 – Airflow UI

图 6.5 – Airflow UI

  1. 为了测试一切是否运行正常,让我们快速创建一个简单的 DAG 并查看其工作方式。以下代码块创建了一个简单的 DAG,包含一个虚拟操作符和一个 Python 操作符:

    from datetime import datetime
    from airflow import DAG
    from airflow.operators.dummy_operator import DummyOperator
    from airflow.operators.python_operator import PythonOperator
    def print_hello():
        return 'Hello world from first Airflow DAG!'
    dag = DAG('hello_world', 
              description='Hello World DAG',
              schedule_interval='@daily',
              start_date=datetime(2017, 3, 20), 
              catchup=False)
    start = DummyOperator(task_id="start", dag=dag)
    hello_operator = PythonOperator(
        task_id='hello_task', 
        python_callable=print_hello, 
        dag=dag)
    start >> hello_operator
    
  2. 在前面的代码中定义的 DAG 相当简单;它有两个任务 – starthello_operatorstart 任务是一个 DummyOperator,什么都不做,用于使 DAG 在 UI 上看起来更美观。hello_operator 任务只是调用一个返回消息的函数。在最后一行,我们定义了操作符之间的依赖关系。

  3. 复制前面的代码块,将其保存为example_dag.py,并将其上传到我们之前创建的 S3 中的dags文件夹。(我的 S3 位置是s3://airflow-for-ml-mar-2022/dags。)上传后,它应该在几秒钟内出现在 Airflow UI 中。以下图显示了带有 DAG 的 Airflow UI:

图 6.6 – 带示例 DAG 的 Airflow UI

图 6.6 – 带示例 DAG 的 Airflow UI

  1. 默认情况下,DAG 是禁用的;因此,当你访问页面时,你可能看不到像图 6.6中显示的确切页面。通过点击最左侧列中的切换按钮启用 DAG。一旦启用,DAG 将首次运行并更新运行结果。你还可以使用链接列中的图标触发 DAG。在 UI 的 DAG 列中点击hello_world超链接。你将看到 DAG 的不同选项卡详情页面。请随意尝试并查看详情页面上的不同选项。

  2. 以下图显示了 DAG 的图形视图:

图 6.7 – DAG 的图形视图

图 6.7 – DAG 的图形视图

  1. 现在我们已经验证了 Airflow 设置正确,让我们为 Airflow 运行 ML 管道添加所需的权限。

  2. 如果你还记得,在环境创建的最后一步(图 6.4之后的段落),我们记录了 Airflow 环境运行 DAG 时使用的角色名称。现在,我们需要向该角色添加权限。为此,使用搜索功能导航到 AWS IAM 角色控制台页面或访问 us-east-1.console.aws.amazon.com/iamv2/home?… Airflow 环境关联的 IAM 角色。选择 IAM 角色;你应该会看到以下页面:

![图 6.8 – Amazon MWAA IAM 角色img/B18024_06_008.jpg

图 6.8 – 亚马逊 MWAA IAM 角色

重要提示

如果你没有做笔记,你可以在 AWS 控制台的“环境详情”页面上找到角色名称。

  1. 图 6.8中,点击添加权限;从下拉菜单中选择添加策略,你将被带到以下页面:

![Figure 6.9 – IAM – 添加策略img/B18024_06_009.jpg

图 6.9 – IAM – 添加策略

  1. 在网页上,搜索并选择以下策略 – AmazonS3FullAccessAWSGlueConsoleFullAccessAmazonRedshiftFullAccessAmazonDynamoDBFullAccess。一旦选择了策略,向下滚动并点击添加策略以保存带有新策略的角色。

    重要提示

    没有任何限制地分配任何资源的完全访问权限从来都不是一个好主意。当你运行企业应用时,建议根据资源限制访问,例如只读访问特定的 S3 桶和 DynamoDB 表。

    如果你是在本地运行 Airflow,你可以在笔记本中使用 IAM 用户凭证。

现在我们已经准备好了编排系统,让我们看看如何使用它来将机器学习流水线投入生产。

批量模型流水线的投入生产

第四章,“将特征存储添加到机器学习模型”中,为了模型训练,我们使用了特征工程笔记本中摄取的特征。我们还创建了一个模型评分笔记本,它从 Feast 中获取一组客户的特征,并使用训练好的模型对其进行预测。为了实验的目的,让我们假设原始数据的新鲜度延迟为一天。这意味着特征需要每天重新生成一次,模型需要每天对客户进行评分,并将结果存储在 S3 桶中以供消费。为了实现这一点,多亏了我们早期的组织和阶段解耦,我们只需要每天连续运行特征工程和模型评分笔记本/Python 脚本。现在我们也有了一个执行这个任务的工具,让我们继续在 Airflow 环境中安排这个工作流程。

下图显示了我们将如何运营化批量模型:

![Figure 6.10 – 批量模型的运营化img/B18024_06_010.jpg

图 6.10 – 批量模型的运营化

正如你在图中所见,为了使工作流程投入运行,我们将使用 Airflow 来编排特征工程和模型评分笔记本。在我们的案例中,特征工程的原始数据源是存储 online-retail.csv 的 S3 存储桶。由于我们已设计好评分笔记本从模型仓库(在我们的案例中是一个 S3 存储桶)加载生产模型并将预测结果存储在 S3 存储桶中,我们将重用相同的笔记本。你可能在这里注意到的一点是我们不是每次运行都使用模型训练笔记本;原因很明显 – 我们希望针对经过验证、测试并且也在测试数据上满足我们性能标准的模型版本进行预测。

在安排此工作流程之前,我对特征工程笔记本和模型预测笔记本进行了少量修改。最终的笔记本可以在以下 GitHub URL 中找到:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter06/notebooks/(ch6_feature_engineering.ipynb,ch6_model_prediction.ipynb)。要安排工作流程,请从 GitHub 下载最终的笔记本并将它们上传到我们之前创建的 S3 存储桶,因为 Airflow 环境在运行期间需要访问这些笔记本。我将将其上传到以下位置:s3://airflow-for-ml-mar-2022/notebooks/

重要提示

AWS 密钥访问密钥和 S3 路径 – 我在两个笔记本中都注释掉了 AWS 凭据,因为我们正在向 Amazon MWAA IAM 角色添加权限。如果你在本地 Airflow 中运行它,请取消注释并添加密钥。同时,在必要时更新 S3 URL,因为 S3 URL 指向我在练习期间创建的私有存储桶。

功能仓库 – 如我们之前所见,我们必须克隆功能仓库,以便 Feast 库可以读取元数据。你可以遵循相同的 git clone 方法(前提是已安装 git)或者设置 GitHub 工作流程将仓库推送到 S3 并在笔记本中下载相同的文件。我已经在笔记本中留下了两个代码块并附有注释。你可以使用任何方便的方法。

S3 方法 – 要使用 S3 下载方法,在你的本地系统中克隆仓库,然后在 Linux 终端中运行以下命令将其上传到特定的 S3 位置:

export AWS_ACCESS_KEY_ID=<aws_key>

export AWS_SECRET_ACCESS_KEY=<aws_secret>

AWS_DEFAULT_REGION=us-east-1

aws s3 cp customer_segmentation s3://<s3_bucket>/customer_segmentation --recursive

上传成功后,你应该能够在 S3 存储桶中看到文件夹内容。

现在笔记本已经准备好了,让我们编写批量模型管道的 Airflow DAG。DAG 将按以下顺序包含以下任务 – start(虚拟操作符)、feature_engineering(Papermill 操作符)、model_prediction(Papermill 操作符)和 end(虚拟操作符)。

以下代码块包含 Airflow DAG 的第一部分:

from datetime import datetime
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from airflow.providers.papermill.operators.papermill import PapermillOperator
import uuid
dag = DAG('customer_segmentation_batch_model', 
          description='Batch model pipeline', 
          schedule_interval='@daily', 
          start_date=datetime(2017, 3, 20), catchup=False)

在前面的代码块中,我们定义了导入和 DAG 参数,如 nameschedule_intervalstart_dateschedule_interval='@daily' 调度表示 DAG 应该每天运行。

以下代码块定义了 DAG 的其余部分(第二部分),其中包含所有任务及其之间的依赖关系:

start = DummyOperator(task_id="start", dag=dag)
run_id = str(uuid.uuid1())
feature_eng = PapermillOperator(
    task_id="feature_engineering",
    input_nb="s3://airflow-for-ml-mar-2022/notebooks/ch6_feature_engineering.ipynb",
    output_nb=f"s3://airflow-for-ml-mar-2022/notebooks/runs/ch6_feature_engineering_{ run_id }.ipynb",
    dag=dag,
    trigger_rule="all_success"
)
model_prediction = PapermillOperator(
    task_id="model_prediction",
    input_nb="s3://airflow-for-ml-mar-2022/notebooks/ch6_model_prediction.ipynb",
    output_nb=f"s3://airflow-for-ml-mar-2022/notebooks/runs/ch6_model_prediction_{run_id}.ipynb",
    dag=dag,
    trigger_rule="all_success"
)
end = DummyOperator(task_id="end", dag=dag, 
                    trigger_rule="all_success")
start >> feature_eng >> model_prediction >> end

如您在代码块中看到的,这里有四个步骤将依次执行。feature_engineeringmodel_prediction 步骤使用 PapermillOperator 运行。这需要输入 S3 笔记本路径。我还设置了一个输出路径到另一个 S3 位置,这样我们就可以检查每次运行的输出笔记本。最后一行定义了任务之间的依赖关系。将前两个代码块(第一部分和第二部分)保存为 Python 文件,并将其命名为 batch-model-pipeline-dag.py。保存文件后,导航到 S3 控制台,将文件上传到我们在 图 6.3 中指向的 Airflow 环境的 dags 文件夹。上传的文件将由 Airflow 调度器处理。当您导航到 Airflow UI 时,您应该在屏幕上看到名为 customer_segmentation_batch_model 的新 DAG。

以下图显示了包含 DAG 的 Airflow UI:

图 6.11 – Airflow 上的批量模型 DAG

图 6.11 – Airflow 上的批量模型 DAG

由于我们在创建 Airflow 环境时没有默认启用 DAG 选项(这可以在 Amazon MWAA 中的 Airflow 配置变量中设置),当 DAG 首次出现在 UI 上时,它将处于禁用状态。点击最左侧列的切换按钮来启用它。一旦启用,DAG 将首次运行。点击 customer_segmentation_batch_model 超链接导航到详情页面,并随意查看 DAG 的不同可视化和属性。如果您导航到 Graph 选项卡,DAG 将如以下截图所示显示:

图 6.12 – 批量模型 DAG 图形视图

图 6.12 – 批量模型 DAG 图形视图

图 6.12 中,您可以查看 DAG 的图形视图。如果上次运行有任何失败,它们将以红色轮廓显示。您还可以查看每个任务的执行日志或失败记录。由于所有任务都是绿色的,这意味着一切顺利。您还可以在 图 6.11 中查看最近几次运行的成果。Airflow 还为您提供所有运行的记录。

现在任务运行已完成,我们可以去检查输出笔记本、新特征集的 S3 桶或新预测集的 S3 桶。所有这三个在成功运行后都应可用。在这里,我们将验证预测结果文件夹,但也请随意验证其他文件夹。

重要提示

如果有任何失败,请验证失败任务的日志(在图形视图中点击失败任务以查看可用信息)。检查 Amazon MWAA 的权限、输入/输出的 S3 路径,以及是否在 Amazon MWAA 环境中安装了所有要求。

下面的截图显示了 S3 桶中的新预测结果:

![Figure 6.13 – The prediction results in an S3 bucket]

![img/B18024_06_013.jpg]

图 6.13 – S3 桶中的预测结果

此外,您还可以使用 Airflow 做各种花哨的事情,例如发送失败时的电子邮件通知、日常运行的 Slack 通知,以及与 PagerDuty 的集成。请随意探索选项。以下是 Airflow 支持的服务提供者列表:airflow.apache.org/docs/apache-airflow-providers/packages-ref.html

现在我们批处理模型已在生产环境中运行,让我们看看如何使用 Feast 将在线模型进行生产化。

生产化在线模型管道

在上一章中,对于在线模型,我们构建了 REST 端点以提供客户细分的需求预测。尽管在线模型以 REST 端点托管,但它需要以下功能的支持基础设施:

  • 为了实时提供特征(我们为此有 Feast)

  • 为了保持特征更新(我们将使用带有 Airflow 编排的特征工程笔记本来完成此操作)

在本章中,我们将继续上一章的内容,并使用在第四章“将特征存储添加到机器学习模型”中构建的特征工程笔记本,结合一个笔记本将离线数据同步到 Feast 的在线存储。

下图展示了在线模型管道的运营化:

![Figure 6.14 – The operationalization of the online model]

![img/B18024_06_014.jpg]

图 6.14 – 在线模型的运营化

如[图 6.14]所示,我们将使用 Airflow 进行特征工程的编排;数据新鲜度仍然是每天一次,并且可以安排更短的时间。如果需要,Feast 还可以支持流数据。以下 URL 有一个可用的示例:docs.Feast.dev/reference/data-sources/push。在第五章“模型训练和推理”中开发的 REST 端点将被 Docker 化并作为 SageMaker 端点部署。

重要提示

一旦容器化,Docker 镜像就可以用于部署到任何容器化环境中,例如 Elastic Container Service,Elastic BeanStalk 和 Kubernetes。我们使用 SageMaker,因为它设置起来更快,并且还具有开箱即用的优势,如数据捕获和 IAM 认证。

特征工程作业的编排

由于我们已经有两个笔记本(特征工程和同步离线到在线存储)并且我们对 Airflow 很熟悉,让我们首先安排特征工程工作流程。同样,在笔记本中,我进行了一些小的修改。请在使用之前验证这些修改。您可以在以下位置找到笔记本(ch6_feature_engineering.ipynbch6_sync_offline_online.ipynb):github.com/PacktPublis… S3 位置。我将会上传到之前相同的位置:s3://airflow-for-ml-mar-2022/notebooks/。现在笔记本已经准备好了,让我们编写在线模型管道的 Airflow DAG。DAG 将按照以下顺序执行步骤 – start(虚拟操作符), feature_engineering(Papermill 操作符), sync_offline_to_online(Papermill 操作符), 和 end(虚拟操作符)。

下面的代码块包含 Airflow DAG 的第一部分:

from datetime import datetime
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from airflow.providers.papermill.operators.papermill import PapermillOperator
dag = DAG('customer_segmentation_online_model', 
          description='Online model pipeline', 
          schedule_interval='@daily', 
          start_date=datetime(2017, 3, 20), catchup=False)

就像批量模型管道 DAG 的情况一样,这包含 DAG 参数。

下面的代码块定义了 DAG 的其余部分(第二部分),其中包含所有任务及其之间的依赖关系:

start = DummyOperator(task_id="start")
run_time = datetime.now()
feature_eng = PapermillOperator(
    task_id="feature_engineering",
    input_nb="s3://airflow-for-ml-mar-2022/notebooks/ch6_feature_engineering.ipynb",
    output_nb=f"s3://airflow-for-ml-mar-2022/notebooks/runs/ch6_feature_engineering_{run_time}.ipynb",
    trigger_rule="all_success",
    dag=dag
)
sync_offline_to_online = PapermillOperator(
    task_id="sync_offline_to_online",
    input_nb="s3://airflow-for-ml-mar-2022/notebooks/ch6_sync_offline_online.ipynb",
    output_nb=f"s3://airflow-for-ml-mar-2022/notebooks/runs/ch6_sync_offline_online_{run_time}.ipynb",
    trigger_rule="all_success",
    dag=dag
)
end = DummyOperator(task_id="end", trigger_rule="all_success")
start >> feature_eng >> sync_offline_to_online >> end

Airflow DAG 的结构与我们之前看到的批量模型 DAG 类似;唯一的区别是第三个任务,sync_offline_to_online。这个笔记本将离线数据中的最新特征同步到在线数据中。将前两个代码块(第一部分和第二部分)保存为 Python 文件,并将其命名为 online-model-pipeline-dag.py。保存文件后,导航到 S3 控制台,将文件上传到我们在 图 6.3 中指向的 Airflow 环境的 dags 文件夹。与批量模型一样,上传的文件将由 Airflow 调度器处理,当你导航到 Airflow UI 时,你应该在屏幕上看到名为 customer_segmentation_online_model 的新 DAG。

下面的截图显示了带有 DAG 的 Airflow UI:

![图 6.15 – 带有在线和批量模型的 Airflow UI![图片`图 6.15 – 带有在线和批量模型的 Airflow UI 要启用 DAG,点击最左侧列的切换按钮。一旦启用,DAG 将首次运行。点击customer_segmentation_online_model超链接导航到详细信息页面,您可以随意浏览以查看 DAG 的不同可视化和属性。如果您导航到图形选项卡,DAG 将会显示,如下面的屏幕截图所示:![Figure 6.16 – 在线模型管道图形视图img/B18024_06_016.jpg

Figure 6.16 – 在线模型管道图形视图

如您在图 6.16中看到的,在成功运行的情况下,图形将显示为绿色。正如在批量模型管道执行期间所讨论的,您可以验证输出笔记本、DynamoDB 表或 S3 存储桶,以确保一切正常工作,并在出现故障的情况下检查日志。

现在在线模型管道的第一部分已经准备好了,让我们将上一章中开发的 REST 端点 Docker 化,并将它们作为 SageMaker 端点部署。

将模型部署为 SageMaker 端点

要将模型部署到 SageMaker,我们首先需要将我们在第五章“模型训练和推理”中构建的 REST API Docker 化。在我们这样做之前,让我们创建一个弹性容器注册库ECR),在那里我们可以保存模型的 Docker 镜像,并在 SageMaker 端点配置中使用它。

Docker 镜像的 ECR

要创建 ECR 资源,从搜索栏导航到 ECR 控制台或使用以下 URL:us-east-1.console.aws.amazon.com/ecr/repositories?region=us-east-1。将显示以下页面:

![Figure 6.17 – ECR 主页img/B18024_06_017.jpg

Figure 6.17 – ECR 主页

图 6.17显示的页面上,您可以选择私有公共存储库选项卡。然后,点击创建存储库按钮:

![Figure 6.18 – ECR – 创建存储库img/B18024_06_018.jpg

Figure 6.18 – ECR – 创建存储库

我在这里选择了私有;根据您选择的是私有还是公共,选项将会有所不同,但无论如何,操作都很简单。填写所需的字段,滚动到页面底部,然后点击创建存储库。一旦创建存储库,进入存储库详细信息页面,您应该会看到一个类似于图 6.19所示的页面。

重要提示

私有存储库通过 IAM 进行保护,而公共存储库则可以被互联网上的任何人访问。公共存储库主要用于与组织外的人共享/开源您的工作:

![ Figure 6.19 – ECR 存储库详细信息img/B18024_06_019.jpg

Figure 6.19 – ECR 存储库详细信息

在前面的页面上,点击查看推送命令,您应该会看到一个类似于图 6.20所示的弹出窗口:

![Figure 6.20 – ECR 推送命令img/B18024_06_020.jpg

图 6.20 – ECR 推送命令

根据你用于构建 Docker 镜像的操作系统,保存必要的命令。我们将使用这些命令来构建 Docker 镜像。

构建 Docker 镜像

如前所述,在本节中我们将使用上一章中构建的 REST 端点。如果你记得正确,我们添加了两个 REST 端点,pinginvocations。这些端点并非随机,尽管它们可以在任何容器环境中托管。要在 SageMaker 端点中托管 Docker 镜像,要求它应该有 ping(这是 GET 方法)和 invocations(这是 POST 方法)路由。我已经在相同的文件夹结构中添加了一些文件,这些文件将有助于构建 Docker 镜像。REST 代码和文件夹结构可在以下 URL 获取:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/tree/main/online-model-rest-api

重要提示

额外的文件是 Dockerfilerequirements.txtserve

连续地,将 REST 代码克隆到本地系统,将特征仓库复制到项目的 root 目录,导出凭据,然后运行 图 6.20 中的命令。

重要提示

你可以使用在 第四章 中创建的相同用户凭据,将特征存储添加到机器学习模型。然而,我们遗漏了向用户添加 ECR 权限。请导航到 IAM 控制台,并将 AmazonEC2ContainerRegistryFullAccess 添加到用户。否则,你将遇到访问错误。

以下是一些示例命令:

cd online-model-rest-api/
export AWS_ACCESS_KEY_ID=<AWS_KEY>
export AWS_SECRET_ACCESS_KEY=<AWS_SECRET>
export AWS_DEFAULT_REGION=us-east-1
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account_number>.dkr.ecr.us-east-1.amazonaws.com
docker build -t customer-segmentation .
docker tag customer-segmentation:latest <account_number>.dkr.ecr.us-east-1.amazonaws.com/customer-segmentation:latest
docker push <account_number>.dkr.ecr.us-east-1.amazonaws.com/customer-segmentation:latest

使用环境变量中设置的凭据登录到 ECR,构建 Docker 镜像,并将 Docker 镜像标记和推送到注册表。一旦镜像被推送,如果你导航回 图 6.19 中的屏幕,你应该会看到新的镜像,如下面的截图所示:

![图 6.21 – 推送到 ECR 的镜像

![img/B18024_06_021.jpg]

图 6.21 – 推送到 ECR 的镜像

现在,镜像已经准备好了,通过点击 图 6.21复制 URI 旁边的图标来复制镜像的 统一资源标识符 (URI)。接下来,让我们将 Docker 镜像作为 SageMaker 端点进行部署。

创建 SageMaker 端点

Amazon SageMaker 致力于为机器学习提供托管基础设施。在本节中,我们只将使用 SageMaker 推理组件。SageMaker 端点用于将模型作为 REST 端点进行实时预测。它支持 Docker 镜像模型,并且开箱即支持一些变体。我们将使用上一节中推送到 ECR 的 Docker 镜像。SageMaker 端点是使用三个构建块构建的 - 模型、端点配置和端点。让我们使用这些构建块并创建一个端点。

SageMaker 模型

模型用于定义模型参数,如名称、模型的存储位置和 IAM 角色。要定义模型,使用搜索栏导航到 SageMaker 控制台,并在推理部分查找模型。或者,访问us-east-1.console.aws.amazon.com/sagemaker/home?region=us-east-1#/models。以下屏幕将显示:

图 6.22 – SageMaker 模型控制台

图 6.22 – SageMaker 模型控制台

在显示的页面上,点击创建模型以跳转到下一屏幕。以下页面将显示:

图 6.23 – SageMaker – 创建模型

图 6.23 – SageMaker – 创建模型

图 6.23所示,输入模型名称,对于 IAM 角色,从下拉菜单中选择创建新角色。将出现一个新的弹出窗口,如下面的屏幕截图所示:

图 6.24 – SageMaker 模型 – 创建 IAM 角色

图 6.24 – SageMaker 模型 – 创建 IAM 角色

在弹出窗口中,为了本次练习的目的,保留所有默认设置,然后点击创建角色。AWS 将创建一个 IAM 角色,在同一屏幕上,你应该在对话框中看到一个带有 IAM 角色链接的消息。以下图显示了显示的消息:

图 6.25 – SageMaker 模型 – 新的执行角色已创建

图 6.25 – SageMaker 模型 – 新的执行角色已创建

现在,如果你记得正确的话,我们正在使用 DynamoDB 作为在线存储;因为我们是从 DynamoDB 表中按需读取数据,所以 IAM 角色需要访问它们。因此,使用页面显示的链接在新标签页中导航到我们刚刚创建的 IAM 角色,添加AmazonDynamoDBFullAccess,然后返回此标签页。向下滚动到容器定义 1部分,你应该会看到以下参数:

图 6.26 – SageMaker 模型 – 容器定义 1 部分

图 6.26 – SageMaker 模型 – 容器定义 1 部分

对于推理代码图像位置参数,粘贴从图 6.21中复制的图像 URI,然后保留其他设置不变,再次滚动到网络部分:

图 6.27 – Sagemaker 模型 – 网络部分

图 6.27 – Sagemaker 模型 – 网络部分

在这里,选择VPC默认 vpc,从列表中选择一个或两个子网,并选择默认的安全组。向下滚动到页面底部,然后点击创建模型

重要提示

在生产部署中,选择默认安全组从来不是一个好主意,因为入站规则不是限制性的。

现在模型已经准备好了,接下来让我们创建端点配置。

端点配置

要设置端点配置,请使用搜索栏导航到 SageMaker 控制台,并在 推理 部分查找 Endpoint Configurations。或者,访问 us-east-1.console.aws.amazon.com/sagemaker/home?region=us-east-1#/endpointConfig。将显示以下页面:

图 6.28 – Sagemaker 端点配置控制台

图 6.28 – Sagemaker 端点配置控制台

在显示的网页上,点击 创建端点配置。您将被导航到以下页面:

图 6.29 – SageMaker – 创建端点配置

图 6.29 – SageMaker – 创建端点配置

在此屏幕上,填写 customer-segmentation-config。滚动到 数据捕获 部分。这用于定义需要捕获多少百分比的实时推理数据,在哪里(S3 位置),以及如何存储(JSON 或 CSV)。您可以选择启用或禁用此功能。我在这个练习中将其禁用了。如果您启用它,它将要求您提供更多信息。数据捕获 之后的部分是 生产变体。这用于设置多个模型变体,以及模型的 A/B 测试。目前,因为我们只有一个变体,所以让我们在这里添加它。要添加一个变体,请在该部分点击 添加模型 链接;以下弹出窗口将出现:

图 6.30 – SageMaker – 将模型添加到端点配置

图 6.30 – SageMaker – 将模型添加到端点配置

在弹出窗口中,选择我们之前创建的模型,滚动到页面底部,然后点击 创建端点配置

SageMaker 端点创建

最后一步是使用端点配置创建端点。要创建 SageMaker 端点,请使用搜索栏导航到 SageMaker 控制台,并在 推理 部分查找 Endpoints。或者,访问 us-east-1.console.aws.amazon.com/sagemaker/h…

图 6.31 – SageMaker 端点控制台

图 6.31 – SageMaker 端点控制台

图 6.31 所示的页面上,点击 创建端点 以导航到以下页面:

图 6.31 – SageMaker – 创建端点

图 6.31 – SageMaker – 创建端点

图 6.31显示的网页上,提供一个端点名称。我已给出名称customer-segmentation-endpoint。向下滚动到端点配置部分,选择我们之前创建的端点配置,然后点击选择端点配置按钮。一旦选择,点击创建端点。创建端点需要几分钟。当端点状态变为可用时,您的模型即可用于实时流量服务。

测试 SageMaker 端点

我们接下来需要了解的是如何消费模型。有不同方式——您可以使用 SageMaker 库、Amazon SDK 客户端(Python、TypeScript 或其他可用的语言),或 SageMaker 端点 URL。所有这些方法默认使用 AWS IAM 身份验证。如果您有特殊要求并希望在不进行身份验证或使用自定义身份验证的情况下公开模型,可以使用 API 网关和 Lambda 授权器来实现。为了本练习的目的,我们将使用boto3客户端来调用 API。无论我们如何调用端点,结果都应该是相同的。

以下代码块使用boto3客户端调用端点:

import json
import boto3
import os
os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key>"
os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
payload = json.dumps({"customer_list":["12747.0", "12841.0"]})
runtime = boto3.client("runtime.sagemaker")
response = runtime.invoke_endpoint(
    EndpointName= "customer-segmentation-endpoint", 
    ContentType="application/json", Body=payload
)
response = response["Body"].read()
result = json.loads(response.decode("utf-8"))
print(results)

在前面的代码块中,我们正在调用我们创建的端点,为具有12747.012841.0 ID 的两个客户运行预测。端点将在毫秒内对给定的客户 ID 做出预测。现在,端点可以与模型消费者共享。

现在模型已投入生产,让我们看看模型转移到生产后的一些后续方面。

超越模型生产

在本节中,我们将讨论机器学习的生产后方面以及我们如何从采用特征存储中受益。

特征漂移监控和模型重新训练

模型投入生产后,接下来经常出现的问题是模型在生产中的表现如何。可能使用不同的指标来衡量模型的表现——例如,对于推荐模型,表现可能通过转化率来衡量,即推荐的产品被购买频率。同样,预测客户的下一步行动可能通过错误率来衡量,等等。没有通用的方法来做这件事。但如果模型的表现不佳,则需要重新训练或用新的模型替换。

定义何时应该重新训练模型的其他方面之一是当特征开始偏离其训练时的值。例如,假设在初始模型训练期间客户的平均频率值为 10,但现在平均频率值为 25。同样,最低货币值最初为 100.00 美元,现在为 500.00 美元。这被称为数据漂移

数据漂移监控衡量的是数据统计分布的变化;在特征监控的情况下,它是比较从时间t1到时间t2的特征统计分布的变化。以下 URL 的文章讨论了数据漂移监控的不同指标:towardsdatascience.com/automating-data-drift-thresholding-in-machine-learning-systems-524e6259f59f

使用特征存储,可以轻松地从两个不同时间点检索训练数据集,即用于模型训练的数据集和用于模型训练的所有特征的最新特征值。现在,我们只需要按计划运行数据漂移监控来生成漂移报告。Feast 带来的标准化是,由于数据是使用标准 API 存储和检索的,因此可以在特征存储中的所有数据集上按计划运行通用的特征漂移监控。特征漂移报告可以用作模型重新训练的指标之一。如果特征漂移影响了模型的性能,可以使用最新的数据集重新训练,并与当前的生产模型部署和 A/B 测试。

模型可复现性和预测问题

如果您还记得第一章中的概述,即“机器学习生命周期概述”,模型可复现性是机器学习的一个常见问题。我们需要一种方法来一致地复现模型(或用于模型的训练数据)。如果没有特征存储,如果用于生成特征的底层原始数据发生变化,就无法复现相同的训练数据集。然而,使用特征存储,正如我们之前讨论的,特征与时间戳版本化(特征 DataFrame 中的一列是事件时间戳)。因此,我们可以查询历史数据来生成用于模型训练的相同特征集。如果用于训练模型的算法不是随机的,模型也可以复现。让我们试试看。

由于我们已经在第五章的“使用特征存储进行模型训练”部分中做了类似的事情,即“模型训练和推理”,我们将重用相同的代码来运行这个实验。复制并运行所有代码,直到你创建实体 DataFrame,然后将event_timestamp列替换为较旧的时间戳(模型训练的时间戳),如下所示。在这种情况下,模型是在2022-03-26 16:24:21训练的,如第五章的“模型训练和推理”中的图 5.1所示:

## replace timestamp to older time stamp.
entity_df["event_timestamp"] = pd.to_datetime("2022-03-26 16:24:21")

一旦替换完时间戳,请继续从第五章的“Dee 的模型训练实验”部分运行代码,模型训练和推理。你应该能够生成与 Dee 模型训练中使用的相同的数据集(在这种情况下,第五章](B18024_05_ePub.xhtml#_idTextAnchor078)中的图 5.2所示的数据集)。因此,如果模型使用非随机算法,则也可以使用特征集重现模型。

特征存储的另一个优点是调试预测问题。让我们考虑一个场景,你有一个面向网站的模型,该模型正在将交易分类为欺诈或非欺诈。在高峰时段,它将几笔交易标记为欺诈,但这些交易实际上是合法的。客户打电话给客服部门投诉,现在轮到数据科学家 Subbu 来找出问题所在。如果项目中没有特征存储,为了重现问题,Subbu 必须进入原始数据,尝试生成特征,并查看行为是否仍然相同。如果不相同,Subbu 必须进入应用程序日志,进行处理,查找事件之前的行为,并从用户交互的角度尝试重现,同时捕获所有这些试验的特征,希望至少能重现一次问题。

另一方面,使用项目中使用的特征存储,Subbu 将找出事件发生的大概时间,模型中使用的实体和特征是什么,以及事件发生时生产中运行的模型版本。有了这些信息,Subbu 将连接到特征存储并获取在问题发生的大概时间范围内所有实体使用的所有特征。比如说,事件发生在今天中午 12:00 到 12:15 之间,特征是流式的,新鲜度间隔大约是 30 秒。这意味着,对于给定的实体,从任何给定时间开始,特征在接下来的 30 秒内可能会发生变化。

为了重现问题,Subbu 将创建一个实体 DataFrame,其中一个列中重复 30 次相同的实体 ID,对于事件时间列,从中午 12:00 到 12:15 的 30 秒间隔的时间戳。有了这个实体 DataFrame,Subbu 将使用 Feast API 查询历史存储并运行生成的特征的预测。如果问题重现,Subbu 就有导致问题的特征集。如果没有重现,使用实体 DataFrame,间隔将减少到小于 30 秒,可能到 10 秒,以确定特征是否比 30 秒更快地发生变化。Subbu 可以继续这样做,直到她找到重现问题的特征集。

为下一个模型赢得先机

现在,模型已经投入生产,数据科学家 Subbu 接手下一个问题陈述。假设下一个机器学习模型需要预测客户的下一次购买日NPD)。这里的用例可能是基于 NPD,我们想要为客户运行一次营销活动。如果客户的购买日较远,我们想要提供特别优惠,以便我们可以鼓励他们尽早购买。现在,在查看原始数据集之前,Subbu 可以根据搜索和可发现性方面如何集成到特征存储中查找可用的特征。由于 Feast 从面向服务的架构迁移到 SDK/CLI 导向,需要目录工具、所有特征存储库的 GitHub 仓库、数据网格门户等。然而,在 SageMaker 或 Databricks 等特征存储的情况下,用户可以通过 API 或从 UI 浏览可用的特征定义来连接到特征存储端点(使用 SageMaker 运行时通过 boto3 或 Databricks 工作区)。我之前没有使用过 Tecton 特征存储,但 Tecton 也为其特征存储提供了一个 UI,可以用来浏览可用的特征。正如你所见,这是 Feast 在 0.9.X 和 0.20.X(0.20 是撰写时的版本)不同版本之间的一大缺点。

假设现在 Subbu 有办法定位所有特征存储库。现在,她可以连接并浏览它们,以找出在 NPD 模型中可能有用的项目和特征定义。到目前为止,我们只有一个包含我们迄今为止一直在使用的客户 RFM 特征的存储库,这些特征在模型中可能很有用。要使用这些特征,Subbu 只需要获取 AWS 资源的读取权限,最新的 RFM 特征将每天可用于实验,如果模型转移到生产环境也可以使用。

为了看到特征存储在后续模型开发中的好处,我们应该尝试构建 NPD。我将通过最初的几个步骤来帮助你开始模型。因为我们遵循了一个博客来开发第一个模型,我们将继续遵循同一博客系列中的另一部分,该部分可以在towardsdatascience.com/predicting-next-purchase-day-15fae5548027找到。请阅读该博客,因为它讨论了方法以及作者为什么认为特定的特征将是有用的。在这里,我们将跳过特征工程部分。

我们将使用博客作者使用的功能集,包括以下内容:

  • RFM 特征和聚类

  • 最后三次购买之间的天数

  • 购买差异的平均值和标准差

第一个特征集已经存在于特征存储库中;我们不需要为此做任何额外的工作。但对于其他两个,我们需要从原始数据中进行特征工程。在github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter06/notebooks/ch6_next_purchase_day_feature_engineering.ipynb的笔记本中有生成前述第二和第三要点中所需特征的必要特征工程。我将把这些特征的导入到特征存储库以及使用前一个模型中的特征(RFM)结合这些特征来训练新模型作为一个练习。随着你开发和生产化这个模型,你将看到特征存储库的好处以及它如何可以加速模型构建。

接下来,让我们讨论当模型处于生产状态时如何更改特征定义。

生产后的特征定义变更

到目前为止,我们已经讨论了在开发阶段对特征集的导入、查询以及对其变更。然而,我们还没有谈到当模型处于生产状态时对特征定义的变更。通常,人们认为一旦模型进入生产状态,更改特征定义是困难的。原因在于,可能存在多个模型正在使用特征定义,对它们的任何变更都可能会对模型产生级联效应。这也是为什么一些特征存储库尚未支持特征定义更新功能的原因之一。我们需要一种有效处理变更的方法。

这仍然是一个灰色地带;没有正确或错误的方法来做这件事。我们可以采用在其他软件工程过程中使用的任何机制。一个简单的例子可以是特征视图的版本控制,类似于我们处理 REST API 或 Python 库的方式。每当需要对生产特征集进行变更时,假设它被其他人使用,就会创建并使用一个新的特征视图版本(让我们称它为customer-segmentation-v2)。然而,在所有模型迁移之前,还需要管理之前的版本。如果由于任何原因,有模型需要旧版本且无法迁移到新版本的特征表/视图,可能需要对其进行管理或转交给需要它的团队。需要对特征和特征工程工作的所有权进行一些讨论。

这就是数据作为产品概念非常有意义的地方。这里缺失的部分是生产者和消费者定义合同和通知变更的框架。数据生产者需要一种发布他们的数据产品的方式;在这里,数据产品是特征视图。产品的消费者可以订阅数据产品并使用它。在特征集变更期间,生产者可以定义数据产品的新版本,并使旧版本过时,以便消费者能够得知变更内容。这只是我对解决方案的看法,但我相信世界上有更聪明的人可能已经在实施另一种解决方案。

有了这些,让我们总结本章所学的内容,并继续下一章。

摘要

在本章中,我们的目标是利用前几章所构建的一切,并将机器学习模型用于批量处理和在线用例的生产化。为此,我们创建了一个 Amazon MWAA 环境,并使用它来编排批量模型管道。对于在线模型,我们使用 Airflow 来编排特征工程管道和 SageMaker 推理组件,以部署一个作为 SageMaker 端点的 Docker 在线模型。我们探讨了特征存储如何促进机器学习的后期生产方面,例如特征漂移监控、模型可复现性、调试预测问题,以及当模型在生产中时如何更改特征集。我们还探讨了数据科学家如何通过使用特征存储来在新模型上取得领先。到目前为止,我们在所有练习中都使用了 Feast;在下一章中,我们将探讨市场上可用的几个特征存储,以及它们与 Feast 的不同之处,并附带一些示例。

第三部分 – 替代方案、最佳实践及一个用例

在本节中,我们将探讨一些 Feast 的替代方案,其中包括一些托管特征存储服务。我们还将深入探讨 Amazon SageMaker Feature Store,这将帮助我们比较 Feast(自托管基础设施)与托管特征存储。我们还将介绍机器学习最佳实践,最后,我们将通过一个包含特征工程、模型训练、模型推理以及特征和模型监控示例的端到端机器学习用例,展示如何使用托管特征存储。

本节包含以下章节:

  • 第七章, Feast 替代方案和机器学习最佳实践

  • 第八章, 客户流失预测用例

第八章:Feast 替代方案和机器学习最佳实践

在上一章中,我们讨论了如何使用 Amazon Managed Workflows 和 Apache Airflow 进行编排,以及如何使用 Feast 将在线和批量模型投入生产。到目前为止,在这本书中,我们一直在讨论一个特征存储库——Feast。然而,目前市场上有很多特征存储库。在本章中,我们将查看其中的一些,并讨论它们与 Feast 的不同之处,以及使用它们相对于 Feast 的优缺点。

在本章中,我们将尝试使用另一个特征存储库,具体是 Amazon SageMaker。我们将使用在构建客户**终身价值(LTV)**模型时生成的相同特征集,并将其导入 SageMaker 特征存储库,并运行几个查询。选择 AWS 而不是 Tecton、Hopworks 和 H2O.ai 等其他特征存储库的原因是易于访问试用版。然而,选择适合您的特征存储库取决于您已经拥有的工具和基础设施,以及更多内容,我们将在本章中讨论。

本章的目的是向您展示市场上可用的内容以及它与自行管理的特征存储库(如 Feast)的不同之处。我们还将讨论这些特征存储库之间的相似性和差异性。本章还想讨论的另一个方面是机器学习开发中的最佳实践。无论我们使用什么工具/软件进行机器学习开发,都有一些事情我们可以普遍采用来提高机器学习工程。

在本章中,我们将讨论以下主题:

  • 市场上可用的特征存储库

  • 使用 SageMaker 特征存储库进行特征管理

  • 机器学习最佳实践

技术要求

为了运行示例并更好地理解本章内容,前几章中涵盖的主题将很有用,但不是必需的。要跟随本章中的代码示例,您需要熟悉笔记本环境,这可以是本地设置,如 Jupyter,或在线笔记本环境,如 Google Colab、Kaggle 或 SageMaker。您还需要一个具有对 SageMaker 和 AWS Glue 控制台完全访问权限的 AWS 账户。您可以在试用期间创建新账户并免费使用所有服务。您可以使用以下 GitHub 链接找到本书的代码示例:

github.com/PacktPublishing/Feature-Store-for-Machine-Learning/tree/main/Chapter07

市场上可用的特征存储库

在本节中,我们将简要讨论市场上可用的特征存储库,以及它们与 Feast 的比较,以及这些特征存储库之间的相似之处和不同之处。

Tecton 特征存储库

Tecton 是由 Uber 机器学习平台 Michelangelo 的创建者构建的企业级特征存储eng.uber.com/michelangel… 也是 Feast 的主要贡献者之一。因此,当你查看 Tecton 的文档(docs.tecton.ai/index.html)时,你会在 API 和术语中看到很多相似之处。然而,Tecton 中有许多功能在 Feast 中不存在。此外,Tecton 是一个托管特征存储,这意味着你不需要构建和管理基础设施;它将为你管理。

与大多数特征存储一样,Tecton 使用在线和离线存储分别用于低延迟和历史存储。然而,与 Feast 相比,在线和离线存储的选项较少,并且目前仅支持 AWS。如果你更喜欢 Azure 或 GCP,你现在没有其他选择,只能等待。我相信最终将支持多个云提供商和数据存储。Tecton 使用软件即服务SaaS)部署模型,并将部署分为数据平面和控制平面。你可以在以下链接中找到他们的部署模型:docs.tecton.ai/setting-up-tecton/07a-deployment_saas.html。最好的部分是数据永远不会离开客户的 AWS 账户,只有控制面板运行所需的元数据由 Tecton 拥有的 AWS 账户访问;此外,UI 将托管在他们账户中。然而,如果你想通过 REST/gRPC API 端点公开在线数据,该服务将托管在 Tecton 的 AWS 账户中。在线特征请求和响应将通过他们的账户路由。

一旦 Tecton 部署到你的 AWS 账户,你可以使用 Python SDK 与之交互。CLI 命令与 Feast 命令类似;然而,有一些选项,例如可以管理特征定义的版本,以及降级到定义的先前版本。以及你可以使用特征存储执行的常见工作流程,如摄取、低延迟查询和执行点时间连接,使用 Tecton,你可以将转换定义为特征存储的一部分。这是 Tecton 我最喜欢的功能之一。以下是特征存储中特征视图和转换页面的链接:docs.tecton.ai/overviews/framework/feature_views/feature_views.html。这意味着你可以为数据仓库(Snowflake)、数据库、Kinesis 或 Kafka 定义原始数据源配置,并定义 PySpark、Spark SQL 或 pandas 转换以生成特征。Tecton 在定义的日程上编排这些作业,生成特征,并将它们摄取到在线和离线存储中。这有助于跟踪数据血缘。

以下是一个示例代码片段,说明如何定义特征视图和转换:

# Feature View type
@batch_feature_view(
    # Pipeline attributes
    inputs=...
    mode=...
    # Entities
    entities=...
    # Materialization and serving configuration
    online=...
    offline=...
    batch_schedule=...
    feature_start_time=...
    ttl=...
    backfill_config=...
    # Metadata
    owner=...
    description=...
    tags=...
)
# Feature View name
def my_feature_view(input_data):
    intermediate_data = my_transformation(input_data)
    output_data = my_transformation_two(intermediate_data)
    return output_data

你可能会认出在前一个代码块中看到的一些参数。在这里,注释说明这是一个批量转换,你可以定义诸如使用哪些实体、什么时间表以及是否应将数据摄入在线和离线存储等参数。在方法定义中,输入数据将根据注释定义中分配给input参数的内容注入(你可以假设它来自原始数据源的 DataFrame)。在 DataFrame 上,你添加你的转换并返回输出 DataFrame,这将作为特征。这些特征将按照定义的时间表摄入在线和离线存储。一旦定义了前面的转换,你必须运行tecton apply,这与feast apply命令类似,以注册此转换。其他功能与其他特征存储提供的功能类似;因此,我将跳过它们,并让你探索它们的文档。

值得注意的是,在撰写本文时,Tecton 的部署是单租户的,这意味着如果存在无法共享数据的团队,你可能需要多个部署。需要创建一组角色,以便 Tecton 可以使用跨账户角色安装和创建所需资源,这涉及到您的一次性初始设置。

Databricks 特征存储

Databricks 特征存储是用户可用的另一个选项。如果你已经将 Databricks 用作你的笔记本环境以及数据处理作业,这很有意义。它包含 Databricks 工作区,所以你不能只有特征存储。然而,你可以获得一个工作区,除了特征存储之外不使用任何其他东西。它可以托管在 AWS、GCP 或 Azure 上。因此,如果你在任何一个主要云服务提供商上,这可以是一个选择。

这些概念与其他特征存储类似,例如特征表、行的时间戳版本、进行点时间连接的能力以及在线和离线存储。它使用 delta lake 作为其离线存储,并使用基于您所在云提供商的关键值存储之一。Databricks 特征存储的最好之处在于它与 Databricks 的所有其他方面和组件集成良好,例如 Spark DataFrame 的摄取、检索、与 MLflow 模型存储库的即插即用集成、访问控制和跟踪用于生成特定特征表的笔记本的谱系。它还有一个友好的用户界面,您可以浏览和搜索特征。最好的下一部分是,如果您已经拥有 Databricks 工作区,则无需设置。以下是笔记本的链接,其中包含特征创建、摄取、检索、训练和模型评分的示例:docs.databricks.com/_static/notebooks/machine-learning/feature-store-taxi-example.html

然而,有一些事情需要记住。Databricks 特征存储没有项目概念;因此,特征表是最高级别的抽象,访问控制是在特征表级别。此外,Databricks 的在线模型托管仍在公共预览中(尽管毫无疑问它最终将成为一项标准服务)。这意味着如果您使用 Databricks 特征存储来托管在 Databricks 之外的在线模型,它可能需要通过直接客户端连接到在线商店。例如,如果您使用 DynamoDB 作为在线商店(Databricks 根据云提供商提供多种选择)并在 Amazon boto3客户端中托管预测期间的特征模型。此外,跨工作区共享特征可能需要额外的配置,无论是访问令牌还是使用中央工作区作为特征存储。以下是 Databricks 特征存储文档的链接,其中包含更多详细信息:docs.databricks.com/applications/machine-learning/feature-store/index.html

Google 的 Vertex AI 特征存储

Google 的 Vertex AI 是 Google 为机器学习和人工智能提供的平台即服务PaaS)产品。Vertex AI 旨在提供一个端到端的机器学习平台,提供一系列用于机器学习开发、训练、编排、模型部署、监控等工具。我们最感兴趣的工具是 Vertex AI 特征存储。如果您已经使用 GCP 来提供服务,它应该是一个自动的选择。

概念和术语与 Feast 非常相似。Vertex AI 中的最高抽象级别称为 特征存储,类似于 Feast 中的 项目,一个 特征存储 可以有 实体,而 特征 应属于 实体。它支持在线和批量服务,就像所有其他特征存储一样。然而,与 Feast 和 Tecton 不同,没有可用的在线和历史存储选项。由于它是一个托管基础设施,用户无需担心安装和选择在线和离线存储——可能只是价格问题。以下是其价格的链接:cloud.google.com/vertex-ai/pricing#featurestore。它使用 IAM(代表 身份和访问管理)进行身份验证和授权,并且您还可以获得一个用于搜索和浏览特征的 UI。

Vertex AI 的最佳部分是其与其他 GCP 组件以及 Vertex AI 服务本身的集成,用于特征生成、管道管理和数据血缘跟踪。我最喜欢的功能之一是漂移监控。您可以在特征表上设置特征监控配置,这样它就可以为您生成数据分布报告,而无需进行任何额外的工作。

再次提醒,有几件事情需要记住。对于在线服务,你需要进行容量规划并设置处理您流量的所需节点数量。在线服务的自动扩展选项目前仍处于公开预览阶段(尽管它很快就会成为标准服务),但容量规划应该是一个需要解决的主要问题。一些负载测试模拟可以帮助你轻松解决这个问题。此外,对于特征存储,您拥有的在线服务节点数量、数据保留长度以及每个实体的特征数量都有配额和限制。其中一些可以在请求后增加,而其他则不行。以下是特征存储配额和限制的链接:cloud.google.com/vertex-ai/docs/quotas#featurestore

Hopsworks 特征存储

Hopsworks 是一个在 AGPL-V3 许可下运行的另一个开源特征存储,可以在本地、AWS 或 Azure 上运行。它还提供了一个支持 GCP 以及任何 Kubernetes 环境的企业版特征存储。与其他机器学习平台服务类似,它也提供多个组件,例如模型管理和计算环境管理。

这些概念与其他特征存储类似;然而,术语不同。它没有实体的概念,Hopsworks中的featuregroupsFeast中的featureviews类似。就像其他特征存储一样,Hopsworks 支持在线和离线服务。它使用 Apache Hive 与 Apache Hudi 作为离线存储,MySQL Cluster 作为在线存储。再次强调,没有在线或离线存储的选项。然而,Hopsworks 开发了不同的存储连接器,可以用来创建按需的外部特征组,例如我们在Feast中定义的RedShiftSource,见第四章将特征存储添加到机器学习模型中。但是,外部特征组有一些限制,意味着没有时间旅行、在线服务等。

Hopsworks 特征存储中有许多有趣且功能强大的特性。以下是一些最好的特性:

  • 项目级多租户:每个项目都有一个所有者,并且可以与其他团队成员以及跨团队共享资源。

  • 特征组版本控制:Hopsworks 支持特征组版本控制,这是市场上任何其他特征存储所不支持的功能。

  • 特征组的统计信息:它为特征组提供了一些开箱即用的统计信息,例如特征相关性计算、特征的频率直方图和唯一性。以下是一个示例特征组:

    store_fg_meta = fs.create_feature_group(
        name="store_fg",
        version=1,
        primary_key=["store"],
        description="Store related features",
        statistics_config={"enabled": True, 
                             "histograms": True, 
                             "correlations": True})
    
  • 特征验证:这是另一个开箱即用的有趣特性。这是一组预定义的验证规则,存在于特征组中,例如特征的最低和最高值、特征的唯一性计数、特征的熵以及特征的长度最大值。它有足够的规则类型,您不会遇到需要自定义验证规则的场景。以下是一些示例规则:

    #the minimum value of the feature needs to be between 0 and 10
    rules=[Rule(name="HAS_MIN", level="WARNING", 
                 min=0, max=10)] 
    #Exactly 10% of all instances of the feature need to be contained in the legal_values list
    rules=[Rule(name="IS_CONTAINED_IN", level="ERROR", 
                 legal_values=["a", "b"], min=0.1, 
                 max=0.1)] 
    
  • 转换函数:与 Tecton 的转换函数类似,在 Hopsworks 中,您可以在训练数据集上定义或使用内置的转换(Hopsworks 有一个训练数据的概念,您可以从不同的特征组中选择特征,并在其上创建训练数据集定义——一个类似于数据库视图的概念)。

然而,有一些事情需要记住。如果您选择开源版本,您可能不会拥有所有这些特性,并且基础设施将需要自行管理。相反,对于企业版本,您将需要与 Hopsworks 工程师合作,创建在云服务提供商上安装 Hopsworks 所需的一些资源和角色。以下是所有文档的链接:docs.hopsworks.ai/feature-sto…

SageMaker 特征存储

SageMaker 是 AWS 提供的一个端到端机器学习平台。就像 Vertex AI 一样,它有一个笔记本环境,AutoML,处理作业和模型管理,特征存储等。如果你是一个专注于 AWS 的公司,这必须是自然的选择,而不是其他选择。

这些概念与其他特征存储的概念相近,尽管一些术语不同。例如,SageMaker Feature Store 也没有实体的概念,Feast 中的featureviews与 SageMaker 中的featuregroups类似。它具有所有基本功能,如在线和离线存储以及服务。然而,你没有选择。它使用 S3 作为离线存储,其中一个键值存储作为在线存储(AWS 在其文档中没有说明在线存储用于什么)。AWS 使用 IAM 进行身份验证和授权。要访问特征存储库,目前需要完全访问 SageMaker 和 AWS Glue 控制台。如果你将 SageMaker 与 Feast 进行比较,两者都使用/支持 S3 作为离线存储,键值存储作为在线存储,以及 Glue 目录来管理模式。除了 SageMaker 是一个托管特征存储之外,另一个区别是 Feast 使用 Redshift 查询离线数据,而 SageMaker 使用 Amazon Athena(无服务器)查询。如果你是服务器无服务技术的粉丝,你可以将此功能添加到 Feast 中。

我最喜欢的 SageMaker Feature Store 的特点之一是无需管理基础设施。除了创建一个 IAM 角色以访问特征存储库之外,你不需要管理任何东西。任何给定负载的所有资源都由 AWS 管理。你只需要关注开发和摄取特征。SageMaker Feature Store 还支持在 EMR 或 Glue 作业(无服务器)上使用 Spark 进行摄取。除了特征外,它还添加了元数据,如write_timeapi_invocation_time,这些可以在查询中使用。最好的部分是你可以使用 Amazon Athena SQL 查询查询离线数据。

虽然有一些需要注意的事项。当前的实现还没有细粒度的访问管理。目前,你需要完全访问 SageMaker 才能使用特征存储,尽管我相信这只是 AWS 开始提供细粒度访问的时间问题。点时间连接不是现成的;然而,这些可以通过 SQL 查询或 Spark 实现。

到目前为止,我们已经查看了一些市场上的可用选项;你可以通过此链接找到其他可用的特征存储:www.featurestore.org/。然而,为你的项目或团队选择正确的特征存储可能很棘手。在挑选特征存储时,以下是一些需要注意的事项:

  • 你的主要云提供商有很大影响。如果你专注于 GCP,使用 SageMaker Feature Store 就没有意义,反之亦然。如果你是多云,那么你将有更多选择。

  • 数据处理框架也是决定使用哪个特征存储的另一个关键因素。例如,如果你使用 SageMaker 作为你的机器学习平台,在其他人之前尝试 SageMaker Feature Store 更有意义。

  • 与你生态系统中的其他组件的集成也很关键——例如,回答诸如它与你的处理平台、编排框架、模型管理服务、数据验证框架以及你的机器学习开发过程如何良好集成等问题,真的有助于选择正确的特征存储。

  • 所需的功能和你的团队结构有很大影响。如果你是一个只想专注于机器学习的中小团队,那么托管特征存储的提供方案是有意义的,而如果你有一个平台团队来管理基础设施,你可能需要考虑开源提供方案,并评估构建与购买选项。如果你有平台团队,他们可能会寻找额外的功能,如多租户、细粒度访问控制和 SaaS/PaaS。

总结来说,除了它提供的功能外,许多因素都会影响特征存储的选择,因为它必须与更广泛的生态系统良好集成。

接下来,让我们看看一个托管特征存储是如何工作的。

使用 SageMaker Feature Store 进行特征管理

在本节中,我们将探讨如果我们使用托管特征存储而不是 Feast 在第四章,“将特征存储添加到机器学习模型”中,我们可能需要采取哪些行动。

重要提示

所有托管特征存储都有一个类似的流程;有些可能是基于 API 的,有些则通过 CLI 工作。但无论哪种方式,使用特征存储所需的工作量将与我们在本节中讨论的内容相似。我之所以选择 SageMaker,是因为熟悉它并且易于访问,利用 AWS 中的免费试用作为特色产品。

使用 SageMaker 的资源

第四章“将特征存储添加到机器学习模型”中,在我们开始使用特征存储之前,我们在 AWS 上创建了一系列资源,例如一个 S3 存储桶、一个 Redshift 集群、一个 IAM 角色和一个 Glue 目录表。相反,对于像 SageMaker 这样的托管特征存储,你所需要的只是一个具有完全访问 SageMaker 的 IAM 角色,你就准备好了。现在让我们试试看。

我们需要一些 IAM 用户凭证和一个 SageMaker Feature Store 可以承担的 IAM 角色。创建 IAM 用户与之前我们所做的是类似的。遵循相同的步骤创建一个 IAM 用户,并分配AmazonS3FullAccessAmazonSageMakerFullAccess权限。IAM 角色的创建与之前我们所做的是一样的;然而,我们需要允许 SageMaker 服务承担该角色。

重要提示

如前所述多次,赋予完全访问权限从来不是一个好主意;权限应该始终基于资源进行限制。

让我们创建一个 IAM 角色:

  1. 登录您的 AWS 账户,并使用搜索栏导航到 IAM 角色页面;或者,访问以下 URL:us-east-1.console.aws.amazon.com/iamv2/home#…

图 7.1 – IAM 角色主页

图 7.1 – IAM 角色主页

  1. 在显示的网页上,点击创建角色以导航到以下屏幕:

图 7.2 – IAM 角色创建页面

图 7.2 – IAM 角色创建页面

  1. 图 7.2显示的屏幕上,在其他 AWS 服务的用例下拉菜单中,选择SageMaker,然后点击SageMaker - 执行单选按钮。向下滚动并点击下一步,在添加权限页面保持默认设置,然后点击下一步。接下来将显示以下页面:

图 7.3 – 名称、审查和创建页面

图 7.3 – 名称、审查和创建页面

  1. 在显示的网页上,填写sagemaker-iam-role。滚动到页面底部并点击arn:aws:iam::<account_number>:role/sagemaker-iam-role

这就是我们访问 SageMaker Feature Store 所需的所有内容。接下来,让我们创建特征定义。

生成特征

要定义特征组,因为我们正在尝试比较它与 Feast 的不同之处,我们将使用相同的特征集。您可以从 S3 存储桶下载之前导入的特征,或者从 GitHub 链接下载:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter07/rfm_features.parquet。下载 Parquet 文件后,将其复制到可以从笔记本访问的位置。下一步是创建一个新的笔记本,我将其命名为ch7-sagemaker-feature-store.ipynb

  1. 让我们先安装所需的库:

    !pip install sagemaker pandas
    
  2. 安装库后,让我们生成特征。在这里,我们将从位置读取复制的文件并对数据集进行一些小的修改:

    import pandas as pd
    import time
    df = pd.read_parquet(path="/content/rfm_features.parquet")
    df = df.drop(columns=["created_timestamp"])
    df["event_timestamp"] = float(round(time.time()))
    df["customerid"] = df['customerid'].astype(float)
    df.head()
    

上述代码块读取文件并删除了created_timestamp列,因为它对于 SageMaker 不是必需的。我们还将event_timestamp列更新为最新时间,并将其类型更改为float而不是datetime。这样做的原因是,SageMaker 在编写时仅支持intfloatstring特征,而datetime文件可以是floatstring对象,格式为datetime ISO 格式。

代码块生成了以下输出:

图 7.4 – 近期、频率和货币价值(RFM)特征

图 7.4 – 近期、频率和货币价值(RFM)特征

现在我们有了 RFM 特征,下一步是定义特征组。如果你正确地回忆起 第四章将特征存储添加到机器学习模型中,在生成特征后,我们创建了特征定义并将它们应用到特征存储中。

定义特征组

要定义特征组,因为它是一项一次性活动,应该在一个单独的笔记本中完成,而不是通过特征工程。对于这个练习,让我们继续在同一个笔记本中定义特征组:

  1. 下面的代码块定义了一些导入并创建 SageMaker 会话:

    import sagemaker
    import sys
    import boto3
    from sagemaker.session import Session
    from sagemaker import get_execution_role
    import os
    os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key_id>"
    os.environ["AWS_SECRET_ACCESS_KEY"] ="<aws_secret_id>"
    os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
    prefix = 'sagemaker-featurestore-introduction'
    role = "arn:aws:iam::<account_number>:role/sagemaker-iam-role"
    sagemaker_session = sagemaker.Session()
    region = sagemaker_session.boto_region_name
    s3_bucket_name = "feast-demo-mar-2022"
    

在代码块中,将 <aws_key_id><aws_secret_id> 替换为之前创建的 IAM 用户的密钥和密钥。同时,使用你的 IAM 角色 ARN 分配 role

  1. 下面的代码块创建特征组对象并从输入 DataFrame 中加载特征定义:

    from sagemaker.feature_store.feature_group import \
      FeatureGroup
    customers_feature_group = FeatureGroup(
        name="customer-rfm-features", 
        sagemaker_session=sagemaker_session
    )
    customers_feature_group.load_feature_definitions(df)
    

前面的代码块产生以下输出:

![图 7.5 – 加载特征定义的调用]

图 7.5 – 加载特征定义的调用

图 7.5 – 加载特征定义的调用

正如你在 图 7.5 中看到的那样,load_feature_definitions 调用读取输入 DataFrame 并自动加载特征定义。

  1. 下一步是创建特征组。下面的代码块在 SageMaker 中创建特征组:

    customers_feature_group.create(
        s3_uri=f"s3://{s3_bucket_name}/{prefix}",
        record_identifier_name="customerid",
        event_time_feature_name="event_timestamp",
        role_arn=role,
        enable_online_store=True
    )
    

前面的代码块通过传递以下参数调用 create API:

  • s3_uri:特征数据将被存储的位置

  • record_identifier_nameid 列的名称(与 Feast 中的实体列相同)

  • event_time_feature_name:将用于时间旅行的时戳列

  • role_arn:SageMaker Feature Store 可以承担的角色

  • enable_online_store:是否为此特征组启用在线服务

代码块在成功创建特征组时产生以下输出:

图 7.6 – 特征组创建

图 7.6 – 特征组创建

图 7.6 – 特征组创建

那就结束了 – 我们的特征组已经准备好使用。接下来让我们导入特征。

特征导入

在 SageMaker Feature Store 中进行特征导入很简单。它是一个简单的 API 调用,如下面的代码块所示:

ingestion_manager = customers_feature_group.ingest(df))
ingestion_manager.wait()
ingestion_manager.failed_rows

前面的代码块将导入特征,如果有任何失败的行数,将打印出来。

在这里需要记住的一件事是,就像 Feast 一样,你不需要做任何额外的事情来将最新功能从离线商店转换为在线商店。如果启用了在线商店,数据将被导入到在线和离线商店,最新数据将立即可在在线商店中查询。

接下来让我们查询在线商店。

从在线商店获取记录

与 Feast 一样,从在线商店查询很简单。你所需要的是记录 ID 和特征组名称。下面的代码块从在线商店获取记录:

customer_id = 12747.0
sg_runtime_client = sagemaker_session.boto_session.client(
    'sagemaker-featurestore-runtime', 
    region_name=region)
record = sg_runtime_client.get_record(
    FeatureGroupName="customer-rfm-features", 
    RecordIdentifierValueAsString=str(customer_id))
print(record)

上一段代码块从在线商店获取了具有12747.0 ID 的客户的全部特征。查询应在毫秒内返回结果。输出将类似于以下代码块:

{'ResponseMetadata': {'RequestId': '55342bbc-c69b-49ca-bbd8-xxxx', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': '55342bbc-c69b-49ca-bbd8-xxx, 'content-type': 'application/json', 'content-length': '729', 'date': 'Mon, 02 May 2022 01:36:27 GMT'}, 'RetryAttempts': 0}, 
'Record': [{'FeatureName': 'customerid', 'ValueAsString': '12747.0'}, {'FeatureName': 'recency', 'ValueAsString': '7'}, {'FeatureName': 'frequency', 'ValueAsString': '35'}, {'FeatureName': 'monetaryvalue', 'ValueAsString': '1082.09'}, {'FeatureName': 'r', 'ValueAsString': '3'}, {'FeatureName': 'f', 'ValueAsString': '2'}, {'FeatureName': 'm', 'ValueAsString': '3'}, {'FeatureName': 'rfmscore', 'ValueAsString': '8'}, {'FeatureName': 'revenue6m', 'ValueAsString': '1666.1100000000001'}, {'FeatureName': 'ltvcluster', 'ValueAsString': '1'}, {'FeatureName': 'segmenthighvalue', 'ValueAsString': '1'}, {'FeatureName': 'segmentlowValue', 'ValueAsString': '0'}, {'FeatureName': 'segmentmidvalue', 'ValueAsString': '0'}, {'FeatureName': 'event_timestamp', 'ValueAsString': '1651455004.0'}]}

如您所见,输出包含所有特征及其对应值。

现在我们已经了解了如何查询在线商店,接下来让我们看看如何生成训练数据集和查询历史数据。

使用 Amazon Athena 查询历史数据

如前所述,SageMaker Feature Store 提供了使用 Amazon Athena 在历史存储上运行 SQL 查询的能力。

以下代码块生成了所有客户及其特征的最新快照:

get_latest_snapshot_query = customers_feature_group.athena_query()
query = f"""SELECT *
FROM
    (SELECT *,
         row_number()
        OVER (PARTITION BY customerid
    ORDER BY  event_timestamp desc, Api_Invocation_Time DESC, write_time DESC) AS row_num
    FROM "{get_latest_snapshot_query.table_name}")
WHERE row_num = 1 and 
NOT is_deleted;"""
get_latest_snapshot_query.run(query_string=query, output_location=f"s3://{s3_bucket_name}/output")
get_latest_snapshot_query.get_query_execution()

该代码块使用嵌套 SQL 查询,其中内部查询从event_timeApi_Invocation_Timewrite_time列按降序获取所有客户及其特征。外部查询从内部查询的结果中选择每个客户的第一个出现。查询成功执行后,代码块输出查询结果的存储位置以及附加详情。

结果可以像以下代码块所示那样加载为 DataFrame:

latest_df = get_latest_snapshot_query.as_dataframe()
latest_df.head()

上一段代码块输出了以下内容:

![图 7.7 – Athena 查询结果

![img/B18024_07_007.jpg]

图 7.7 – Athena 查询结果

随意尝试在特征存储上运行其他 Athena 查询。以下是 Amazon Athena 查询的文档:docs.aws.amazon.com/athena/latest/ug/what-is.html

清理 SageMaker 特征组

在我们继续前进之前,让我们清理 SageMaker 资源以节省成本。清理很简单;它只是另一个 API 调用以删除特征组。以下代码块执行此操作:

customers_feature_group.delete()

就这些了。在成功执行后,它将删除特征组,但会留下 S3 和 Glue 目录中的数据,如果需要,仍然可以使用 Amazon Athena(使用boto3客户端)进行查询。为了确保一切都被清理干净,请在同一笔记本中运行以下代码块。它应该返回一个空的特征组列表:

sagemaker_client = sagemaker_session.boto_session.client(
    "sagemaker", region_name=region
) 
sagemaker_client.list_feature_groups()

现在我们已经了解了 SageMaker 特征组,接下来让我们看看机器学习的最佳实践。

机器学习最佳实践

到目前为止,本书中我们讨论了特征存储,如何使用它们进行机器学习开发和生产,以及选择特征存储时可供选择的各种选项。尽管特征存储是机器学习的主要组件/方面之一,但本书中我们并未过多关注机器学习的其他方面。在本节中,让我们简要地讨论一下机器学习的其他方面和最佳实践。

源数据验证

不论我们用于构建机器学习模型的科技、算法和基础设施如何,如果数据中存在错误和异常,模型的表现将受到严重影响。数据应该被视为任何机器学习系统中的第一公民。因此,在数据进入机器学习流程之前检测错误和异常非常重要。

要对原始数据源进行验证,我们需要一个组件来创建和编排针对数据的验证规则。数据的使用者应该能够在 SQL 查询、Python 脚本或 Spark SQL 中编写任何自定义规则。任何规则失败都应通知数据消费者,他们反过来应该能够决定是否停止管道执行、重新训练模型或采取不采取行动。

一些常见的规则包括按计划对数据集进行描述性分析,这可以提供数据漂移的见解。更高级的统计,如库尔巴克-莱布勒KL)散度和人口稳定性指数PSI),也是很好的。拥有简单的数据验证规则,如数据新鲜度、唯一值、字符串字段长度、模式和值范围阈值,可以非常有帮助。模式验证是数据验证的另一个重要方面。任何验证的变化都可能影响所有消费者和流程。我们在源头拥有的数据验证越好,我们的模型和流程就越健康、性能越好。

分解机器学习流程和编排

一种不良的做法是将所有内容都在单个笔记本中开发,从数据验证和特征工程到模型预测。这不是一个可扩展或可重用的方法。大部分时间都花在清理不必要的代码和将模型投入生产上。因此,将机器学习流程分解成多个更小的步骤是一个好主意,例如数据验证、清理、转换、特征工程、模型训练和模型预测。转换步骤越小,代码的可读性、可重用性和调试错误就越容易。这也是为什么 Tecton 中的特征视图和转换,以及 Hopsworks 中的存储连接器和转换函数是优秀功能的原因之一。许多提取、转换和加载ETL)框架也提供了类似的功能。

除了分解机器学习流程外,编排也是机器学习平台的重要部分。每个云提供商都有自己的编排工具,同时也有很多开源的提供。开发无需太多工作即可编排的流程步骤是关键。如今,有很多编排工具,只要步骤小且有意义,就应能够与任何现有框架轻松编排。

跟踪数据血缘和版本控制

如果您还记得第六章模型到生产及之后,我们讨论了预测问题的调试。在那个例子中,我们讨论了生成导致预测异常的相同特征集;然而,很多时候仅仅找出系统出了什么问题以及是否由代码或数据集引起是不够的。因此,能够追踪该特征集的数据来源直到数据源,对于调试问题非常有帮助。

对于管道的每一次运行,保存每个步骤的输入和输出以及时间戳版本是关键。有了这个,我们可以追踪预测中的异常直到其源头,即数据。例如,除了拥有生成网站客户不良推荐的特性外,更好的是能够追踪这些特性直到它们在事件发生时的交互以及在此事件发生时在机器学习管道中生成的不同转换。

以下是一些有助于更好地跟踪血缘信息的管道信息:

  • 在步骤中使用到的所有库的版本

  • 在管道中运行的代码版本,包括管道本身的版本

  • 管道每个步骤产生的输入参数和工件,例如原始数据、数据集和模型

接下来,让我们看看特征库。

特征库

拥有一个特征库对于机器学习开发非常有好处。尽管在更新特征表模式方面存在一些灰色区域,但特征存储的好处,如可重用性、可浏览的特征、在线服务的准备就绪、时间旅行和点时间连接,在模型开发中非常有用。正如我们在上一章中观察到的,在客户终身价值模型开发期间开发的特征在下一购买日模型中很有用。同样,随着特征库规模的扩大,越来越多的特征可供使用,数据科学家和工程师的工作重复性减少,从而加速了模型的开发。

以下截图展示了开发机器学习模型的成本与特征存储中精心策划的特征数量之间的关系:

![图 7.8 – 模型的平均成本与特征存储中精心策划的特征数量之间的关系

![img/B18024_07_008.jpg]

图 7.8 – 模型的平均成本与特征存储中精心策划的特征数量之间的关系

图 7.8所示,随着特征仓库的增长,开发和生产模型的成本会降低。按照重用和添加新特征(如果不可用)的原则,特征仓库中所有可用的特征要么是生产就绪的,要么正在为生产模型提供服务。我们只需为每个新模型添加增量特征。这意味着在基础设施上增加的唯一成本就是运行这些额外的特征工程转换和新特征表,其余的假设在如果我们使用托管特征存储的情况下会自动扩展以适应生产负载。因此,新模型开发和生产的成本应该随着时间的推移而降低,并在特征仓库饱和后趋于平稳。

实验跟踪、模型版本控制和模型仓库。

实验跟踪和模型仓库是机器学习开发的其他重要方面。在开发模型时,我们会运行不同的实验——可能是不同的算法,不同的实现,如 TensorFlow 与 PyTorch,超参数调整,为模型选择不同的特征集,不同的训练数据集,以及训练数据集上的不同转换。跟踪这些实验并不容易,因为其中一些实验可能持续数天或数周。因此,使用与许多笔记本环境一起提供的实验跟踪软件非常重要。

每次运行都应该记录以下内容:

  • 模型训练笔记本或脚本的版本。

  • 一些关于数据集的参数,可用于重现相同的训练和评估数据集。如果你使用特征存储,那么可能是时间戳和实体;如果没有,你也可以将训练数据集保存到文件中并记录数据集的位置。

  • 训练算法中使用的所有参数。

  • 每次运行的性能指标。

  • 对结果的任何可视化也可能非常有用。

每次运行的记录指标可用于比较不同运行中模型的性能指标。这些指标在决定哪个模型的运行性能更好并应转移到新阶段,如部署阶段和 A/B 测试阶段时至关重要。此外,每次运行也有助于你浏览实验的历史记录,如果你或团队中的其他人需要回顾并重现某些特定的运行。

同样,模型仓库可以帮助跟踪所有不同版本的模型。模型注册/仓库存储了加载和运行模型所需的信息——例如,MLflow 模型仓库存储有关 conda 环境、模型的pickle文件以及模型的其他任何附加依赖项的信息。如果您有一个模型的中央仓库,这对于消费者浏览和搜索以及模型的生命周期管理(如将模型移动到不同的阶段——开发、预发布、生产以及存档)非常有用。模型仓库还可以用于扫描代码中的任何漏洞以及模型中使用的任何包。因此,模型仓库在机器学习开发中发挥着关键作用。

特征和模型监控

正如我们在上一章中讨论的,特征监控是另一个重要的方面。特征仓库的一个重要对应物是对变化和异常的监控。特征监控规则将与数据监控的规则相似。一些有用的特征规则包括特征新鲜度、最小和最大规则、监控异常值、最新特征的描述性统计以及如 KL 散度和 PSI 等指标。Hopsworks 的监控规则应该是您可能拥有的特征规则列表的一个良好起点。以下是文档链接:docs.hopsworks.ai/feature-store-api/2.5.8/generated/feature_validation/

模型监控是另一个重要的方面。在将模型部署到生产环境中后,其性能往往会随着时间的推移而下降。这是因为用户行为发生变化;因此,数据特征也会随之变化。跟踪模型在生产中的表现非常重要。这些性能报告应该按计划生成,如果不是实时生成,还必须采取适当的行动,例如使用新数据重新训练模型或启动全新的迭代。

其他事项

在机器学习开发过程中需要注意的其他事项包括跟踪运行时环境、库升级和降级。最好是主动采取行动。例如,如果您使用与特定环境严格绑定的工具,如 Python 或 Spark 版本,一旦特定的运行时被弃用并从生产支持中移除,作业可能会开始失败,生产系统可能会受到影响。另一个例子是 Databricks 的运行时与特定的 Python 和 Spark 版本绑定。如果您在已弃用的版本上运行作业,一旦它停止支持,如果新版本中有破坏性更改,作业可能会开始失败。因此,最好是主动升级。

在此基础上,让我们总结一下本章所学的内容,然后再查看下一章中的端到端用例。

摘要

在本章中,我们审视了市场上一些可用的特征存储。我们讨论了其中的五个,分别是 Tecton、Databricks、Vertex AI、Hopsworks 和 SageMaker Feature Store。我们还深入探讨了 SageMaker Feature Store,以了解使用托管特征存储而非 Feast 的感觉,以及它在资源创建、特征摄取和查询方面的差异。在最后一节,我们简要讨论了机器学习开发的一些最佳实践。

在下一章中,我们将通过一个端到端的用例来介绍一个托管机器学习平台。

第九章:用例 - 客户流失预测

在上一章中,我们讨论了市场上可用的Feast特征存储的替代方案。我们查看了一些云提供商的特征存储产品,这些产品是机器学习ML)平台产品的一部分,即 SageMaker、Vertex AI 和 Databricks。我们还查看了一些其他供应商,他们提供可以与您的云提供商一起使用的托管特征存储,例如 Tecton 和 Hopsworks,其中 Hopsworks 也是开源的。为了了解托管特征存储,我们尝试了 SageMaker 特征存储的练习,并简要讨论了 ML 最佳实践。

在本章中,我们将讨论使用电信数据集进行客户流失的端到端用例。我们将逐步介绍数据清洗、特征工程、特征摄取、模型训练、部署和监控。对于这个练习,我们将使用托管特征存储——Amazon SageMaker。选择 SageMaker 而不是上一章中讨论的其他替代方案的原因很简单,那就是软件试用版的易于访问。

本章的目标是逐步通过使用托管特征存储的客户流失预测 ML 用例。这应该能让你了解它与自管理特征存储的不同之处,以及特征存储帮助的基本特征监控和模型监控方面。

在本章中,我们将按以下顺序讨论以下主题:

  • 基础设施设置

  • 问题与数据集的介绍

  • 数据处理和特征工程

  • 特征组定义和摄取

  • 模型训练

  • 模型预测

  • 特征监控

  • 模型监控

技术要求

为了运行示例并更好地理解本章内容,了解前几章涵盖的主题将很有用,但不是必需的。为了跟随本章的代码示例,你需要熟悉笔记本环境,这可以是本地设置,如 Jupyter Notebook,或者在线笔记本环境,如 Google Colab、Kaggle 或 SageMaker。你还需要一个 AWS 账户,并完全访问 SageMaker 和 Glue 控制台。你可以在试用期间创建新账户并免费使用所有服务。你可以在以下 GitHub 链接找到本书的代码示例:

github.com/PacktPublis…

基础设施设置

在本章的练习中,我们需要一个 S3 存储桶来存储数据,一个 IAM 角色,以及一个可以访问 SageMaker Feature Store 和 S3 存储桶的 IAM 用户。由于我们已经完成了所有这些资源的创建,我将跳过这一部分。请参阅第四章将特征存储添加到机器学习模型,了解 S3 存储桶的创建,以及第七章Feast 替代方案和机器学习最佳实践,了解 IAM 角色和 IAM 用户的创建。这就是本章初始设置所需的所有内容。

重要提示

我尽量少使用 AWS SageMaker 的资源,因为如果你的免费试用已经结束,这将产生费用。你可以使用 SageMaker Studio 来获得更好的笔记本和特征存储的 UI 体验。

问题及数据集介绍

在这个练习中,我们将使用可在 Kaggle 的 URL www.kaggle.com/datasets/bl… 上找到的电信客户流失数据集。练习的目标是使用这个数据集,为模型训练准备数据,并训练一个 XGBoost 模型来预测客户流失。该数据集有 21 列,列名具有自解释性。以下是对数据集的预览:

![图 8.1 – 电信数据集img/B18024_08_001.jpg

图 8.1 – 电信数据集

图 8.1 展示了标记的电信客户流失数据集。customerID 列是客户的 ID。除了 Churn 列之外的所有列代表属性集,而 Churn 列是目标列。

让我们动手进行特征工程。

数据处理和特征工程

在本节中,我们将使用电信客户流失数据集,并生成可用于训练模型的特征。让我们创建一个笔记本,命名为 feature-engineering.ipynb,并安装所需的依赖项:

!pip install pandas sklearn python-slugify s3fs sagemaker

完成库的安装后,读取数据。对于这个练习,我已经从 Kaggle 下载了数据,并将其保存在可以从笔记本访问的位置。

以下命令从 S3 读取数据:

import os
import numpy as np
import pandas as pd
from slugify import slugify
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
""" If you are executing the notebook outside AWS(Local jupyter lab, google collab or kaggle etc.), please uncomment the following 3 lines of code and set the AWS credentials """
#os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key>"
#os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
#os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
telcom = pd.read_csv("s3://<bucket_name_path>/telco-customer-churn.csv")

重要提示

如果你在外部 AWS 中执行笔记本,则使用环境变量设置用户凭据。

如果你预览该数据集,有几个列需要重新格式化,转换为分类列,或删除空值。让我们依次执行这些转换。

TotalCharges 列包含一些空字符串。让我们删除包含空或 null 值 TotalCharges 的行:

# Replace empty strings with nan
churn_data['TotalCharges'] = churn_data["TotalCharges"].replace(" ",np.nan)
# remove null values
churn_data = churn_data[churn_data["TotalCharges"].notnull()]
churn_data = churn_data.reset_index()[churn_data.columns]
churn_data["TotalCharges"] = churn_data["TotalCharges"].astype(float)

上一段代码块将所有空字符串替换为 np.nan,并删除了 TotalCharges 列中包含空的行。

接下来,让我们看看tenure列。这个列有整数值,代表客户以月为单位的服务期限。除了值之外,我们还可以将客户分为三组:短期(0-24 个月)、中期(24-48 个月)和长期(大于 48 个月)。

以下代码添加了具有定义组的客户tenure_group列:

# Create tenure_group columns using the tenure
def tenure_label(churn_data) :
    if churn_data["tenure"] <= 24 :
        return "0-24"
    elif (churn_data["tenure"] > 24) & (churn_data["tenure"] <= 48) :
        return "24-48"
    elif churn_data["tenure"] > 48:
        return "48-end"
churn_data["tenure_group"] = churn_data.apply(
    lambda churn_data: tenure_label(churn_data), axis = 1)

上一代码块创建了一个分类列tenure_group,它将根据客户服务期限的长度具有三个值,0-2424-4848-end

数据集中的一些列依赖于其他列。例如,OnlineSecurity依赖于客户是否有InternetService。因此,一些这些列,即OnlineSecurityOnlineBackupDeviceProtectionTechSupportStreamingTVStreamingMovies,其值用No internet service代替No。让我们将这些列中的No internet service替换为No

以下代码块执行替换操作:

# Replace 'No internet service' to No for the following columns
replace_cols = ['OnlineSecurity', 'OnlineBackup', 
                'DeviceProtection', 'TechSupport',
                'StreamingTV', 'StreamingMovies']
for i in replace_cols : 
    churn_data[i] = churn_data[i].replace({'No internet service' : 'No'})

我们已经进行了一系列数据清洗操作。在继续进行进一步转换之前,让我们预览一次数据集。

以下代码块对churn_data DataFrame 进行采样:

churn_data.sample(5)

以下代码输出一个样本预览,如图所示:

![图 8.2 – Churn 数据集img/B18024_08_002.jpg

图 8.2 – Churn 数据集

图 8.2所示,数据集是干净的,只包含分类或数值列。下一步是将这些分类值转换为数值编码。让我们查看数据集,看看哪些是分类的,哪些是数值的。

以下代码计算每个列的唯一值:

churn_data.nunique()

上一代码块显示以下输出:

![图 8.3 – 每个列的唯一值计数img/B18024_08_003.jpg

图 8.3 – 每个列的唯一值计数

图 8.3所示,除了MonthlyChargestenureTotalCharges之外,所有其他列都是分类列。

在数据集中,有二进制列和多值分类列。让我们找出哪些是二进制列,哪些是多值列。以下代码块检查列是否为二进制列:

# filter all the col if unique values in the column is 2
bin_cols = churn_data.nunique()[churn_data.nunique() == 2].keys().tolist()

现在我们有了二进制列的列表,让我们使用标签编码器将它们转换为 0 和 1。

以下代码使用标签编码器对二进制列进行转换:

le = LabelEncoder()
for i in bin_cols :
    churn_data[i] = le.fit_transform(churn_data[i])

下一步是将多值分类列转换为 0 和 1。为此,让我们首先过滤出多值列名。

以下代码块选择多值列:

all_categorical_cols = churn_data.nunique()[churn_data.nunique() <=4].keys().tolist()
multi_value_cols = [col for col in all_categorical_cols if col not in bin_cols]

上一代码块首先过滤掉所有分类列,然后过滤掉二进制列,这样我们只剩下多值列。

以下代码块将多值列转换为二进制编码:

churn_data = pd.get_dummies(data = churn_data, columns=multi_value_cols)

最后的部分是将数值转换为标准范围。由于数值列可能具有不同的范围,将列缩放到标准范围对于机器学习算法可能有益。这也有助于算法更快地收敛。因此,让我们将数值列缩放到标准范围。

下面的代码块使用 StandardScaler 将所有数值列缩放到标准范围:

numerical_cols = ['tenure','MonthlyCharges','TotalCharges']
std = StandardScaler()
churn_data[numerical_cols] = std.fit_transform(churn_data[numerical_cols])

前面的代码块缩放了数值列:tenureMonthlyChargesTotalCharges。现在我们的特征工程已经完成,让我们预览最终的特性集并将其摄取到 SageMaker Feature Store 中。

下面的代码块显示了特征集预览:

churn_data.columns = [slugify(col, lowercase=True, separator='_') for col in churn_data.columns]
churn_data.head()

前面的代码块将列名格式化为小写,并将字符串中的所有分隔符(如空格和连字符)替换为下划线。最终的特征在下面的屏幕截图中显示:

![图 8.4 – 特征集]

![图片 B18024_08_004.jpg]

图 8.4 – 特征集

最终的特征集有 33 列,如 图 8.4 所示。如果你还记得在 第四章 中,将特征存储添加到机器学习模型,在创建特征定义时,我们根据实体或逻辑组对实体和特征进行了分组。尽管这些特征可以分成多个组,但我们将创建一个单独的特征组并将所有特征摄取到其中。

在下一节中,让我们创建特征定义并摄取数据。

特征组定义和特征摄取

现在我们已经准备好了用于摄取的特征集,让我们创建特征定义并将特征摄取到特征存储中。正如之前提到的,我们将使用 SageMaker Feature Store。如果你还记得前面的章节,我们总是将特征定义保存在一个单独的笔记本中,因为这是一个一次性活动。在这个练习中,我们将尝试一种不同的方法,即使用条件语句在不存在的情况下创建特征组。你可以使用这两种方法中的任何一种。

让我们在同一个笔记本中继续,初始化 boto3 会话并检查我们的特征组是否已经存在:

import boto3
FEATURE_GROUP_NAME = "telcom-customer-features"
feature_group_exist = False
client = boto3.client('sagemaker')
response = client.list_feature_groups(
    NameContains=FEATURE_GROUP_NAME)
if FEATURE_GROUP_NAME in response["FeatureGroupSummaries"]:
  feature_group_exist = True

前面的代码块查询 SageMaker 以检查名为 telcom-customer-features 的特征组是否存在,并根据该结果设置一个布尔值。我们将使用这个布尔值来创建特征组或跳过创建,直接将数据摄取到特征存储中。

下面的代码块初始化了与 SageMaker Feature Store 交互所需的对象:

import sagemaker
from sagemaker.session import Session
import time
from sagemaker.feature_store.feature_definition import FeatureDefinition, FeatureTypeEnum
role = "arn:aws:iam::<account_number>:role/sagemaker-iam-role"
sagemaker_session = sagemaker.Session()
region = sagemaker_session.boto_region_name
s3_bucket_name = "feast-demo-mar-2022"

重要提示

在前面的代码块中使用之前创建的 IAM 角色。IAM 角色应该具有 AmazonSageMakerFullAccessAmazonS3FullAccess

下一步是初始化 FeatureGroup 对象。下面的代码初始化了特征组对象:

from sagemaker.feature_store.feature_group import FeatureGroup
customers_feature_group = FeatureGroup(
    name=FEATURE_GROUP_NAME, 
    sagemaker_session=sagemaker_session
)

现在我们将使用之前设置的布尔值来有条件地创建特征组,如果特征组不存在。以下代码块加载特征定义并调用 create 方法,如果特征组不存在:

churn_data["event_timestamp"] = float(round(time.time()))
if not feature_group_exist:
  customers_feature_group.load_feature_definitions(
      churn_data[[col 
                  for col in churn_data.columns 
                  if col not in ["customerid"]]]) 
  customer_id_def = FeatureDefinition(
      feature_name='customerid', 
      feature_type=FeatureTypeEnum.STRING)
  customers_feature_group.feature_definitions = [customer_id_def] + customers_feature_group.feature_definitions
  customers_feature_group.create(
    s3_uri=f"s3://{s3_bucket_name}/{FEATURE_GROUP_NAME}",
    record_identifier_name="customerid",
    event_time_feature_name="event_timestamp",
    role_arn=role,
    enable_online_store=False
    )

重要提示

load_feature_definitions 调用中,如果你注意到,我正在加载所有特征定义列,除了 customerid 列,并在下一行手动将 customerid 列添加到特征定义列表中。这样做的原因是 sagemaker 库无法将 string 数据类型识别为 pandas dtype 中的 object

create 特征组调用很简单。我通过传递 enable_online_storeFalse 来禁用在线存储,因为我们将会尝试批量管道,并将在线模型留作练习。一旦前面的代码块执行,根据条件语句,第一次将创建特征组,而对于后续的运行,将跳过特征组创建。

最后一步是摄取 DataFrame。以下代码块执行摄取并打印任何失败信息:

ingestion_results = customers_feature_group.ingest(
    churn_data, max_workers=1)
ingestion_results.failed_rows

重要提示

如果你只有批量使用案例,SageMaker 有一个 Spark 库可以直接用于将数据导入离线存储,这也是一种经济高效的方法。

这完成了特征工程和摄取。在下一节中,让我们看看模型训练。

模型训练

与之前一样,对于模型训练,特征存储是源。因此,让我们创建我们的模型训练笔记本并安装和初始化查询特征存储所需的所需对象。以下是笔记本的链接:

github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter08/Ch8_model_training.ipynb

以下代码块安装模型训练所需的库:

!pip install sagemaker==2.88.0 s3fs joblib scikit-learn==1.0.2 xgboost

安装所需的库后,初始化 SageMaker 会话和所需的对象:

import sagemaker
from sagemaker.session import Session
from sagemaker.feature_store.feature_group import FeatureGroup
#import os
#os.environ["AWS_ACCESS_KEY_ID"] = "<aws_key_id>"
#os.environ["AWS_SECRET_ACCESS_KEY"] = "<aws_secret>"
#os.environ["AWS_DEFAULT_REGION"] = "us-east-1"
role = "arn:aws:iam::<account_number>:role/sagemaker-iam-role"
FEATURE_GROUP_NAME = "telcom-customer-features"
sagemaker_session = sagemaker.Session()
region = sagemaker_session.boto_region_name
s3_bucket_name = "feast-demo-mar-2022"
customers_feature_group = FeatureGroup(
    name=FEATURE_GROUP_NAME, 
    sagemaker_session=sagemaker_session
)

前面的代码块初始化了 SageMaker 会话并初始化了特征组对象。特征组的 name 应该与我们特征工程笔记本中创建的特征组 name 相同。

重要提示

将之前创建的 IAM 角色分配给 role 变量。另外,如果你在 AWS 之外运行笔记本,你需要取消注释并设置前面的代码块中的 AWS 凭据。

下一步是查询历史存储以生成训练数据。与 Feast 不同,我们在这里不需要实体 DataFrame。相反,我们使用 SQL 查询来获取历史数据。它具有与 Feast 相同的时间旅行功能。为此练习,让我们使用与上一章中在 SageMaker 概述部分使用的类似查询获取所有客户的最新特征:

get_latest_snapshot_query = customers_feature_group.athena_query()
query = f"""SELECT *
FROM
    (SELECT *,
         row_number()
        OVER (PARTITION BY customerid
    ORDER BY  event_timestamp desc, Api_Invocation_Time DESC, write_time DESC) AS row_num
    FROM "{get_latest_snapshot_query.table_name}")
WHERE row_num = 1 and 
NOT is_deleted;"""
get_latest_snapshot_query.run(
    query_string=query, 
    output_location=f"s3://{s3_bucket_name}/output")
get_latest_snapshot_query.wait()

如果你记得正确的话,我们在上一章中使用了类似的嵌套查询。前面的代码块获取了所有客户及其最新特征。查询的输出将被写入run API 调用中提到的特定 S3 位置。

一旦查询成功运行,可以使用以下代码块获取数据集:

churn_data = get_latest_snapshot_query.as_dataframe()
churn_data = churn_data.drop(columns=["event_timestamp", "write_time", "api_invocation_time", "is_deleted", "row_num"])

重要提示

请注意,我们将从本节(模型训练)开始,直到前面的代码块,执行相同的步骤进行模型预测和特征监控。

前面的代码块获取数据集并删除了不需要的列。获取的数据集类似于图 8.4中所示的数据,但增加了以下列:write_timeapi_invocation_timeis_deletedrow_num。前三个是 SageMaker 在摄取过程中添加的额外元数据列,而row_num是我们为了获取每个客户的最新特征而在查询中创建的列。

现在我们有了数据集,让我们将其分为训练集和测试集。下面的代码块从数据集中删除了训练中不需要的列,并将数据分为训练集和测试集:

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix,accuracy_score,classification_report
from sklearn.metrics import roc_auc_score,roc_curve
from sklearn.metrics import precision_score,recall_score
Id_col = ["customerid"]
target_col = ["churn"]
# Split into a train and test set
train, test = train_test_split(churn_data,
                               test_size = .25,
                               random_state = 111)
cols    = [i for i in churn_data.columns if i not in Id_col + target_col]
training_x = train[cols]
training_y = train[target_col]
testing_x  = test[cols]
testing_y  = test[target_col]

前面的代码块省略了 ID 列,并执行了 75/25 的训练和测试分割。

其余的都很直接,基本上是训练 XGBoost 模型、参数调整和比较性能。以下是一个用于训练、样本分析和记录模型的示例代码块:

import joblib
import boto3
model = XGBClassifier(max_depth=7, 
                      objective='binary:logistic')
model.fit(training_x, training_y)
predictions = model.predict(testing_x)
probabilities = model.predict_proba(testing_x)
print("\n Classification report : \n", 
      classification_report(testing_y, predictions))
print("Accuracy   Score : ", 
      accuracy_score(testing_y, predictions))
# confusion matrix
conf_matrix = confusion_matrix(testing_y, predictions)
model_roc_auc = roc_auc_score(testing_y, predictions)
print("Area under curve : ", model_roc_auc, "\n")
joblib.dump(model, '/content/customer-churn-v0.0')
s3_client = boto3.client('s3')
response = s3_client.upload_file('/content/customer-churn-v0.0', s3_bucket_name, "model-repo/customer-churn-v0.0")

前面的代码块还将模型记录到 S3 的特定位置。这是一种粗略的做法。始终最好使用实验训练工具来记录性能和模型。

现在模型训练已完成,让我们看看模型评分。

模型预测

如前一小节最后一条注释所述,由于这是一个批量模型,因此从离线存储中获取数据的模型评分步骤与之前相似。然而,根据需要评分的客户(可能是所有客户),您可能需要过滤数据集。一旦过滤了数据集,其余步骤再次直接,即加载模型、运行预测和存储结果。

以下是一个用于加载模型、运行预测并将结果存储回 S3 以供消费的示例代码块:

import boto3
from datetime import date
s3 = boto3.client('s3')
s3.download_file(s3_bucket_name, f"model-repo/customer-churn-v0.0", "customer-churn-v0.0")
features = churn_data.drop(['customerid', 'churn'], axis=1)
loaded_model = joblib.load('/content/customer-churn-v0.0')
prediction = loaded_model.predict(features)
prediction.tolist()
file_name = f"customer_churn_prediction_{date.today()}.parquet"
churn_data["predicted_churn"] = prediction.tolist()
s3_url = f's3://{s3_bucket_name}/prediction_results/{file_name}'
churn_data.to_parquet(s3_url)

前面的代码块从 S3 下载模型,加载模型,将其与从历史存储中获取的数据评分,并将结果存储在 S3 桶中以供消费。

注意

XGBoost、Joblib 和 scikit-learn 的库版本应与保存模型时使用的版本相同,否则加载模型可能会失败。

为了将此机器学习管道投入生产,我们可以使用与我们在第六章,“模型到生产及之后”中所做的类似的编排。我将将其留作练习,因为它是重复的内容。接下来,让我们看看特征监控的例子。

特征监控

我们在书中多次讨论了特征监控在机器学习系统中的重要性。我们还讨论了特征存储如何标准化特征监控。在本节中,让我们看看一个对任何模型都很有用的特征监控示例。由于特征监控是在特征数据上计算一组统计信息并通知数据科学家或数据工程师变化,因此它需要模型使用的最新特征。

在本节中,我们将计算特征数据的摘要统计和特征相关性,这些可以按计划运行并定期发送给相关人员,以便他们可以根据这些信息采取行动。如“模型训练”部分的最后一条注释中提到的,获取特征步骤与该部分所做的是相同的。一旦您拥有了所有特征,下一步就是计算所需的统计量。

重要提示

请注意,您可能需要安装额外的库。以下是笔记本的 URL:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter08/Ch8_feature_monitoring.ipynb

以下代码块计算了特征数据的摘要统计并绘制了相关指标:

import numpy as np
import warnings
warnings.filterwarnings("ignore")
import plotly.offline as py
import plotly.graph_objs as go
churn_data.describe(include='all').T

上一行代码生成了数据集的描述性统计信息,包括最小值、最大值、计数、标准差等。

除了描述性统计之外,特征的相关矩阵对于所有机器学习模型来说也是非常有用的。以下代码块计算了特征的相关矩阵并绘制了热图:

corr = churn_data.corr()
cols = corr.columns.tolist()
trace = go.Heatmap(z=np.array(corr),
                   x=cols,
                   y=cols,
                   colorscale="Viridis",
                   colorbar=dict(
                       title="Pearson Coefficient",
                       titleside="right"
                       ),
                   )
layout = go.Layout(dict(title="Correlation Matrix",
                        height=720,
                        width=800,
                        margin=dict(r=0, l=210,
                                    t=25, b=210,
                                    ),
                        )
                   )
fig = go.Figure(data=[trace], layout=layout)
py.iplot(fig)

前面的代码块输出了以下热图:

![图 8.5 – 特征相关性img/B18024_08_005.jpg

图 8.5 – 特征相关性

与之前的运行相比,您可以添加更多统计信息,通过电子邮件、Slack 通知等方式发出警报。这可以放在另一个笔记本/Python 脚本中,可以按与特征工程笔记本相同的频率或更低的频率进行调度,并将自动报告发送给您。以下是完整笔记本的链接:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter08/Ch8_feature_monitoring.ipynb

重要提示

这只是一个特征监控的例子。还有更复杂的统计和指标可以用来确定特征的健康状况。

接下来,让我们看看模型监控。

模型监控

机器学习的另一个重要方面是模型监控。模型监控有不同的方面:对于在线模型,可能是系统监控,其中你监控延迟、CPU、内存利用率、每分钟模型请求等。另一个方面是模型性能监控。再次强调,有多种不同的方法来衡量性能。在这个例子中,我们将查看一个简单的分类报告和模型的准确率。

为了生成分类报告并计算实时模型的准确率,你需要预测数据和实时数据的真实值。对于这个例子,让我们假设流失率模型每周运行一次以生成流失预测,真实值将在模型运行后的每 4 周可用。这意味着如果模型预测客户 x 的流失为True,并且在接下来的 4 周内,如果我们因为任何原因失去了这个客户,那么模型预测正确;否则,预测是错误的。因此,对于每次模型预测的运行,我们需要等待 4 周才能获得真实值。

为了这个练习的简便性,让我们也假设每周将真实值填充回特征存储库。这意味着特征存储库总是包含最新的真实值。现在,我们的任务是获取特征存储库中最新特征对应的预测结果(4 周前运行的预测)并计算所需的指标。让我们接下来这么做。

如前所述,从特征存储库获取最新特征的步骤与我们在前三个部分中所做的是相同的。一旦从特征存储库获取了数据,下面的代码获取相应的预测结果并合并数据集:

from datetime import date, timedelta
import pandas as pd
pred_date = date.today()-timedelta(weeks=4)
file_name = f"customer_churn_prediction_{pred_date}.parquet"
prediction_data = pd.read_parquet(f"s3://{s3_bucket_name}/prediction_results/{file_name}")
prediction_y = prediction_data[["customerid", 
                                "predicted_churn"]]
acutal_y = churn_data[["customerid", "churn"]]
merged_data = prediction_y.merge(acutal_y, on="customerid")
merged_data.head()

之前的代码块应该产生类似于以下快照的输出。

图 8.6 – 模型监控合并数据

图 8.6 – 模型监控合并数据

重要提示

由于我们假设预测值和真实值之间相隔 4 周,之前的代码块尝试获取今天起 4 周后的数据。对于这个练习,你可以将file_name变量替换为预测输出的 Parquet 文件。

一旦你有了图 8.6中的 DataFrame,下面的代码块使用predicted_churnchurn列来生成分类报告和准确率:

testing_y = merged_data["churn"]
predictions = merged_data["predicted_churn"]
print("\n Classification report : \n", 
      classification_report(testing_y, predictions))
print("Accuracy   Score : ", 
      accuracy_score(testing_y, predictions))
# confusion matrix
conf_matrix = confusion_matrix(testing_y, predictions)
# roc_auc_score
model_roc_auc = roc_auc_score(testing_y, predictions)
print("Area under curve : ", model_roc_auc, "\n")

之前的代码块产生的输出类似于以下内容。

图 8.7 – 分类报告

图 8.7 – 分类报告

如前所述,这是样本监控。它可以安排与特征工程笔记本相同的间隔进行,尽管由于预测数据的不可用,它会在前四次迭代中失败。同时,请确保根据您的需求适当地调整预测文件名。以下是完整笔记本的 URL:github.com/PacktPublishing/Feature-Store-for-Machine-Learning/blob/main/Chapter08/Ch8_model_monitoring.ipynb

带着这些,让我们总结一下本章所学的内容。

摘要

本章的目的是尝试一个用例,即使用从 Kaggle 可获得的数据库进行电信客户流失预测。为此用例,我们使用了在上章中介绍的管理 SageMaker 特征存储。在练习中,我们经历了机器学习的不同阶段,如数据处理、特征工程、模型训练和模型预测。我们还查看了一个特征监控和模型监控的示例。本章的目标不是构建模型,而是展示如何使用管理特征存储进行模型构建以及它为监控带来的机会。要了解更多关于特征存储的信息,apply 会议(www.applyconf.com/)和特征存储论坛(www.featurestore.org/)是很好的资源。为了跟上机器学习领域的新发展以及其他公司如何解决类似问题,有一些有趣的播客,例如 TWIML AI (twimlai.com/)和数据怀疑论者(dataskeptic.com/)。这些资源应该能帮助…