线性回归 R² 与调整后 R²:Python 实战对比 3 种模型复杂度下的指标变化
线性回归 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² | 调整后 R² |
|---|---|---|
| 取值范围 | (-∞, 1] | (-∞, 1] |
| 对新增变量的反应 | 总是增加或不变 | 可能减少 |
| 适用场景 | 同复杂度模型比较 | 不同复杂度模型比较 |
| 计算复杂度 | 简单 | 稍复杂 |
| 对过拟合的敏感性 | 高 | 低 |
2. Python 实战:模型复杂度对指标的影响
2.1 实验设计
我们将通过一个系统的实验来观察模型复杂度如何影响这两个指标。实验设置如下:
- 数据生成 :创建一个包含10个潜在自变量的合成数据集
- 模型复杂度 :
- 简单模型:仅使用1个最有预测力的自变量
- 中等模型:使用3个自变量
- 复杂模型:使用全部10个自变量
- 评估指标 :计算每种情况下的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()
从结果中我们可以观察到几个关键现象:
- R² 的单调性 :随着模型复杂度增加,普通 R² 持续上升,即使新增的变量中有些与因变量关系不大。
- 调整后 R² 的峰值 :调整后 R² 在3个变量时接近峰值,之后增长放缓,说明新增变量的边际效益递减。
- 差距扩大 :模型越复杂,R² 和调整后 R² 的差距越大,反映出调整后 R² 对无关变量的惩罚作用。
3. 深入解析:数学原理与实用建议
3.1 数学本质的再认识
要真正理解这两个指标的差异,我们需要深入其数学本质:
-
R² 的局限性 :
- 只考虑残差平方和与总平方和的比例
- 没有考虑用于获取这一比例所"消耗"的自由度
- 容易通过增加无关变量来人为提高数值
-
调整后 R² 的改进 :
- 引入了"自由度"的概念:(n-1)是总平方和的自由度,(n-p-1)是残差平方和的自由度
- 实质上是在比较"单位自由度"所能解释的变异量
- 更符合统计学中的"节俭原则"(Principle of Parsimony)
3.2 实际应用中的判断准则
在真实项目中选择模型时,建议遵循以下准则:
-
比较不同模型 :
- 在同复杂度模型中,选择 R² 更高的
- 在不同复杂度模型中,选择调整后 R² 更高的
-
警惕过拟合信号 :
- 当新增变量使 R² 增加但调整后 R² 下降时,很可能是过拟合
- 当调整后 R² 开始下降时,通常意味着模型复杂度已超过最优值
-
结合其他指标 :
- 同时考虑 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² 更可靠的信号。
更多推荐


所有评论(0)