|
| 1 | +# [Alpha] Static Artifacts Loading |
| 2 | + |
| 3 | +**Warning**: This is an experimental feature. To our knowledge, this is stable, but there are still rough edges in the experience. Contributions are welcome! |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Static Artifacts Loading allows you to load models, lookup tables, and other static resources once during feature server startup instead of loading them on each request. These artifacts are cached in memory and accessible to on-demand feature views for real-time inference. |
| 8 | + |
| 9 | +This feature optimizes the performance of on-demand feature views that require external resources by eliminating the overhead of repeatedly loading the same artifacts during request processing. |
| 10 | + |
| 11 | +### Why Use Static Artifacts Loading? |
| 12 | + |
| 13 | +Static artifacts loading enables data scientists and ML engineers to: |
| 14 | + |
| 15 | +1. **Improve performance**: Eliminate model loading overhead from each feature request |
| 16 | +2. **Enable complex transformations**: Use pre-trained models in on-demand feature views without performance penalties |
| 17 | +3. **Share resources**: Multiple feature views can access the same loaded artifacts |
| 18 | +4. **Simplify deployment**: Package models and lookup tables with your feature repository |
| 19 | + |
| 20 | +Common use cases include: |
| 21 | +- Sentiment analysis using pre-trained transformers models |
| 22 | +- Text classification with small neural networks |
| 23 | +- Lookup-based transformations using static dictionaries |
| 24 | +- Embedding generation with pre-computed vectors |
| 25 | + |
| 26 | +## How It Works |
| 27 | + |
| 28 | +1. **Feature Repository Setup**: Create a `static_artifacts.py` file in your feature repository root |
| 29 | +2. **Server Startup**: When `feast serve` starts, it automatically looks for and loads the artifacts |
| 30 | +3. **Memory Storage**: Artifacts are stored in the FastAPI application state and accessible via global references |
| 31 | +4. **Request Processing**: On-demand feature views access pre-loaded artifacts for fast transformations |
| 32 | + |
| 33 | +## Example 1: Basic Model Loading |
| 34 | + |
| 35 | +Create a `static_artifacts.py` file in your feature repository: |
| 36 | + |
| 37 | +```python |
| 38 | +# static_artifacts.py |
| 39 | +from fastapi import FastAPI |
| 40 | +from transformers import pipeline |
| 41 | + |
| 42 | +def load_sentiment_model(): |
| 43 | + """Load sentiment analysis model.""" |
| 44 | + return pipeline( |
| 45 | + "sentiment-analysis", |
| 46 | + model="cardiffnlp/twitter-roberta-base-sentiment-latest", |
| 47 | + device="cpu" |
| 48 | + ) |
| 49 | + |
| 50 | +def load_artifacts(app: FastAPI): |
| 51 | + """Load static artifacts into app.state.""" |
| 52 | + app.state.sentiment_model = load_sentiment_model() |
| 53 | + |
| 54 | + # Update global references for access from feature views |
| 55 | + import example_repo |
| 56 | + example_repo._sentiment_model = app.state.sentiment_model |
| 57 | +``` |
| 58 | + |
| 59 | +Use the pre-loaded model in your on-demand feature view: |
| 60 | + |
| 61 | +```python |
| 62 | +# example_repo.py |
| 63 | +import pandas as pd |
| 64 | +from feast.on_demand_feature_view import on_demand_feature_view |
| 65 | +from feast import Field |
| 66 | +from feast.types import String, Float32 |
| 67 | + |
| 68 | +# Global reference for static artifacts |
| 69 | +_sentiment_model = None |
| 70 | + |
| 71 | +@on_demand_feature_view( |
| 72 | + sources=[text_input_request], |
| 73 | + schema=[ |
| 74 | + Field(name="predicted_sentiment", dtype=String), |
| 75 | + Field(name="sentiment_confidence", dtype=Float32), |
| 76 | + ], |
| 77 | +) |
| 78 | +def sentiment_prediction(inputs: pd.DataFrame) -> pd.DataFrame: |
| 79 | + """Sentiment prediction using pre-loaded model.""" |
| 80 | + global _sentiment_model |
| 81 | + |
| 82 | + results = [] |
| 83 | + for text in inputs["input_text"]: |
| 84 | + predictions = _sentiment_model(text) |
| 85 | + best_pred = max(predictions, key=lambda x: x["score"]) |
| 86 | + |
| 87 | + results.append({ |
| 88 | + "predicted_sentiment": best_pred["label"], |
| 89 | + "sentiment_confidence": best_pred["score"], |
| 90 | + }) |
| 91 | + |
| 92 | + return pd.DataFrame(results) |
| 93 | +``` |
| 94 | + |
| 95 | +## Example 2: Multiple Artifacts with Lookup Tables |
| 96 | + |
| 97 | +Load multiple types of artifacts: |
| 98 | + |
| 99 | +```python |
| 100 | +# static_artifacts.py |
| 101 | +from fastapi import FastAPI |
| 102 | +from transformers import pipeline |
| 103 | +import json |
| 104 | +from pathlib import Path |
| 105 | + |
| 106 | +def load_sentiment_model(): |
| 107 | + """Load sentiment analysis model.""" |
| 108 | + return pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english") |
| 109 | + |
| 110 | +def load_lookup_tables(): |
| 111 | + """Load static lookup tables.""" |
| 112 | + return { |
| 113 | + "sentiment_labels": {"NEGATIVE": "negative", "POSITIVE": "positive"}, |
| 114 | + "domain_categories": {"twitter.com": "social", "news.com": "news", "github.com": "tech"}, |
| 115 | + "priority_users": {"user_123", "user_456", "user_789"} |
| 116 | + } |
| 117 | + |
| 118 | +def load_config(): |
| 119 | + """Load application configuration.""" |
| 120 | + return { |
| 121 | + "model_threshold": 0.7, |
| 122 | + "max_text_length": 512, |
| 123 | + "default_sentiment": "neutral" |
| 124 | + } |
| 125 | + |
| 126 | +def load_artifacts(app: FastAPI): |
| 127 | + """Load all static artifacts.""" |
| 128 | + app.state.sentiment_model = load_sentiment_model() |
| 129 | + app.state.lookup_tables = load_lookup_tables() |
| 130 | + app.state.config = load_config() |
| 131 | + |
| 132 | + # Update global references |
| 133 | + import example_repo |
| 134 | + example_repo._sentiment_model = app.state.sentiment_model |
| 135 | + example_repo._lookup_tables = app.state.lookup_tables |
| 136 | + example_repo._config = app.state.config |
| 137 | +``` |
| 138 | + |
| 139 | +Use multiple artifacts in feature transformations: |
| 140 | + |
| 141 | +```python |
| 142 | +# example_repo.py |
| 143 | +import pandas as pd |
| 144 | +from feast.on_demand_feature_view import on_demand_feature_view |
| 145 | + |
| 146 | +# Global references for static artifacts |
| 147 | +_sentiment_model = None |
| 148 | +_lookup_tables: dict = {} |
| 149 | +_config: dict = {} |
| 150 | + |
| 151 | +@on_demand_feature_view( |
| 152 | + sources=[text_input_request, user_input_request], |
| 153 | + schema=[ |
| 154 | + Field(name="predicted_sentiment", dtype=String), |
| 155 | + Field(name="is_priority_user", dtype=Bool), |
| 156 | + Field(name="domain_category", dtype=String), |
| 157 | + ], |
| 158 | +) |
| 159 | +def enriched_prediction(inputs: pd.DataFrame) -> pd.DataFrame: |
| 160 | + """Multi-artifact feature transformation.""" |
| 161 | + global _sentiment_model, _lookup_tables, _config |
| 162 | + |
| 163 | + results = [] |
| 164 | + for i, row in inputs.iterrows(): |
| 165 | + text = row["input_text"] |
| 166 | + user_id = row["user_id"] |
| 167 | + domain = row.get("domain", "") |
| 168 | + |
| 169 | + # Use pre-loaded model |
| 170 | + predictions = _sentiment_model(text) |
| 171 | + sentiment_scores = {pred["label"]: pred["score"] for pred in predictions} |
| 172 | + |
| 173 | + # Use lookup tables |
| 174 | + predicted_sentiment = _lookup_tables["sentiment_labels"].get( |
| 175 | + max(sentiment_scores, key=sentiment_scores.get), |
| 176 | + _config["default_sentiment"] |
| 177 | + ) |
| 178 | + |
| 179 | + is_priority = user_id in _lookup_tables["priority_users"] |
| 180 | + category = _lookup_tables["domain_categories"].get(domain, "unknown") |
| 181 | + |
| 182 | + results.append({ |
| 183 | + "predicted_sentiment": predicted_sentiment, |
| 184 | + "is_priority_user": is_priority, |
| 185 | + "domain_category": category, |
| 186 | + }) |
| 187 | + |
| 188 | + return pd.DataFrame(results) |
| 189 | +``` |
| 190 | + |
| 191 | +## Container Deployment |
| 192 | + |
| 193 | +Static artifacts work with containerized deployments. Include your artifacts in the container image: |
| 194 | + |
| 195 | +```dockerfile |
| 196 | +FROM python:3.11-slim |
| 197 | + |
| 198 | +# Install dependencies |
| 199 | +COPY requirements.txt . |
| 200 | +RUN pip install -r requirements.txt |
| 201 | + |
| 202 | +# Copy feature repository including static_artifacts.py |
| 203 | +COPY feature_repo/ /app/feature_repo/ |
| 204 | + |
| 205 | +WORKDIR /app/feature_repo |
| 206 | + |
| 207 | +# Start feature server |
| 208 | +CMD ["feast", "serve", "--host", "0.0.0.0"] |
| 209 | +``` |
| 210 | + |
| 211 | +The server will automatically load static artifacts during container startup. |
| 212 | + |
| 213 | +## Supported Artifact Types |
| 214 | + |
| 215 | +### Recommended Artifacts |
| 216 | +- **Small ML models**: Sentiment analysis, text classification, small neural networks |
| 217 | +- **Lookup tables**: Label mappings, category dictionaries, user segments |
| 218 | +- **Configuration data**: Model parameters, feature mappings, business rules |
| 219 | +- **Pre-computed embeddings**: User vectors, item features, static representations |
| 220 | + |
| 221 | +### Not Recommended |
| 222 | +- **Large Language Models**: Use dedicated serving solutions (vLLM, TensorRT-LLM, TGI) |
| 223 | +- **Models requiring specialized hardware**: GPU clusters, TPUs |
| 224 | +- **Frequently updated models**: Consider model registries with versioning |
| 225 | +- **Large datasets**: Use feature views with proper data sources instead |
| 226 | + |
| 227 | +## Error Handling |
| 228 | + |
| 229 | +Static artifacts loading includes graceful error handling: |
| 230 | +- **Missing file**: Server starts normally without static artifacts |
| 231 | +- **Loading errors**: Warnings are logged, feature views should implement fallback logic |
| 232 | +- **Partial failures**: Successfully loaded artifacts remain available |
| 233 | + |
| 234 | +Always implement fallback behavior in your feature transformations: |
| 235 | + |
| 236 | +```python |
| 237 | +@on_demand_feature_view(...) |
| 238 | +def robust_prediction(inputs: pd.DataFrame) -> pd.DataFrame: |
| 239 | + global _sentiment_model |
| 240 | + |
| 241 | + results = [] |
| 242 | + for text in inputs["input_text"]: |
| 243 | + if _sentiment_model is not None: |
| 244 | + # Use pre-loaded model |
| 245 | + predictions = _sentiment_model(text) |
| 246 | + sentiment = max(predictions, key=lambda x: x["score"])["label"] |
| 247 | + else: |
| 248 | + # Fallback when artifacts aren't available |
| 249 | + sentiment = "neutral" |
| 250 | + |
| 251 | + results.append({"predicted_sentiment": sentiment}) |
| 252 | + |
| 253 | + return pd.DataFrame(results) |
| 254 | +``` |
| 255 | + |
| 256 | +## Starting the Feature Server |
| 257 | + |
| 258 | +Start the feature server as usual: |
| 259 | + |
| 260 | +```bash |
| 261 | +feast serve |
| 262 | +``` |
| 263 | + |
| 264 | +You'll see log messages indicating artifact loading: |
| 265 | + |
| 266 | +``` |
| 267 | +INFO:fastapi:Loading static artifacts from static_artifacts.py |
| 268 | +INFO:fastapi:Static artifacts loading completed |
| 269 | +INFO:uvicorn:Application startup complete |
| 270 | +``` |
| 271 | + |
| 272 | +## Template Example |
| 273 | + |
| 274 | +The PyTorch NLP template demonstrates static artifacts loading: |
| 275 | + |
| 276 | +```bash |
| 277 | +feast init my-nlp-project -t pytorch_nlp |
| 278 | +cd my-nlp-project/feature_repo |
| 279 | +feast serve |
| 280 | +``` |
| 281 | + |
| 282 | +This template includes a complete example with sentiment analysis model loading, lookup tables, and integration with on-demand feature views. |
| 283 | + |
| 284 | +## Performance Considerations |
| 285 | + |
| 286 | +- **Startup time**: Artifacts are loaded during server initialization, which may increase startup time |
| 287 | +- **Memory usage**: All artifacts remain in memory for the server's lifetime |
| 288 | +- **Concurrency**: Artifacts are shared across all request threads |
| 289 | +- **Container resources**: Ensure sufficient memory allocation for your artifacts |
| 290 | + |
| 291 | +## Configuration |
| 292 | + |
| 293 | +Currently, static artifacts loading uses convention-based configuration: |
| 294 | +- **File name**: Must be named `static_artifacts.py` |
| 295 | +- **Location**: Must be in the feature repository root directory |
| 296 | +- **Function name**: Must implement `load_artifacts(app: FastAPI)` function |
| 297 | + |
| 298 | +## Limitations |
| 299 | + |
| 300 | +- File name and location are currently fixed (not configurable) |
| 301 | +- Artifacts are loaded synchronously during startup |
| 302 | +- No built-in artifact versioning or hot reloading |
| 303 | +- Limited to Python-based artifacts (no external binaries) |
| 304 | + |
| 305 | +## Contributing |
| 306 | + |
| 307 | +This is an alpha feature and we welcome contributions! Areas for improvement: |
| 308 | +- Configurable artifact file locations |
| 309 | +- Asynchronous artifact loading |
| 310 | +- Built-in artifact versioning |
| 311 | +- Performance monitoring and metrics |
| 312 | +- Integration with model registries |
| 313 | + |
| 314 | +Please report issues and contribute improvements via the [Feast GitHub repository](https://github.com/feast-dev/feast). |
0 commit comments