BigQuery ML su GA4: prevedere la churn dai dati grezzi

·5 min lettura
BigQuery ML su GA4: prevedere la churn dai dati grezzi
In questo articolo

Su un SaaS B2B con cui ho lavorato, il marketing spendeva 42% del budget retention su utenti che sarebbero rimasti comunque. Lo abbiamo scoperto solo dopo aver costruito un modello di churn prediction in BigQuery ML alimentato dai dati grezzi di GA4. Il modello segmentava gli utenti in quattro fasce: chi era sicuramente perso (non disturbare), chi era già fedele (non spendere), chi era a rischio (target attivo), chi era incerto (test A/B). Riallocare la spesa sulla fascia 3 ha alzato il retention rate del 9% in un trimestre.

Non serve un team di data scientist per replicare questo. BigQuery ML permette di scrivere modelli SQL che girano nella stessa piattaforma dove vivono già i dati GA4. Vediamo come.

Perché BigQuery ML invece di Python/sklearn

Tre motivi pratici, non religiosi:

  1. I dati GA4 sono già in BigQuery, quindi salti l'export, il pre-processing in pandas e i problemi di campionamento
  2. BigQuery ML scala automaticamente: tabelle da centinaia di milioni di righe vengono trainate in minuti, senza setup di cluster
  3. Il modello vive vicino alla activation: una volta trainato, lo riusi via SQL per scorare nuovi utenti e mandarli direttamente nelle audience di Google Ads

Lo svantaggio: meno controllo fine sull'hyperparameter tuning rispetto a XGBoost in Python. Per i casi d'uso marketing tipici (churn binaria, valore previsto, propensity score) BQML basta e avanza.

Definire la churn

Prima di scrivere SQL, decidi cosa significa "churn" nel tuo business. Non è la stessa cosa per tutti:

Tipo di businessDefinizione tipica
SaaS B2BNessun login per 30 giorni dopo la prova gratuita
Ecommerce ad alto frequenzaNessun acquisto per 60 giorni dopo l'ultimo
Editoria/contenutoNessuna visita per 21 giorni dopo l'iscrizione newsletter
MarketplaceNessuna ricerca per 14 giorni

Sbagliare la definizione di churn vanifica il modello. Concorda con il business prima di scrivere una riga di SQL.

Feature engineering su GA4 raw

Il dataset GA4 in BigQuery ha una riga per evento. Per addestrare un modello servono feature aggregate per utente. Tipico set per un SaaS:

CREATE OR REPLACE TABLE `prog.ml.user_features_202604` AS
WITH base AS (
  SELECT
    user_pseudo_id,
    DATE(TIMESTAMP_MICROS(event_timestamp)) AS event_date,
    event_name,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'session_duration') AS session_duration
  FROM `prog.analytics_XXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260301' AND '20260430'
)
SELECT
  user_pseudo_id,
  COUNT(DISTINCT event_date) AS active_days_60d,
  COUNTIF(event_name = 'login') AS login_count_60d,
  COUNTIF(event_name = 'feature_used') AS feature_used_count_60d,
  COUNT(DISTINCT page) AS unique_pages_60d,
  DATE_DIFF(CURRENT_DATE(), MAX(event_date), DAY) AS days_since_last_event,
  AVG(session_duration) AS avg_session_duration,
  COUNTIF(event_name = 'error') AS error_count_60d
FROM base
GROUP BY user_pseudo_id;

Le feature più predittive nei modelli di churn marketing che ho visto sono quasi sempre le stesse:

  • Recency: giorni dall'ultimo evento (la più forte, spesso da sola spiega il 60% della predizione)
  • Frequency: numero di sessioni / login nelle ultime N settimane
  • Engagement depth: pagine uniche, feature uniche, eventi distinti
  • Friction signals: errori, sessioni brevi (sotto i 10 secondi), bounce diretti
  • Lifecycle stage: giorni dalla registrazione (la churn al day 7 è diversa dalla churn al day 90)

Evita feature derivate da rapporto/divisione: spesso introducono outlier che destabilizzano il modello.

Etichettare il target

Prima di trainare serve una colonna churned (1 = sì, 0 = no). Su un SaaS con definizione "30 giorni senza login":

CREATE OR REPLACE TABLE `prog.ml.user_features_labeled` AS
WITH features AS (
  SELECT * FROM `prog.ml.user_features_202604`
),
labels AS (
  SELECT
    user_pseudo_id,
    -- L'utente è "churned" se non ha login nei 30 giorni successivi alla snapshot
    CASE
      WHEN MAX(DATE(TIMESTAMP_MICROS(event_timestamp))) <= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
      THEN 1 ELSE 0
    END AS churned
  FROM `prog.analytics_XXX.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260501' AND '20260530'
    AND event_name = 'login'
  GROUP BY user_pseudo_id
)
SELECT
  f.*,
  COALESCE(l.churned, 1) AS churned  -- chi non compare nel periodo successivo è churned
FROM features f
LEFT JOIN labels l USING (user_pseudo_id);

Importante: la finestra di feature (60 giorni passati) e la finestra di label (30 giorni futuri) non si sovrappongono. Sovrapporle è il bug numero uno che inquina i modelli di churn nei tutorial online.

Trainare il modello

