跳到主要内容

回归算法

回归是监督学习的重要任务,用于预测连续数值型目标变量。与分类问题预测离散类别不同,回归关注的是预测具体的数值。从房价预测到销量预估,从温度预测到股票趋势分析,回归算法在实际应用中无处不在。

回归问题概述

什么是回归?

回归分析的目标是找到一个函数,将输入特征映射到连续的输出值。数学上,我们希望找到一个函数 ff,使得:

yf(x)y \approx f(x)

其中 yy 是目标变量(连续值),xx 是特征向量。

回归问题的分类

简单线性回归:只有一个特征,预测单个数值。例如,根据房屋面积预测房价。

多元回归:使用多个特征预测。例如,根据面积、房间数、位置、年龄等多个特征预测房价。

多项式回归:捕捉特征与目标之间的非线性关系。例如,根据温度预测冰淇淋销量(可能是二次曲线关系)。

回归 vs 分类

方面回归分类
输出类型连续值离散类别
例子房价:350万类别:涨/跌
评估指标MSE、R²、MAE准确率、F1
常用算法线性回归、随机森林回归逻辑回归、SVM

线性回归

线性回归是最基础的回归方法,它假设目标变量与特征之间存在线性关系。

普通最小二乘法(OLS)

普通最小二乘法通过最小化预测值与真实值之间的残差平方和来估计模型参数。

数学原理

线性回归模型假设:

y^=w0+w1x1+w2x2+...+wpxp=w0+wTx\hat{y} = w_0 + w_1 x_1 + w_2 x_2 + ... + w_p x_p = w_0 + w^T x

目标是最小化残差平方和:

minwi=1n(yiy^i)2=minwXwy22\min_{w} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 = \min_{w} ||Xw - y||_2^2

其中 XX 是特征矩阵(包含截距项),ww 是参数向量,yy 是目标向量。

为什么是最小二乘法?

从概率角度,假设误差项 ϵN(0,σ2)\epsilon \sim N(0, \sigma^2),即误差服从均值为零的正态分布。那么最大似然估计等价于最小化残差平方和。这就是最小二乘法的统计基础。

解析解

线性回归有闭式解(正规方程):

w=(XTX)1XTyw = (X^T X)^{-1} X^T y

但当特征之间存在高度相关性(多重共线性)时,XTXX^T X 接近奇异矩阵,解变得不稳定。

基本用法

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# 创建示例数据
np.random.seed(42)
X = np.random.rand(100, 1) * 10 # 100个样本,1个特征
y = 2 * X.ravel() + 1 + np.random.randn(100) * 2 # y = 2x + 1 + 噪声

# 划分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建并训练模型
model = LinearRegression()
model.fit(X_train, y_train)

# 预测
y_pred = model.predict(X_test)

# 输出结果
print(f"系数 (斜率): {model.coef_[0]:.4f}")
print(f"截距: {model.intercept_:.4f}")
print(f"R² 分数: {r2_score(y_test, y_pred):.4f}")
print(f"均方误差: {mean_squared_error(y_test, y_pred):.4f}")

输出示例:

系数 (斜率): 1.9923
截距: 1.1567
R² 分数: 0.8923
均方误差: 3.8421

线性回归的假设

理解线性回归的假设条件对于正确使用和解释结果至关重要。

假设 1:线性关系

目标变量与特征之间存在线性关系。可以通过散点图或残差图检验。

假设 2:误差独立性

残差之间相互独立,没有自相关。对于时间序列数据尤为重要,可以用 Durbin-Watson 检验。

假设 3:同方差性

残差的方差在所有特征值范围内保持恒定。如果残差的方差随预测值变化,称为异方差。可以通过残差图检验。

假设 4:误差正态性

残差服从正态分布。可以用 Q-Q 图或 Shapiro-Wilk 检验。

假设 5:无多重共线性

