ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Feature engineering pour le ML dans dbt

Comment structurer les modèles intermédiaires dbt comme tables de features ML — y compris les agrégations par fenêtre temporelle, les ensembles de features séparés par domaine, et leur assemblage en dataset d'entraînement étiqueté.

Planté
dbtbigquerydata modelingdata engineeringai

Un modèle de machine learning n’est aussi bon que ses entrées. Le terme désignant la préparation de ces entrées est le feature engineering — transformer des données d’événements bruts et des tables d’entités en une matrice structurée où chaque ligne est une entité (un lead, un utilisateur, un client) et chaque colonne est un signal dont le modèle peut apprendre.

Dans un projet dbt, le feature engineering se situe dans la couche intermédiaire. Vous construisez un modèle intermédiaire par domaine de features, puis vous les assemblez dans un dataset d’entraînement au niveau mart avec l’étiquette de résultat attachée.

Le pattern de séparation par domaine

N’essayez pas de mettre toutes les features dans un seul grand modèle intermédiaire. Séparez par domaine de features :

  • int__lead__behavioral_features.sql — événements web, engagement email, interactions produit
  • int__lead__demographic_features.sql — titre de poste, ancienneté, département
  • int__lead__firmographic_features.sql — taille de l’entreprise, secteur d’activité, chiffre d’affaires
  • int__lead__temporal_features.sql — récence, fréquence, signaux au niveau session

Chaque modèle a une ligne par entité (lead), une colonne par feature. Ils se joignent proprement parce qu’ils partagent le même grain. Ce pattern correspond directement à ce qu’attendent les frameworks ML en entrée : une table large et plate sans imbrication.

Maintenir les features séparées par domaine rend aussi la maintenance gérable. Lorsque l’équipe marketing demande « peut-on ajouter une feature pour la participation aux webinaires ? », vous ajoutez une colonne dans int__lead__behavioral_features.sql et réentraînez. Les autres modèles de features ne sont pas touchés.

Agrégations par fenêtre temporelle

Les comptages bruts d’événements sont une feature faible. Un lead avec 20 pages vues pourrait être quelqu’un qui était très engagé il y a six mois et qui s’est depuis refroidi, ou quelqu’un qui a parcouru votre site cette semaine. Le modèle ne peut pas faire la différence à partir d’un seul comptage.

Les fenêtres temporelles résolvent ce problème. Pour les features comportementales, calculez la même métrique à plusieurs horizons temporels :

-- models/intermediate/int__lead__behavioral_features.sql
SELECT
lead_id,
COUNT(CASE
WHEN event_name = 'page_view'
AND occurred_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY) THEN 1
END) AS lead__pageviews_last_7d,
COUNT(CASE
WHEN event_name = 'page_view'
AND occurred_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY) THEN 1
END) AS lead__pageviews_last_30d,
COUNT(CASE
WHEN event_name = 'page_view'
AND occurred_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY) THEN 1
END) AS lead__pageviews_last_90d,
COUNT(CASE
WHEN event_name = 'form_submit' THEN 1
END) AS lead__form_submissions,
DATE_DIFF(
CURRENT_DATE(), MAX(occurred_at), DAY
) AS lead__days_since_last_activity
FROM {{ ref('int__lead__events') }}
GROUP BY lead_id

Trois fenêtres (7, 30 et 90 jours) donnent au modèle suffisamment d’informations pour détecter la vélocité. Un lead avec pageviews_last_7d = 15 et pageviews_last_30d = 16 était presque entièrement inactif pendant des semaines et est soudainement devenu actif — signal d’intention fort. Un lead avec pageviews_last_7d = 2 et pageviews_last_90d = 45 était actif il y a des semaines et s’est depuis calmé — urgence moindre.

lead__days_since_last_activity est particulièrement utile comme feature continue. Elle capture la récence sans nécessiter de prédéfinir des constantes de décroissance, laissant le modèle apprendre la pondération appropriée de la récence à partir des données.

Assemblage en dataset d’entraînement

Le dataset d’entraînement au niveau mart assemble tous les modèles de features avec l’étiquette de conversion :

