Engenharia de Features

Um grande gargalo na produção ML é treinamento-serving sketcha discrepância entre como os recursos são calculados durante o treinamento do modelo versus como eles são consultados no momento da inferência. Para resolver isso, usamos Feast como nosso Feature Store.

Conceitos chave Feast neste projeto

  1. Entidade: A chave primária. Aqui, o pickup_location_id representa uma zona de táxi.
  2. Fonte de dados: A fonte da verdade para características. Definimos uma fonte de arquivo em lote apontando para o nosso arquivo de parquet off-line: data/processed/hourly_demand_features.parquet.
  3. Característica Ver: Mapas recursos para entidades e define metadados (esquema, TTL e fonte de dados).

Offline & Lojas Online

Feast lida com duas bases de dados sob o capô:

  • Loja offline (DuckDB): Otimizado para consultas analíticas. Usado durante o treinamento para executar Point-in-Time se junta e recuperar registros históricos de recursos.
  • Loja Online (SQLite): Otimizado para pesquisas rápidas e de baixa latência. Recursos materializados são carregados aqui para previsões em tempo real.
Cell 02Code
import ipywidgets as widgets
import matplotlib.pyplot as plt
import pandas as pd

# Load feature data from the processed parquet destination
features_path = "../data/processed/hourly_demand_features.parquet"
df = pd.read_parquet(features_path)
df["event_timestamp"] = pd.to_datetime(df["event_timestamp"])
print(f"Loaded {len(df)} feature rows. Columns:")
df.head()
Stream
Loaded 71253 feature rows. Columns:
pickup_location_id event_timestamp pickup_count hour day_of_week is_weekend month
0 1 2022-10-24 17:00:00 1 17 0 False 10
1 1 2022-10-25 03:00:00 1 3 1 False 10
2 1 2023-01-01 05:00:00 1 5 6 True 1
3 1 2023-01-01 08:00:00 1 8 6 True 1
4 1 2023-01-01 11:00:00 1 11 6 True 1

Visualize as captações de táxi ao longo do tempo

Vamos olhar para a curva de demanda média ao longo de horas do dia.

Cell 04Code
hourly_avg = df.groupby("hour")["pickup_count"].mean()

plt.figure(figsize=(10, 5))
plt.plot(
    hourly_avg.index,
    hourly_avg.values,
    marker="o",
    color="#3182bd",
    linewidth=2,
)
plt.title("Average NYC Taxi Pickups by Hour of Day", fontsize=14, pad=15)
plt.xlabel("Hour of Day", fontsize=12)
plt.ylabel("Average Pickups", fontsize=12)
plt.grid(True, linestyle="--", alpha=0.6)
plt.xticks(range(24))
plt.show()
Notebook output image
ChartNotebook output image

Explorador de Zona Interativa

Use este widget interativo para inspecionar recursos para uma ID de localização específica da zona de coleta.

Cell 06Code
unique_zones = sorted(df["pickup_location_id"].unique())
zone_select = widgets.Dropdown(
    options=[str(z) for z in unique_zones[:30]],
    value=str(unique_zones[0]),
    description="Location ID:",
    disabled=False,
)


def plot_zone_demand(zone_id):
    zone_df = df[df["pickup_location_id"] == zone_id].sort_values(
        "event_timestamp"
    )
    if zone_df.empty:
        print("No records found for this location.")
        return

    # Plot pickups for the first week of the month
    sample_df = zone_df.head(168)  # 24 * 7
    plt.figure(figsize=(12, 4))
    plt.plot(
        sample_df["event_timestamp"],
        sample_df["pickup_count"],
        color="#e6550d",
        label=f"Zone {zone_id}",
    )
    plt.title(
        f"Hourly Taxi Pickups for Location ID {zone_id} (First 7 Days)",
        fontsize=13,
    )
    plt.xlabel("Timestamp", fontsize=11)
    plt.ylabel("Pickups", fontsize=11)
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()


# Precompute all outputs for the 30 zones client-side
plot_outputs = []
for zone_id in unique_zones[:30]:
    out = widgets.Output()
    with out:
        plot_zone_demand(zone_id)
    plot_outputs.append(out)

stack = widgets.Stack(children=plot_outputs, selected_index=0)
widgets.jslink((zone_select, "index"), (stack, "selected_index"))

widgets.VBox([zone_select, stack])