特征之间不存在高度相关性。多重共线性会导致系数估计不稳定,标准误变大。可以用方差膨胀因子(VIF)检验:

from statsmodels.stats.outliers_influence import variance_inflation_factor
import pandas as pd

def calculate_vif(X):
"""计算方差膨胀因子"""
vif_data = pd.DataFrame()
vif_data["特征"] = range(X.shape[1])
vif_data["VIF"] = [variance_inflation_factor(X, i) for i in range(X.shape[1])]
return vif_data

# VIF > 10 表示存在严重多重共线性
# VIF > 5 需要关注

非负最小二乘

当系数必须为非负时(如价格、频率等物理量),可以使用非负最小二乘:

model = LinearRegression(positive=True)
model.fit(X_train, y_train)

计算复杂度

普通最小二乘法的计算复杂度为 O(nsamplesnfeatures2)O(n_{samples} \cdot n_{features}^2),通过奇异值分解(SVD)求解。当特征数远大于样本数时,计算成本较高。

正则化回归

当特征之间存在相关性或特征数量较多时,普通线性回归容易过拟合。正则化通过在损失函数中添加惩罚项来约束模型复杂度。

岭回归(Ridge Regression)

岭回归在普通线性回归的基础上添加 L2 正则化项。

数学原理

minwXwy22+αw22\min_{w} ||Xw - y||_2^2 + \alpha ||w||_2^2

其中 α0\alpha \geq 0 是正则化参数,控制惩罚强度。α\alpha 越大,系数收缩越明显。

为什么岭回归有效?

从数学角度看,岭回归相当于在正规方程中加入对角矩阵:

w=(XTX+αI)1XTyw = (X^T X + \alpha I)^{-1} X^T y

这使得矩阵即使 XTXX^T X 接近奇异也能求逆。从贝叶斯角度,岭回归等价于假设系数服从零均值高斯先验。

基本用法

from sklearn.linear_model import Ridge

# 岭回归
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)

print(f"系数: {ridge.coef_}")
print(f"截距: {ridge.intercept_:.4f}")
print(f"测试 R²: {ridge.score(X_test, y_test):.4f}")

正则化参数 α\alpha 的影响

import matplotlib.pyplot as plt

alphas = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
coefs = []

for alpha in alphas:
ridge = Ridge(alpha=alpha)
ridge.fit(X_train, y_train)
coefs.append(ridge.coef_)

# 可视化系数随 alpha 的变化
plt.figure(figsize=(10, 6))
plt.plot(alphas, coefs)
plt.xscale('log')
plt.xlabel('alpha (正则化强度)')
plt.ylabel('系数值')
plt.title('岭回归系数路径')
plt.grid(True)
plt.show()

随着 α\alpha 增大,系数逐渐收缩向零,但不会变为零。

岭回归的适用场景

  • 特征之间存在多重共线性
  • 特征数量接近或超过样本数量
  • 需要稳定的系数估计
  • 不需要特征选择

Lasso 回归

Lasso(Least Absolute Shrinkage and Selection Operator)使用 L1 正则化,其重要特性是可以将系数压缩为零,从而实现特征选择。

数学原理

minw12nsamplesXwy22+αw1\min_{w} \frac{1}{2n_{samples}} ||Xw - y||_2^2 + \alpha ||w||_1

L1 范数的几何特性使得最优解往往落在坐标轴上,即某些系数恰好为零。

为什么 Lasso 能做特征选择?

L1 范数的等高线是菱形,与平方损失函数的等高线相切时,切点容易出现在坐标轴上。这意味着某些系数恰好为零。

基本用法

from sklearn.linear_model import Lasso

# Lasso 回归
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)

print(f"系数: {lasso.coef_}")
print(f"非零系数数量: {np.sum(lasso.coef_ != 0)}")
print(f"选择的特征索引: {np.where(lasso.coef_ != 0)[0]}")

Lasso vs Ridge 对比

from sklearn.datasets import make_regression

