EasyMLOps

Feature Store Lookup

MLOps

Easy

Problem

At prediction time, an ML service often combines precomputed offline features with request-time online features. Given a feature store keyed by user ID, inference requests, and default offline features, build one combined feature dictionary for each request.

For a known user, use the stored offline features. For an unknown user, use defaults. Then add the request's online_features. Offline and online keys do not overlap. Preserve request order and return a list of combined dictionaries.

Theory

When a machine learning model makes a prediction in production, it needs access to features. These features come from two fundamentally different sources, each with its own characteristics and constraints.

Online features are computed in real-time from the current request or session. Examples include the number of items in a shopping cart, the current time of day, the user's current location, or the text of a search query. These features capture the immediate context but are limited by what can be computed in a few milliseconds.

Offline features are pre-computed in batch processes and stored for later retrieval. Examples include a user's total lifetime spend, their average order frequency over the past year, their preferred product categories based on historical behavior, or their computed risk score from a nightly batch job. These features are richer and more complex but may be hours or even days stale.

The challenge in production ML systems is seamlessly joining these two sources at inference time, ensuring that the model receives a complete feature vector with both real-time context and historical information.


What is a Feature Store?

A feature store is a specialized data infrastructure component designed to manage and serve features for machine learning systems. It serves as the bridge between data engineering (feature computation) and machine learning (feature consumption).

A feature store typically provides:

Storage: A low-latency database optimized for key-value lookups. Given an entity key (like a user ID or item ID), it returns all pre-computed features for that entity in milliseconds.

Dual Serving: Most feature stores maintain two copies of features. An offline store (like a data lake or warehouse) supports batch queries for training data generation. An online store (like Redis or DynamoDB) supports low-latency lookups for inference.

Feature Registry: A catalog of available features with metadata including descriptions, data types, owners, and update frequencies. This helps teams discover and reuse existing features.

Point-in-Time Correctness: When generating training data, the feature store can reconstruct what the features looked like at any historical timestamp, preventing data leakage.

Monitoring: Tracking feature distributions, freshness, and serving latency to catch issues before they impact model performance.

Popular feature store implementations include Feast (open source), Tecton (managed service), AWS SageMaker Feature Store, Google Vertex AI Feature Store, and Databricks Feature Store.


The Lookup Pattern in Detail

When a prediction request arrives, the inference pipeline must assemble a complete feature vector. Here is the detailed process:

Step 1: Parse the Request

Extract all online features directly available in the request payload. Also extract entity identifiers (user_id, item_id, etc.) needed for offline feature lookups.

Step 2: Query the Feature Store

Using the entity identifier as a key, fetch the pre-computed offline features. This is typically a single key-value lookup operation with sub-millisecond latency in well-optimized stores.

Step 3: Handle Missing Entities

If the entity does not exist in the feature store (new user, new item, etc.), the lookup returns nothing. In this case, fall back to a pre-defined set of default values. These defaults ensure the model always receives a complete feature vector.

Step 4: Validate Feature Freshness (Optional)

Some systems check if the retrieved features are too stale. If the last update timestamp exceeds a threshold, they may trigger a warning or use defaults instead.

Step 5: Merge Feature Sets

Combine the offline features with the online features into a single feature dictionary or vector. Since offline and online features typically have different names, this is usually a simple union operation.

Step 6: Type Conversion and Formatting

Ensure all features are in the format the model expects. Convert strings to numerics where needed, apply any final transformations, and arrange features in the expected order.


Handling Missing Entities and Default Values

Not every entity you encounter in production will exist in the feature store. This happens in several scenarios:

New Users: A user who just signed up has no historical behavior. Their user_id will not appear in any offline feature table until the next batch job runs.

Cold Start Items: A newly listed product has no engagement statistics, no reviews, and no sales history.

Delayed Processing: Even for existing entities, there may be a lag between when an event occurs and when features are updated.

Data Quality Issues: Sometimes records fail validation and are excluded from feature tables.

The standard solution is to provide default feature values for each offline feature. These defaults should be carefully chosen:

The key principle is that default values should not bias predictions inappropriately. A new user with all-zero features should receive reasonable predictions, not extreme ones.


Feature Vector Assembly Mathematics

Let F_{offline} be the set of offline feature names and F_{online} be the set of online feature names. Typically, these are disjoint:

F_{offline} \cap F_{online} = \emptyset

The complete feature set is the union:

F_{complete} = F_{offline} \cup F_{online}

For a given request with entity key k:

