【机器学习】掌握多项式回归与正则化:用岭回归与Lasso解决过拟合问题
【机器学习】掌握多项式回归与正则化:用岭回归与Lasso解决过拟合问题
简介
在机器学习中,回归问题是预测任务的常见形式之一。
在简单的线性回归中,假设数据之间存在线性关系,但当数据本身呈现出非线性关系时,线性回归模型就无法有效拟合数据。
此时,多项式回归便成为一种有效的选择,它通过引入多项式特征来扩展线性回归模型的表现能力。然而,多项式回归也有过拟合的风险,尤其是当多项式的阶数过高时。本文将通过一个 Python 示例代码,演示如何使用多项式回归来拟合数据,并通过正则化技术(岭回归和Lasso回归)来解决过拟合问题。
一、多项式回归
多项式回归是一种通过将特征变量升高到多项式的幂来实现非线性回归的技术。
- 原理:多项式回归通过引入高阶项(如 X2,X3等)来拓展回归模型的表达能力,使其能够拟合非线性数据。
- 数学表示:

二、岭回归(Ridge Regression)
岭回归是一种通过对模型的回归系数加入L2正则化项的线性回归方法。L2正则化(也称为平方正则化)的作用是对模型的回归系数施加惩罚,使得模型在训练时不仅仅拟合数据,还要保持系数的较小值,从而避免过拟合。
- 正则化项(L2范数):
岭回归的损失函数在普通线性回归损失函数(最小化均方误差)基础上增加了一个L2正则化项。 - 数学表示:

三、Lasso回归(Lasso Regression)
Lasso回归(Least Absolute Shrinkage and Selection Operator)是一种通过对回归系数施加L1正则化的线性回归方法。与岭回归的L2正则化不同,Lasso回归的L1正则化会使得部分回归系数变为零,从而达到特征选择的效果。
- 正则化项(L1范数):
Lasso回归的损失函数在普通线性回归损失函数的基础上增加了一个L1正则化项。 - 数学表示:

