Poisson-Regression und nicht-normaler Verlust#

Dieses Beispiel illustriert die Verwendung der log-linearen Poisson-Regression auf dem Datensatz Französische Kfz-Haftpflichtschäden von [1] und vergleicht ihn mit einem linearen Modell, das mit der üblichen Methode der kleinsten Quadrate und einem nicht-linearen GBRT-Modell, das mit dem Poisson-Verlust (und einem Log-Link) angepasst wurde, angepasst wurde.

Ein paar Definitionen

  • Eine Police ist ein Vertrag zwischen einer Versicherungsgesellschaft und einer Einzelperson: dem Versicherungsnehmer, d. h. in diesem Fall dem Fahrzeugführer.

  • Ein Schaden ist die vom Versicherungsnehmer an den Versicherer gerichtete Forderung auf Entschädigung für einen durch die Versicherung abgedeckten Verlust.

  • Die Exposition ist die Dauer des Versicherungsschutzes einer bestimmten Police in Jahren.

  • Die Schadenhäufigkeit ist die Anzahl der Schäden geteilt durch die Exposition, typischerweise gemessen in Anzahl der Schäden pro Jahr.

In diesem Datensatz entspricht jede Stichprobe einer Versicherungspolice. Verfügbare Merkmale sind unter anderem das Alter des Fahrers, das Alter des Fahrzeugs, die Fahrzeugleistung usw.

Unser Ziel ist es, die erwartete Schadenhäufigkeit für einen neuen Versicherungsnehmer auf der Grundlage historischer Daten über eine Population von Versicherungsnehmern vorherzusagen.

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

Der Datensatz Französische Kfz-Haftpflichtschäden#

Laden wir den Datensatz für Kfz-Schäden von OpenML: https://www.openml.org/d/41214

from sklearn.datasets import fetch_openml

df = fetch_openml(data_id=41214, as_frame=True).frame
df
IDpol SchadenNr Exposition Gebiet FzgLeistung FzgAlter FahrerAlter BonusMalus FzgMarke FzgKraftstoff Dichte Region
0 1.0 1 0.10000 D 5 0 55 50 B12 'Regulär' 1217 R82
1 3.0 1 0.77000 D 5 0 55 50 B12 'Regulär' 1217 R82
2 5.0 1 0.75000 B 6 2 52 50 B12 'Diesel' 54 R22
3 10.0 1 0.09000 B 7 0 46 50 B12 'Diesel' 76 R72
4 11.0 1 0.84000 B 7 0 46 50 B12 'Diesel' 76 R72
... ... ... ... ... ... ... ... ... ... ... ... ...
678008 6114326.0 0 0.00274 E 4 0 54 50 B12 'Regulär' 3317 R93
678009 6114327.0 0 0.00274 E 4 0 41 95 B12 'Regulär' 9850 R11
678010 6114328.0 0 0.00274 D 6 2 45 50 B12 'Diesel' 1323 R82
678011 6114329.0 0 0.00274 B 4 0 60 50 B12 'Regulär' 95 R26
678012 6114330.0 0 0.00274 B 7 6 29 54 B12 'Diesel' 65 R72

678013 Zeilen × 12 Spalten



Die Anzahl der Schäden (ClaimNb) ist eine positive ganze Zahl, die als Poisson-Verteilung modelliert werden kann. Sie wird dann als die Anzahl diskreter Ereignisse angenommen, die mit einer konstanten Rate in einem gegebenen Zeitintervall (Exposure, in Jahren) auftreten.

Hier möchten wir die Häufigkeit y = ClaimNb / Exposure bedingt durch X über eine (skalierte) Poisson-Verteilung modellieren und Exposure als sample_weight verwenden.

df["Frequency"] = df["ClaimNb"] / df["Exposure"]

print(
    "Average Frequency = {}".format(np.average(df["Frequency"], weights=df["Exposure"]))
)

print(
    "Fraction of exposure with zero claims = {0:.1%}".format(
        df.loc[df["ClaimNb"] == 0, "Exposure"].sum() / df["Exposure"].sum()
    )
)

