Home Marketing Predictions Artikel 2
Teil 2 Generative AI

Engagement-Prediction mit klassischen Modellen

Bild: KI-generiert mit Beuys


Engagement-Prediction mit klassischen Modellen

Wird dieser Post performen? Eine kompakte scikit-learn-Pipeline, die aus Post-Metadaten (Tageszeit, Hashtags, Länge, Medientyp) Engagement vorhersagt – mit Logistischer Regression und Gradient Boosting als faire Baselines, evaluiert über ROC-AUC und PR-AUC.


1. Das Problem als Klassifikation formulieren

„Wird dieser Post performen?" ist die häufigste Frage im Content-Marketing. Bevor ein Modell trainiert wird, muss sie in ein wohldefiniertes Lernproblem übersetzt werden:

Element Konkretisierung
Eingabe Post-Metadaten zum Zeitpunkt der Veröffentlichung
Ausgabe high_engagement ∈ {0,1} (oberste 20 % der Engagement-Verteilung)
Schätzer Kalibrierte Wahrscheinlichkeit P(high_engagement = 1 | features)
Erfolgsmaß ROC-AUC + PR-AUC + Calibration

Eine binäre Schwelle (Top-20 %) ist robuster als eine Regression auf rohe Like-Zahlen: Letztere wird von viralen Ausreißern dominiert.

2. Feature Engineering aus Post-Metadaten

Vier Feature-Familien decken den Großteil des erklärbaren Signals ab:

Quick-Check: Welches Feature ist am ehesten ein 'Data-Leak' beim Engagement-Modell?

❌ Imperativ: Verstreute Feature-Erzeugung

# Anti-Pattern: Features werden ad-hoc in Notebooks erzeugt,
# Train- und Test-Logik divergieren.
df["hour"] = df["timestamp"].dt.hour
df["prime_time"] = ((df["hour"] >= 18) & (df["hour"] <= 22)).astype(int)
df_train["text_len"] = df_train["text"].str.len()
df_test["text_len"] = df_test["text"].str.len()
# ... 50 weitere Zeilen, jede mit potentieller Inkonsistenz

✅ Funktional: Pipeline als komponierte Transformationen

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler

# Eine einzige, wiederverwendbare Definition – kein Drift zwischen Train und Test.
pipeline = Pipeline([
    ("preprocess", ColumnTransformer([
        ("num", StandardScaler(), ["hour", "text_len", "n_hashtags"]),
        # Kategoriale Features würden hier OneHot/Target-Encoding bekommen.
    ])),
    ("clf", LogisticRegression(max_iter=1000)),
])

3. End-to-End-Pipeline: Daten → Modell → Evaluation

Der folgende lauffähige Block trainiert beide Modelle (LogReg, GradientBoosting) auf einem synthetischen Datensatz und evaluiert sie mit beiden AUC-Varianten plus Calibration.

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score, average_precision_score, brier_score_loss

rng = np.random.default_rng(42)
n = 3000

# Synthetischer Post-Datensatz mit realistischen Treibern.
hour = rng.integers(0, 24, n)
has_image = rng.integers(0, 2, n)
has_video = rng.integers(0, 2, n)
text_len = rng.integers(20, 280, n)
n_hashtags = rng.integers(0, 8, n)
account_avg = rng.normal(50, 20, n).clip(5, 200)

prime_time = ((hour >= 18) & (hour <= 22)).astype(int)
logit = (
    -1.5
    + 0.9 * has_image
    + 0.6 * has_video
    + 0.7 * prime_time
    + 0.01 * (account_avg - 50)
    - 0.002 * (text_len - 150)
    + 0.05 * n_hashtags
    + rng.normal(0, 0.5, n)
)
p = 1 / (1 + np.exp(-logit))
y = (rng.random(n) < p).astype(int)

X = pd.DataFrame({
    "hour": hour, "has_image": has_image, "has_video": has_video,
    "text_len": text_len, "n_hashtags": n_hashtags, "account_avg": account_avg,
    "prime_time": prime_time,
})

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y)

logreg = Pipeline([("sc", StandardScaler()), ("clf", LogisticRegression(max_iter=1000))])
gbm = GradientBoostingClassifier(random_state=0, n_estimators=100, max_depth=3)

def evaluate(name, model):
    model.fit(X_tr, y_tr)
    p_hat = model.predict_proba(X_te)[:, 1]
    print(f"{name:8s}  ROC-AUC={roc_auc_score(y_te, p_hat):.3f}  "
          f"PR-AUC={average_precision_score(y_te, p_hat):.3f}  "
          f"Brier={brier_score_loss(y_te, p_hat):.3f}")

print(f"Positiv-Rate im Testset: {y_te.mean():.3f}")
evaluate("LogReg", logreg)
evaluate("GBM",    gbm)

Drei Metriken erzählen drei verschiedene Geschichten:

Quick-Check: Was misst der AUC-Wert (ROC-AUC)?

4. Feature-Importance und Erklärbarkeit

LogReg liefert direkt interpretierbare Koeffizienten (nach Standardisierung). GBM benötigt feature_importances_ oder besser SHAP-Werte. Das folgende Snippet zeigt beide Wege nebeneinander:

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

rng = np.random.default_rng(42)
n = 3000
X = pd.DataFrame({
    "hour": rng.integers(0, 24, n),
    "has_image": rng.integers(0, 2, n),
    "has_video": rng.integers(0, 2, n),
    "text_len": rng.integers(20, 280, n),
    "n_hashtags": rng.integers(0, 8, n),
})
y = ((0.9 * X["has_image"] + 0.6 * X["has_video"] + rng.normal(0, 0.5, n)) > 0.3).astype(int)

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0)

logreg = Pipeline([("sc", StandardScaler()), ("clf", LogisticRegression(max_iter=1000))]).fit(X_tr, y_tr)
gbm = GradientBoostingClassifier(random_state=0).fit(X_tr, y_tr)

print("LogReg-Koeffizienten (standardisiert):")
for name, coef in zip(X.columns, logreg.named_steps["clf"].coef_[0]):
    print(f"  {name:12s}  {coef:+.3f}")

print("\nGBM Feature-Importances:")
for name, imp in zip(X.columns, gbm.feature_importances_):
    print(f"  {name:12s}  {imp:.3f}")

5. Diskussion: Trade-offs zwischen den Modellklassen

Aspekt Logistische Regression Gradient Boosting
Performance auf typischen Marketing-Daten Solide Baseline Oft 1–3 Prozentpunkte AUC besser
Trainingszeit Sekunden Minuten bis Stunden
Erklärbarkeit Direkte Koeffizienten SHAP, Permutation Importance
Robustheit gegen Drift Hoch Mittel
Out-of-the-box Kalibrierung Gut Eher schlecht → CalibratedClassifierCV

In der Praxis ist die ehrliche Frage selten „Welches Modell ist besser?", sondern „Lohnt sich der Performance-Zugewinn die zusätzliche Komplexität?". Bei einem Engagement-Prediction-Use-Case mit 50.000 Posts pro Tag und einer Geschäfts-Hebelwirkung im einstelligen Prozentbereich ist die Antwort fast immer: Eine kalibrierte LogReg gewinnt, wenn man Wartung mit einrechnet.

6. Abschluss-Quiz

Welche Aussage zum Vergleich LogReg vs. Gradient Boosting ist im Marketing-Kontext am sinnvollsten?

Warum ist eine `Pipeline` (sklearn) besser als manuelles Feature Engineering im Notebook?

In welchem Szenario ist PR-AUC aussagekräftiger als ROC-AUC?

7. Weiterführend

Quellen

📄 Als PDF