线性回归 R² 与调整后 R²:Python 实战对比 3 种模型复杂度下的指标变化

在数据分析的实际应用中,线性回归是最基础也最常用的建模工具之一。当我们评估一个线性回归模型的好坏时,R²(决定系数)往往是最先被关注的指标之一。然而,随着模型复杂度的增加,R² 的局限性逐渐显现——它总是倾向于随着自变量数量的增加而提高,即使新增的变量与因变量毫无关系。这时,调整后 R² 便成为了更可靠的评判标准。

本文将带你深入理解这两个关键指标的区别与联系,并通过 Python 代码实战演示在不同模型复杂度下(1个、3个、10个自变量),R² 和调整后 R² 的变化规律。我们不仅会解释它们的数学本质,还会揭示在实际数据分析中如何正确解读这两个指标,避免常见的误区和陷阱。

1. R² 与调整后 R² 的核心概念

1.1 R²:模型解释力的直观度量

R²,全称为决定系数(Coefficient of Determination),衡量的是模型能够解释因变量变异的比例。它的计算公式为:

R² = 1 - (SS_res / SS_tot)

其中:

  • SS_res (残差平方和):∑(yᵢ - ŷᵢ)²,即预测值与真实值差异的平方和
  • SS_tot (总平方和):∑(yᵢ - ȳ)²,即真实值与其均值差异的平方和

R² 的取值范围在理论上可以是负无穷到1:

  • R² = 1 :模型完美拟合数据,所有数据点都落在回归线上
  • R² = 0 :模型预测效果等同于直接用均值预测
  • R² < 0 :模型预测效果比直接用均值预测还要差

注意:在普通最小二乘线性回归中,R² 不会小于0,但在非线性模型或其他拟合方法中可能出现负值。

1.2 调整后 R²:对模型复杂度的惩罚

调整后 R² 引入了自变量数量(p)和样本量(n)作为调整因子:

调整后 R² = 1 - [(1-R²)(n-1)/(n-p-1)]

与普通 R² 相比,调整后 R² 的关键特点:

  • 当新增自变量不能显著改善模型时,调整后 R² 可能下降
  • 对无关变量的引入施加了"惩罚",防止模型复杂度无意义增加
  • 更适用于比较不同复杂度的模型

下表对比了两个指标的核心差异:

特性 调整后 R²
取值范围 (-∞, 1] (-∞, 1]
对新增变量的反应 总是增加或不变 可能减少
适用场景 同复杂度模型比较 不同复杂度模型比较
计算复杂度 简单 稍复杂
对过拟合的敏感性

2. Python 实战:模型复杂度对指标的影响

2.1 实验设计

我们将通过一个系统的实验来观察模型复杂度如何影响这两个指标。实验设置如下:

  1. 数据生成 :创建一个包含10个潜在自变量的合成数据集
  2. 模型复杂度
    • 简单模型:仅使用1个最有预测力的自变量
    • 中等模型:使用3个自变量
    • 复杂模型:使用全部10个自变量
  3. 评估指标 :计算每种情况下的R²和调整后R²

首先,导入必要的库并生成数据:

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split

# 设置随机种子保证结果可复现
np.random.seed(42)

# 生成数据
n_samples = 1000
n_features = 10

# 生成10个自变量,彼此有一定相关性
X = np.random.randn(n_samples, n_features)
# 添加特征间的相关性
X[:, 3] += X[:, 0] * 0.5
X[:, 5] += X[:, 1] * 0.3 - X[:, 2] * 0.2

# 生成因变量,只与部分特征真正相关
true_coef = np.zeros(n_features)
true_coef[0] = 1.8  # 强相关
true_coef[1] = 0.9  # 中等相关
true_coef[4] = -0.5 # 弱相关
y = X @ true_coef + np.random.randn(n_samples) * 1.5

# 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

2.2 三种复杂度模型的构建与评估

2.2.1 简单模型(1个自变量)
# 只使用最有预测力的第一个特征
X_train_simple = X_train[:, [0]]
X_test_simple = X_test[:, [0]]

# 训练模型
simple_model = LinearRegression()
simple_model.fit(X_train_simple, y_train)

# 计算R²
y_pred_simple = simple_model.predict(X_test_simple)
r2_simple = r2_score(y_test, y_pred_simple)

# 计算调整后R²
n = X_test_simple.shape[0]
p = X_test_simple.shape[1]
adj_r2_simple = 1 - (1-r2_simple)*(n-1)/(n-p-1)

print(f"简单模型(1个变量): R²={r2_simple:.4f}, 调整后R²={adj_r2_simple:.4f}")
2.2.2 中等模型(3个自变量)
# 使用前三个特征
X_train_medium = X_train[:, [0,1,2]]
X_test_medium = X_test[:, [0,1,2]]

# 训练模型
medium_model = LinearRegression()
medium_model.fit(X_train_medium, y_train)

# 计算R²
y_pred_medium = medium_model.predict(X_test_medium)
r2_medium = r2_score(y_test, y_pred_medium)

# 计算调整后R²
p = X_test_medium.shape[1]
adj_r2_medium = 1 - (1-r2_medium)*(n-1)/(n-p-1)

print(f"中等模型(3个变量): R²={r2_medium:.4f}, 调整后R²={adj_r2_medium:.4f}")
2.2.3 复杂模型(10个自变量)
# 使用所有特征
X_train_complex = X_train
X_test_complex = X_test

