目录
#学习记录#
1 提升扩展阶数产生的现象
过拟合现象
通过之前的程序我们发现,使用多项式扩展完美的解决了欠拟合问题。如果我们使用更多阶的多项式,甚至可以将拟合度提高为1,但是问题来了,是否阶数越多越好呢?
我们通过代码来看一下:
from sklearn.model_selection import train_test_split
def true_fun(x):
"""数据分布的函数,用于生成由x -> y的映射。
根据x数组中的每个元素,返回该元素的余弦计算值y。
Parameters
----------
x : array-like
训练数据集。
Returns
-------
y : array-like
x对应的余弦映射结果。
"""
return np.cos(1.5 * np.pi * x)
def fit_and_plot(model):
"""用来训练模型,并且绘制模型的拟合效果。
Parameters
----------
model : object
模型对象。
"""
np.random.seed(0)
x = np.random.rand(50)
# 在映射函数上,增加一定的误差(噪声)。这样更符合现实中数据的分布。
# 误差服从正态分布。
y = true_fun(x) + np.random.randn(len(x)) * 0.1
X = x[:, np.newaxis]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5,
random_state=0)
model.fit(X_train, y_train)
train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
plt.scatter(X_train, y_train, c="g", label="训练集")
plt.scatter(X_test, y_test, c="orange", marker="D", label="测试集")
s = np.linspace(0, 1, 100).reshape(-1, 1)
plt.plot(s, model.predict(s), c="r", label="拟合线")
plt.xlabel("x")
plt.ylabel("y")
plt.xlim((0, 1))
plt.ylim((-2, 2))
plt.legend(loc="upper center")
plt.title(f"训练集:{train_score:.3f} 测试集:{test_score:.3f}")
# 定义多项式扩展的阶数。
degrees = [1, 3, 8, 15]
plt.figure(figsize=(15, 12))
for i, n in enumerate(degrees):
plt.subplot(2, 2, i + 1)
pipe = Pipeline([("poly", PolynomialFeatures(degree=n, include_bias=False)),("lr", LinearRegression())])
fit_and_plot(pipe)
# named_steps返回字典对象,提供流水线中每个步骤的名称(key)与对象(value)的映射。
print(pipe.named_steps["lr"].coef_)
输出:
2 常用的正则化方法
背景: 在进行线性回归等模型训练时,过度复杂的模型往往表现为参数值过大。这不仅会导致过拟合现象,也使模型难以泛化到新数据上。为了缓解这一问题,正则化技术应运而生。
定义: 正则化是一种在损失函数中加入额外惩罚项的技术,旨在限制模型参数的大小,减少模型复杂度。这种额外的惩罚项,也称为正则项,有助于降低过拟合的风险。
常见类型:
- L2正则化(岭回归): 在损失函数中添加参数权重的平方和作为惩罚项。它倾向于使参数值较小,但不会将它们完全减至零。
- L1正则化(套索回归): 损失函数中包含参数的绝对值和。这种方法除了惩罚大的参数值外,还可能将某些参数完全压缩为零,从而实现特征选择的效果。
- Elastic Net: 是L1和L2正则化的组合,包含两者的惩罚项。这种方法结合了岭回归和套索回归的优点,既可以压缩参数,也可以完全消除某些参数。
2.1 正则化对权重的影响:以L2正则化为例
概念: L2正则化(也称为岭回归)通过在损失函数中添加一个正则项,该正则项是模型权重的平方和与一个正则化系数(α)的乘积,来减少模型参数的复杂度。这种做法旨在减少模型对训练数据的过度拟合,同时保留其预测能力。
权重影响: 在L2正则化中,正则化系数α的选择对模型的权重有显著影响:
- 较小的α值: 当α值较小,正则化的影响减少,模型倾向于增加权重的复杂度,从而更好地拟合训练数据。这可能会导致过拟合。
- 较大的α值: 反之,较大的α值增强了正则化的作用,迫使模型权重变得更小,减少了模型的复杂度。这有助于防止过拟合,但如果α过大,可能会导致模型欠拟合,即在训练数据上的表现也不佳。
我们通过代码来观察:
from sklearn.datasets import make_regression
from sklearn.linear_model import Ridge
# 注意:当坐标轴使用对数比例后,这里需要改成英文字体,否则无法正常显示。
plt.rcParams["font.family"] = "serif"
# 创建回归数据集。
# n_samples:样本数量。
# n_features:特征数量。
# coef:是否返回权重。默认为False。
# random_state:随机种子。
# bias:偏置。
# noise:增加的噪声干扰,值越大,干扰越大。
X, y, w = make_regression(n_samples=10, n_features=10, coef=True,
random_state=1, bias=3.5, noise=0.0)
alphas = np.logspace(-4, 4, 200)
# 定义列表,用来保存在不同alpha取值下,模型最优的权重(w)值。
coefs = []
# 创建岭回归对象。
ridge = Ridge()
for a in alphas:
# alpha:惩罚力度,值越大,惩罚力度越大。
ridge.set_params(alpha=a)
ridge.fit(X, y)
# 将每个alpha取值下,Ridge回归拟合的最佳解(w)加入到列表中。
coefs.append(ridge.coef_)
# gca get current axes 获取当前的绘图对象。
ax = plt.gca()
# 当y是二维数组时,每一列会认为是一个单独的数据集。
ax.plot(alphas, coefs)
# 设置x轴的比例。(对数比例)
ax.set_xscale("log")
# 设置x轴的标签。
ax.set_xlabel("alpha")
# 设置y轴的标签。
ax.set_ylabel("weights")
输出:
这张图展示的是在不同α值(正则化强度)下,L2正则化对模型权重的影响。我们可以看到,随着α值的增加(从左至右),各个权重的绝对值普遍减小。这是因为较高的正则化强度对模型参数施加了更严格的约束,迫使模型偏向于更小的权重值。
在α值较小的左侧区域,权重值较大,模型可能会更加复杂,有较大的过拟合风险。而在α值增大到某个程度之后,权重逐渐趋于平稳,这有助于降低过拟合的风险,但也可能导致欠拟合,特别是当α值非常大时。
2.2 正则化对权重的影响:以L1正则化为例
L1正则化,也被称为套索回归(Lasso Regression),它通过惩罚模型的损失函数中权重的绝对值来工作。与L2正则化不同,L1正则化倾向于产生稀疏的权重矩阵,即模型的许多权重将被设置为零。这种特性使得L1正则化能够进行特征选择,因为它可以减少不重要特征的权重到零。
代码:
from sklearn.datasets import make_regression
from sklearn.linear_model import Ridge
# 注意:当坐标轴使用对数比例后,这里需要改成英文字体,否则无法正常显示。
plt.rcParams["font.family"] = "serif"
# 创建回归数据集。
# n_samples:样本数量。
# n_features:特征数量。
# coef:是否返回权重。默认为False。
# random_state:随机种子。
# bias:偏置。
# noise:增加的噪声干扰,值越大,干扰越大。
X, y, w = make_regression(n_samples=10, n_features=10, coef=True,
random_state=1, bias=3.5, noise=0.0)
alphas = np.logspace(-4, 4, 200)
# 定义列表,用来保存在不同alpha取值下,模型最优的权重(w)值。
coefs = []
# 创建Lasso回归对象。
lasso = Lasso()
for a in alphas:
# alpha:惩罚力度,值越大,惩罚力度越大。
lasso.set_params(alpha=a)
lasso.fit(X, y)
# 将每个alpha取值下,Lasso回归拟合的最佳解(w)加入到列表中。
coefs.append(lasso.coef_)
# gca get current axes 获取当前的绘图对象。
ax = plt.gca()
# 当y是二维数组时,每一列会认为是一个单独的数据集。
ax.plot(alphas, coefs)
# 设置x轴的比例。(对数比例)
ax.set_xscale("log")
# 设置x轴的标签。
ax.set_xlabel("alpha")
# 设置y轴的标签。
ax.set_ylabel("weights")
输出:
- 在α较小(接近0)的区域,大多数权重都远离零点,表明正则化的影响较小,模型可能会保留更多的特征。
- 随着α值的增加,曲线急剧下降,权重迅速减小到零,这反映了L1正则化对于特征选择的效果。在某些权重上这一点尤为明显,我们可以看到它们突然降到零,这意味着对应的特征被模型彻底排除。
- 在α继续增加时,几乎所有的权重都趋近于零,这可能意味着模型已经非常简化,可能会导致欠拟合。
2.3 三种正则化方法解决过拟合:
现在我们分别使用三种正则化方法,尝试解决过拟合问题并进行对比分析:
from sklearn.linear_model import ElasticNet
# 将字体改回中文字体。
plt.rcParams["font.family"] = "SimHei"
np.random.seed(0)
x = np.random.rand(50)
y = true_fun(x) + np.random.randn(len(x)) * 0.1
X = x[:, np.newaxis]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
models = [("线性回归(无正则化)", LinearRegression()), ("L1正则化:", Lasso(alpha=0.02)),
("L2正则化", Ridge(alpha=0.02)), ("弹性网络", ElasticNet(alpha=0.02,
l1_ratio=0.5))]
plt.figure(figsize=(15, 12))
for i, (name, model) in enumerate(models):
plt.subplot(2, 2, i + 1)
pipe = Pipeline([("poly", PolynomialFeatures(degree=15, include_bias=False)),
("model", model)])
fit_and_plot(pipe)
print(model.coef_)
输出:
3 交叉验证调整超参数
通过刚刚的试验,我们已经发现过拟合得到一定程度的解决,但是效果并不是非常好,因为我们的a值设置的比较随意。如果使用合适的a值,或许我们还能提高模型的效果,我们用交叉验证来调整参数a的值
在sklearn中,LassoCV
、RidgeCV
和ElasticNetCV
是三个用于执行带有交叉验证的线性模型的类。它们分别对应于L1正则化、L2正则化和弹性网(Elastic Net)正则化,并内置了交叉验证功能,可以自动选择最佳的正则化参数α。这些类非常方便,因为它们简化了正则化强度的选择过程,使用内置的交叉验证来确定α值,从而避免了手动调整α值并运行多次模型以确定最佳值的需要。
代码如下:
from sklearn.linear_model import LassoCV,RidgeCV,ElasticNetCV
alphas = [0.001,0.005,0.01,0.05,0.1,0.5]
models = [("L1正则化:", LassoCV(max_iter=5000)), ("L2正则化", RidgeCV()),
("弹性网络", ElasticNetCV(l1_ratio=0.5))]
plt.figure(figsize=(15, 5))
for i, (name, model) in enumerate(models):
plt.subplot(1, 3, i + 1)
pipe = Pipeline([("poly", PolynomialFeatures(degree=15)), ("model", model)])
# 将模型设置为10折交叉验证,其实在models中,各个模型的构造器中,可以指定cv=10,但是需要三个都指定。
# 这里在循环中,只需要使用一行代码就可以了。
pipe.set_params(model__cv=10)
pipe.set_params(model__alphas=alphas)
fit_and_plot(pipe)
# 输出最佳的超参数alpha。
print(name, model.alpha_)
输出
L1正则化: 0.001
L2正则化 0.05
弹性网络 0.001
在上面我们就可看到,经过交叉验证最佳参数的选择使得拟合效果更好了
以上
学习在于总结和坚持,共勉