为 SVC 缩放正则化参数#

以下示例说明了在使用 支持向量机 进行 分类 时缩放正则化参数的影响。对于 SVC 分类,我们感兴趣的是以下方程的风险最小化

\[C \sum_{i=1, n} \mathcal{L} (f(x_i), y_i) + \Omega (w)\]

其中

  • \(C\) 用于设置正则化量

  • \(\mathcal{L}\) 是我们样本和模型参数的 loss 函数。

  • \(\Omega\) 是我们模型参数的 penalty 函数

如果我们将损失函数视为每个样本的单个误差,那么数据拟合项或每个样本误差的总和随着我们添加更多样本而增加。然而,惩罚项不会增加。

例如,当使用 交叉验证 使用 C 设置正则化量时,主问题和交叉验证折叠内的小问题之间会有不同的样本量。

由于损失函数取决于样本量,因此后者会影响所选的 C 值。由此产生的问题是“我们如何最佳地调整 C 以考虑不同的训练样本量?”

# Author: Andreas Mueller <[email protected]>
#         Jaques Grobler <[email protected]>
# License: BSD 3 clause

数据生成#

在本示例中,我们研究了重新参数化正则化参数 C 以考虑使用 L1 或 L2 惩罚时的样本数量的影响。为此,我们创建了一个具有大量特征的合成数据集,其中只有少数特征是有信息的。因此,我们预计正则化会将系数缩小到零(L2 惩罚)或完全为零(L1 惩罚)。

from sklearn.datasets import make_classification

n_samples, n_features = 100, 300
X, y = make_classification(
    n_samples=n_samples, n_features=n_features, n_informative=5, random_state=1
)

L1 惩罚情况#

在 L1 情况下,理论表明,只要有强正则化,估计器就不能像知道真实分布的模型那样好地进行预测(即使在样本量增长到无穷大时也是如此),因为它可能会将其他预测特征的一些权重设置为零,从而导致偏差。然而,它确实表明,可以通过调整 C 来找到正确的非零参数集及其符号。

我们定义了一个带有 L1 惩罚的线性 SVC。

from sklearn.svm import LinearSVC

model_l1 = LinearSVC(penalty="l1", loss="squared_hinge", dual=False, tol=1e-3)

我们通过交叉验证计算不同 C 值的平均测试分数。

import numpy as np
import pandas as pd

from sklearn.model_selection import ShuffleSplit, validation_curve

Cs = np.logspace(-2.3, -1.3, 10)
train_sizes = np.linspace(0.3, 0.7, 3)
labels = [f"fraction: {train_size}" for train_size in train_sizes]
shuffle_params = {
    "test_size": 0.3,
    "n_splits": 150,
    "random_state": 1,
}

results = {"C": Cs}
for label, train_size in zip(labels, train_sizes):
    cv = ShuffleSplit(train_size=train_size, **shuffle_params)
    train_scores, test_scores = validation_curve(
        model_l1,
        X,
        y,
        param_name="C",
        param_range=Cs,
        cv=cv,
        n_jobs=2,
    )
    results[label] = test_scores.mean(axis=1)
results = pd.DataFrame(results)
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(12, 6))

# plot results without scaling C
results.plot(x="C", ax=axes[0], logx=True)
axes[0].set_ylabel("CV score")
axes[0].set_title("No scaling")

for label in labels:
    best_C = results.loc[results[label].idxmax(), "C"]
    axes[0].axvline(x=best_C, linestyle="--", color="grey", alpha=0.7)

# plot results by scaling C
for train_size_idx, label in enumerate(labels):
    train_size = train_sizes[train_size_idx]
    results_scaled = results[[label]].assign(
        C_scaled=Cs * float(n_samples * np.sqrt(train_size))
    )
    results_scaled.plot(x="C_scaled", ax=axes[1], logx=True, label=label)
    best_C_scaled = results_scaled["C_scaled"].loc[results[label].idxmax()]
    axes[1].axvline(x=best_C_scaled, linestyle="--", color="grey", alpha=0.7)

axes[1].set_title("Scaling C by sqrt(1 / n_samples)")

_ = fig.suptitle("Effect of scaling C with L1 penalty")
Effect of scaling C with L1 penalty, No scaling, Scaling C by sqrt(1 / n_samples)

在小 C(强正则化)区域,模型学习的所有系数都为零,导致严重欠拟合。实际上,该区域的准确率处于机会水平。

使用默认比例会导致 C 的最佳值相对稳定,而从欠拟合区域过渡取决于训练样本的数量。重新参数化会导致更稳定的结果。

例如,参见 关于 Lasso 的预测性能Lasso 和 Dantzig 选择器的同时分析 中的定理 3,其中正则化参数始终假定与 1 / sqrt(n_samples) 成正比。

L2 惩罚情况#

我们可以对 L2 惩罚进行类似的实验。在这种情况下,理论表明,为了实现预测一致性,应该保持惩罚参数不变,因为样本数量会随着样本数量的增加而增加。

model_l2 = LinearSVC(penalty="l2", loss="squared_hinge", dual=True)
Cs = np.logspace(-8, 4, 11)

labels = [f"fraction: {train_size}" for train_size in train_sizes]
results = {"C": Cs}
for label, train_size in zip(labels, train_sizes):
    cv = ShuffleSplit(train_size=train_size, **shuffle_params)
    train_scores, test_scores = validation_curve(
        model_l2,
        X,
        y,
        param_name="C",
        param_range=Cs,
        cv=cv,
        n_jobs=2,
    )
    results[label] = test_scores.mean(axis=1)
results = pd.DataFrame(results)
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(12, 6))

# plot results without scaling C
results.plot(x="C", ax=axes[0], logx=True)
axes[0].set_ylabel("CV score")
axes[0].set_title("No scaling")

for label in labels:
    best_C = results.loc[results[label].idxmax(), "C"]
    axes[0].axvline(x=best_C, linestyle="--", color="grey", alpha=0.8)

# plot results by scaling C
for train_size_idx, label in enumerate(labels):
    results_scaled = results[[label]].assign(
        C_scaled=Cs * float(n_samples * np.sqrt(train_sizes[train_size_idx]))
    )
    results_scaled.plot(x="C_scaled", ax=axes[1], logx=True, label=label)
    best_C_scaled = results_scaled["C_scaled"].loc[results[label].idxmax()]
    axes[1].axvline(x=best_C_scaled, linestyle="--", color="grey", alpha=0.8)
axes[1].set_title("Scaling C by sqrt(1 / n_samples)")

fig.suptitle("Effect of scaling C with L2 penalty")
plt.show()
Effect of scaling C with L2 penalty, No scaling, Scaling C by sqrt(1 / n_samples)

对于 L2 惩罚情况,重新参数化似乎对正则化最佳值的稳定性影响较小。从过拟合区域过渡发生在更广泛的范围内,并且准确率似乎没有降级到机会水平。

尝试将值增加到 n_splits=1_000 以在 L2 情况下获得更好的结果,由于文档构建器上的限制,此处未显示。

脚本的总运行时间:(0 分钟 20.514 秒)

相关示例

逻辑回归中的 L1 惩罚和稀疏性

逻辑回归中的 L1 惩罚和稀疏性

绘制分类概率

绘制分类概率

作为 L2 正则化函数的岭系数

作为 L2 正则化函数的岭系数

L1 逻辑回归的正则化路径

L1 逻辑回归的正则化路径

由 Sphinx-Gallery 生成的画廊