# 生成高维稀疏数据
X, y = make_regression(n_samples=100, n_features=50, n_informative=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Lasso
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
lasso_nonzero = np.sum(lasso.coef_ != 0)

# Ridge
ridge = Ridge(alpha=0.1)
ridge.fit(X_train, y_train)
ridge_nonzero = np.sum(np.abs(ridge.coef_) > 0.01)

print(f"Lasso 非零系数: {lasso_nonzero}")
print(f"Ridge 近似非零系数: {ridge_nonzero}")
print(f"Lasso 测试 R²: {lasso.score(X_test, y_test):.4f}")
print(f"Ridge 测试 R²: {ridge.score(X_test, y_test):.4f}")

Lasso 的适用场景

  • 需要进行特征选择
  • 真实模型是稀疏的(只有少数特征重要)
  • 高维数据(特征数远大于样本数)
  • 需要模型可解释性

弹性网络(ElasticNet)

弹性网络结合了 L1 和 L2 正则化,兼顾 Lasso 的特征选择能力和 Ridge 的稳定性。

数学原理

minw12nsamplesXwy22+αρw1+α(1ρ)2w22\min_{w} \frac{1}{2n_{samples}} ||Xw - y||_2^2 + \alpha \rho ||w||_1 + \frac{\alpha(1-\rho)}{2} ||w||_2^2

其中 ρ\rhol1_ratio 参数)控制 L1 和 L2 的混合比例:

  • ρ=1\rho = 1:等同于 Lasso
  • ρ=0\rho = 0:等同于 Ridge
  • 0<ρ<10 < \rho < 1:弹性网络

为什么需要弹性网络?

当多个特征高度相关时,Lasso 倾向于随机选择其中一个,而弹性网络倾向于选择一组相关特征。这使得弹性网络在特征相关性强的场景下更稳定。

基本用法

from sklearn.linear_model import ElasticNet

# 弹性网络
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5) # 50% L1, 50% L2
elastic.fit(X_train, y_train)

print(f"系数: {elastic.coef_}")
print(f"非零系数数量: {np.sum(elastic.coef_ != 0)}")
print(f"测试 R²: {elastic.score(X_test, y_test):.4f}")

参数选择

from sklearn.linear_model import ElasticNetCV

# 自动选择最优 alpha 和 l1_ratio
elastic_cv = ElasticNetCV(
alphas=[0.01, 0.1, 1, 10],
l1_ratio=[0.1, 0.5, 0.7, 0.9, 0.95, 1],
cv=5
)
elastic_cv.fit(X_train, y_train)

print(f"最优 alpha: {elastic_cv.alpha_}")
print(f"最优 l1_ratio: {elastic_cv.l1_ratio_}")

三种正则化方法对比

方法正则化类型特征选择系数收缩适用场景
RidgeL2是(不归零)多重共线性、稳定性优先
LassoL1是(可归零)稀疏模型、特征选择
ElasticNetL1 + L2相关特征组、通用场景

贝叶斯回归

贝叶斯回归将正则化参数视为可以从数据中学习的随机变量,提供了参数的不确定性估计。

贝叶斯岭回归

贝叶斯岭回归假设系数服从高斯先验,自动调整正则化强度。

from sklearn.linear_model import BayesianRidge

# 贝叶斯岭回归
bayes_ridge = BayesianRidge()
bayes_ridge.fit(X_train, y_train)

print(f"系数均值: {bayes_ridge.coef_}")
print(f"系数标准差: {bayes_ridge.sigma_}")
print(f"估计的噪声方差: {bayes_ridge.alpha_}")
print(f"估计的先验精度: {bayes_ridge.lambda_}")

贝叶斯回归的优势

  • 自动调整正则化参数
  • 提供参数的不确定性估计
  • 适合小样本数据
  • 可以输出预测的不确定性

自动相关性确定(ARD)