1、数据生成
假设拟合一个二次方程,可以先自生成一些模拟数据,并且在数据中加入了一些高斯噪声。
import numpy as np
import matplotlib.pyplot as plt
# 随机选择100个点数据
m = 100
X = 6 * np.random.rand(m, 1) - 3 # X的取值范围为[-3, 3]
y = 0.5 * X ** 2 + X + np.random.randn(m, 1) # y = 0.5 * X^2 + X + 高斯噪声
# 数据可视化
plt.plot(X, y, 'b.')
plt.xlabel('X_1')
plt.ylabel('y')
plt.axis([-3, 3, -5, 10])
plt.show()
运行结果
2、多项式回归
接下来,使用 PolynomialFeatures 将输入数据转换为多项式特征,然后应用线性回归模型。
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# 多项式变换
poly_features = PolynomialFeatures(degree=2, include_bias=False) # degree=2 进行二次变换
X_poly = poly_features.fit_transform(X)
# 线性回归拟合
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
3、可视化拟合效果
绘制原始数据与拟合曲线,比较模型的表现。
X_new = np.linspace(-3, 3, 100).reshape(100, 1)
X_new_poly = poly_features.transform(X_new)
y_new = lin_reg.predict(X_new_poly)
plt.plot(X, y, 'b.')
plt.plot(X_new, y_new, 'r--', label='prediction')
plt.axis([-3, 3, -5, 10])
plt.legend()
plt.show()
运行结果
4、对比试验:不同多项式阶数的拟合效果
在实际应用中,选择适当的多项式阶数对于拟合效果至关重要。过低的阶数可能导致欠拟合,而过高的阶数可能会导致过拟合。因此,可以尝试不同阶数的多项式进行拟合,观察它们在数据上的表现。
for style, width, degree in (('g-', 1, 100), ('b--', 2, 2), ('r-+', 1, 1)):
poly_features = PolynomialFeatures(degree=degree, include_bias=False)
std = StandardScaler() # 标准化特征
lin_reg = LinearRegression()
polynomial_reg = Pipeline([('poly_features', poly_features),
('StandardScaler', std),
('lin_reg', lin_reg)])
polynomial_reg.fit(X, y)
y_new_2 = polynomial_reg.predict(X_new)
plt.plot(X_new, y_new_2, style, label='degree ' + str(degree), linewidth=width)
plt.plot(X, y, 'b.')
plt.axis([-3, 3, -5, 10])
plt.legend()
plt.show()
该实验展示了不同多项式阶数下模型的拟合效果。可以看到,阶数为 1 的模型表现较差,无法很好地拟合数据,而阶数过高(例如100)的模型可能会对数据中的噪声过度拟合,导致拟合曲线显得非常弯曲。
运行结果
5、 过拟合实验:高阶多项式的过拟合
为了更好地展示过拟合的现象,可以使用多项式回归进行实验。随着多项式的阶数增加,模型的复杂度也随之增加,这通常会导致过拟合——尤其是当数据量较少时,模型过度学习训练数据中的噪声。
5.1 训练集和验证集误差随样本数的变化
在本实验中将绘制学习曲线,通过不同的训练集大小来观察训练集误差与验证集误差的变化。
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
# 数据样本数量对结果的影响
def plot_learning_curve(model, X, y):
# 训练集占80%,测试集占20%,再指定一个随机种子(相当于每一次数据切分都是相同的方式)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=0)
train_errors, val_errors = [], []
# 测试不同样本的个数对结果的影响
for m in range(1, len(X_train)):
# 训练
model.fit(X_train[:m], y_train[:m])
# 预测和评估(先算训练集的结果,然后验证集的结果)
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train[:m], y_train_predict[:m]))
val_errors.append(mean_squared_error(y_val, y_val_predict))
# 绘制学习曲线
plt.plot(np.sqrt(train_errors), 'r-+', linewidth=2, label='train_error')
plt.plot(np.sqrt(val_errors), 'b-', linewidth=2, label='val_error')
plt.xlabel('Training set size')
plt.ylabel('RMSE')
plt.legend()
# 线性回归的学习曲线
plot_learning_curve(lin_reg, X, y)
plt.axis([0, 80, 0, 3])
plt.show()
运行结果
5.2 多项式回归的过拟合风险
接下来,使用高阶多项式回归来观察过拟合的风险。通过将多项式的阶数设置为较高的值(例如20),可以看到过拟合的现象。随着阶数增加,模型越来越复杂,导致训练误差急剧下降,但验证误差却在增大。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# 多项式回归的过拟合风险
polynomial_reg = Pipeline([('poly_features', PolynomialFeatures(degree=20, include_bias=False)),
('lin_reg', LinearRegression())])
plot_learning_curve(polynomial_reg, X, y)
plt.axis([0, 80, 0, 4])
plt.show()
degree值越大,过拟合的风险越高,所以千万别把degree值设置的很大
运行结果
6、正则化实验:通过岭回归和Lasso回归解决过拟合
为了解决过拟合问题,我们可以使用正则化技术。常见的正则化方法有岭回归(Ridge)和Lasso回归(Lasso)。这两种方法分别通过加上L2正则化和L1正则化来减少模型的复杂性,抑制过拟合现象。
6.1 岭回归(Ridge Regression)
岭回归通过在损失函数中增加一个正则化项(L2范数)来惩罚过大的回归系数,从而防止过拟合。
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# 岭回归正则化实验
def plot_model(model_class, polynomial, alphas, **model_kwargs):
for alpha, style in zip(alphas, ('b-', 'r--', 'g:')):
model = model_class(alpha, **model_kwargs)
if polynomial:
model = Pipeline([('poly_features', PolynomialFeatures(degree=10, include_bias=False)),
('StandardScaler', StandardScaler()),
('lin_reg', model)])
model.fit(X, y)
y_new_regul = model.predict(X_new)
lw = 2 if alpha > 0 else 1
plt.plot(X_new, y_new_regul, style, linewidth=lw, label='alpha={}'.format(alpha))
plt.plot(X, y, 'b.', linewidth=3)
plt.legend()
plt.figure(figsize=(10, 5))
plt.subplot(121)
plot_model(Ridge, polynomial=False, alphas=(0, 10, 100)) # alpha值控制正则化力度
plt.subplot(122)
plot_model(Ridge, polynomial=True, alphas=(0, 10**-5, 1))
plt.show()
解释:这里设置了不同的 alpha 值来调整正则化的强度。alpha 值越大,正则化的效果越强,回归系数的幅度会被限制,从而减少过拟合。通过岭回归,模型能够在保证较小误差的同时,避免过度复杂的拟合。
运行结果
6.2 Lasso回归(Lasso Regression)
Lasso回归与岭回归类似,不同的是它使用L1范数来进行正则化,这种方法不仅能减少过拟合,还能进行特征选择(即某些特征的系数会被压缩为零)。
from sklearn.linear_model import Lasso
# Lasso回归正则化实验
plt.figure(figsize=(10,5))
plt.subplot(121)
plot_model(Lasso, polynomial=False, alphas=(0, 0.01, 1)) # alpha控制正则化强度
plt.subplot(122)
plot_model(Lasso, polynomial=True, alphas=(0, 10**-1, 1))
plt.show()
解释:在Lasso回归中,较大的 alpha 会使得一些回归系数变为零,从而实现特征选择。这意味着Lasso回归不仅能避免过拟合,还能通过压缩不重要的特征来简化模型,提升模型的可解释性。
运行结果
完整代码
# 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
# 1. 随机生成数据
# 随机选择100个点数据
m = 100
X = 6 * np.random.rand(m, 1) - 3 # X的取值范围为[-3, 3]
y = 0.5 * X ** 2 + X + np.random.randn(m, 1) # y = 0.5 * X^2 + X + 高斯噪声
# 数据可视化
plt.plot(X, y, 'b.')
plt.xlabel('X_1')
plt.ylabel('y')
plt.axis([-3, 3, -5, 10])
plt.show()
# 2. 多项式回归 - 基本模型
# 多项式变换(度数为2)
poly_features = PolynomialFeatures(degree=2, include_bias=False) # degree=2 进行二次变换
X_poly = poly_features.fit_transform(X)
# 线性回归拟合
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
# 用训练好的模型预测
X_new = np.linspace(-3, 3, 100).reshape(100, 1)
X_new_poly = poly_features.transform(X_new)
y_new = lin_reg.predict(X_new_poly)
# 可视化拟合效果
plt.plot(X, y, 'b.')
plt.plot(X_new, y_new, 'r--', label='prediction')
plt.axis([-3, 3, -5, 10])
plt.legend()
plt.show()
# 3. 对比试验:不同多项式阶数的拟合效果
for style, width, degree in (('g-', 1, 100), ('b--', 2, 2), ('r-+', 1, 1)):
poly_features = PolynomialFeatures(degree=degree, include_bias=False)
std = StandardScaler() # 标准化特征
lin_reg = LinearRegression()
polynomial_reg = Pipeline([('poly_features', poly_features),
('StandardScaler', std),
('lin_reg', lin_reg)])
polynomial_reg.fit(X, y)
y_new_2 = polynomial_reg.predict(X_new)
plt.plot(X_new, y_new_2, style, label='degree ' + str(degree), linewidth=width)
plt.plot(X, y, 'b.')
plt.axis([-3, 3, -5, 10])
plt.legend()
plt.show()
# 4. 数据样本数量对结果的影响:学习曲线
def plot_learning_curve(model, X, y):
# 训练集占80%,测试集占20%,再指定一个随机种子
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=0)
train_errors, val_errors = [], []
# 测试不同样本的个数对结果的影响
for m in range(1, len(X_train)):
# 训练
model.fit(X_train[:m], y_train[:m])
# 预测和评估
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train[:m], y_train_predict[:m]))
val_errors.append(mean_squared_error(y_val, y_val_predict))
# 绘制学习曲线
plt.plot(np.sqrt(train_errors), 'r-+', linewidth=2, label='train_error')
plt.plot(np.sqrt(val_errors), 'b-', linewidth=2, label='val_error')
plt.xlabel('Training set size')
plt.ylabel('RMSE')
plt.legend()
# 线性回归的学习曲线
plot_learning_curve(lin_reg, X, y)
plt.axis([0, 80, 0, 3])
plt.show()
# 5. 多项式回归的过拟合风险
polynomial_reg = Pipeline([('poly_features', PolynomialFeatures(degree=20, include_bias=False)),
('lin_reg', LinearRegression())])
plot_learning_curve(polynomial_reg, X, y)
plt.axis([0, 80, 0, 4])
plt.show()
# 6. 正则化解决过拟合的问题
# 岭回归正则化实验
def plot_model(model_class, polynomial, alphas, **model_kwargs):
for alpha, style in zip(alphas, ('b-', 'r--', 'g:')):
model = model_class(alpha, **model_kwargs)
if polynomial:
model = Pipeline([('poly_features', PolynomialFeatures(degree=10, include_bias=False)),
('StandardScaler', StandardScaler()),
('lin_reg', model)])
model.fit(X, y)
y_new_regul = model.predict(X_new)
lw = 2 if alpha > 0 else 1
plt.plot(X_new, y_new_regul, style, linewidth=lw, label='alpha={}'.format(alpha))
plt.plot(X, y, 'b.', linewidth=3)
plt.legend()
# 岭回归正则化(无多项式和多项式两种情况)
plt.figure(figsize=(10, 5))
plt.subplot(121)
plot_model(Ridge, polynomial=False, alphas=(0, 10, 100)) # alpha值控制正则化力度
plt.subplot(122)
plot_model(Ridge, polynomial=True, alphas=(0, 10**-5, 1))
plt.show()
# Lasso回归正则化实验
plt.figure(figsize=(10, 5))
plt.subplot(121)
plot_model(Lasso, polynomial=False, alphas=(0, 0.01, 1)) # alpha控制正则化强度
plt.subplot(122)
plot_model(Lasso, polynomial=True, alphas=(0, 10**-1, 1))
plt.show()
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐









所有评论(0)