fig, (ax0, ax1, ax2) = plt.subplots(ncols=3, figsize=(16, 4))
ax0.set_title("Number of claims")
_ = df["ClaimNb"].hist(bins=30, log=True, ax=ax0)
ax1.set_title("Exposure in years")
_ = df["Exposure"].hist(bins=30, log=True, ax=ax1)
ax2.set_title("Frequency (number of claims per year)")
_ = df["Frequency"].hist(bins=30, log=True, ax=ax2)
Number of claims, Exposure in years, Frequency (number of claims per year)
Average Frequency = 0.10070308464041305
Fraction of exposure with zero claims = 93.9%

Die verbleibenden Spalten können zur Vorhersage der Schadenhäufigkeit verwendet werden. Diese Spalten sind sehr heterogen mit einer Mischung aus kategorialen und numerischen Variablen unterschiedlicher Skalen, die möglicherweise sehr ungleich verteilt sind.

Um lineare Modelle mit diesen Prädiktoren anzupassen, ist es daher notwendig, Standard-Merkmalstransformationen wie folgt durchzuführen.

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import (
    FunctionTransformer,
    KBinsDiscretizer,
    OneHotEncoder,
    StandardScaler,
)

log_scale_transformer = make_pipeline(
    FunctionTransformer(np.log, validate=False), StandardScaler()
)

linear_model_preprocessor = ColumnTransformer(
    [
        ("passthrough_numeric", "passthrough", ["BonusMalus"]),
        (
            "binned_numeric",
            KBinsDiscretizer(
                n_bins=10, quantile_method="averaged_inverted_cdf", random_state=0
            ),
            ["VehAge", "DrivAge"],
        ),
        ("log_scaled_numeric", log_scale_transformer, ["Density"]),
        (
            "onehot_categorical",
            OneHotEncoder(),
            ["VehBrand", "VehPower", "VehGas", "Region", "Area"],
        ),
    ],
    remainder="drop",
)

Eine konstante Vorhersage-Baseline#

Es ist anzumerken, dass mehr als 93 % der Versicherungsnehmer null Schäden haben. Wenn wir dieses Problem in eine binäre Klassifizierungsaufgabe umwandeln würden, wäre es signifikant unausgeglichen, und selbst ein einfaches Modell, das nur den Mittelwert vorhersagt, könnte eine Genauigkeit von 93 % erzielen.

Um die Relevanz der verwendeten Metriken zu bewerten, betrachten wir als Baseline einen „Dummy“-Schätzer, der konstant die mittlere Häufigkeit der Trainingsstichprobe vorhersagt.

from sklearn.dummy import DummyRegressor
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline

df_train, df_test = train_test_split(df, test_size=0.33, random_state=0)

dummy = Pipeline(
    [
        ("preprocessor", linear_model_preprocessor),
        ("regressor", DummyRegressor(strategy="mean")),
    ]
).fit(df_train, df_train["Frequency"], regressor__sample_weight=df_train["Exposure"])

Berechnen wir die Leistung dieser konstanten Vorhersage-Baseline mit 3 verschiedenen Regressionsmetriken.

from sklearn.metrics import (
    mean_absolute_error,
    mean_poisson_deviance,
    mean_squared_error,
)


def score_estimator(estimator, df_test):
    """Score an estimator on the test set."""
    y_pred = estimator.predict(df_test)

    print(
        "MSE: %.3f"
        % mean_squared_error(
            df_test["Frequency"], y_pred, sample_weight=df_test["Exposure"]
        )
    )
    print(
        "MAE: %.3f"
        % mean_absolute_error(
            df_test["Frequency"], y_pred, sample_weight=df_test["Exposure"]
        )
    )

    # Ignore non-positive predictions, as they are invalid for
    # the Poisson deviance.
    mask = y_pred > 0
    if (~mask).any():
        n_masked, n_samples = (~mask).sum(), mask.shape[0]
        print(
            "WARNING: Estimator yields invalid, non-positive predictions "
            f" for {n_masked} samples out of {n_samples}. These predictions "
            "are ignored when computing the Poisson deviance."
        )

    print(
        "mean Poisson deviance: %.3f"
        % mean_poisson_deviance(
            df_test["Frequency"][mask],
            y_pred[mask],
            sample_weight=df_test["Exposure"][mask],
        )
    )


