Deploy and Serve a Feature List for Model Predictions¶
Once you have a feature list ready, it's essential to serve these features effectively to make real-time or batch predictions using your machine learning model.
In this section, we'll take the feature list we previously crafted and explore two primary ways to serve its values:
REST API: Ideal for real-time predictions where you need instantaneous results. For instance, in applications where user interactions require immediate feedback based on model predictions.
Batch Processing: Best suited for scenarios where you have a bulk of data and don't require instant results.
In [1]:
Copied!
import featurebyte as fb
from datetime import datetime
# Set your profile to the tutorial environment
fb.use_profile("tutorial")
catalog_name = "Grocery Dataset Tutorial"
catalog = fb.Catalog.activate(catalog_name)
import featurebyte as fb
from datetime import datetime
# Set your profile to the tutorial environment
fb.use_profile("tutorial")
catalog_name = "Grocery Dataset Tutorial"
catalog = fb.Catalog.activate(catalog_name)
22:04:02 | INFO | Using configuration file at: /Users/gxav/.featurebyte/config.yaml 22:04:02 | INFO | Active profile: tutorial (https://tutorials.featurebyte.com/api/v1) 22:04:02 | WARNING | Remote SDK version (0.5.0.dev6) is different from local (0.5.0.dev1). Update local SDK to avoid unexpected behavior. 22:04:02 | INFO | No catalog activated. 22:04:03 | INFO | 6 feature lists, 31 features deployed 22:04:03 | INFO | Using profile: tutorial 22:04:03 | INFO | Using configuration file at: /Users/gxav/.featurebyte/config.yaml 22:04:03 | INFO | Active profile: tutorial (https://tutorials.featurebyte.com/api/v1) 22:04:03 | WARNING | Remote SDK version (0.5.0.dev6) is different from local (0.5.0.dev1). Update local SDK to avoid unexpected behavior. 22:04:03 | INFO | No catalog activated. 22:04:04 | INFO | 6 feature lists, 31 features deployed 22:04:05 | INFO | Catalog activated: Grocery Dataset Tutorial
List feature lists in Catalog¶
In [2]:
Copied!
catalog.list_feature_lists()
catalog.list_feature_lists()
Out[2]:
id | name | num_feature | status | deployed | readiness_frac | online_frac | tables | entities | primary_entities | created_at | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 64ff1dec72f1e0466e55f6a3 | Customer Simple FeatureList | 7 | DRAFT | False | 0.0 | 0.0 | [GROCERYCUSTOMER, GROCERYINVOICE, INVOICEITEMS... | [customer] | [customer] | 2023-09-11T14:02:35.008000 |
Get a feature list from Catalog¶
In [3]:
Copied!
simple_feature_list = catalog.get_feature_list("Customer Simple FeatureList")
simple_feature_list = catalog.get_feature_list("Customer Simple FeatureList")
Loading Feature(s) |████████████████████████████████████████| 7/7 [100%] in 1.3s
Deploy feature list¶
In [4]:
Copied!
# Create a deployment
deployment = simple_feature_list.deploy(
deployment_name="Customer Spending forecast",
make_production_ready=True,
)
# Create a deployment
deployment = simple_feature_list.deploy(
deployment_name="Customer Spending forecast",
make_production_ready=True,
)
Loading Feature(s) |████████████████████████████████████████| 7/7 [100%] in 1.0s Done! |████████████████████████████████████████| 100% in 3.4s (0.30%/s)
In [5]:
Copied!
# Enable deployment
deployment.enable()
# Enable deployment
deployment.enable()
Done! |████████████████████████████████████████| 100% in 40.6s (0.02%/s)
In [6]:
Copied!
# Check that the deployment is enabled
catalog.list_deployments()
# Check that the deployment is enabled
catalog.list_deployments()
Out[6]:
id | name | feature_list_name | feature_list_version | num_feature | enabled | |
---|---|---|---|---|---|---|
0 | 64ff1e5c1b8535f0fa03767c | Customer Spending forecast | Customer Simple FeatureList | V230911 | 7 | True |
In [7]:
Copied!
# Check status of the feature list
print("simple_feature_list.status:", simple_feature_list.status)
# Check status of the feature list
print("simple_feature_list.status:", simple_feature_list.status)
simple_feature_list.status: DEPLOYED
Get template for online serving¶
In [8]:
Copied!
# Get a python template for consuming the feature serving API
deployment.get_online_serving_code(language="python")
# Get a python template for consuming the feature serving API
deployment.get_online_serving_code(language="python")
Loading Feature(s) |████████████████████████████████████████| 7/7 [100%] in 0.9s
Out[8]:
from typing import Any, Dict
import pandas as pd
import requests
def request_features(entity_serving_names: Dict[str, Any]) -> pd.DataFrame:
"""
Send POST request to online serving endpoint
Parameters
----------
entity_serving_names: Dict[str, Any]
Entity serving name values to used for serving request
Returns
-------
pd.DataFrame
"""
response = requests.post(
url="https://tutorials.featurebyte.com/api/v1/deployment/64ff1e5c1b8535f0fa03767c/online_features",
headers={"Content-Type": "application/json", "active-catalog-id": "64ff1c7d9a683e4e22365111", "Authorization": "Bearer gUaCOUiCSYpHazO10CQ4qXSccT0j6U2vsyFYxWhc5yA"},
json={"entity_serving_names": entity_serving_names},
)
assert response.status_code == 200, response.json()
return pd.DataFrame.from_dict(response.json()["features"])
request_features([{"GROCERYCUSTOMERGUID": "9bf67c68-0d34-48e2-9b0a-b5c3ee495b1c"}])
In [9]:
Copied!
# Get shell script
deployment.get_online_serving_code(language="sh")
# Get shell script
deployment.get_online_serving_code(language="sh")
Loading Feature(s) |████████████████████████████████████████| 7/7 [100%] in 0.9s
Out[9]:
#!/bin/sh
curl -X POST \
-H 'Content-Type: application/json' \
-H 'active-catalog-id: 64ff1c7d9a683e4e22365111' \
-H 'Authorization: Bearer gUaCOUiCSYpHazO10CQ4qXSccT0j6U2vsyFYxWhc5yA' \
-d '{"entity_serving_names": [{"GROCERYCUSTOMERGUID": "9bf67c68-0d34-48e2-9b0a-b5c3ee495b1c"}]}' \
https://tutorials.featurebyte.com/api/v1/deployment/64ff1e5c1b8535f0fa03767c/online_features
Create batch request table¶
In [10]:
Copied!
# Get view of current customer
customer_table = catalog.get_table("GROCERYCUSTOMER")
customer_view = customer_table.get_view(view_mode="manual")
cond = customer_view.CurrentRecord == True
current_customer_view = customer_view[cond]
# Get view of current customer
customer_table = catalog.get_table("GROCERYCUSTOMER")
customer_view = customer_table.get_view(view_mode="manual")
cond = customer_view.CurrentRecord == True
current_customer_view = customer_view[cond]
In [11]:
Copied!
# Check primary entity of the deployed feature list to obtain its serving name
simple_feature_list.primary_entity
# Check primary entity of the deployed feature list to obtain its serving name
simple_feature_list.primary_entity
Out[11]:
[<featurebyte.api.entity.Entity at 0x7f79d8061780> { 'name': 'customer', 'created_at': '2023-09-11T13:56:58.863000', 'updated_at': '2023-09-11T13:57:16.943000', 'description': None, 'serving_names': [ 'GROCERYCUSTOMERGUID' ], 'catalog_name': 'Grocery Dataset Tutorial' }]
In [12]:
Copied!
# Create batch request table from the view
batch_request_table = current_customer_view.create_batch_request_table(
name="Current Customers at " + datetime.now().strftime("%Y%m%d:%H%M"),
columns=["GroceryCustomerGuid"],
columns_rename_mapping={
"GroceryCustomerGuid": "GROCERYCUSTOMERGUID",
}
)
# Create batch request table from the view
batch_request_table = current_customer_view.create_batch_request_table(
name="Current Customers at " + datetime.now().strftime("%Y%m%d:%H%M"),
columns=["GroceryCustomerGuid"],
columns_rename_mapping={
"GroceryCustomerGuid": "GROCERYCUSTOMERGUID",
}
)
Done! |████████████████████████████████████████| 100% in 6.7s (0.15%/s)
In [13]:
Copied!
# Get name of the batch request table
batch_request_table.name
# Get name of the batch request table
batch_request_table.name
Out[13]:
'Current Customers at 20230911:2205'
In [14]:
Copied!
# List batch request tables in catalog
catalog.list_batch_request_tables()
# List batch request tables in catalog
catalog.list_batch_request_tables()
Out[14]:
id | name | type | shape | feature_store_name | created_at | |
---|---|---|---|---|---|---|
0 | 64ff1ea91b8535f0fa03767d | Current Customers at 20230911:2205 | view | [500, 1] | playground | 2023-09-11T14:05:31.608000 |
Compute batch feature table¶
In [15]:
Copied!
# Get deployment and batch request table
deployment = catalog.get_deployment("Customer Spending forecast")
batch_request_table = catalog.get_batch_request_table(batch_request_table.name)
# Get deployment and batch request table
deployment = catalog.get_deployment("Customer Spending forecast")
batch_request_table = catalog.get_batch_request_table(batch_request_table.name)
In [16]:
Copied!
# Compute batch features
batch_features = deployment.compute_batch_feature_table(
batch_request_table=batch_request_table,
batch_feature_table_name =
f"Customer Simple FeatureList for Spending forecast with {batch_request_table.name}"
)
# Compute batch features
batch_features = deployment.compute_batch_feature_table(
batch_request_table=batch_request_table,
batch_feature_table_name =
f"Customer Simple FeatureList for Spending forecast with {batch_request_table.name}"
)
Done! |████████████████████████████████████████| 100% in 6.7s (0.15%/s)
In [17]:
Copied!
# List observation tables
catalog.list_batch_feature_tables()
# List observation tables
catalog.list_batch_feature_tables()
Out[17]:
id | name | feature_store_name | batch_request_table_name | shape | created_at | |
---|---|---|---|---|---|---|
0 | 64ff1eb41b8535f0fa03767e | Customer Simple FeatureList for Spending forec... | playground | Current Customers at 20230911:2205 | [500, 8] | 2023-09-11T14:05:44.586000 |
In [18]:
Copied!
# Convert to pandas
batch_features.to_pandas()
# Convert to pandas
batch_features.to_pandas()
Downloading table |████████████████████████████████████████| 500/500 [100%] in 0
Out[18]:
GROCERYCUSTOMERGUID | CUSTOMER_Age_band | CUSTOMER_Latest_invoice_Amount | CUSTOMER_Count_of_invoice_14d | CUSTOMER_Avg_of_invoice_Amount_14d | CUSTOMER_Std_of_invoice_Amount_14d | CUSTOMER_Latest_invoice_Amount_Z_Score_to_invoice_Amount_28d | CUSTOMER_vs_OVERALL_item_TotalCost_across_product_ProductGroups_26w | |
---|---|---|---|---|---|---|---|---|
0 | 217d44e9-d85b-4dba-9847-2c4c071f7303 | 40-44 | 11.86 | 4.0 | 9.2025 | 2.485482 | 0.218373 | 0.458607 |
1 | 59232f53-fd8c-4ea6-abe1-91c7da41e793 | 75-79 | 12.16 | 4.0 | 18.1350 | 5.300818 | -0.187217 | 0.432918 |
2 | f1ee8601-2f7e-4aa3-9756-19ca5c8f3bcc | 70-74 | 57.16 | 1.0 | 57.1600 | 0.000000 | NaN | 0.328127 |
3 | aa5a6b30-7acd-4d96-a3b3-f09f75345ca9 | 80-84 | 0.99 | 1.0 | 0.9900 | 0.000000 | NaN | 0.601628 |
4 | 682fb64a-5ca6-48f6-9de1-6d9d6321db39 | 65-69 | 12.75 | 1.0 | 12.7500 | 0.000000 | 0.886151 | 0.662089 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
495 | da9493e7-46b5-4e97-9f4c-497048901a0d | 25-29 | 6.29 | 1.0 | 6.2900 | 0.000000 | 1.000000 | 0.467478 |
496 | 71b4cc67-21e7-4a73-819c-adc6da260127 | 40-44 | 29.75 | 0.0 | NaN | NaN | NaN | 0.655595 |
497 | 29dc945d-1c9e-46a7-a5d7-ba6d24182195 | 65-69 | 7.51 | 1.0 | 7.5100 | 0.000000 | NaN | 0.540942 |
498 | dbb91a85-9e60-4f3a-90bc-f156d6763249 | 50-54 | 11.95 | 0.0 | NaN | NaN | NaN | 0.668405 |
499 | 77fac0cb-6a8d-4f59-9a1a-c324ab36e885 | 65-69 | 34.45 | 0.0 | NaN | NaN | NaN | 0.247293 |
500 rows × 8 columns
In [19]:
Copied!
# download parquet file
batch_features.download()
# download parquet file
batch_features.download()
Downloading table |████████████████████████████████████████| 500/500 [100%] in 0
Out[19]:
PosixPath('BATCH_FEATURE_TABLE_64ff1eb5ceb61c9075514aaf.parquet')
In [20]:
Copied!
# delete if not needed any more
batch_features.delete()
batch_request_table.delete()
# delete if not needed any more
batch_features.delete()
batch_request_table.delete()
Done! |████████████████████████████████████████| 100% in 6.8s (0.15%/s) Done! |████████████████████████████████████████| 100% in 6.7s (0.15%/s)
Manage Deployment¶
In [21]:
Copied!
deployment = catalog.get_deployment("Customer Spending forecast")
deployment = catalog.get_deployment("Customer Spending forecast")
In [22]:
Copied!
# get feature jobs status (this will produce meaningful results once multiple jobs have been run)
deployment.get_feature_jobs_status()
# get feature jobs status (this will produce meaningful results once multiple jobs have been run)
deployment.get_feature_jobs_status()
Loading Feature(s) |████████████████████████████████████████| 7/7 [100%] in 1.0s
Out[22]:
Job statistics (last 1 hours)
request_date | job_history_window | job_duration_tolerance | |
---|---|---|---|
0 | 2023-09-11T14:06:13.209534 | 1 | 60 |
feature_name | aggregation_hash | |
---|---|---|
0 | CUSTOMER_Avg_of_invoice_Amount_14d | b67eafff |
1 | CUSTOMER_Count_of_invoice_14d | 081a79b3 |
2 | CUSTOMER_Latest_invoice_Amount | 3e664139 |
3 | CUSTOMER_Latest_invoice_Amount_Z_Score_to_invoice_Amount_28d | 3e664139 |
4 | CUSTOMER_Latest_invoice_Amount_Z_Score_to_invoice_Amount_28d | b67eafff |
5 | CUSTOMER_Latest_invoice_Amount_Z_Score_to_invoice_Amount_28d | cf255092 |
6 | CUSTOMER_Std_of_invoice_Amount_14d | cf255092 |
7 | CUSTOMER_vs_OVERALL_item_TotalCost_across_product_ProductGroups_26w | c8d4aa8c |
8 | CUSTOMER_vs_OVERALL_item_TotalCost_across_product_ProductGroups_26w | 6d77adb1 |
aggregation_hash | frequency(min) | completed_jobs | max_duration(s) | 95 percentile | frac_late | exceed_period | failed_jobs | incomplete_jobs | time_since_last | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 3e664139 | 60 | 0 | NaN | NaN | NaN | 0 | 0 | 1 | NaT |
1 | 081a79b3 | 60 | 0 | NaN | NaN | NaN | 0 | 0 | 1 | NaT |
2 | b67eafff | 60 | 0 | NaN | NaN | NaN | 0 | 0 | 1 | NaT |
3 | cf255092 | 60 | 0 | NaN | NaN | NaN | 0 | 0 | 1 | NaT |
4 | c8d4aa8c | 60 | 0 | NaN | NaN | NaN | 0 | 0 | 1 | NaT |
5 | 6d77adb1 | 60 | 0 | NaN | NaN | NaN | 0 | 0 | 1 | NaT |
In [23]:
Copied!
# Disable deployment
deployment.disable()
# Disable deployment
deployment.disable()
Done! |████████████████████████████████████████| 100% in 10.2s (0.10%/s)
In [24]:
Copied!
# The deployment is still part of the catalog but disabled
catalog.list_deployments()
# The deployment is still part of the catalog but disabled
catalog.list_deployments()
Out[24]:
id | name | feature_list_name | feature_list_version | num_feature | enabled | |
---|---|---|---|---|---|---|
0 | 64ff1e5c1b8535f0fa03767c | Customer Spending forecast | Customer Simple FeatureList | V230911 | 7 | False |
In [ ]:
Copied!