Scikit-learn 线性回归:完整指南与示例

更新于 2026-02-05

Mark Pedigo 2025-03-05

线性回归是统计学和机器学习中的一项基础技术,用于建模变量之间的关系。简单来说,它使我们能够根据一个或多个影响因素来预测结果。它被广泛应用于房地产定价、销售预测、风险评估以及许多其他领域。

在本教程中,我们将探讨 scikit-learn 中的线性回归,涵盖其工作原理、为何有用,以及如何使用 scikit-learn 实现它。到最后,你将能够构建并评估一个线性回归模型,以进行数据驱动的预测。

房价与房间数量的散点图 房价与房间数量的散点图

线性回归与机器学习

除了在确定房价方面的直接用途外,线性回归在机器学习中也扮演着重要角色:

  • 它是理解更高级技术(如逻辑回归、神经网络和支持向量机)的基础模型。
  • 训练速度快,非常适合快速原型设计。
  • 它还可作为比较的基准。如果更复杂的模型没有显著优于它,那么它们增加的复杂性可能并不合理。
  • 与某些技术(如深度学习)不同,它是易于解释的。
  • 它有助于特征选择,识别最有用的预测因子。

尽管简单,但由于其高效性、可解释性和多功能性,线性回归在机器学习中仍然是不可或缺的工具。

线性回归与 scikit-learn 库

scikit-learn 库使得线性回归的实现变得非常简单。该库具有诸多优势:

  • 它拥有统一的接口。实现不同机器学习算法所需的代码结构相似。
  • 代码简洁,复杂的数学和实现细节被封装起来。例如,只需使用 model.fit(X_train, y_train) 这一行代码即可在训练数据上拟合模型。
  • 它提供对模型系数的便捷访问。
  • 它内置了用于评估模型性能的指标。
  • 它可以轻松地将线性回归(或其他任何机器学习算法)与预处理步骤(如缩放和特征选择)通过 Pipeline 集成。

理解线性回归

正如我们所见,在简单线性回归中,数据使用一条“最佳拟合直线”进行建模。该直线的公式为:

y=mx+by = mx + b

其中, mm 是直线的斜率, bb 是截距。

“多元线性回归”将单个预测变量的情况推广到多个预测变量(如房间数量、距海洋的距离、社区的中位收入)。其公式被推广为:

y=b0+b1x1+b2x2++bnxny = b_0 + b_1x_1 + b_2x_2 + \dots + b_nx_n

其中每个 xix_i 是一个自变量,对应的 bib_i 是其系数。在三维空间中,这条直线被推广为一个平面。在更高维空间中,该平面变成一个“超平面”。

我们如何解释这些系数和截距?截距是在所有自变量都为 0 时 yy 的预测值;换句话说,它是当预测变量没有任何贡献时因变量的基线值。每个系数 bib_i 表示在其他所有自变量保持不变的情况下, xix_i 每变化一个单位,因变量 yy 的变化量。

设置环境

安装 scikit-learn 非常简单。只需使用命令 pip install scikit-learn。如果你想安装特定版本,比如 1.2.2,则修改命令为:pip install scikit-learn==1.2.2。如果你使用 Anaconda,scikit-learn 应该已经预装。如果出于某种原因仍需在 Anaconda 环境中安装,请使用命令 conda install scikit-learn

在使用 scikit-learn 时,有几个库是必需或推荐的。numpy 库用于存储特征和标签。pandas 库推荐用于加载、预处理和探索数据集。

如果你正在使用 scikit-learn,很可能已经在使用 pandas 进行数据准备。为了绘制结果,你可能会使用 matplotlibseaborn,或者两者结合。这些库都可以使用类似于上面的 pip install 命令进行安装。你甚至可以使用一条命令安装多个库:

pip install scikit-learn numpy pandas matplotlib seaborn

在 sklearn 中实现线性回归

在加载数据集之前,让我们先导入常用的库。

# 导入库。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

加载数据集

让我们使用著名的加州住房数据集。

# 读取加州住房数据集。
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()

准备数据

让我们将数据拆分为训练集和测试集。从 sklearn.model_selection 导入 train_test_split() 方法,然后调用它,指定测试集比例和 random_state。我们还将使用简单线性回归,仅使用对应于平均房间数的特征。

# 导入 train_test_split。
from sklearn.model_selection import train_test_split

# 创建特征 X 和目标 y。
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = housing.target  # 房屋价值中位数,单位为 $100,000

# 将数据集拆分为训练集(80%)和测试集(20%)。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

现在我们已经将数据拆分为训练集和测试集,接下来对特征进行标准化。此过程确保所有变量处于同一尺度,这可以提高模型性能和数值稳定性。