print("Constant mean frequency evaluation:")
score_estimator(dummy, df_test)
Constant mean frequency evaluation:
MSE: 0.564
MAE: 0.189
mean Poisson deviance: 0.625

(Generalisierte) lineare Modelle#

Wir beginnen mit der Modellierung der Zielvariablen mit dem (l2-penalisierten) linearen Regressionsmodell der kleinsten Quadrate, besser bekannt als Ridge-Regression. Wir verwenden eine geringe Straffung alpha, da wir erwarten, dass ein solches lineares Modell bei einem so großen Datensatz zu wenig Anpassung führt.

from sklearn.linear_model import Ridge

ridge_glm = Pipeline(
    [
        ("preprocessor", linear_model_preprocessor),
        ("regressor", Ridge(alpha=1e-6)),
    ]
).fit(df_train, df_train["Frequency"], regressor__sample_weight=df_train["Exposure"])

Die Poisson-Devianz kann nicht für nicht-positive Werte berechnet werden, die vom Modell vorhergesagt werden. Für Modelle, die einige nicht-positive Vorhersagen zurückgeben (z. B. Ridge), ignorieren wir die entsprechenden Stichproben, was bedeutet, dass die erhaltene Poisson-Devianz annähernd ist. Ein alternativer Ansatz könnte die Verwendung des Meta-Schätzers TransformedTargetRegressor sein, um y_pred auf eine streng positive Domäne abzubilden.

print("Ridge evaluation:")
score_estimator(ridge_glm, df_test)
Ridge evaluation:
MSE: 0.560
MAE: 0.186
WARNING: Estimator yields invalid, non-positive predictions  for 595 samples out of 223745. These predictions are ignored when computing the Poisson deviance.
mean Poisson deviance: 0.597

Als Nächstes passen wir den Poisson-Regressor an die Zielvariable an. Wir setzen die Regularisierungsstärke alpha auf ungefähr 1e-6 pro Anzahl von Stichproben (d. h. 1e-12), um den Ridge-Regressor zu imitieren, dessen L2-Strafglied anders mit der Anzahl der Stichproben skaliert.

Da der Poisson-Regressor intern das Logarithmus des erwarteten Zielwerts und nicht den erwarteten Wert direkt modelliert (Log- vs. Identitäts-Linkfunktion), ist die Beziehung zwischen X und y nicht mehr exakt linear. Daher wird der Poisson-Regressor als verallgemeinertes lineares Modell (GLM) und nicht als einfaches lineares Modell bezeichnet, wie es bei der Ridge-Regression der Fall ist.

from sklearn.linear_model import PoissonRegressor

n_samples = df_train.shape[0]

poisson_glm = Pipeline(
    [
        ("preprocessor", linear_model_preprocessor),
        ("regressor", PoissonRegressor(alpha=1e-12, solver="newton-cholesky")),
    ]
)
poisson_glm.fit(
    df_train, df_train["Frequency"], regressor__sample_weight=df_train["Exposure"]
)

print("PoissonRegressor evaluation:")
score_estimator(poisson_glm, df_test)
PoissonRegressor evaluation:
MSE: 0.560
MAE: 0.186
mean Poisson deviance: 0.594

Gradient Boosting Regression Trees für Poisson-Regression#

Schließlich betrachten wir ein nicht-lineares Modell, nämlich Gradient Boosting Regression Trees. Baum-basierte Modelle erfordern nicht, dass kategoriale Daten One-Hot-kodiert werden: Stattdessen können wir jede Kategoriebezeichnung mit einer beliebigen ganzen Zahl mithilfe von OrdinalEncoder kodieren. Mit dieser Kodierung behandeln die Bäume die kategorialen Merkmale als geordnete Merkmale, was möglicherweise nicht immer erwünscht ist. Dieser Effekt ist jedoch bei ausreichend tiefen Bäumen begrenzt, die in der Lage sind, die kategoriale Natur der Merkmale wiederherzustellen. Der Hauptvorteil von OrdinalEncoder gegenüber OneHotEncoder ist, dass das Training schneller wird.