ARD 是一种更激进的贝叶斯回归,可以为每个系数设置不同的精度,产生更稀疏的模型。

from sklearn.linear_model import ARDRegression

# ARD 回归
ard = ARDRegression()
ard.fit(X_train, y_train)

print(f"系数: {ard.coef_}")
print(f"非零系数数量: {np.sum(np.abs(ard.coef_) > 1e-4)}")

非线性回归

当数据呈现非线性关系时,线性模型效果有限。以下是处理非线性关系的几种方法。

多项式回归

多项式回归通过添加特征的高次项来捕捉非线性关系。

原理

将原始特征 xx 扩展为 [1,x,x2,x3,...][1, x, x^2, x^3, ...],然后在这些扩展特征上进行线性回归。

基本用法

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
import numpy as np
import matplotlib.pyplot as plt

# 生成非线性数据
np.random.seed(42)
X = np.sort(np.random.rand(100, 1) * 5, axis=0)
y = np.sin(X).ravel() + np.random.normal(0, 0.1, X.shape[0])

# 不同次数的多项式回归
degrees = [1, 3, 5, 15]

plt.figure(figsize=(14, 10))

for i, degree in enumerate(degrees, 1):
# 创建多项式回归管道
model = Pipeline([
('poly', PolynomialFeatures(degree=degree)),
('linear', LinearRegression())
])

model.fit(X, y)
y_pred = model.predict(X)

plt.subplot(2, 2, i)
plt.scatter(X, y, s=10, label='数据')
plt.plot(X, y_pred, 'r-', label=f'{degree}次多项式')
plt.xlabel('X')
plt.ylabel('y')
plt.title(f'多项式次数: {degree}')
plt.legend()

plt.tight_layout()
plt.show()

多项式次数的选择

  • 次数过低:欠拟合,无法捕捉复杂模式
  • 次数过高:过拟合,对噪声敏感
  • 通常从 2-3 次开始尝试

注意事项

多项式回归容易产生极端值,特别是外推预测时。高次多项式在训练数据范围外的行为往往不可预测。

核岭回归

核岭回归结合了岭回归和核技巧,可以处理复杂的非线性关系。

from sklearn.kernel_ridge import KernelRidge

# 核岭回归
krr = KernelRidge(
alpha=1.0, # 正则化参数
kernel='rbf', # 核函数
gamma=0.1 # RBF 核参数
)
krr.fit(X_train, y_train)

print(f"测试 R²: {krr.score(X_test, y_test):.4f}")

常用核函数

核函数参数特点
linear等同于普通岭回归
polydegree, gamma, coef0多项式映射
rbfgamma高斯径向基,最常用
sigmoidgamma, coef0类似神经网络

支持向量回归(SVR)

支持向量回归使用核技巧和最大间隔原理进行回归。

from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

# SVR 需要特征标准化
pipeline = Pipeline([
('scaler', StandardScaler()),
('svr', SVR(kernel='rbf', C=1.0, epsilon=0.1))
])

pipeline.fit(X_train, y_train)
print(f"测试 R²: {pipeline.score(X_test, y_test):.4f}")

SVR 参数说明

  • C:正则化参数,值越大对错误的惩罚越大
  • epsilon:容忍边界,预测值在真实值 ± epsilon 范围内不计入损失
  • kernel:核函数类型

决策树回归

决策树不仅可以用于分类,也可以用于回归。它通过递归地划分特征空间来预测目标值。

决策树回归

from sklearn.tree import DecisionTreeRegressor

# 决策树回归
tree = DecisionTreeRegressor(
max_depth=5, # 最大深度
min_samples_split=5, # 分裂所需最小样本数
min_samples_leaf=2, # 叶节点最小样本数
random_state=42
)

tree.fit(X_train, y_train)

print(f"训练 R²: {tree.score(X_train, y_train):.4f}")
print(f"测试 R²: {tree.score(X_test, y_test):.4f}")
print(f"特征重要性: {tree.feature_importances_}")