-- models/marts/mrt__sales__lead_training_dataset.sql
SELECT
l.lead_id,
-- Features comportementales
b.lead__pageviews_last_7d,
b.lead__pageviews_last_30d,
b.lead__pageviews_last_90d,
b.lead__form_submissions,
b.lead__days_since_last_activity,
-- Features démographiques
d.lead__job_seniority,
d.lead__job_title,
-- Features firmographiques
f.lead__company_size,
f.lead__industry,
f.lead__annual_revenue,
-- Étiquette
CASE WHEN l.lead__status = 'Converted' THEN 1 ELSE 0 END AS lead__is_converted
FROM {{ ref('base__crm__leads') }} l
LEFT JOIN {{ ref('int__lead__behavioral_features') }} b USING (lead_id)
LEFT JOIN {{ ref('int__lead__demographic_features') }} d USING (lead_id)
LEFT JOIN {{ ref('int__lead__firmographic_features') }} f USING (lead_id)

Le LEFT JOIN est intentionnel. Certains leads auront des données incomplètes — pas d’enrichissement firmographique, pas d’historique comportemental. Vous voulez ces leads dans le dataset d’entraînement avec des valeurs NULL pour que le modèle apprenne à gérer les données manquantes. Filtrer pour ne conserver que les enregistrements complets biaise votre données d’entraînement vers les leads bien enrichis et dégrade la qualité des prédictions pour les leads avec des informations partielles.

Cette structure de dataset est l’entrée de CREATE MODEL dans BigQuery ML. Le même modèle, avec toutes les features présentes, est utilisé pour la prédiction sur les leads actuels (non étiquetés).

La distinction entraînement / scoring

Le dataset d’entraînement inclut lead__is_converted comme colonne d’étiquette. La table de features de scoring (utilisée pour les leads actifs à scorer) est identique en structure mais n’a pas le résultat de conversion — ces leads n’ont pas encore converti.

En pratique, vous maintenez deux modèles mart parallèles :

  • mrt__sales__lead_training_dataset — leads historiques avec étiquettes de conversion, utilisé pour entraîner le modèle
  • int__lead__scoring_features — leads actifs actuels, utilisé pour générer des prédictions

Ils peuvent partager les mêmes modèles intermédiaires de features parce que la logique de features est identique. La différence réside uniquement dans la table de base (leads historiques convertis/non convertis vs leads actifs actuels) et dans la présence ou non de la colonne d’étiquette.

Ce qui fait une bonne feature ML

Tous les signaux ne font pas de bonnes features ML.

Bonnes features :

  • Varient de façon significative dans la population (forte cardinalité pour les continues, valeurs multiples pour les catégorielles)
  • Sont disponibles au moment de la prédiction (les features calculées à partir d’événements futurs ne fonctionnent pas)
  • Sont stables dans le temps (une feature qui signifie quelque chose de différent en 2024 qu’en 2022 dégrade les performances du modèle sur les données historiques)
  • Sont causalement plausibles (les vues de la page tarifaire prédisent la conversion parce que les personnes qui vous évaluent sérieusement visitent la page tarifaire — pas seulement parce qu’elles sont corrélées dans les données d’entraînement)

Features faibles à éviter :

  • Les identifiants uniques (lead_id, email) — ils surapprentissent sur les données d’entraînement sans généralisation
  • Les features parfaitement colinéaires (si vous incluez à la fois lead__pageviews_last_30d et lead__pageviews_last_90d, le comptage sur 30 jours est un sous-ensemble du comptage sur 90 jours et ils sont fortement corrélés)
  • Les signaux post-hoc (features qui changent à cause de l’événement de conversion, pas avant — par exemple, « contacté par les ventes » augmente probablement après qu’une démo est réservée)

La sortie ML.GLOBAL_EXPLAIN après l’entraînement indique quelles features le modèle a trouvées prédictives. Utilisez cette sortie pour élaguer les features faibles et concentrer les efforts de collecte de données sur les features qui améliorent réellement la précision. Consultez BigQuery ML for Lead Scoring pour le workflow d’évaluation complet.

Connexion à l’architecture dbt plus large

Les tables de features suivent les mêmes conventions d’architecture à trois couches que tout le reste du projet. Les modèles de base nettoient et renomment les données CRM. Les modèles intermédiaires joignent et agrègent en features. Les modèles mart assemblent les datasets d’entraînement et de scoring.

Le pipeline ML n’est pas un système séparé — c’est une extension de la modélisation en entrepôt qui se fait déjà. Les features comportementales existent souvent déjà sous une forme quelconque dans des modèles intermédiaires alimentant d’autres marts. Les exposer explicitement comme modèle int__lead__behavioral_features.sql les rend réutilisables pour le scoring sans dupliquer la logique d’agrégation.

Pour l’alternative basée sur des règles qui utilise ces mêmes features sans ML, consultez Rule-Based Lead Scoring in dbt.