Gradient Boosting bietet auch die Möglichkeit, die Bäume mit einem Poisson-Verlust (mit einer impliziten Log-Link-Funktion) anstelle des Standard-Fehlerquadrat-Verlusts anzupassen. Hier passen wir nur Bäume mit dem Poisson-Verlust an, um dieses Beispiel kurz zu halten.

from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.preprocessing import OrdinalEncoder

tree_preprocessor = ColumnTransformer(
    [
        (
            "categorical",
            OrdinalEncoder(),
            ["VehBrand", "VehPower", "VehGas", "Region", "Area"],
        ),
        ("numeric", "passthrough", ["VehAge", "DrivAge", "BonusMalus", "Density"]),
    ],
    remainder="drop",
)
poisson_gbrt = Pipeline(
    [
        ("preprocessor", tree_preprocessor),
        (
            "regressor",
            HistGradientBoostingRegressor(loss="poisson", max_leaf_nodes=128),
        ),
    ]
)
poisson_gbrt.fit(
    df_train, df_train["Frequency"], regressor__sample_weight=df_train["Exposure"]
)

print("Poisson Gradient Boosted Trees evaluation:")
score_estimator(poisson_gbrt, df_test)
Poisson Gradient Boosted Trees evaluation:
MSE: 0.566
MAE: 0.184
mean Poisson deviance: 0.575

Wie das obige Poisson-GLM minimieren die durch Gradient Boosting angepassten Bäume die Poisson-Devianz. Aufgrund einer höheren Vorhersagekraft erreichen sie jedoch niedrigere Werte der Poisson-Devianz.

Die Bewertung von Modellen mit einer einzigen Trainings-/Testaufteilung ist anfällig für zufällige Schwankungen. Wenn die Rechenressourcen dies zulassen, sollte überprüft werden, ob kreuzvalidierte Leistungsmetriken zu ähnlichen Schlussfolgerungen führen würden.

Der qualitative Unterschied zwischen diesen Modellen kann auch durch den Vergleich des Histogramms der beobachteten Zielwerte mit dem der vorhergesagten Werte visualisiert werden.

fig, axes = plt.subplots(nrows=2, ncols=4, figsize=(16, 6), sharey=True)
fig.subplots_adjust(bottom=0.2)
n_bins = 20
for row_idx, label, df in zip(range(2), ["train", "test"], [df_train, df_test]):
    df["Frequency"].hist(bins=np.linspace(-1, 30, n_bins), ax=axes[row_idx, 0])

    axes[row_idx, 0].set_title("Data")
    axes[row_idx, 0].set_yscale("log")
    axes[row_idx, 0].set_xlabel("y (observed Frequency)")
    axes[row_idx, 0].set_ylim([1e1, 5e5])
    axes[row_idx, 0].set_ylabel(label + " samples")

    for idx, model in enumerate([ridge_glm, poisson_glm, poisson_gbrt]):
        y_pred = model.predict(df)

        pd.Series(y_pred).hist(
            bins=np.linspace(-1, 4, n_bins), ax=axes[row_idx, idx + 1]
        )
        axes[row_idx, idx + 1].set(
            title=model[-1].__class__.__name__,
            yscale="log",
            xlabel="y_pred (predicted expected Frequency)",
        )
plt.tight_layout()
Data, Ridge, PoissonRegressor, HistGradientBoostingRegressor, Data, Ridge, PoissonRegressor, HistGradientBoostingRegressor

Die experimentellen Daten weisen eine Langschwanzverteilung für y auf. Bei allen Modellen sagen wir die erwartete Häufigkeit einer Zufallsvariablen voraus, daher werden wir zwangsläufig weniger extreme Werte haben als für die beobachteten Realisierungen dieser Zufallsvariablen. Dies erklärt, dass der Modus der Histogramme der Modellvorhersagen nicht unbedingt dem kleinsten Wert entspricht. Darüber hinaus hat die in Ridge verwendete Normalverteilung eine konstante Varianz, während für die Poisson-Verteilung, die in PoissonRegressor und HistGradientBoostingRegressor verwendet wird, die Varianz proportional zum vorhergesagten Erwartungswert ist.

Unter den betrachteten Schätzern sind PoissonRegressor und HistGradientBoostingRegressor a priori besser geeignet, die Langschwanzverteilung der nicht-negativen Daten zu modellieren, verglichen mit dem Ridge-Modell, das eine falsche Annahme über die Verteilung der Zielvariablen macht.