决策树回归的特点

  • 不需要特征缩放
  • 可以处理非线性关系
  • 容易过拟合(需要限制深度或剪枝)
  • 输出是阶梯函数(非平滑)

随机森林回归

随机森林集成多棵决策树,通过平均降低方差,通常比单棵决策树效果更好。

from sklearn.ensemble import RandomForestRegressor

# 随机森林回归
rf = RandomForestRegressor(
n_estimators=100, # 树的数量
max_depth=10, # 最大深度
min_samples_split=5,
random_state=42,
n_jobs=-1 # 并行计算
)

rf.fit(X_train, y_train)

print(f"训练 R²: {rf.score(X_train, y_train):.4f}")
print(f"测试 R²: {rf.score(X_test, y_test):.4f}")

袋外评估

rf = RandomForestRegressor(
n_estimators=100,
oob_score=True, # 启用袋外评估
random_state=42
)
rf.fit(X_train, y_train)

print(f"袋外 R²: {rf.oob_score_:.4f}")

袋外评估不需要额外的验证集,利用训练时未被抽样的样本进行评估。

梯度提升回归

梯度提升通过逐步训练弱学习器来减少残差,通常能提供最好的预测性能。

from sklearn.ensemble import GradientBoostingRegressor

# 梯度提升回归
gb = GradientBoostingRegressor(
n_estimators=100, # 树的数量
learning_rate=0.1, # 学习率
max_depth=5, # 每棵树的最大深度
min_samples_split=5,
random_state=42
)

gb.fit(X_train, y_train)

print(f"训练 R²: {gb.score(X_train, y_train):.4f}")
print(f"测试 R²: {gb.score(X_test, y_test):.4f}")

# 查看各阶段的训练分数
print(f"训练过程分数: {gb.train_score_[-5:]}")

学习率与树数量的权衡

  • 较小的学习率需要更多的树,但通常泛化更好
  • 常用组合:learning_rate=0.01, n_estimators=1000
  • 或:learning_rate=0.1, n_estimators=100

模型评估指标

选择合适的评估指标对于正确评估回归模型至关重要。

常用指标详解

均方误差(MSE)

MSE=1ni=1n(yiy^i)2\text{MSE} = \frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2

特点:

  • 对大误差敏感(平方惩罚)
  • 单位是目标变量单位的平方
  • 损失函数可导,便于优化
from sklearn.metrics import mean_squared_error

mse = mean_squared_error(y_test, y_pred)
print(f"MSE: {mse:.4f}")

均方根误差(RMSE)

RMSE=MSE=1ni=1n(yiy^i)2\text{RMSE} = \sqrt{\text{MSE}} = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2}

特点:

  • 与目标变量单位相同,更易解释
  • 对大误差敏感
  • 是最常用的回归指标之一
import numpy as np
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"RMSE: {rmse:.4f}")

平均绝对误差(MAE)

MAE=1ni=1nyiy^i\text{MAE} = \frac{1}{n}\sum_{i=1}^{n}|y_i - \hat{y}_i|

特点:

  • 对异常值相对稳健
  • 单位与目标变量相同
  • 不如 MSE 便于优化(在零点不可导)
from sklearn.metrics import mean_absolute_error

mae = mean_absolute_error(y_test, y_pred)
print(f"MAE: {mae:.4f}")

决定系数(R²)

R2=1i=1n(yiy^i)2i=1n(yiyˉ)2=1SSresSStotR^2 = 1 - \frac{\sum_{i=1}^{n}(y_i - \hat{y}_i)^2}{\sum_{i=1}^{n}(y_i - \bar{y})^2} = 1 - \frac{SS_{res}}{SS_{tot}}

其中 yˉ\bar{y} 是目标变量的均值。