# 导入 StandardScaler。
from sklearn.preprocessing import StandardScaler

# 实例化 StandardScaler。
scaler = StandardScaler()

# 拟合并转换训练数据。
X_train_scaled = scaler.fit_transform(X_train)

# 同样转换测试数据。
X_test_scaled = scaler.transform(X_test)

在此代码中,StandardScaler 是一种数据预处理工具,用于去除均值并将特征缩放到单位方差。这有助于防止某些特征因其尺度差异而在模型中占据主导地位。

使用 fit_transform() 方法在训练数据上拟合缩放器。然后使用 transform() 方法单独转换测试数据,以确保其使用与训练数据相同的缩放参数,从而防止数据泄露。

训练线性回归模型

要创建线性回归模型,从 sklearn.linear_model 导入 LinearRegression()。调用它并将其赋值给一个变量。

# 导入 LinearRegression。
from sklearn.linear_model import LinearRegression

# 实例化线性回归模型。
model = LinearRegression()

使用训练数据拟合模型非常简单。

# 将模型拟合到训练数据。
model.fit(X_train_scaled, y_train)

进行预测

现在我们已经训练好了模型,可以在测试集上进行预测。

# 对测试数据进行预测。
y_pred = model.predict(X_test_scaled)

评估模型性能

现在我们已经在测试集上做出了预测,需要了解这些预测与实际情况的匹配程度。有几种指标可用于评估回归算法的性能。其中最常见的是决定系数(R²)、均方误差(MSE)和均方根误差(RMSE)。

决定系数(记作 R²)衡量回归模型解释目标变量变异性的能力。换句话说,它量化了目标变量中的变异性有多少是由预测变量解释的,也称为拟合优度。

为了进一步理解这一点,让我们看一下其公式:

R2=1(yactualypredicted)2(yactualyˉ)2R^2 = 1 - \frac{\sum (y_{\text{actual}} - y_{\text{predicted}})^2}{\sum (y_{\text{actual}} - \bar{y})^2}

其中, yactualy_{\text{actual}} 是目标变量的实际值, ypredictedy_{\text{predicted}} 是模型的预测值, yˉ\bar{y} 是实际值的均值。该公式帮助我们理解模型解释了多少目标变量的方差。分母表示数据中的总方差,而分子表示应用回归模型后未被解释的方差。因此,该比率给出了模型所解释的方差百分比。

我们如何解释 R²?

  • R² = 1:模型完美解释了目标变量的所有方差。
  • R² = 0:模型无法解释任何方差;预测效果不比直接使用均值更好。
  • R² < 0:模型表现比使用均值还差,表明拟合效果很差。

一些需要注意的关键点:

  • 更高的 R² 并不总是更好。高 R² 可能表示过拟合,尤其是在复杂模型中。
  • 添加更多特征会人为地提高 R²,因此更高的值不一定代表更好的模型。
  • 对于多元回归,应使用调整后的 R²,它考虑了预测变量的数量,避免因引入不必要的变量而导致的误导性改进。

使用 scikit-learn 评估模型性能中的决定系数非常简单。

# 导入指标。
from sklearn.metrics import mean_squared_error, r2_score

# 计算并打印 R² 分数。
r2 = r2_score(y_test, y_pred)
print(f"R-squared: {r2:.4f}")

输出:

R-squared: 0.0138

其他常用指标包括均方误差(MSE)和均方根误差(RMSE)。这些指标衡量模型预测值与实际值之间的偏离程度。

MSE 计算实际值与预测值之间平方差的平均值:

MSE=1ni=1n(yactual,iypredicted,i)2\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_{\text{actual},i} - y_{\text{predicted},i})^2

由于在求平均前对误差进行了平方,较大的误差会被更严重地惩罚,因此 MSE 对异常值敏感。较低的 MSE 表示模型拟合得更好。

为了解决这个问题,使用 RMSE,它只是 MSE 的平方根。由于 RMSE 与目标变量具有相同的单位,因此它提供了一个更直观的度量,表示预测平均偏离实际值的程度。

使用 scikit-learn 计算 MSE 和 RMSE 非常简单。

# 计算并打印 MSE。
mse = mean_squared_error(y_test, y_pred)
print(f"Mean squared error: {mse:.4f}")

# 计算并打印 RMSE。
rmse = mse ** 0.5
print(f"Root mean squared error: {rmse:.4f}")

输出:

Mean squared error: 1.2923
Root mean squared error: 1.1368

在 scikit-learn 中使用多元线性回归

让我们使用所有可用特征重新运行模型,而不仅仅是平均房间数。你预期结果会更好还是更差?

# 使用所有特征。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

