15. Deploy and Serve a Feature List
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.
Important Note for FeatureByte Enterprise Users¶
In Catalogs with Approval Flow enabled, moving features to production-ready status involves a comprehensive approval process.
This includes several evaluations, such as checking the feature's compliance with default cleaning operations and the feature job setting of its source tables. It also involves confirming the status of these tables and backtesting the feature job setting to prevent future training-serving inconsistencies. Additionally, essential details of the feature, particularly its feature definition file, are shared and subjected to a thorough review.
import featurebyte as fb
from datetime import datetime
16:53:36 | WARNING | Service endpoint is inaccessible: http://featurebyte-server:8088/
Activate Catalog¶
# Set your profile to the tutorial environment
fb.use_profile("tutorial")
catalog_name = "Credit Default Dataset SDK Tutorial"
catalog = fb.Catalog.activate(catalog_name)
16:53:36 | INFO | Using profile: tutorial 16:53:36 | INFO | Using configuration file at: /Users/gxav/.featurebyte/config.yaml 16:53:36 | INFO | Active profile: tutorial (https://tutorials.featurebyte.com/api/v1) 16:53:36 | INFO | SDK version: 2.1.0.dev113 16:53:36 | INFO | No catalog activated. 16:53:36 | INFO | Catalog activated: Credit Default Dataset SDK Tutorial 16:13:11 | WARNING | Remote SDK version (1.1.0.dev7) is different from local (1.1.0.dev1). Update local SDK to avoid unexpected behavior. 16:13:11 | INFO | No catalog activated. 16:13:11 | INFO | Catalog activated: Grocery Dataset Tutorial
List feature lists in Catalog¶
catalog.list_feature_lists()
id | name | num_feature | status | deployed | readiness_frac | online_frac | tables | entities | primary_entity | created_at | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 67c2c8de53e241b5a68dd616 | 51 features for Credit Default | 51 | DRAFT | False | 0.0 | 0.0 | [NEW_APPLICATION, PRIOR_APPLICATIONS, CONSUMER... | [New Application, Client] | [New Application] | 2025-03-01T08:44:22.006000 |
Get a feature list from Catalog¶
feature_list_name = "51 features for Credit Default"
simple_feature_list = catalog.get_feature_list(feature_list_name)
Loading Feature(s) |████████████████████████████████████████| 51/51 [100%] in 0.
Deploy feature list¶
deployment_name = "Early Stage Loan Application: 51 features for Credit Default"
# Create a deployment
deployment = simple_feature_list.deploy(
deployment_name=deployment_name,
make_production_ready=True,
)
Done! |████████████████████████████████████████| 100% in 9.1s (0.11%/s) |████████████████████████████████████████| ▆▄▂ 100% in 8s (~0s, 0.1%/ Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s) Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s) Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s)
# Enable deployment
deployment.enable()
Done! |████████████████████████████████████████| 100% in 8:45.4 (0.00%/s) Done! |████████████████████████████████████████| 100% in 1:49.2 (0.01%/s)
# Check that the deployment is enabled
catalog.list_deployments()
id | name | feature_list_name | feature_list_version | num_feature | enabled | |
---|---|---|---|---|---|---|
0 | 67c2cb1a3be1dbfd5a465cee | Early Stage Loan Application: 51 features for ... | 51 features for Credit Default | V250301 | 51 | True |
# 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¶
# Get a python template for consuming the feature serving API
deployment.get_online_serving_code(language="python")
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/67c2cb1a3be1dbfd5a465cee/online_features",
headers={"Content-Type": "application/json", "active-catalog-id": "67c2c726799a655db130206e", "Authorization": "Bearer alZJY8HvLxoi_emU_eukghMpSMzxL0eQ4ufUSWxvsLk"},
json={"entity_serving_names": entity_serving_names},
)
assert response.status_code == 200, response.json()
return pd.DataFrame.from_dict(response.json()["features"])
request_features([{"NEW_APPLICATION_ID": 100002}])
# Get shell script
deployment.get_online_serving_code(language="sh")
#!/bin/sh
curl -X POST \
-H 'Content-Type: application/json' \
-H 'active-catalog-id: 67c2c726799a655db130206e' \
-H 'Authorization: Bearer alZJY8HvLxoi_emU_eukghMpSMzxL0eQ4ufUSWxvsLk' \
-d '{"entity_serving_names": [{"NEW_APPLICATION_ID": 100002}]}' \
https://tutorials.featurebyte.com/api/v1/deployment/67c2cb1a3be1dbfd5a465cee/online_features
Create batch request table¶
Let's run a batch request for the last 24 hours applications.
# Get view of new applications
application_view = catalog.get_view("NEW_APPLICATION")
from datetime import datetime, timedelta, timezone
import pandas as pd
date_time_now_utc = datetime.now(timezone.utc)
now_less_24h = date_time_now_utc - timedelta(hours=24)
cond = application_view.APPLICATION_TIME > pd.Timestamp(now_less_24h)
recent_application_view = application_view[cond]
recent_application_view.preview()
BIRTHDATE | CODE_GENDER | INCOME_TYPE | EDUCATION_TYPE | OCCUPATION_TYPE | ORGANIZATION_TYPE | APPLICATION_ID | CLIENT_ID | AMT_CREDIT | AMT_ANNUITY | AMT_GOODS_VALUE | REGION_POPULATION_RELATIVE | APPLICATION_TIME | DAYS_EMPLOYED | DAYS_REGISTRATION | DAYS_LAST_PHONE_CHANGE | FLOORSMAX_MEDI | LANDAREA_MEDI | FLAG_DOCUMENT_3 | AMT_REQ_CREDIT_BUREAU_QRT | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1988-07-27 | F | Working | Incomplete higher | Laborers | Industry: type 12 | 100245 | 100245 | 364896.0 | 20947.5 | 315000.0 | 0.016612 | 2025-02-28 17:58:39 | -1397 | -5725.0 | -2264.0 | 0.1667 | 0.0773 | 1 | 0.0 |
1 | 1971-12-15 | M | Working | Secondary / secondary special | Drivers | Self-employed | 100754 | 100754 | 364500.0 | 13221.0 | 364500.0 | 0.007330 | 2025-02-28 15:44:54 | -553 | -9402.0 | -1714.0 | 0.3333 | 0.1034 | 1 | 1.0 |
2 | 1989-10-25 | F | Working | Higher education | Core staff | Kindergarten | 102315 | 102315 | 234000.0 | 12825.0 | 234000.0 | 0.018209 | 2025-02-28 09:40:13 | -1465 | -6899.0 | -475.0 | 0.3333 | 0.0207 | 0 | 1.0 |
3 | 1987-06-29 | F | Working | Higher education | NaN | Other | 104044 | 104044 | 675000.0 | 21775.5 | 675000.0 | 0.003541 | 2025-02-28 14:27:50 | -1905 | -4475.0 | -537.0 | 0.1667 | 0.0267 | 1 | 1.0 |
4 | 1984-10-29 | F | Commercial associate | Higher education | Laborers | Trade: type 7 | 109015 | 109015 | 790830.0 | 62613.0 | 675000.0 | 0.001276 | 2025-02-28 11:44:47 | -3087 | -8855.0 | -2447.0 | 0.1667 | 0.0964 | 1 | 1.0 |
5 | 1982-01-26 | F | Working | Secondary / secondary special | Core staff | Self-employed | 112271 | 112271 | 1141686.0 | 41139.0 | 922500.0 | 0.005144 | 2025-02-28 10:06:41 | -861 | -2457.0 | 0.0 | 0.1667 | 0.0911 | 1 | 0.0 |
6 | 1981-07-24 | M | Working | Secondary / secondary special | Drivers | Business Entity Type 3 | 112973 | 112973 | 450000.0 | 28759.5 | 450000.0 | 0.035792 | 2025-02-28 14:29:59 | -1435 | -1487.0 | -1994.0 | 0.0000 | 0.0246 | 0 | 1.0 |
7 | 1971-05-18 | M | Working | Secondary / secondary special | Laborers | Business Entity Type 3 | 116726 | 116726 | 337500.0 | 17361.0 | 337500.0 | 0.007020 | 2025-02-28 10:12:28 | -5046 | -6519.0 | -799.0 | NaN | NaN | 0 | 0.0 |
8 | 1984-03-18 | F | State servant | Secondary / secondary special | Laborers | Other | 119771 | 119771 | 280332.0 | 25839.0 | 234000.0 | 0.018850 | 2025-02-28 14:43:49 | -1505 | -878.0 | -592.0 | NaN | NaN | 1 | 1.0 |
9 | 1984-12-08 | M | Working | Secondary / secondary special | Laborers | Business Entity Type 2 | 128213 | 128213 | 104256.0 | 12501.0 | 90000.0 | 0.006305 | 2025-03-01 05:03:04 | -2399 | -8720.0 | -1883.0 | 0.1667 | 0.0165 | 1 | 0.0 |
# Check primary entity of the deployed feature list to obtain its serving name
simple_feature_list.primary_entity
[<featurebyte.api.entity.Entity at 0x329245b70> { 'name': 'New Application', 'created_at': '2025-03-01T08:38:05.280000', 'updated_at': '2025-03-01T08:38:07.461000', 'description': None, 'serving_names': [ 'NEW_APPLICATION_ID' ], 'catalog_name': 'Credit Default Dataset SDK Tutorial' }]
# Create batch request table from the view
batch_request_table = recent_application_view.create_batch_request_table(
name="Latest Applications the latest 24 hours at " + date_time_now_utc.strftime("%Y%m%d:%H%M"),
columns=["APPLICATION_ID"],
columns_rename_mapping={
"APPLICATION_ID": "NEW_APPLICATION_ID",
}
)
Done! |████████████████████████████████████████| 100% in 9.2s (0.11%/s) Done! |████████████████████████████████████████| 100% in 12.2s (0.08%/s)
# Get name of the batch request table
batch_request_table.name
'Latest Applications the latest 24 hours at 20250301:0902'
# List batch request tables in catalog
catalog.list_batch_request_tables()
id | name | type | shape | feature_store_name | created_at | |
---|---|---|---|---|---|---|
0 | 67c2cd363be1dbfd5a465cef | Latest Applications the latest 24 hours at 202... | view | [141, 1] | playground | 2025-03-01T09:02:51.026000 |
Compute batch feature table¶
# Get deployment and batch request table
deployment = catalog.get_deployment("Early Stage Loan Application: 51 features for Credit Default")
batch_request_table = catalog.get_batch_request_table(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"{feature_list_name} with {batch_request_table.name}"
)
Done! |████████████████████████████████████████| 100% in 1:12.9 (0.01%/s) Done! |████████████████████████████████████████| 100% in 12.1s (0.08%/s)
# List observation tables
catalog.list_batch_feature_tables()
id | name | feature_store_name | batch_request_table_name | shape | created_at | |
---|---|---|---|---|---|---|
0 | 67c2cd403be1dbfd5a465cf0 | 51 features for Credit Default with Latest App... | playground | Latest Applications the latest 24 hours at 202... | [141, 52] | 2025-03-01T09:04:04.573000 |
# Convert to pandas
batch_features.to_pandas()
Downloading table |████████████████████████████████████████| 141/141 [100%] in 0
NEW_APPLICATION_ID | NEW_APPLICATION_AMT_ANNUITY | NEW_APPLICATION_AMT_ANNUITY_To_AMT_CREDIT | NEW_APPLICATION_AMT_ANNUITY_To_AMT_GOODS_VALUE | NEW_APPLICATION_AMT_CREDIT | NEW_APPLICATION_AMT_GOODS_VALUE_To_AMT_CREDIT | NEW_APPLICATION_AMT_REQ_CREDIT_BUREAU_QRT | NEW_APPLICATION_CODE_GENDER | NEW_APPLICATION_Credit-Goods_Gap | NEW_APPLICATION_DAYS_EMPLOYED | ... | CLIENT_Max_of_Approved_Status_Prior_Applications_AMT_ANNUITYs_104w | CLIENT_Min_of_Cash_loans_Contract_type_Prior_Applications_CNT_PAYMENTs_104w | CLIENT_Max_of_Prior_Applications_AMT_CREDIT_To_AMT_APPLICATIONs_104w | CLIENT_Max_of_Prior_Applications_Application-Credit_Gaps_104w | CLIENT_Max_of_Prior_Applications_CNT_PAYMENTs_104w | CLIENT_Pct_of_Prior_Applications_is_Refused_Status_52w | CLIENT_Pct_of_Prior_Applications_is_Refused_Status_Cash_loans_Contract_type_104w | CLIENT_Sum_of_Approved_Status_Prior_Applications_AMT_ANNUITYs_To_Sum_of_Approved_Status_Prior_Applications_AMT_CREDITs_104w | CLIENT_Time_Since_Latest_Prior_Application_DECISION_DATE_104w | CLIENT_vs_OVERALL_Count_of_Approved_Status_Cash_loans_Contract_type_Prior_Applications_by_Prior_Application_PRODUCT_COMBINATION_104w | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 100245 | 20947.5 | 0.057407 | 0.066500 | 364896.0 | 0.863260 | 0.0 | F | 49896.0 | -1397.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 100754 | 13221.0 | 0.036272 | 0.036272 | 364500.0 | 1.000000 | 1.0 | M | 0.0 | -553.0 | ... | 24928.065 | 60.0 | 1.396000 | 0.0 | 60.0 | 0.250000 | 0.000000 | 0.029394 | 73.470966 | 0.757184 |
2 | 102315 | 12825.0 | 0.054808 | 0.054808 | 234000.0 | 1.000000 | 1.0 | F | 0.0 | -1465.0 | ... | NaN | 60.0 | 1.198000 | 0.0 | 60.0 | 0.333333 | 0.277778 | NaN | 196.724219 | NaN |
3 | 104044 | 21775.5 | 0.032260 | 0.032260 | 675000.0 | 1.000000 | 1.0 | F | 0.0 | -1905.0 | ... | 10880.100 | 48.0 | 1.000000 | 0.0 | 48.0 | 0.000000 | 0.000000 | 0.109890 | 277.482818 | NaN |
4 | 109015 | 62613.0 | 0.079174 | 0.092760 | 790830.0 | 0.853534 | 1.0 | F | 115830.0 | -3087.0 | ... | 16673.940 | NaN | 1.066007 | -9090.0 | 10.0 | 0.000000 | 0.000000 | 0.113580 | 260.596047 | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
136 | 441014 | 18382.5 | 0.041263 | 0.041263 | 445500.0 | 1.000000 | NaN | M | 0.0 | -261.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
137 | 442468 | 11875.5 | 0.044536 | 0.058644 | 266652.0 | 0.759417 | 0.0 | F | 64152.0 | -1543.0 | ... | 37550.700 | 36.0 | 1.118802 | 0.0 | 36.0 | 0.000000 | 0.000000 | 0.068231 | 155.709138 | 0.625046 |
138 | 450233 | 18918.0 | 0.042040 | 0.042040 | 450000.0 | 1.000000 | 1.0 | F | 0.0 | -5855.0 | ... | 1758.600 | NaN | 0.799835 | 4360.5 | 12.0 | NaN | 0.000000 | 0.100930 | 471.477587 | NaN |
139 | 453893 | 18261.0 | 0.079569 | 0.079569 | 229500.0 | 1.000000 | 0.0 | F | 0.0 | -786.0 | ... | 17653.275 | 24.0 | 1.211196 | 112500.0 | 24.0 | NaN | 0.000000 | 0.065458 | 393.620341 | 0.625046 |
140 | 453971 | 24840.0 | 0.079420 | 0.092000 | 312768.0 | 0.863260 | 1.0 | M | 42768.0 | -3574.0 | ... | 19646.685 | 36.0 | 1.138600 | 0.0 | 36.0 | 0.500000 | 0.200000 | 0.038345 | 99.617540 | 0.757184 |
141 rows × 52 columns
Downloading table |████████████████████████████████████████| 500/500 [100%] in 0
GROCERYCUSTOMERGUID | PRODUCTGROUP | 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 | CUSTOMER_x_PRODUCTGROUP_Sum_of_item_TotalCost_14d | CUSTOMER_x_PRODUCTGROUP_Time_Since_Latest_Timestamp | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 14f94723-4cae-4041-a11a-a044df1a5a5f | Fromages | 60-64 | 14.28 | NaN | NaN | NaN | 1.000000 | 0.373275 | NaN | 1527.861052 |
1 | d4559f7d-eb28-42c6-b47d-847de24952c2 | Fromages | 65-69 | 20.04 | NaN | NaN | NaN | NaN | 0.370545 | NaN | 2177.017718 |
2 | 1d363f8c-88f4-49eb-a107-4f1e2fbe6722 | Fromages | 25-29 | 73.16 | NaN | NaN | NaN | NaN | 0.781391 | NaN | 1118.766885 |
3 | 768e57df-7513-412b-a466-effd270ad6a6 | Fromages | 65-69 | 7.16 | 3.0 | 8.246667 | 4.395121 | -0.644055 | 0.650126 | NaN | 1357.819107 |
4 | 48f6e550-ce63-457e-ad14-d511ee934661 | Fromages | 40-44 | 24.62 | 1.0 | 24.620000 | 0.000000 | -0.219845 | 0.753619 | NaN | 475.681329 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
495 | c1b3224a-5dbb-427d-87d3-3882c6bb1278 | Fromages | 50-54 | 6.69 | NaN | NaN | NaN | NaN | 0.104953 | NaN | NaN |
496 | 261d2979-0568-4544-8be0-32355049e565 | Fromages | 65-69 | 2.00 | NaN | NaN | NaN | NaN | 0.317982 | NaN | NaN |
497 | 825ee709-2f85-49e2-bec7-27bea06efe3a | Fromages | 45-49 | 3.00 | 2.0 | 5.995000 | 2.995000 | -0.990561 | 0.375129 | NaN | NaN |
498 | ea68ce6b-b3c3-4ba5-a471-f11b1c5bd892 | Fromages | 45-49 | 4.50 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
499 | b8b00613-2e48-4eed-8971-61718990a994 | Fromages | 45-49 | 10.00 | 2.0 | 6.000000 | 4.000000 | 1.264961 | 0.579119 | NaN | NaN |
500 rows × 11 columns
# download parquet file
batch_features.download()
Downloading table |████████████████████████████████████████| 141/141 [100%] in 0
PosixPath('BATCH_FEATURE_TABLE_67c2cd40306bec3def2f7249.parquet')
Downloading table |████████████████████████████████████████| 500/500 [100%] in 0
PosixPath('BATCH_FEATURE_TABLE_666959284c98806ac301afa0.parquet')
# delete if not needed any more
batch_features.delete()
batch_request_table.delete()
Done! |████████████████████████████████████████| 100% in 9.2s (0.11%/s) Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s) Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s) Done! |████████████████████████████████████████| 100% in 6.1s (0.17%/s)
Manage Deployment¶
deployment = catalog.get_deployment(deployment_name)
# get feature jobs status (this will produce meaningful results only once multiple jobs have been run)
# deployment.get_feature_jobs_status(job_history_window=12)
# Disable deployment
deployment.disable()
Done! |████████████████████████████████████████| 100% in 24.4s (0.04%/s) Done! |████████████████████████████████████████| 100% in 12.1s (0.08%/s)
# The deployment is still part of the catalog but disabled
catalog.list_deployments()
id | name | feature_list_name | feature_list_version | num_feature | enabled | |
---|---|---|---|---|---|---|
0 | 67c2cb1a3be1dbfd5a465cee | Early Stage Loan Application: 51 features for ... | 51 features for Credit Default | V250301 | 51 | False |