特点:

  • 取值范围通常在 [0, 1],但也可能为负
  • R² = 1 表示完美预测
  • R² = 0 表示模型等于简单预测均值
  • R² < 0 表示模型比均值预测还差
  • 可解释为模型解释目标变量方差的比例
from sklearn.metrics import r2_score

r2 = r2_score(y_test, y_pred)
print(f"R²: {r2:.4f}")

调整 R²

普通 R² 会随着特征数增加而增加,即使是无用特征。调整 R² 考虑了特征数量的惩罚:

Radj2=1(1R2)(n1)np1R^2_{adj} = 1 - \frac{(1-R^2)(n-1)}{n-p-1}

其中 nn 是样本数,pp 是特征数。

def adjusted_r2(r2, n, p):
"""计算调整 R²"""
return 1 - (1 - r2) * (n - 1) / (n - p - 1)

adj_r2 = adjusted_r2(r2, len(y_test), X_test.shape[1])
print(f"调整 R²: {adj_r2:.4f}")

平均绝对百分比误差(MAPE)

MAPE=100%ni=1nyiy^iyi\text{MAPE} = \frac{100\%}{n}\sum_{i=1}^{n}\left|\frac{y_i - \hat{y}_i}{y_i}\right|

特点:

  • 结果是百分比,便于理解
  • 目标变量不能为零
  • 对小目标值敏感
def mape(y_true, y_pred):
"""计算 MAPE"""
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100

mape_value = mape(y_test, y_pred)
print(f"MAPE: {mape_value:.2f}%")

指标选择指南

指标优点缺点适用场景
MSE可导、数学性质好单位不直观、对异常值敏感优化目标、理论分析
RMSE单位直观对异常值敏感一般回归任务
MAE稳健、单位直观零点不可导有异常值的数据
可解释、无量纲可能误导(特征多时)模型比较、解释
MAPE百分比直观不能处理零值销量预测、金融

残差分析

残差分析是检验回归模型假设的重要工具。

import matplotlib.pyplot as plt

# 计算残差
residuals = y_test - y_pred

# 残差图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 1. 残差 vs 预测值
axes[0, 0].scatter(y_pred, residuals, alpha=0.5)
axes[0, 0].axhline(y=0, color='r', linestyle='--')
axes[0, 0].set_xlabel('预测值')
axes[0, 0].set_ylabel('残差')
axes[0, 0].set_title('残差 vs 预测值')

# 2. 残差直方图
axes[0, 1].hist(residuals, bins=30, edgecolor='black')
axes[0, 1].set_xlabel('残差')
axes[0, 1].set_ylabel('频数')
axes[0, 1].set_title('残差分布')

# 3. Q-Q 图
from scipy import stats
stats.probplot(residuals, dist="norm", plot=axes[1, 0])
axes[1, 0].set_title('Q-Q 图')

# 4. 残差 vs 特征(以第一个特征为例)
axes[1, 1].scatter(X_test[:, 0], residuals, alpha=0.5)
axes[1, 1].axhline(y=0, color='r', linestyle='--')
axes[1, 1].set_xlabel('特征值')
axes[1, 1].set_ylabel('残差')
axes[1, 1].set_title('残差 vs 特征')

plt.tight_layout()
plt.show()

如何解读残差图

  • 残差 vs 预测值:应该随机分布,无规律。如果呈漏斗形,说明异方差;如果呈曲线,说明非线性。
  • 残差直方图:应该近似正态分布,均值接近零。
  • Q-Q 图:点应该大致落在对角线上,表示残差正态。

完整示例:加州房价预测

下面是一个完整的回归任务示例,展示从数据探索到模型评估的全过程:

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.pipeline import Pipeline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 1. 加载数据
housing = fetch_california_housing()
X = housing.data
y = housing.target
feature_names = housing.feature_names

print(f"数据形状: {X.shape}")
print(f"特征名称: {feature_names}")

# 2. 数据探索
df = pd.DataFrame(X, columns=feature_names)
df['target'] = y
print("\n数据统计:")
print(df.describe())