Der HistGradientBoostingRegressor-Schätzer hat die größte Flexibilität und ist in der Lage, höhere Erwartungswerte vorherzusagen.

Beachten Sie, dass wir für das HistGradientBoostingRegressor-Modell den kleinsten Fehlerverlust hätten verwenden können. Dies würde fälschlicherweise eine normalverteilte Antwortvariable annehmen, wie es das Ridge-Modell tut, und möglicherweise auch zu leicht negativen Vorhersagen führen. Die Gradienten-Boosted-Bäume würden jedoch immer noch relativ gut abschneiden und insbesondere besser als PoissonRegressor dank der Flexibilität der Bäume in Kombination mit der großen Anzahl von Trainingsstichproben.

Bewertung der Kalibrierung von Vorhersagen#

Um sicherzustellen, dass die Schätzer für verschiedene Arten von Versicherungsnehmern vernünftige Vorhersagen liefern, können wir Teststichproben gemäß y_pred, die von jedem Modell zurückgegeben werden, bündeln. Dann vergleichen wir für jeden Bin den mittleren vorhergesagten y_pred mit dem mittleren beobachteten Zielwert.

from sklearn.utils import gen_even_slices


def _mean_frequency_by_risk_group(y_true, y_pred, sample_weight=None, n_bins=100):
    """Compare predictions and observations for bins ordered by y_pred.

    We order the samples by ``y_pred`` and split it in bins.
    In each bin the observed mean is compared with the predicted mean.

    Parameters
    ----------
    y_true: array-like of shape (n_samples,)
        Ground truth (correct) target values.
    y_pred: array-like of shape (n_samples,)
        Estimated target values.
    sample_weight : array-like of shape (n_samples,)
        Sample weights.
    n_bins: int
        Number of bins to use.

    Returns
    -------
    bin_centers: ndarray of shape (n_bins,)
        bin centers
    y_true_bin: ndarray of shape (n_bins,)
        average y_pred for each bin
    y_pred_bin: ndarray of shape (n_bins,)
        average y_pred for each bin
    """
    idx_sort = np.argsort(y_pred)
    bin_centers = np.arange(0, 1, 1 / n_bins) + 0.5 / n_bins
    y_pred_bin = np.zeros(n_bins)
    y_true_bin = np.zeros(n_bins)

    for n, sl in enumerate(gen_even_slices(len(y_true), n_bins)):
        weights = sample_weight[idx_sort][sl]
        y_pred_bin[n] = np.average(y_pred[idx_sort][sl], weights=weights)
        y_true_bin[n] = np.average(y_true[idx_sort][sl], weights=weights)
    return bin_centers, y_true_bin, y_pred_bin


print(f"Actual number of claims: {df_test['ClaimNb'].sum()}")
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))
plt.subplots_adjust(wspace=0.3)

for axi, model in zip(ax.ravel(), [ridge_glm, poisson_glm, poisson_gbrt, dummy]):
    y_pred = model.predict(df_test)
    y_true = df_test["Frequency"].values
    exposure = df_test["Exposure"].values
    q, y_true_seg, y_pred_seg = _mean_frequency_by_risk_group(
        y_true, y_pred, sample_weight=exposure, n_bins=10
    )

    # Name of the model after the estimator used in the last step of the
    # pipeline.
    print(f"Predicted number of claims by {model[-1]}: {np.sum(y_pred * exposure):.1f}")

    axi.plot(q, y_pred_seg, marker="x", linestyle="--", label="predictions")
    axi.plot(q, y_true_seg, marker="o", linestyle="--", label="observations")
    axi.set_xlim(0, 1.0)
    axi.set_ylim(0, 0.5)
    axi.set(
        title=model[-1],
        xlabel="Fraction of samples sorted by y_pred",
        ylabel="Mean Frequency (y_pred)",
    )
    axi.legend()