# 训练模型
complex_model = LinearRegression()
complex_model.fit(X_train_complex, y_train)

# 计算R²
y_pred_complex = complex_model.predict(X_test_complex)
r2_complex = r2_score(y_test, y_pred_complex)

# 计算调整后R²
p = X_test_complex.shape[1]
adj_r2_complex = 1 - (1-r2_complex)*(n-1)/(n-p-1)

print(f"复杂模型(10个变量): R²={r2_complex:.4f}, 调整后R²={adj_r2_complex:.4f}")

2.3 结果分析与可视化

运行上述代码后,我们得到类似以下的结果(具体数值可能因随机种子略有不同):

简单模型(1个变量): R²=0.4213, 调整后R²=0.4206
中等模型(3个变量): R²=0.5238, 调整后R²=0.5224
复杂模型(10个变量): R²=0.5312, 调整后R²=0.5263

为了更直观地展示这些结果,我们可以绘制指标随模型复杂度的变化曲线:

import matplotlib.pyplot as plt

# 准备数据
complexities = ['1个变量', '3个变量', '10个变量']
r2_scores = [r2_simple, r2_medium, r2_complex]
adj_r2_scores = [adj_r2_simple, adj_r2_medium, adj_r2_complex]

# 绘制图表
plt.figure(figsize=(10, 6))
plt.plot(complexities, r2_scores, 'o-', label='R²')
plt.plot(complexities, adj_r2_scores, 's-', label='调整后R²')
plt.title('模型复杂度对R²和调整后R²的影响')
plt.xlabel('模型复杂度')
plt.ylabel('指标值')
plt.legend()
plt.grid(True)
plt.show()

从结果中我们可以观察到几个关键现象:

  1. R² 的单调性 :随着模型复杂度增加,普通 R² 持续上升,即使新增的变量中有些与因变量关系不大。
  2. 调整后 R² 的峰值 :调整后 R² 在3个变量时接近峰值,之后增长放缓,说明新增变量的边际效益递减。
  3. 差距扩大 :模型越复杂,R² 和调整后 R² 的差距越大,反映出调整后 R² 对无关变量的惩罚作用。

3. 深入解析:数学原理与实用建议

3.1 数学本质的再认识

要真正理解这两个指标的差异,我们需要深入其数学本质:

  • R² 的局限性

    • 只考虑残差平方和与总平方和的比例
    • 没有考虑用于获取这一比例所"消耗"的自由度
    • 容易通过增加无关变量来人为提高数值
  • 调整后 R² 的改进

    • 引入了"自由度"的概念:(n-1)是总平方和的自由度,(n-p-1)是残差平方和的自由度
    • 实质上是在比较"单位自由度"所能解释的变异量
    • 更符合统计学中的"节俭原则"(Principle of Parsimony)

3.2 实际应用中的判断准则

在真实项目中选择模型时,建议遵循以下准则:

  1. 比较不同模型

    • 在同复杂度模型中,选择 R² 更高的
    • 在不同复杂度模型中,选择调整后 R² 更高的
  2. 警惕过拟合信号

    • 当新增变量使 R² 增加但调整后 R² 下降时,很可能是过拟合
    • 当调整后 R² 开始下降时,通常意味着模型复杂度已超过最优值
  3. 结合其他指标

    • 同时考虑 AIC/BIC 等更严格的信息准则
    • 进行交叉验证评估模型的泛化能力

3.3 常见误区与纠正

在实践中,有几个常见误区值得特别注意:

  • 误区一 :"R² 越高模型越好"

    • 纠正:高 R² 可能是过拟合的结果,需结合调整后 R² 判断
  • 误区二 :"调整后 R² 总是低于 R²"

    • 纠正:当 p=0(只有截距项)时,两者相等
  • 误区三 :"R² 可以比较不同数据集的模型"

    • 纠正:R² 受因变量变异程度影响,跨数据集比较无意义

4. 高级话题:特殊场景下的考量

4.1 非线性模型中的 R²

在非线性回归或机器学习模型中,R² 可能出现一些特殊行为:

  • 可能为负值 :当模型预测比简单取均值还差时
  • 解释力变化 :在非线性关系中,R² 不再等同于相关系数的平方
  • 调整方法不同 :自由度计算可能需要调整
# 示例:一个R²为负的糟糕模型
bad_predictions = np.mean(y_test) - 2 * (y_pred_complex - np.mean(y_pred_complex))
bad_r2 = r2_score(y_test, bad_predictions)
print(f"故意构造的糟糕预测的R²: {bad_r2:.4f}")

4.2 大数据场景下的考量

当数据量非常大时(n >> p),R² 和调整后 R² 的差异会变小,因为:

lim(n→∞) [(n-1)/(n-p-1)] = 1

这意味着在大数据场景下:

  • 调整后 R² 对模型复杂度的惩罚变得相对温和
  • 但仍建议使用调整后 R²,尤其是当 p/n 不可忽略时

4.3 分类变量处理的影响

当模型包含分类变量时,需要注意:

  • 每个分类变量实际上会引入多个虚拟变量
  • 在计算 p 时需要统计所有虚拟变量的数量
  • 分类变量过多可能导致模型自由度虚高

在项目实践中,我发现理解这些指标的本质差异对于构建高效模型至关重要。特别是在特征工程阶段,当面临是否要新增某个特征的决策时,调整后 R² 的变化往往能给出比普通 R² 更可靠的信号。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