{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "p5JTeKfCVBZf" }, "source": [ "# Overview\n", "\n", "In this tutorial, we'll use Feast to generate training data and power online model inference for a \n", "ride-sharing driver satisfaction prediction model. Feast solves several common issues in this flow:\n", "\n", "1. **Training-serving skew and complex data joins:** Feature values often exist across multiple tables. Joining \n", " these datasets can be complicated, slow, and error-prone.\n", " * Feast joins these tables with battle-tested logic that ensures _point-in-time_ correctness so future feature \n", " values do not leak to models.\n", "2. **Online feature availability:** At inference time, models often need access to features that aren't readily \n", " available and need to be precomputed from other data sources.\n", " * Feast manages deployment to a variety of online stores (e.g. DynamoDB, Redis, Google Cloud Datastore) and \n", " ensures necessary features are consistently _available_ and _freshly computed_ at inference time.\n", "3. **Feature and model versioning:** Different teams within an organization are often unable to reuse \n", " features across projects, resulting in duplicate feature creation logic. Models have data dependencies that need \n", " to be versioned, for example when running A/B tests on model versions.\n", " * Feast enables discovery of and collaboration on previously used features and enables versioning of sets of \n", " features (via _feature services_).\n", " * _(Experimental)_ Feast enables light-weight feature transformations so users can re-use transformation logic \n", " across online / offline use cases and across models.\n", "\n", "We will:\n", "1. Deploy a local feature store with a **Parquet file offline store** and **Sqlite online store**.\n", "2. Build a training dataset using our time series features from our **Parquet files**.\n", "3. Materialize feature values from the offline store into the online store.\n", "4. Read the latest features from the online store for inference." ] }, { "cell_type": "markdown", "metadata": { "id": "9_Y997DzvOMI" }, "source": [ "## Step 1: Install Feast\n", "\n", "Install Feast using pip:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rXNMAAJKQPG5" }, "outputs": [], "source": [ "%%sh\n", "pip install feast -U -q\n", "echo \"Please restart your runtime now (Runtime -> Restart runtime). This ensures that the correct dependencies are loaded.\"" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "id": "sOX_LwjaAhKz" }, "source": [ "**Reminder**: Please restart your runtime after installing Feast (Runtime -> Restart runtime). This ensures that the correct dependencies are loaded.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "OZetvs5xx4GP" }, "source": [ "## Step 2: Create a feature repository\n", "\n", "A feature repository is a directory that contains the configuration of the feature store and individual features. This configuration is written as code (Python/YAML) and it's highly recommended that teams track it centrally using git. See [Feature Repository](https://docs.feast.dev/reference/feature-repository) for a detailed explanation of feature repositories.\n", "\n", "The easiest way to create a new feature repository to use the `feast init` command. This creates a scaffolding with initial demo data.\n", "\n", "### Demo data scenario \n", "- We have surveyed some drivers for how satisfied they are with their experience in a ride-sharing app. \n", "- We want to generate predictions for driver satisfaction for the rest of the users so we can reach out to potentially dissatisfied users." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "IhirSkgUvYau", "outputId": "664367b9-6a2a-493d-fd78-6495fb459fa2" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Creating a new Feast repository in \u001b[1m\u001b[32m/content/feature_repo\u001b[0m.\n", "\n" ] } ], "source": [ "!feast init feature_repo" ] }, { "cell_type": "markdown", "metadata": { "id": "OdTASZPvyKCe" }, "source": [ "### Step 2a: Inspecting the feature repository\n", "\n", "Let's take a look at the demo repo itself. It breaks down into\n", "\n", "\n", "* `data/` contains raw demo parquet data\n", "* `example_repo.py` contains demo feature definitions\n", "* `feature_store.yaml` contains a demo setup configuring where data sources are\n", "* `test_workflow.py` showcases how to run all key Feast commands, including defining, retrieving, and pushing features.\n", " * You can run this with `python test_workflow.py`.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9jXuzt4ovzA3", "outputId": "9e326892-f0cc-4d86-d0b2-f33f822f83a9" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/content/feature_repo\n", "README.md feature_store.yaml\n", "__init__.py example_repo.py test_workflow.py\n", "\n", "./data:\n", "driver_stats.parquet\n" ] } ], "source": [ "%cd feature_repo/feature_repo\n", "!ls -R" ] }, { "cell_type": "markdown", "metadata": { "id": "MJk_WNsbeUP6" }, "source": [ "### Step 2b: Inspecting the project configuration\n", "Let's inspect the setup of the project in `feature_store.yaml`. \n", "\n", "The key line defining the overall architecture of the feature store is the **provider**. \n", "\n", "The provider value sets default offline and online stores. \n", "* The offline store provides the compute layer to process historical data (for generating training data & feature \n", " values for serving). \n", "* The online store is a low latency store of the latest feature values (for powering real-time inference).\n", "\n", "Valid values for `provider` in `feature_store.yaml` are:\n", "\n", "* local: use file source with SQLite/Redis\n", "* gcp: use BigQuery/Snowflake with Google Cloud Datastore/Redis\n", "* aws: use Redshift/Snowflake with DynamoDB/Redis\n", "\n", "Note that there are many other offline / online stores Feast works with, including Azure, Hive, Trino, and PostgreSQL via community plugins. See https://docs.feast.dev/roadmap for all supported connectors.\n", "\n", "A custom setup can also be made by following [Customizing Feast](https://docs.feast.dev/v/master/how-to-guides/customizing-feast)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9_YJ--uYdtcP", "outputId": "af56a8da-9ca2-4dd9-f73c-a60dd3e1613a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[94mproject\u001b[39;49;00m:\u001b[37m \u001b[39;49;00mfeature_repo\u001b[37m\u001b[39;49;00m\n", "\u001b[37m# By default, the registry is a file (but can be turned into a more scalable SQL-backed registry)\u001b[39;49;00m\u001b[37m\u001b[39;49;00m\n", "\u001b[94mregistry\u001b[39;49;00m:\u001b[37m \u001b[39;49;00mdata/registry.db\u001b[37m\u001b[39;49;00m\n", "\u001b[37m# The provider primarily specifies default offline / online stores & storing the registry in a given cloud\u001b[39;49;00m\u001b[37m\u001b[39;49;00m\n", "\u001b[94mprovider\u001b[39;49;00m:\u001b[37m \u001b[39;49;00mlocal\u001b[37m\u001b[39;49;00m\n", "\u001b[94monline_store\u001b[39;49;00m:\u001b[37m\u001b[39;49;00m\n", "\u001b[37m \u001b[39;49;00m\u001b[94mpath\u001b[39;49;00m:\u001b[37m \u001b[39;49;00mdata/online_store.db\u001b[37m\u001b[39;49;00m\n", "\u001b[94mentity_key_serialization_version\u001b[39;49;00m:\u001b[37m \u001b[39;49;00m2\u001b[37m\u001b[39;49;00m\n" ] } ], "source": [ "!pygmentize feature_store.yaml" ] }, { "cell_type": "markdown", "metadata": { "id": "FnMlk4zshywp" }, "source": [ "### Inspecting the raw data\n", "\n", "The raw feature data we have in this demo is stored in a local parquet file. The dataset captures hourly stats of a driver in a ride-sharing app." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 423 }, "id": "sIF2lO59dwzi", "outputId": "8931930b-b32f-43e1-d45b-de230489c7b8" }, "outputs": [ { "data": { "text/html": [ "
| \n", " | event_timestamp | \n", "driver_id | \n", "conv_rate | \n", "acc_rate | \n", "avg_daily_trips | \n", "created | \n", "
|---|---|---|---|---|---|---|
| 0 | \n", "2022-07-24 14:00:00+00:00 | \n", "1005 | \n", "0.423913 | \n", "0.082831 | \n", "201 | \n", "2022-08-08 14:14:11.200 | \n", "
| 1 | \n", "2022-07-24 15:00:00+00:00 | \n", "1005 | \n", "0.507126 | \n", "0.427470 | \n", "690 | \n", "2022-08-08 14:14:11.200 | \n", "
| 2 | \n", "2022-07-24 16:00:00+00:00 | \n", "1005 | \n", "0.139810 | \n", "0.129743 | \n", "845 | \n", "2022-08-08 14:14:11.200 | \n", "
| 3 | \n", "2022-07-24 17:00:00+00:00 | \n", "1005 | \n", "0.383574 | \n", "0.071728 | \n", "839 | \n", "2022-08-08 14:14:11.200 | \n", "
| 4 | \n", "2022-07-24 18:00:00+00:00 | \n", "1005 | \n", "0.959131 | \n", "0.440051 | \n", "2 | \n", "2022-08-08 14:14:11.200 | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
| 1802 | \n", "2022-08-08 12:00:00+00:00 | \n", "1001 | \n", "0.994883 | \n", "0.020145 | \n", "650 | \n", "2022-08-08 14:14:11.200 | \n", "
| 1803 | \n", "2022-08-08 13:00:00+00:00 | \n", "1001 | \n", "0.663844 | \n", "0.864639 | \n", "359 | \n", "2022-08-08 14:14:11.200 | \n", "
| 1804 | \n", "2021-04-12 07:00:00+00:00 | \n", "1001 | \n", "0.068696 | \n", "0.624977 | \n", "624 | \n", "2022-08-08 14:14:11.200 | \n", "
| 1805 | \n", "2022-08-01 02:00:00+00:00 | \n", "1003 | \n", "0.980869 | \n", "0.244420 | \n", "790 | \n", "2022-08-08 14:14:11.200 | \n", "
| 1806 | \n", "2022-08-01 02:00:00+00:00 | \n", "1003 | \n", "0.980869 | \n", "0.244420 | \n", "790 | \n", "2022-08-08 14:14:11.200 | \n", "
1807 rows × 6 columns
\n", "