plt.tight_layout()
Ridge(alpha=1e-06), PoissonRegressor(alpha=1e-12, solver='newton-cholesky'), HistGradientBoostingRegressor(loss='poisson', max_leaf_nodes=128), DummyRegressor()
Actual number of claims: 11935
Predicted number of claims by Ridge(alpha=1e-06): 11933.4
Predicted number of claims by PoissonRegressor(alpha=1e-12, solver='newton-cholesky'): 11932.0
Predicted number of claims by HistGradientBoostingRegressor(loss='poisson', max_leaf_nodes=128): 12196.1
Predicted number of claims by DummyRegressor(): 11931.2

Das Dummy-Regressionsmodell sagt eine konstante Häufigkeit voraus. Dieses Modell weist nicht allen Stichproben denselben Rang zu, ist aber dennoch global gut kalibriert (zur Schätzung der mittleren Häufigkeit der gesamten Population).

Das Ridge-Regressionsmodell kann sehr niedrige Erwartungshäufigkeiten vorhersagen, die nicht mit den Daten übereinstimmen. Es kann daher das Risiko für einige Versicherungsnehmer stark unterschätzen.

PoissonRegressor und HistGradientBoostingRegressor zeigen eine bessere Konsistenz zwischen vorhergesagten und beobachteten Zielen, insbesondere bei niedrigen vorhergesagten Zielwerten.

Die Summe aller Vorhersagen bestätigt auch das Kalibrierungsproblem des Ridge-Modells: Es unterschätzt die Gesamtzahl der Schäden im Test-Set um mehr als 3 %, während die anderen drei Modelle die Gesamtzahl der Schäden des Testportfolios annähernd wiederherstellen können.

Bewertung der Rangfolgekraft#

Für einige Geschäftsanwendungen sind wir an der Fähigkeit des Modells interessiert, die riskantesten von den sichersten Versicherungsnehmern zu ranken, unabhängig vom absoluten Wert der Vorhersage. In diesem Fall würde die Modellbewertung das Problem als Ranking-Problem und nicht als Regressionsproblem behandeln.

Um die 3 Modelle aus dieser Perspektive zu vergleichen, kann man den kumulativen Anteil der Schäden gegen den kumulativen Anteil der Exposition für die Teststichproben plotten, sortiert nach den Modellvorhersagen, von sichersten zu riskantesten gemäß jedem Modell.

Dieser Plot wird als Lorenz-Kurve bezeichnet und kann durch den Gini-Koeffizienten zusammengefasst werden.

from sklearn.metrics import auc


def lorenz_curve(y_true, y_pred, exposure):
    y_true, y_pred = np.asarray(y_true), np.asarray(y_pred)
    exposure = np.asarray(exposure)

    # order samples by increasing predicted risk:
    ranking = np.argsort(y_pred)
    ranked_frequencies = y_true[ranking]
    ranked_exposure = exposure[ranking]
    cumulated_claims = np.cumsum(ranked_frequencies * ranked_exposure)
    cumulated_claims /= cumulated_claims[-1]
    cumulated_exposure = np.cumsum(ranked_exposure)
    cumulated_exposure /= cumulated_exposure[-1]
    return cumulated_exposure, cumulated_claims


fig, ax = plt.subplots(figsize=(8, 8))

for model in [dummy, ridge_glm, poisson_glm, poisson_gbrt]:
    y_pred = model.predict(df_test)
    cum_exposure, cum_claims = lorenz_curve(
        df_test["Frequency"], y_pred, df_test["Exposure"]
    )
    gini = 1 - 2 * auc(cum_exposure, cum_claims)
    label = "{} (Gini: {:.2f})".format(model[-1], gini)
    ax.plot(cum_exposure, cum_claims, linestyle="-", label=label)

# Oracle model: y_pred == y_test
cum_exposure, cum_claims = lorenz_curve(
    df_test["Frequency"], df_test["Frequency"], df_test["Exposure"]
)
gini = 1 - 2 * auc(cum_exposure, cum_claims)
label = "Oracle (Gini: {:.2f})".format(gini)
ax.plot(cum_exposure, cum_claims, linestyle="-.", color="gray", label=label)

# Random Baseline
ax.plot([0, 1], [0, 1], linestyle="--", color="black", label="Random baseline")
ax.set(
    title="Lorenz curves by model",
    xlabel="Cumulative proportion of exposure (from safest to riskiest)",
    ylabel="Cumulative proportion of claims",
)
ax.legend(loc="upper left")
Lorenz curves by model
<matplotlib.legend.Legend object at 0x7fb485ae2910>