# 加载数据集。
housing = fetch_california_housing()

# 拆分为 X, y。
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = housing.target  # 房屋价值中位数,单位为 $100,000
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 缩放数据。
scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 创建模型并将其拟合到训练数据。
model = LinearRegression()
model.fit(X_train_scaled, y_train)

# 进行预测。
y_pred = model.predict(X_test_scaled)

# 计算并打印误差。
r2 = r2_score(y_test, y_pred)
print(f"R-squared: {r2:.4f}")

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

rmse = mse ** 0.5
print(f"Root mean squared error: {rmse:.4f}")

输出:

R-squared: 0.5758
Mean squared error: 0.5559
Root mean squared error: 0.7456

我们可以看到,结果比仅使用一个特征时要好得多。然而,这引发了一个问题:我们是否真的需要所有特征?有些特征是否比其他特征更重要?从数据集中选择最相关的特征被称为特征选择

特征选择之所以重要,原因如下:

  • 减少过拟合:特征越少,模型越简单,从而降低过拟合风险。
  • 提高准确性:移除无关或冗余特征有助于模型聚焦于有意义的模式。
  • 增强可解释性:通过突出最重要的因素,使模型更易于理解。
  • 加快训练速度:减少特征数量可降低计算时间和内存消耗。

当多个特征高度相关时,它们是冗余的,意味着它们本质上向模型提供了相同的信息。这种情况被称为多重共线性。虽然多重共线性并不总是影响预测模型的准确性,但它会使特征选择和解释变得复杂,尤其是在线性回归及相关模型中。

方差膨胀因子(VIF)是一种用于检测预测变量之间多重共线性的指标。对于每个预测变量,VIF 的计算公式为:

VIF(Xi)=11Ri2\text{VIF}(X_i) = \frac{1}{1 - R_i^2}

其中, Ri2R_i^2 是当预测变量 XiX_i 对模型中所有其他预测变量进行回归时得到的 R² 值。VIF 越高,说明该预测变量与其他变量的相关性越强。

  • VIF = 1:无多重共线性(理想情况)。
  • VIF < 5:低至中等多重共线性(通常可接受)。
  • VIF > 5:高多重共线性(考虑移除或合并相关变量)。
  • VIF > 10:严重多重共线性(强烈建议存在变量冗余)。
# 导入库。
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_california_housing
from statsmodels.stats.outliers_influence import variance_inflation_factor

# 加载数据集。
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)

# 计算相关矩阵。
corr_matrix = X.corr()

# 识别高共线性特征对(相关系数 > 0.8 或 < -0.8)。
high_corr_features = [(col1, col2, corr_matrix.loc[col1, col2])
                     for col1 in corr_matrix.columns
                     for col2 in corr_matrix.columns
                     if col1 != col2 and abs(corr_matrix.loc[col1, col2]) > 0.8]

# 转换为 DataFrame 以便更好地可视化。
collinearity_df = pd.DataFrame(high_corr_features, columns=["Feature 1", "Feature 2", "Correlation"])
print("\nHighly Correlated Features:\n", collinearity_df)

# 计算每个特征的方差膨胀因子(VIF)。
vif_data = pd.DataFrame()
vif_data["Feature"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]

# 打印 VIF 值。
print("\nVariance Inflation Factor (VIF) for each feature:\n", vif_data)

输出:

   Highly Correlated Features:
       Feature 1  Feature 2  Correlation
   0   AveRooms  AveBedrms     0.847621
   1  AveBedrms   AveRooms     0.847621
   2   Latitude  Longitude    -0.924664
   3  Longitude   Latitude    -0.924664
  
   Variance Inflation Factor (VIF) for each feature:
          Feature         VIF
   0      MedInc   11.511140
   1    HouseAge    7.195917
   2    AveRooms   45.993601
   3   AveBedrms   43.590314
   4  Population    2.935745
   5    AveOccup    1.095243
   6    Latitude  559.874071
   7   Longitude  633.711654

让我们从模型中移除 AveBedrms 特征。

# 导入库。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

# 加载加州住房数据集。
housing = fetch_california_housing()

# 创建 DataFrame 并移除 "AveBedrms" 特征。
X = pd.DataFrame(housing.data, columns=housing.feature_names).drop(columns=["AveBedrms"])
y = housing.target  # 房屋价值中位数,单位为 $100,000

# 将数据拆分为训练集和测试集。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 缩放数据(标准化)。
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 创建线性回归模型并进行训练。
model = LinearRegression()
model.fit(X_train_scaled, y_train)

# 对测试集进行预测。
y_pred = model.predict(X_test_scaled)