# 3. 划分数据
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)

# 4. 定义模型
models = {
"线性回归": Pipeline([
('scaler', StandardScaler()),
('model', LinearRegression())
]),
"岭回归": Pipeline([
('scaler', StandardScaler()),
('model', Ridge(alpha=1.0))
]),
"Lasso": Pipeline([
('scaler', StandardScaler()),
('model', Lasso(alpha=0.01))
]),
"弹性网络": Pipeline([
('scaler', StandardScaler()),
('model', ElasticNet(alpha=0.01, l1_ratio=0.5))
]),
"随机森林": RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1),
"梯度提升": GradientBoostingRegressor(n_estimators=100, random_state=42)
}

# 5. 训练和评估
results = []

for name, model in models.items():
# 训练
model.fit(X_train, y_train)

# 预测
y_pred = model.predict(X_test)

# 计算指标
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# 交叉验证
cv_scores = cross_val_score(model, X_train, y_train, cv=5, scoring='r2')

results.append({
"模型": name,
"MSE": mse,
"RMSE": rmse,
"MAE": mae,
"R²": r2,
"CV R² 均值": cv_scores.mean(),
"CV R² 标准差": cv_scores.std()
})

# 6. 显示结果
df_results = pd.DataFrame(results)
print("\n模型比较:")
print(df_results.to_string(index=False))

# 7. 特征重要性分析(使用随机森林)
rf_model = models["随机森林"]
importance = pd.DataFrame({
'feature': feature_names,
'importance': rf_model.feature_importances_
}).sort_values('importance', ascending=False)

print("\n特征重要性:")
print(importance)

# 8. 可视化特征重要性
plt.figure(figsize=(10, 6))
plt.barh(importance['feature'], importance['importance'])
plt.xlabel('重要性')
plt.title('随机森林特征重要性')
plt.gca().invert_yaxis()
plt.tight_layout()
plt.show()

# 9. 预测 vs 真实值
best_model = models["梯度提升"]
y_pred_best = best_model.predict(X_test)

plt.figure(figsize=(8, 8))
plt.scatter(y_test, y_pred_best, alpha=0.3)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('真实值')
plt.ylabel('预测值')
plt.title('预测 vs 真实值')
plt.tight_layout()
plt.show()

算法选择指南

选择合适的回归算法需要考虑多个因素:

按数据特点选择

数据特点推荐算法
线性关系、少量特征线性回归
多重共线性岭回归
需要特征选择Lasso
相关特征组弹性网络
非线性关系随机森林、梯度提升
小样本、需要不确定性贝叶斯回归
高维稀疏数据Lasso、弹性网络

按需求选择

需求推荐算法
可解释性线性回归、决策树
最高精度梯度提升、随机森林
快速训练线性回归、岭回归
特征选择Lasso、弹性网络
概率输出贝叶斯回归

实践建议

  1. 从简单开始:先用线性回归建立基线,再尝试复杂模型
  2. 交叉验证:使用交叉验证评估模型泛化能力
  3. 特征工程:好的特征往往比复杂模型更有效
  4. 正则化:特征多或相关性高时,优先使用正则化模型
  5. 集成方法:追求高精度时,随机森林和梯度提升是首选
  6. 残差分析:训练后检查残差,验证模型假设

小结

回归是机器学习的核心任务之一:

  1. 理解假设:线性回归有一系列假设,违反假设可能导致结果不可靠
  2. 正则化重要:当特征相关或数量多时,正则化能有效防止过拟合
  3. 选对指标:根据业务需求选择合适的评估指标
  4. 残差分析:通过残差分析验证模型假设,发现模型问题
  5. 算法选择:没有万能算法,根据数据特点和需求选择
  6. 特征工程:好的特征往往比复杂模型更有效

下一步可以学习 模型选择与评估,深入了解如何科学地评估和优化机器学习模型。