Wie erwartet, ist der Dummy-Regressor nicht in der Lage, die Stichproben korrekt zu ranken und schneidet daher in diesem Plot am schlechtesten ab.

Das baumbasierte Modell ist signifikant besser darin, Versicherungsnehmer nach Risiko zu ranken, während die beiden linearen Modelle ähnlich abschneiden.

Alle drei Modelle sind signifikant besser als der Zufall, aber auch sehr weit davon entfernt, perfekte Vorhersagen zu treffen.

Dieser letzte Punkt ist auf die Natur des Problems zurückzuführen: Das Auftreten von Unfällen wird hauptsächlich von umstandsbedingten Ursachen dominiert, die nicht in den Spalten des Datensatzes erfasst sind und tatsächlich als rein zufällig betrachtet werden können.

Die linearen Modelle gehen von keinen Wechselwirkungen zwischen den Eingabevariablen aus, was wahrscheinlich zu Unteranpassung führt. Die Einführung eines polynomialen Merkmalsextraktors (PolynomialFeatures) erhöht tatsächlich ihre Diskriminierungsfähigkeit um 2 Punkte im Gini-Koeffizienten. Insbesondere verbessert es die Fähigkeit der Modelle, die Top 5 % der riskantesten Profile zu identifizieren.

Wichtigste Erkenntnisse#

  • Die Leistung der Modelle kann anhand ihrer Fähigkeit, gut kalibrierte Vorhersagen und eine gute Rangfolge zu liefern, bewertet werden.

  • Die Kalibrierung des Modells kann durch den Vergleich des mittleren beobachteten Wertes mit dem mittleren vorhergesagten Wert auf Gruppen von Teststichproben, die nach vorhergesagtem Risiko gebündelt sind, beurteilt werden.

  • Der Fehlerquadratsummen-Verlust (zusammen mit der impliziten Verwendung der Identitäts-Linkfunktion) des Ridge-Regressionsmodells scheint dazu zu führen, dass dieses Modell schlecht kalibriert ist. Insbesondere neigt es dazu, das Risiko zu unterschätzen und kann sogar ungültige negative Häufigkeiten vorhersagen.

  • Die Verwendung des Poisson-Verlusts mit einem Log-Link kann diese Probleme beheben und zu einem gut kalibrierten linearen Modell führen.

  • Der Gini-Koeffizient spiegelt die Fähigkeit eines Modells wider, Vorhersagen unabhängig von ihren absoluten Werten zu ranken, und beurteilt daher nur ihre Rangfolgekraft.

  • Trotz der Verbesserung der Kalibrierung ist die Rangfolgekraft beider linearer Modelle vergleichbar und deutlich unter der Rangfolgekraft der Gradient Boosting Regression Trees.

  • Die Poisson-Devianz, die als Bewertungsmetrik berechnet wird, spiegelt sowohl die Kalibrierung als auch die Rangfolgekraft des Modells wider. Sie macht auch eine lineare Annahme über die ideale Beziehung zwischen dem Erwartungswert und der Varianz der Antwortvariablen. Der Einfachheit halber haben wir nicht geprüft, ob diese Annahme zutrifft.

  • Traditionelle Regressionsmetriken wie der mittlere quadratische Fehler (Mean Squared Error) und der mittlere absolute Fehler (Mean Absolute Error) sind bei Zählwerten mit vielen Nullen schwer sinnvoll zu interpretieren.

Gesamtlaufzeit des Skripts: (0 Minuten 17,753 Sekunden)

Verwandte Beispiele

Tweedie-Regression auf Versicherungsansprüchen

Tweedie-Regression auf Versicherungsansprüchen

Vergleich der Kalibrierung von Klassifikatoren

Vergleich der Kalibrierung von Klassifikatoren

Release Highlights für scikit-learn 0.23

Release Highlights für scikit-learn 0.23

Gewöhnliche kleinste Quadrate und Ridge Regression

Gewöhnliche kleinste Quadrate und Ridge Regression

Galerie generiert von Sphinx-Gallery