核岭回归(KRR)和支持向量回归(SVR)都是通过核技巧来学习非线性函数的机器学习方法。核技巧允许它们在由核函数诱导的空间中学习线性函数,这在原始空间中对应于非线性函数。这两种方法的主要区别在于它们的损失函数:KRR使用岭损失,而SVR使用ε-不敏感损失。与SVR相比,KRR可以以封闭形式拟合,对于中等规模的数据集来说,通常训练速度更快。然而,学习到的模型是非稀疏的,因此在预测时比SVR慢。
本例在包含正弦目标函数和强噪声的数据集上展示了这两种方法。噪声被添加到每第五个数据点上。这个例子由scikit-learn开发者编写,遵循BSD-3-Clause许可证。
首先,生成样本数据。使用numpy库来创建随机数生成器,并生成10000个数据点。然后,计算这些数据点的正弦值作为目标值,并人为地在每第五个数据点上添加噪声。接下来,为绘图创建了一个更密集的数据点集合。
import numpy as np
rng = np.random.RandomState(42)
X = 5 * rng.rand(10000, 1)
y = np.sin(X).ravel()
# 人为添加噪声
y[::5] += 3 * (0.5 - rng.rand(X.shape[0]//5))
X_plot = np.linspace(0, 5, 100000)[:, None]
接下来,构建基于核的回归模型。使用sklearn库中的KernelRidge和SVR类,并利用GridSearchCV进行参数搜索,以找到最佳的C和gamma值。
from sklearn.kernel_ridge import KernelRidge
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVR
train_size = 100
svr = GridSearchCV(SVR(kernel="rbf", gamma=0.1), param_grid={"C": [1e0, 1e1, 1e2, 1e3], "gamma": np.logspace(-2, 2, 5)})
kr = GridSearchCV(KernelRidge(kernel="rbf", gamma=0.1), param_grid={"alpha": [1e0, 0.1, 1e-2, 1e-3], "gamma": np.logspace(-2, 2, 5)})
比较了SVR和KRR的训练时间。结果显示,KRR的训练速度大约是SVR的3-4倍。尽管理论上SVR应该能够更快地预测100000个目标值,因为它只使用了大约1/3的训练数据点作为支持向量,学习了一个稀疏模型。然而,在实践中,由于模型实现细节的不同,KRR可能和SVR一样快,甚至更快。
import time
t0 = time.time()
svr.fit(X[:train_size], y[:train_size])
svr_fit = time.time() - t0
print(f"Best SVR with params: {svr.best_params_} and R2 score: {svr.best_score_:.3f}")
print("SVR complexity and bandwidth selected and model fitted in %.3f s" % svr_fit)
t0 = time.time()
kr.fit(X[:train_size], y[:train_size])
kr_fit = time.time() - t0
print(f"Best KRR with params: {kr.best_params_} and R2 score: {kr.best_score_:.3f}")
print("KRR complexity and bandwidth selected and model fitted in %.3f s" % kr_fit)
还可视化了训练和预测时间。图表显示了不同训练集大小下,KRR和SVR的训练和预测时间。对于中等规模的训练集(少于几千个样本),KRR的训练速度比SVR快;然而,对于更大的训练集,SVR的扩展性更好。在预测时间方面,理论上SVR应该比KRR快,但由于实现细节的不同,实际情况并非总是如此。
import matplotlib.pyplot as plt
sv_ind = svr.best_estimator_.support_
plt.scatter(X[sv_ind], y[sv_ind], c="r", s=50, label="SVR support vectors", zorder=2, edgecolors=(0, 0, 0))
plt.scatter(X[:100], y[:100], c="k", label="data", zorder=1, edgecolors=(0, 0, 0))
plt.plot(X_plot, y_svr, c="r", label="SVR (fit: %.3f s, predict: %.3f s)" % (svr_fit, svr_predict))
plt.plot(X_plot, y_kr, c="g", label="KRR (fit: %.3f s, predict: %.3f s)" % (kr_fit, kr_predict))
plt.xlabel("data")
plt.ylabel("target")
plt.title("SVR versus Kernel Ridge")
_ = plt.legend()
最后,展示了学习曲线,以了解随着训练集大小的变化,模型的性能如何变化。通过这些图表,可以更好地理解不同模型在不同数据规模下的表现。
from sklearn.model_selection import LearningCurveDisplay
_, ax = plt.subplots()
svr = SVR(kernel="rbf", C=1e1, gamma=0.1)
kr = KernelRidge(kernel="rbf", alpha=0.1, gamma=0.1)
common_params = {"X": X[:100], "y": y[:100], "train_sizes": np.linspace(0.1, 1, 10), "scoring": "neg_mean_squared_error", "negate_score": True, "score_name": "Mean Squared Error", "score_type": "test", "std_display_style": None, "ax": ax}
LearningCurveDisplay.from_estimator(svr, **common_params)
LearningCurveDisplay.from_estimator(kr, **common_params)
ax.set_title("Learning curves")
ax.legend(handles=ax.get_legend_handles_labels()[0], labels=["SVR", "KRR"])
plt.show()