# 计算性能指标。
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
# 打印评估指标
print(f"R-squared: {r2:.4f}")
print(f"Mean squared error: {mse:.4f}")
print(f"Root mean squared error: {rmse:.4f}")

输出:

R-squared: 0.5823
Mean squared error: 0.5473
Root mean squared error: 0.7398

结果(略微)有所改善。

提取模型洞察

构建回归模型只是第一步;理解其输出同样重要。通过分析模型的系数,我们可以确定哪些特征对预测的影响最大。

理解回归系数

一旦线性回归模型被训练完成,可以使用 model.coef_ 访问系数。可以使用 model.intercept_ 访问截距。

print("Intercept:", model.intercept_)

coeff_df = pd.DataFrame({"Feature": X.columns, "Coefficient": model.coef_})
print("\nFeature Coefficients:\n", coeff_df)

输出:

Intercept: 2.0719469373788777  

Feature Coefficients:

      Feature  Coefficient
0      MedInc     0.725747
1    HouseAge     0.121519
2    Latitude    -0.943105
3   Longitude    -0.900735

总结模型结果

由于 Scikit-Learn 不像 Statsmodels 那样提供内置的 summary() 方法,我们可以手动提取并使用回归系数可视化每个特征的重要性。绝对值较大的系数对应的特征对目标变量的影响更强。请参考以下代码。

# 按系数对 dataframe 排序。
coef_df_sorted = coef_df.sort_values(by="Coefficient", ascending=False)

# 创建图表。
plt.figure(figsize=(8,6))
plt.barh(coef_df["Feature"], coef_df_sorted["Coefficient"], color="blue")
plt.xlabel("Coefficient Value")
plt.ylabel("Feature")
plt.title("Feature Importance (Linear Regression Coefficients)")
plt.show()

基于系数值的特征重要性图 基于系数值的特征重要性图

现在,让我们可视化残差和回归拟合情况。

# 计算残差。
residuals = y_test - y_pred

# 创建图表。
plt.figure(figsize=(12,5))

# 图1:残差分布。
plt.subplot(1,2,1)
sns.histplot(residuals, bins=30, kde=True, color="blue")
plt.axvline(x=0, color='red', linestyle='--')
plt.title("Residuals Distribution")
plt.xlabel("Residuals (y_actual - y_predicted)")
plt.ylabel("Frequency")

# 图2:回归拟合(实际值 vs 预测值)。
plt.subplot(1,2,2)
sns.scatterplot(x=y_test, y=y_pred, alpha=0.5)
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], color='red', linestyle='--')  # 完美拟合线
plt.title("Regression Fit: Actual vs Predicted")
plt.xlabel("Actual Prices (in $100,000s)")
plt.ylabel("Predicted Prices (in $100,000s)")

# 显示图表。
plt.tight_layout()
plt.show()

残差分布与回归拟合图 残差分布与回归拟合图

残差分布(左图)应围绕零值居中,表明误差是随机分布的。如果残差服从正态分布,则说明模型拟合良好;但如果存在偏斜或趋势,则可能表明存在系统性误差。回归拟合图(右图)比较了实际值与预测值,红色虚线代表完美拟合。如果点紧密围绕该线分布,则预测准确;但如果出现某种模式(例如曲线),则说明变量间的关系可能并非真正的线性。

这些可视化有助于诊断过拟合或欠拟合,揭示残差中的模式(暗示缺失关系),并清晰评估模型的有效性。

现实世界的应用

线性回归在各行各业中被广泛用于预测和决策制定。在房地产领域,它根据面积和位置等因素估算房价。

销售和市场营销部门使用它进行需求预测和预算优化,医疗保健领域则将其应用于疾病风险评估。在金融领域,它有助于股票价格预测和信用评分;在制造业中,它用于质量控制和故障预测。

何时使用线性回归

  • 特征与目标变量之间存在线性关系。
  • 可解释性和简洁性比复杂建模更重要。
  • 数据需要最少的特征工程。

何时不使用线性回归

  • 目标变量与特征之间的关系复杂且非线性。
  • 数据高度相关。
  • 数据包含无法移除的异常值。在这种情况下,你可能需要应用数据变换或使用策略来减轻其影响。

结论

线性回归仍然是机器学习和统计建模中最基本、应用最广泛的技术之一。尽管其简单,但它是一种强大的工具,可用于理解变量之间的关系,并在各种现实应用场景中进行预测。

以下是本教程的关键要点:

  • 多样化的应用:线性回归在多个行业和问题领域提供有价值的见解。
  • 可解释性强:与复杂的黑盒模型不同,线性回归提供基于系数的清晰解释,使其易于理解和说明。
  • 特征选择:恰当地选择特征并处理多重共线性,可确保模型准确、稳定且可靠。