CO6 Topic 22 of 25
Feature stores and training-serving consistency
When many models need the same features, a feature store gives one definition, for both training and serving. Point-in-time joins keep the future out of training data.
You will be able to
- Explain the offline and online stores
- Perform a point-in-time correct join
- Show how a naive join leaks the future
- Say when a feature store is worth having
Read
- Reference book 2, Ch. 6, pp. 173–210; Ch. 7, pp. 211–246
- Reference book 4, Ch. 5, pp. 103–138
Intuition
Topic 3's cure for skew was to ship one pipeline. Now picture ten models and fifty features, some calculated from recent activity. A feature store is a single place to define, calculate and fetch these features, so that training and serving always agree.
How it works
digraph G {
node [shape=box, style=rounded];
"Feature definitions" -> "Offline store\n(history, for training)";
"Feature definitions" -> "Online store\n(latest value, fast)";
"Offline store\n(history, for training)" -> "Training";
"Online store\n(latest value, fast)" -> "Serving";
}
| Offline store | Online store | |
|---|---|---|
| Holds | Full history | Latest value per student |
| Speed | Seconds to minutes | Milliseconds |
| Used by | Training | Live prediction |
Point-in-time correctness. A training row must use each feature as it was known at that moment.
| Student | Mock test date | Score |
|---|---|---|
| S1 | 1 Jan | 40 |
| S1 | 1 Mar | 70 |
| S1 | 1 May | 90 |
The student is labelled on 1 Apr. The correct value is 70. The latest score, 90, comes from the future, and using it is leakage: training looks better than serving ever can.
Feast is a popular open-source feature store. You declare entities and feature views in Python, then fetch historical features for training with get_historical_features, and live features with get_online_features. This course treats it at the conceptual level.
Do you need one? With one model and simple features, a shared ColumnTransformer in the model file is enough. A store is worth having when several teams and models reuse time-based features.
Run it: a point-in-time join and a mini store
import pandas as pd
from placement_predict import load_data
history = pd.DataFrame({
"student": ["S1", "S1", "S1", "S2", "S2"],
"date": pd.to_datetime(["2026-01-01", "2026-03-01", "2026-05-01", "2026-02-01", "2026-06-01"]),
"mock_score": [40, 70, 90, 55, 85],
}).sort_values("date")
labels = pd.DataFrame({
"student": ["S1", "S2"],
"label_date": pd.to_datetime(["2026-04-01", "2026-04-01"]),
"placed": [1, 0],
}).sort_values("label_date")
naive = labels.merge(history.groupby("student", as_index=False)["mock_score"].last(), on="student")
print("Naive join, using the latest value (leaks the future):")
print(naive[["student", "label_date", "mock_score"]].to_string(index=False))
correct = pd.merge_asof(labels, history, left_on="label_date", right_on="date", by="student")
print("\nPoint-in-time join, using the value known at label_date:")
print(correct[["student", "label_date", "date", "mock_score"]].to_string(index=False))
class MiniFeatureStore:
"""One definition, two ways of reading it."""
def __init__(self, history):
self.history = history.sort_values("date")
def get_historical_features(self, entity_df):
return pd.merge_asof(entity_df.sort_values("label_date"), self.history, left_on="label_date", right_on="date", by="student")
def get_online_features(self, student):
row = self.history[self.history["student"] == student].iloc[-1]
return {"student": student, "mock_score": int(row["mock_score"])}
store = MiniFeatureStore(history)
print("\nOffline (training):", store.get_historical_features(labels)["mock_score"].tolist())
print("Online (serving S1 today):", store.get_online_features("S1"))
Try this: set a label date before a student's first score. What does the join return, and how should the model handle it?
Common mistakes
- Joining with the latest value. It's the classic hidden leak, and it often looks like a big accuracy gain.
- Adopting a feature store too early. It's a system to run, with its own costs.
- Calculating a feature in SQL for training and in Python for serving. That is how skew begins.
Problems
- A student's scores are 50 (Feb), 60 (Apr) and 80 (Jul). Which value do you use for a label on 1 May?
- Why does a naive latest-value join boost validation scores?
- Which features for PlacementPredict would belong in a store? Which wouldn't?
- Add a "number of mock tests so far" feature to
MiniFeatureStore. - Write down what would have to be true for you to recommend Feast to a team.
Flashcards
Say the meaning to yourself, then select the card to check.
- Feature store
- One definition of each feature, for training and serving.
- Offline store
- Full history, for building training sets.
- Online store
- Latest values, read in milliseconds.
- Point-in-time correctness
- Use each feature as it was known at the label's date.