Calcul des fonctionnalités à la demande

Dans Azure Databricks, les fonctionnalités calculées à la demande sont calculées lors de l’inférence à l’aide de fonctions Python définies par l’utilisateur (UDF). Utilisez-les lorsque les valeurs de fonctionnalité ne sont pas connues à l’avance et dépendent des entrées au moment de la demande.

Pour utiliser les fonctionnalités à la demande, votre espace de travail doit être activé pour Unity Catalog et vous devez utiliser Databricks Runtime 13.3 LTS ML ou une version ultérieure.

Que sont les fonctionnalités à la demande ?

Le terme « à la demande » fait référence à des caractéristiques dont les valeurs ne sont pas connues à l’avance, mais qui sont calculées au moment de l’inférence. Dans Azure Databricks, vous utilisez des fonctions Python définies par l’utilisateur (UDF) pour spécifier comment calculer les caractéristiques à la demande. Ces fonctions sont régies par Unity Catalog et sont découvrables dans Catalog Explorer.

Flux de travail

Pour calculer une caractéristique à la demande, spécifiez une fonction Python définie par l’utilisateur (UDF) qui décrit comment calculer les valeurs de la caractéristique.

  • Pendant l’entraînement, fournissez cette fonction et ses liaisons d’entrée au paramètre feature_lookups de l’API create_training_set.
  • Vous devez enregistrer le modèle entraîné à l’aide de la méthode Feature Store log_model. De cette façon, le modèle évalue automatiquement les caractéristiques à la demande quand il est utilisé pour l’inférence.
  • Pour le scoring par lots, l’API score_batch calcule et retourne automatiquement toutes les valeurs de caractéristiques, y compris celles à la demande.
  • Lorsque vous servez un modèle avec Model Serving, le modèle utilise automatiquement l’UDF Python pour calculer les fonctionnalités à la demande pour chaque demande de scoring.

Créer une fonction UDF Python

Vous pouvez créer une fonction UDF Python à l’aide de code SQL ou Python. Les exemples suivants créent une fonction UDF Python dans le catalogue main et le schéma default.

Python

Pour utiliser Python, vous devez d’abord installer le databricks-sdk[openai] package. Utilisez %pip install ce qui suit :

%pip install unitycatalog-ai[databricks]
dbutils.library.restartPython()

Ensuite, utilisez du code similaire à ce qui suit pour créer une fonction UDF Python :

from unitycatalog.ai.core.databricks import DatabricksFunctionClient

client = DatabricksFunctionClient()

CATALOG = "main"
SCHEMA = "default"

def add_numbers(number_1: float, number_2: float) -> float:
  """
  A function that accepts two floating point numbers, adds them,
  and returns the resulting sum as a float.

  Args:
      number_1 (float): The first of the two numbers to add.
      number_2 (float): The second of the two numbers to add.

  Returns:
      float: The sum of the two input numbers.
  """
  return number_1 + number_2

function_info = client.create_python_function(
  func=add_numbers,
  catalog=CATALOG,
  schema=SCHEMA,
  replace=True
)

Databricks SQL - Outil de requête de base de données

Le code suivant montre comment utiliser Databricks SQL pour créer une fonction UDF Python :

%sql
CREATE OR REPLACE FUNCTION main.default.add_numbers(x INT, y INT)
RETURNS INT
LANGUAGE PYTHON
COMMENT 'add two numbers'
AS $$
def add_numbers(n1: int, n2: int) -> int:
  return n1 + n2

return add_numbers(x, y)
$$

Après avoir exécuté le code, vous pouvez parcourir l’espace de noms à trois niveaux dans Catalog Explorer pour voir la définition de la fonction :

fonction dans Catalog Explorer

Pour plus d’informations sur la création de fonctions UDF Python, consultez Inscrire une fonction UDF Python auprès d’Unity Catalog et le manuel du langage SQL.

Comment gérer les valeurs de fonctionnalité manquantes

Lorsqu’une fonction UDF Python dépend du résultat d’une fonctionnalité Lookup, la valeur retournée si la clé de recherche demandée est introuvable dépend de l’environnement. Lors de l’utilisation de score_batch, la valeur retournée est None. Lors de l’utilisation du service en ligne, la valeur retournée est float("nan").

Le code suivant est un exemple de la manière de traiter les deux cas.

%sql
CREATE OR REPLACE FUNCTION square(x INT)
RETURNS INT
LANGUAGE PYTHON AS
$$
import numpy as np
if x is None or np.isnan(x):
  return 0
return x * x
$$

Entraîner un modèle à l’aide de variables calculées à la volée

Pour entraîner le modèle, utilisez une FeatureFunction, qui est transmise à l’API create_training_set via le paramètre feature_lookups.

L’exemple de code suivant utilise la fonction UDF Python main.default.example_feature définie dans la section précédente.

# Install databricks-feature-engineering first with:
# %pip install databricks-feature-engineering
# dbutils.library.restartPython()

from databricks.feature_engineering import FeatureEngineeringClient
from databricks.feature_engineering import FeatureFunction, FeatureLookup
from sklearn import linear_model