BigQuery ML supporta diversi modelli per classificazione binaria. Per partire uso quasi sempre BOOSTED_TREE_CLASSIFIER: pratico, robusto sui dati sparsi, accetta nativamente sia feature numeriche che categoriche.

CREATE OR REPLACE MODEL `prog.ml.churn_model_v1`
OPTIONS(
  model_type = 'BOOSTED_TREE_CLASSIFIER',
  input_label_cols = ['churned'],
  max_iterations = 50,
  early_stop = TRUE,
  min_rel_progress = 0.01,
  data_split_method = 'AUTO_SPLIT',
  data_split_eval_fraction = 0.2
) AS
SELECT
  active_days_60d,
  login_count_60d,
  feature_used_count_60d,
  unique_pages_60d,
  days_since_last_event,
  avg_session_duration,
  error_count_60d,
  churned
FROM `prog.ml.user_features_labeled`
WHERE user_pseudo_id IS NOT NULL;

Tempo di training su 1.2M righe: ~4 minuti su BigQuery standard. Costo: pochi euro.

Valutare il modello

Sempre prima di mettere un modello in produzione:

SELECT *
FROM ML.EVALUATE(MODEL `prog.ml.churn_model_v1`);

Cosa guardare:

  • AUC ROC sopra 0.75: il modello discrimina bene
  • Precision alta sul positivo (churned=1) se ti interessa non sprecare budget retention su falsi positivi
  • Recall alta se preferisci non lasciarti sfuggire utenti a rischio

Su un modello B2B SaaS reale, AUC = 0.83, precision = 0.71, recall = 0.79: numeri da modello utile per il marketing, non da paper accademico.

Activation: dal modello all'audience Google Ads

Qui il modello produce valore. Scoriamo gli utenti attivi oggi e li mandiamo come audience.

CREATE OR REPLACE TABLE `prog.ml.churn_scores_today` AS
WITH today_features AS (
  -- Stesso CTE di feature engineering ma sui 60 giorni che terminano oggi
  SELECT ...
),
predictions AS (
  SELECT
    user_pseudo_id,
    predicted_churned_probs[OFFSET(0)].prob AS churn_probability
  FROM ML.PREDICT(MODEL `prog.ml.churn_model_v1`, TABLE today_features)
)
SELECT
  user_pseudo_id,
  churn_probability,
  CASE
    WHEN churn_probability > 0.80 THEN 'lost_cause'
    WHEN churn_probability BETWEEN 0.50 AND 0.80 THEN 'at_risk'
    WHEN churn_probability BETWEEN 0.20 AND 0.50 THEN 'uncertain'
    ELSE 'loyal'
  END AS segment
FROM predictions;

Da qui due strade:

  1. Audience GA4 personalizzate via user_pseudo_id: importi la tabella come Custom Dimensions o usi il Measurement Protocol per inviare un evento segment_assigned con il valore. Le audience GA4 si possono attivare in Google Ads.
  2. Activation diretta via Google Ads Customer Match: se hai email associate al user_pseudo_id nel CRM, esporti gli hash e li carichi come lista Customer Match.

La seconda è più granulare ma richiede consenso ad_user_data granted (vedi Enhanced Conversions per il pattern di hashing).

Schedulare il refresh

Una volta che il modello esiste, schedula un refresh giornaliero o settimanale via Scheduled Queries di BigQuery:

-- Query schedulata ogni 24h alle 06:00 Europe/Rome
CREATE OR REPLACE TABLE `prog.ml.churn_scores_today` AS
WITH today_features AS (...)
SELECT ... FROM ML.PREDICT(MODEL `prog.ml.churn_model_v1`, TABLE today_features);

Il modello stesso si retrenara mensile: i pattern di churn cambiano con il prodotto, e un modello fresco performa meglio di uno vecchio anche se ha la stessa architettura.

Limiti che valgono come avvertenza

Il modello impara dal passato, non legge il futuro. Se il prodotto cambia (release importante, nuovo onboarding, prezzo modificato) il modello smette di essere predittivo. Va monitorato e ritarato.

Correlazione, non causazione. Il modello dice "questo utente ha l'80% di probabilità di churn", non "questo utente sta per fare churn perché X". Se vuoi capire il "perché", servono analisi separate sui drop point del funnel, non il modello di score.

ML non sostituisce la qualità del dato GA4. Se il tuo tracking è rotto, il modello impara da spazzatura. Prima della modellazione, sempre un audit tracking GA4. Sempre.

BigQuery costa. Il free tier GA4 export copre solo i siti piccoli. Su volumi enterprise, ogni query ML.PREDICT su 5M righe costa decine di euro: vale la pena materializzare le tabelle intermediate e schedulare con parsimonia.

Prossimi passi

Se hai già GA4 collegato a BigQuery e vuoi partire, ti serve:

  1. Almeno 90 giorni di dati storici puliti (l'audit del tracking è un prerequisito)
  2. Una definizione di churn allineata con il business
  3. Un'idea di cosa farai con i segment, prima di trainare (l'activation guida le feature)

Per il setup BigQuery + GA4 di base, il pattern parte da User journey analytics con GA4 e BigQuery SQL. Per visualizzare i segment ottenuti in una vista business, la copertura è in Dashboard Looker Studio (sezione blended data). E se ti serve aiuto operativo per costruire il modello sul tuo dataset, partiamo dalla consulenza GA4 o, su casi più ampi, dagli agenti AI per l'analytics.

Correlati