\text{features}(k) = \begin{cases} \text{lookup}(k) \cup \text{online}(request) & \text{if } k \in \text{store} \\ \text{defaults} \cup \text{online}(request) & \text{otherwise} \end{cases}

In Python, this dictionary merge is elegantly expressed with unpacking:

offline = feature_store.get(user_id, default_features)
combined = {**offline, **online_features}

The order of unpacking matters if there are key collisions (later values overwrite earlier ones), but with disjoint feature sets this is not a concern.


A Detailed Worked Example

Consider an e-commerce recommendation system with these features:

Offline Features (from feature store):

Online Features (from request):

Default Offline Features:

Feature Store Contents:

Scenario 1: Known User

Request arrives for user_123 with cart_value=50.0, page_views=8, time_bucket=2

  1. Look up user_123: Found, retrieve {avg_order_value: 85.50, total_orders: 12, days_since_last_order: 7, favorite_category_id: 42}
  2. Extract online features: {current_cart_value: 50.0, session_page_views: 8, time_of_day_bucket: 2}
  3. Merge: {avg_order_value: 85.50, total_orders: 12, days_since_last_order: 7, favorite_category_id: 42, current_cart_value: 50.0, session_page_views: 8, time_of_day_bucket: 2}

Scenario 2: Unknown User

Request arrives for user_789 (new user) with cart_value=25.0, page_views=2, time_bucket=1

  1. Look up user_789: Not found
  2. Use defaults: {avg_order_value: 0.0, total_orders: 0, days_since_last_order: 999, favorite_category_id: -1}
  3. Extract online features: {current_cart_value: 25.0, session_page_views: 2, time_of_day_bucket: 1}
  4. Merge: {avg_order_value: 0.0, total_orders: 0, days_since_last_order: 999, favorite_category_id: -1, current_cart_value: 25.0, session_page_views: 2, time_of_day_bucket: 1}

The model can now make predictions for both users, despite one having no history.


Why This Architecture?

Latency Requirements: Real-time prediction systems often have strict latency budgets (50-100ms total). Computing complex aggregations on the fly would blow this budget. Pre-computation moves the heavy lifting to batch processes.

Training-Serving Consistency: One of the biggest sources of bugs in ML systems is computing features differently in training versus serving. The feature store ensures both pipelines use the same feature definitions.

Feature Reuse: If five different models need "user_average_order_value," computing it once and storing it is far more efficient than having each model compute it independently.

Separation of Concerns: Data engineers focus on building and maintaining feature pipelines. ML engineers focus on model development. The feature store is the interface between these teams.

Cost Efficiency: Aggregating a user's entire purchase history on every request would require expensive database scans. Doing it once per day in batch and serving from a fast key-value store is orders of magnitude cheaper.


Where Feature Store Lookups Show Up

Recommendation Systems: Netflix, Spotify, and Amazon all combine long-term user preferences (offline) with current session behavior (online) to generate personalized recommendations in real-time.

Fraud Detection: Financial institutions combine account history and risk scores (offline) with transaction details and device fingerprints (online) to approve or decline transactions in milliseconds.

Search Ranking: Google and Bing merge document quality signals and link graphs (offline) with query understanding and user context (online) to rank billions of web pages.

Dynamic Pricing: Uber and airlines combine historical demand patterns and price elasticity models (offline) with current supply, time, and competitive factors (online) to set prices in real-time.

Content Moderation: Social platforms combine user reputation scores and historical violation patterns (offline) with content signals and context (online) to prioritize content for human review.

Examples

Example 1

Input
feature_store = {"user_1": {"avg_spend": 45, "total_orders": 12}}, requests = [{"user_id": "user_1", "online_features": {"pages_viewed": 8, "session_mins": 5}}], defaults = {"avg_spend": 0, "total_orders": 0}
Output
[{"avg_spend": 45, "total_orders": 12, "pages_viewed": 8, "session_mins": 5}]
Explanation
The stored features for user_1 are combined with that request's online features.

Example 2

Input
feature_store = {"user_1": {"avg_spend": 45, "total_orders": 12}}, requests = [{"user_id": "user_99", "online_features": {"session_mins": 1}}], defaults = {"avg_spend": 0, "total_orders": 0}
Output
[{"avg_spend": 0, "total_orders": 0, "session_mins": 1}]

Hints

  1. Use feature_store.get(request["user_id"], defaults) for the offline lookup.
  2. Create each result with {**offline, **request["online_features"]}.

Requirements

Constraints

Starter Code

def feature_store_lookup(feature_store: dict, requests: list, defaults: dict) -> list:
    """
    Returns a list of feature dictionaries.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Basic lookuppublic
Missing user uses defaultspublic