Home Marketing Predictions Artikel 4
Teil 4 Generative AI

A/B-Testing & Uplift-Modelling

Bild: KI-generiert mit Beuys


A/B-Testing & Uplift-Modelling

Statistische Tests beantworten „Ist der Unterschied real?", Uplift-Modelle beantworten „Bei wem wirkt die Kampagne?". Zwei verwandte, aber grundsätzlich verschiedene Fragen – mit zwei verschiedenen Methoden-Familien und einem from-scratch-Two-Model-Beispiel inklusive Qini-Curve.


1. Zwei verschiedene Fragen, zwei verschiedene Werkzeuge

Wer nur Conversions vorhersagt, optimiert die falsche Größe: ein Conversion-Modell schickt Coupons an Menschen, die ohnehin gekauft hätten. Die saubere Trennung sieht so aus:

Frage Werkzeug Liefert
Ist die durchschnittliche Wirkung der Kampagne signifikant? A/B-Test, Bootstrap-CI Eine Zahl: durchschnittlicher Treatment-Effekt (ATE)
Bei welchen Individuen wirkt die Kampagne? Uplift-Modelling Individuelle Schätzung des Treatment-Effekts (CATE)

Beide Methoden teilen denselben Datentyp: eine randomisierte Stichprobe mit Treatment-Indikator T ∈ {0,1} und Outcome Y ∈ {0,1}.

2. A/B-Tests: Was wirklich signifikant ist

❌ Pitfall: Peeking

Mehrfaches Auswerten während der Laufzeit inflationiert die False-Positive-Rate. Bei 10 Zwischenchecks à α=5 % liegt die Wahrscheinlichkeit eines falschen Treffers schnell bei >30 %.

✅ Saubere Praxis

Quick-Check: Was bedeutet 'Peeking' in A/B-Tests und warum ist es problematisch?

3. Uplift-Modelling: Die vier Segmente

Jedes Individuum lässt sich konzeptuell einem von vier Segmenten zuordnen:

Segment Y(T=0) Y(T=1) Kampagnen-Strategie
Persuadables 0 1 Anzeigen – hier liegt der ROI
Sure Things 1 1 Nicht anzeigen – verschwendetes Budget
Lost Causes 0 0 Nicht anzeigen – chancenlos
Do-Not-Disturb 1 0 Nicht anzeigen – schädlich!

Das Kernproblem: Für jedes Individuum beobachten wir nur eine der beiden Welten (Y(T=0) oder Y(T=1)), nie beide. Uplift-Modelle schätzen den individuellen Treatment-Effekt aus randomisierten Daten.

Quick-Check: Welche Personengruppe wollen wir mit Uplift-Modellen primär ansprechen?

4. Two-Model-Approach: From-scratch mit Qini-Curve

Der einfachste Uplift-Ansatz trainiert zwei separate Modelle (eines auf der Treatment-, eines auf der Control-Gruppe) und nimmt die Differenz der Wahrscheinlichkeiten als Uplift-Schätzung. Zur Evaluation wird die Qini-Curve verwendet: Sie trägt die kumulierte inkrementelle Conversion gegen den Anteil der gerankten Population auf.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

rng = np.random.default_rng(2)
n = 6000
X = rng.normal(size=(n, 4))
T = rng.integers(0, 2, n)                                      # randomisiertes Treatment

# Heterogener Treatment-Effekt: Wirkung nur bei X[:,1] > 0
p_ctrl = 1 / (1 + np.exp(-(0.5 * X[:, 0] - 1.0)))             # Basisrate ~ 25 %
uplift_true = 0.25 * (X[:, 1] > 0)                            # echter CATE
p = np.clip(p_ctrl + T * uplift_true, 0, 1)
y = (rng.random(n) < p).astype(int)

X_tr, X_te, T_tr, T_te, y_tr, y_te = train_test_split(
    X, T, y, test_size=0.4, random_state=0,
)

# Two-Model-Approach: separate Modelle für T=0 und T=1
m_t = LogisticRegression(max_iter=1000).fit(X_tr[T_tr == 1], y_tr[T_tr == 1])
m_c = LogisticRegression(max_iter=1000).fit(X_tr[T_tr == 0], y_tr[T_tr == 0])
uplift_hat = m_t.predict_proba(X_te)[:, 1] - m_c.predict_proba(X_te)[:, 1]

print(f"Geschätztes Uplift-Mittel: {uplift_hat.mean():+.3f}")
print(f"Echtes Uplift-Mittel im Test: {(0.25 * (X_te[:, 1] > 0)).mean():+.3f}")

# Qini-Curve: Population nach uplift_hat absteigend sortieren,
# kumulative inkrementelle Conversions plotten.
order = np.argsort(-uplift_hat)
y_sorted = y_te[order]
T_sorted = T_te[order]

# Kumulierte Conversions in Treatment und Control, skaliert auf gleiche Größe
cum_t = np.cumsum(y_sorted * (T_sorted == 1))
cum_c = np.cumsum(y_sorted * (T_sorted == 0))
n_t = np.cumsum(T_sorted == 1).clip(min=1)
n_c = np.cumsum(T_sorted == 0).clip(min=1)
# Inkrement = T-Rate − C-Rate, skaliert auf die Population
incremental = cum_t - cum_c * (n_t / n_c)
share = np.arange(1, len(y_te) + 1) / len(y_te)

# Zufalls-Baseline (gerade Linie zum Endwert)
random_line = share * incremental[-1]

fig, ax = plt.subplots(figsize=(7, 5))
ax.plot(share, incremental, label="Two-Model Uplift")
ax.plot(share, random_line, "k--", label="Zufalls-Targeting")
ax.set_xlabel("Anteil der adressierten Population (sortiert nach Uplift-Score)")
ax.set_ylabel("Kumulierte inkrementelle Conversions")
ax.set_title("Qini-Curve")
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()

# Qini-Score: Fläche zwischen Modell-Kurve und Zufalls-Linie
qini_score = np.trapz(incremental - random_line, share)
print(f"Qini-Score (höher = besser): {qini_score:.3f}")

Die Qini-Curve liegt für ein nützliches Uplift-Modell oberhalb der Zufalls-Linie. Je größer die Fläche zwischen den Kurven (Qini-Score), desto besser unterscheidet das Modell Persuadables von den übrigen Segmenten. Der Knick am Ende ist normal: Wenn man auch noch die unteren 30–40 % der Population adressiert, sinkt der inkrementelle Effekt – dort sitzen Sure Things, Lost Causes und Do-Not-Disturb.

Quick-Check: Was zeigt die Qini-Curve?

5. Diskussion: Wann Two-Model und wann etwas Besseres?

Two-Model ist die einfachste, aber nicht die effizienteste Methode:

Praktische Heuristik: Beim ersten Uplift-Projekt mit Two-Model starten, Qini-Curve gegen Zufall validieren. Erst wenn der ROI das rechtfertigt, in komplexere Schätzer investieren.

6. Abschluss-Quiz

Worin unterscheidet sich Uplift-Modelling von gewöhnlicher Response-Prediction?

Welche Voraussetzung müssen die Trainingsdaten für Uplift-Modelle erfüllen?

Welches Segment ist für die Kampagne *schädlich*, wenn es angesprochen wird?

7. Weiterführend

Quellen

📄 Als PDF