Hinweis
Gehe zum Ende, um den vollständigen Beispielcode herunterzuladen oder dieses Beispiel in Ihrem Browser über JupyterLite oder Binder auszuführen.
Modellkomplexitätseinfluss#
Demonstriert, wie sich die Modellkomplexität sowohl auf die Vorhersagegenauigkeit als auch auf die Rechenleistung auswirkt.
- Wir werden zwei Datensätze verwenden
Diabetes-Datensatz für die Regression. Dieser Datensatz besteht aus 10 Messungen von Diabetes-Patienten. Die Aufgabe ist es, den Krankheitsfortschritt vorherzusagen;
Der 20 Newsgroups Textdatensatz für die Klassifizierung. Dieser Datensatz besteht aus Newsgroup-Beiträgen. Die Aufgabe ist es, vorherzusagen, zu welchem Thema (von 20 Themen) der Beitrag verfasst wurde.
- Wir werden den Einfluss der Komplexität auf drei verschiedene Schätzer modellieren
SGDClassifier(für Klassifizierungsdaten), der stochastisches Gradientenabstiegs-Lernen implementiert;NuSVR(für Regressionsdaten), der Nu-Support-Vektor-Regression implementiert;GradientBoostingRegressorbaut ein additives Modell in einem vorwärts-schrittweisen Ansatz auf. Beachten Sie, dassHistGradientBoostingRegressorbei mittelgroßen Datensätzen (n_samples >= 10_000) viel schneller ist alsGradientBoostingRegressor, was hier nicht der Fall ist.
Wir lassen die Modellkomplexität durch die Wahl relevanter Modellparameter in jedem unserer ausgewählten Modelle variieren. Als Nächstes messen wir den Einfluss auf die Rechenleistung (Latenz) und die Vorhersagekraft (MSE oder Hamming Loss).
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
import time
import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import hamming_loss, mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.svm import NuSVR
# Initialize random generator
np.random.seed(0)
Daten laden#
Zuerst laden wir beide Datensätze.
Hinweis
Wir verwenden fetch_20newsgroups_vectorized, um den 20 Newsgroups-Datensatz herunterzuladen. Er gibt gebrauchsfertige Merkmale zurück.
Hinweis
X des 20 Newsgroups-Datensatzes ist eine spärliche Matrix, während X des Diabetes-Datensatzes ein Numpy-Array ist.
def generate_data(case):
"""Generate regression/classification data."""
if case == "regression":
X, y = datasets.load_diabetes(return_X_y=True)
train_size = 0.8
elif case == "classification":
X, y = datasets.fetch_20newsgroups_vectorized(subset="all", return_X_y=True)
train_size = 0.4 # to make the example run faster
X_train, X_test, y_train, y_test = train_test_split(
X, y, train_size=train_size, random_state=0
)
data = {"X_train": X_train, "X_test": X_test, "y_train": y_train, "y_test": y_test}
return data
regression_data = generate_data("regression")
classification_data = generate_data("classification")
Benchmark-Einfluss#
Als Nächstes können wir den Einfluss der Parameter auf den gegebenen Schätzer berechnen. In jeder Runde werden wir den Schätzer mit dem neuen Wert von changing_param einstellen und die Vorhersagezeiten, die Vorhersageleistung und die Komplexitäten sammeln, um zu sehen, wie sich diese Änderungen auf den Schätzer auswirken. Wir berechnen die Komplexität mithilfe von complexity_computer, der als Parameter übergeben wird.
def benchmark_influence(conf):
"""
Benchmark influence of `changing_param` on both MSE and latency.
"""
prediction_times = []
prediction_powers = []
complexities = []
for param_value in conf["changing_param_values"]:
conf["tuned_params"][conf["changing_param"]] = param_value
estimator = conf["estimator"](**conf["tuned_params"])
print("Benchmarking %s" % estimator)
estimator.fit(conf["data"]["X_train"], conf["data"]["y_train"])
conf["postfit_hook"](estimator)
complexity = conf["complexity_computer"](estimator)
complexities.append(complexity)
start_time = time.time()
for _ in range(conf["n_samples"]):
y_pred = estimator.predict(conf["data"]["X_test"])
elapsed_time = (time.time() - start_time) / float(conf["n_samples"])
prediction_times.append(elapsed_time)
pred_score = conf["prediction_performance_computer"](
conf["data"]["y_test"], y_pred
)
prediction_powers.append(pred_score)
print(
"Complexity: %d | %s: %.4f | Pred. Time: %fs\n"
% (
complexity,
conf["prediction_performance_label"],
pred_score,
elapsed_time,
)
)
return prediction_powers, prediction_times, complexities
Parameter auswählen#
Wir wählen die Parameter für jeden unserer Schätzer aus, indem wir ein Wörterbuch mit allen erforderlichen Werten erstellen. changing_param ist der Name des Parameters, der in jedem Schätzer variieren wird. Die Komplexität wird durch complexity_label definiert und mithilfe von complexity_computer berechnet. Beachten Sie auch, dass wir je nach Schätzertyp unterschiedliche Daten übergeben.
def _count_nonzero_coefficients(estimator):
a = estimator.coef_.toarray()
return np.count_nonzero(a)
configurations = [
{
"estimator": SGDClassifier,
"tuned_params": {
"penalty": "elasticnet",
"alpha": 0.001,
"loss": "modified_huber",
"fit_intercept": True,
"tol": 1e-1,
"n_iter_no_change": 2,
},
"changing_param": "l1_ratio",
"changing_param_values": [0.25, 0.5, 0.75, 0.9],
"complexity_label": "non_zero coefficients",
"complexity_computer": _count_nonzero_coefficients,
"prediction_performance_computer": hamming_loss,
"prediction_performance_label": "Hamming Loss (Misclassification Ratio)",
"postfit_hook": lambda x: x.sparsify(),
"data": classification_data,
"n_samples": 5,
},
{
"estimator": NuSVR,
"tuned_params": {"C": 1e3, "gamma": 2**-15},
"changing_param": "nu",
"changing_param_values": [0.05, 0.1, 0.2, 0.35, 0.5],
"complexity_label": "n_support_vectors",
"complexity_computer": lambda x: len(x.support_vectors_),
"data": regression_data,
"postfit_hook": lambda x: x,
"prediction_performance_computer": mean_squared_error,
"prediction_performance_label": "MSE",
"n_samples": 15,
},
{
"estimator": GradientBoostingRegressor,
"tuned_params": {
"loss": "squared_error",
"learning_rate": 0.05,
"max_depth": 2,
},
"changing_param": "n_estimators",
"changing_param_values": [10, 25, 50, 75, 100],
"complexity_label": "n_trees",
"complexity_computer": lambda x: x.n_estimators,
"data": regression_data,
"postfit_hook": lambda x: x,
"prediction_performance_computer": mean_squared_error,
"prediction_performance_label": "MSE",
"n_samples": 15,
},
]
Code ausführen und Ergebnisse plotten#
Wir haben alle erforderlichen Funktionen für unseren Benchmark definiert. Nun durchlaufen wir die zuvor definierten verschiedenen Konfigurationen. Anschließend können wir die aus dem Benchmark erhaltenen Diagramme analysieren: Das Entspannen der L1-Strafe im SGD-Klassifikator reduziert den Vorhersagefehler, führt aber zu einer Erhöhung der Trainingszeit. Eine ähnliche Analyse können wir bezüglich der Trainingszeit durchführen, die mit der Anzahl der Support-Vektoren bei einem Nu-SVR zunimmt. Wir haben jedoch beobachtet, dass es eine optimale Anzahl von Support-Vektoren gibt, die den Vorhersagefehler reduziert. Tatsächlich führen zu wenige Support-Vektoren zu einem unterangepassten Modell, während zu viele Support-Vektoren zu einem überangepassten Modell führen. Die exakt gleiche Schlussfolgerung kann für das Gradient-Boosting-Modell gezogen werden. Der einzige Unterschied zum Nu-SVR ist, dass zu viele Bäume im Ensemble nicht so nachteilig sind.
def plot_influence(conf, mse_values, prediction_times, complexities):
"""
Plot influence of model complexity on both accuracy and latency.
"""
fig = plt.figure()
fig.subplots_adjust(right=0.75)
# first axes (prediction error)
ax1 = fig.add_subplot(111)
line1 = ax1.plot(complexities, mse_values, c="tab:blue", ls="-")[0]
ax1.set_xlabel("Model Complexity (%s)" % conf["complexity_label"])
y1_label = conf["prediction_performance_label"]
ax1.set_ylabel(y1_label)
ax1.spines["left"].set_color(line1.get_color())
ax1.yaxis.label.set_color(line1.get_color())
ax1.tick_params(axis="y", colors=line1.get_color())
# second axes (latency)
ax2 = fig.add_subplot(111, sharex=ax1, frameon=False)
line2 = ax2.plot(complexities, prediction_times, c="tab:orange", ls="-")[0]
ax2.yaxis.tick_right()
ax2.yaxis.set_label_position("right")
y2_label = "Time (s)"
ax2.set_ylabel(y2_label)
ax1.spines["right"].set_color(line2.get_color())
ax2.yaxis.label.set_color(line2.get_color())
ax2.tick_params(axis="y", colors=line2.get_color())
plt.legend(
(line1, line2), ("prediction error", "prediction latency"), loc="upper center"
)
plt.title(
"Influence of varying '%s' on %s"
% (conf["changing_param"], conf["estimator"].__name__)
)
for conf in configurations:
prediction_performances, prediction_times, complexities = benchmark_influence(conf)
plot_influence(conf, prediction_performances, prediction_times, complexities)
plt.show()
Benchmarking SGDClassifier(alpha=0.001, l1_ratio=0.25, loss='modified_huber',
n_iter_no_change=2, penalty='elasticnet', tol=0.1)
Complexity: 4944 | Hamming Loss (Misclassification Ratio): 0.2687 | Pred. Time: 0.032131s
Benchmarking SGDClassifier(alpha=0.001, l1_ratio=0.5, loss='modified_huber',
n_iter_no_change=2, penalty='elasticnet', tol=0.1)
Complexity: 1847 | Hamming Loss (Misclassification Ratio): 0.3264 | Pred. Time: 0.026102s
Benchmarking SGDClassifier(alpha=0.001, l1_ratio=0.75, loss='modified_huber',
n_iter_no_change=2, penalty='elasticnet', tol=0.1)
Complexity: 997 | Hamming Loss (Misclassification Ratio): 0.3383 | Pred. Time: 0.019467s
Benchmarking SGDClassifier(alpha=0.001, l1_ratio=0.9, loss='modified_huber',
n_iter_no_change=2, penalty='elasticnet', tol=0.1)
Complexity: 799 | Hamming Loss (Misclassification Ratio): 0.3481 | Pred. Time: 0.017868s
Benchmarking NuSVR(C=1000.0, gamma=3.0517578125e-05, nu=0.05)
Complexity: 18 | MSE: 5558.7313 | Pred. Time: 0.000174s
Benchmarking NuSVR(C=1000.0, gamma=3.0517578125e-05, nu=0.1)
Complexity: 36 | MSE: 5289.8022 | Pred. Time: 0.000248s
Benchmarking NuSVR(C=1000.0, gamma=3.0517578125e-05, nu=0.2)
Complexity: 72 | MSE: 5193.8353 | Pred. Time: 0.000390s
Benchmarking NuSVR(C=1000.0, gamma=3.0517578125e-05, nu=0.35)
Complexity: 124 | MSE: 5131.3279 | Pred. Time: 0.000605s
Benchmarking NuSVR(C=1000.0, gamma=3.0517578125e-05)
Complexity: 178 | MSE: 5149.0779 | Pred. Time: 0.000837s
Benchmarking GradientBoostingRegressor(learning_rate=0.05, max_depth=2, n_estimators=10)
Complexity: 10 | MSE: 4066.4812 | Pred. Time: 0.000145s
Benchmarking GradientBoostingRegressor(learning_rate=0.05, max_depth=2, n_estimators=25)
Complexity: 25 | MSE: 3551.1723 | Pred. Time: 0.000165s
Benchmarking GradientBoostingRegressor(learning_rate=0.05, max_depth=2, n_estimators=50)
Complexity: 50 | MSE: 3445.2171 | Pred. Time: 0.000201s
Benchmarking GradientBoostingRegressor(learning_rate=0.05, max_depth=2, n_estimators=75)
Complexity: 75 | MSE: 3433.0358 | Pred. Time: 0.000236s
Benchmarking GradientBoostingRegressor(learning_rate=0.05, max_depth=2)
Complexity: 100 | MSE: 3456.0602 | Pred. Time: 0.000269s
Schlussfolgerung#
Zusammenfassend können wir folgende Erkenntnisse ableiten
ein komplexeres (oder ausdrucksstärkeres) Modell erfordert eine längere Trainingszeit;
ein komplexeres Modell garantiert nicht, den Vorhersagefehler zu reduzieren.
Diese Aspekte hängen mit der Modellverallgemeinerung und der Vermeidung von Unter- oder Überanpassung zusammen.
Gesamtlaufzeit des Skripts: (0 Minuten 4,435 Sekunden)
Verwandte Beispiele
Analyse des Konzentrations-Prior-Typs der Variation im Bayes'schen Gaußschen Gemisch