fe = FeatureEngineeringClient()

features = [
  # The feature 'on_demand_feature' is computed as the sum of the input value 'new_source_input'
  # and the pre-materialized feature 'materialized_feature_value'.
  # - 'new_source_input' must be included in base_df and also provided at inference time.
  #   - For batch inference, it must be included in the DataFrame passed to 'FeatureEngineeringClient.score_batch'.
  #   - For real-time inference, it must be included in the request.
  # - 'materialized_feature_value' is looked up from a feature table.

  FeatureFunction(
      udf_name="main.default.example_feature",    # UDF must be in Unity Catalog so uses a three-level namespace
      input_bindings={
        "x": "new_source_input",
        "y": "materialized_feature_value"
      },
      output_name="on_demand_feature",
  ),
  # retrieve the prematerialized feature
  FeatureLookup(
    table_name = 'main.default.table',
    feature_names = ['materialized_feature_value'],
    lookup_key = 'id'
  )
]

# base_df includes the columns 'id', 'new_source_input', and 'label'
training_set = fe.create_training_set(
  df=base_df,
  feature_lookups=features,
  label='label',
  exclude_columns=['id', 'new_source_input', 'materialized_feature_value']     # drop the columns not used for training
)

# The training set contains the columns 'on_demand_feature' and 'label'.
training_df = training_set.load_df().toPandas()

# training_df columns ['materialized_feature_value', 'label']
X_train = training_df.drop(['label'], axis=1)
y_train = training_df.label

model = linear_model.LinearRegression().fit(X_train, y_train)

Spécifier les valeurs par défaut

Pour spécifier des valeurs par défaut pour les fonctionnalités, utilisez le default_values paramètre dans le FeatureLookup.

FeatureLookup(
  table_name = 'main.default.table',
  feature_names = ['materialized_feature_value'],
  lookup_key = 'id',
  default_values={
    "materialized_feature_value": 0
  }
)

Si les colonnes de caractéristiques sont renommées à l’aide du rename_outputs paramètre, default_values doivent utiliser les noms de fonctionnalités renommés.

FeatureLookup(
  table_name = 'main.default.table',
  feature_names = ['materialized_feature_value'],
  lookup_key = 'id',
  rename_outputs={"materialized_feature_value": "feature_value"},
  default_values={
    "feature_value": 0
  }
)

Journaliser le modèle et l’enregistrer dans Unity Catalog

Les modèles empaquetés avec des métadonnées de caractéristique peuvent être inscrits auprès d’Unity Catalog. Les tables de caractéristiques utilisées pour créer le modèle doivent être stockées dans Unity Catalog.

Pour vous assurer que le modèle évalue automatiquement les caractéristiques à la demande quand il est utilisé pour l’inférence, vous devez définir l’URI du registre, puis consigner le modèle, comme suit :

import mlflow
mlflow.set_registry_uri("databricks-uc")

fe.log_model(
    model=model,
    artifact_path="main.default.model",
    flavor=mlflow.sklearn,
    training_set=training_set,
    registered_model_name="main.default.recommender_model"
)

Si la fonction UDF Python qui définit les caractéristiques à la demande importe des packages Python, vous devez spécifier ces packages à l’aide de l’argument extra_pip_requirements. Par exemple :

import mlflow
mlflow.set_registry_uri("databricks-uc")

fe.log_model(
    model=model,
    artifact_path="model",
    flavor=mlflow.sklearn,
    training_set=training_set,
    registered_model_name="main.default.recommender_model",
    extra_pip_requirements=["scikit-learn==1.20.3"]
)

Limites

  • Pour les points de terminaison de Feature Serving, tous les types de données pris en charge par Feature Store sont pris en charge comme types de sortie des fonctions de variables, à l’exception de ArrayType, MapType et StructType. StructType n’est pas non plus pris en charge comme type d’entrée pour une fonction de fonctionnalité.
  • Pour les versions de databricks-feature-engineering antérieures à 0.14.0, les autorisations de catalogue Unity suivantes sont requises pour utiliser une fonction définie par l’utilisateur (UDF) afin de créer un ensemble d'entraînement ou de créer un point de terminaison de mise en service.
    • Privilège USE CATALOG sur le catalogue system
    • USE SCHEMA privilège sur le system.information_schema schéma

Exemples de notebooks : fonctionnalités à la demande

Le notebook suivant présente un exemple montrant comment effectuer l’apprentissage et le scoring d’un modèle qui utilise une caractéristique à la demande.

Notebook de base de démonstration des caractéristiques à la demande

Obtenir le notebook

Le notebook suivant présente un exemple de modèle de recommandation de restaurant. L’emplacement du restaurant est recherché à partir d’une table Databricks en ligne. L’emplacement actuel de l’utilisateur est envoyé dans le cadre de la demande de scoring. Le modèle utilise une caractéristique à la demande pour calculer la distance en temps réel entre l’utilisateur et le restaurant. Cette distance est ensuite utilisée comme entrée du modèle.

Fonctionnalités de recommandation de restaurants à la demande à l’aide d’un notebook de démonstration sur les tables en ligne

Obtenir le notebook