ML 学习站
跳到正文

相关与线性回归

相关系数 r、最小二乘、预测区间、残差分析。

40 分钟1 / 43,664
加载中...

本章探讨了相关与线性回归的核心概念及其应用。首先,介绍了相关性的概念,通过散点图和Pearson相关系数r来量化两个变量之间的线性关系。r的范围从-1到1,值越接近1或-1,表示线性关系越强,但需注意r=0不代表独立,r≠因果关系,且r易受异常值影响。接着,阐述了线性回归,通过最小二乘法(OLS)找到最拟合的直线,目标是最小化残差平方和(RSS)。线性回归有四个关键假设:线性、独立、正态和等方差。R²作为拟合优度指标,衡量模型解释数据方差的比例,但需注意调整R²以避免无意义特征的干扰。此外,区分了预测区间和置信区间,前者用于估计单个值,后者用于估计均值范围。最后,线性回归被视为机器学习监督学习的基石,展示了从简单回归到多元回归,再到正则化方法的学习路径。读者将掌握如何量化变量关系、构建预测模型、评估模型性能,并理解线性回归在统计和机器学习中的重要性。

相关与线性回归

本章问题: 房价 80% 由面积决定, 但还有 20% 是什么? 怎么用"已知"预测"未知"? 答案: 线性回归, 几乎所有 ML 监督学习的祖父。

1. 相关: 两个变量的"同向性"

1.1 散点图先看

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
# 4 种典型关系
x = np.linspace(0, 10, 50)
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
for ax, slope, noise, title in zip(
    axes, [2, -1.5, 0, 0],
    [1, 1, 0.5, 3],
    ["正相关", "负相关", "无线性相关", "曲线关系"]):
    y = slope * x + np.random.normal(0, noise, len(x))
    ax.scatter(x, y, alpha=0.6)
    ax.set_title(title)
    ax.grid(True, alpha=0.3)
plt.tight_layout(); plt.show()

1.2 Pearson 相关系数 r

r 范围解释
0无线性相关 (但可能有其他关系)
0.0 ~ 0.3
0.3 ~ 0.7
0.7 ~ 1.0
1完美正线性
-1完美负线性
from scipy.stats import pearsonr
np.random.seed(42)
x = np.random.normal(0, 1, 100)
y = 0.7 * x + np.random.normal(0, 0.5, 100)
r, p = pearsonr(x, y)
print(f"r = {r:.3f}, p = {p:.4f}")
# r ≈ 0.81 (强), p ≈ 0 (极显著)

1.3 r 的 3 个常见误解

  1. r = 0 不代表独立: 可能 U 形、抛物线等非线性关系
  2. r ≠ 因果: 冰淇淋销量 vs 溺水率 r = 0.8, 但都因夏天
  3. r 受异常值影响: 1 个离群点能拉高/拉低 r 很多

1.4 显著检验

H₀: ρ = 0 (无相关) 检验统计量: t = r√(n-2) / √(1-r²) ~ t(n-2)

# scipy 的 pearsonr 已经给出 p 值
r, p = pearsonr(x, y)  # p 就是 ρ=0 的检验 p 值

2. 线性回归: 从"看"到"算"

2.1 最小二乘法 (OLS)

找最拟合的直线:

目标: 最小化残差平方和 (RSS):

(对 b₀, b₁ 求偏导令为 0):

import numpy as np
from sklearn.linear_model import LinearRegression

# 数据
np.random.seed(42)
x = np.random.uniform(0, 10, 50)
y = 3 + 2 * x + np.random.normal(0, 2, 50)

# sklearn
model = LinearRegression()
model.fit(x.reshape(-1, 1), y)
print(f"b0 = {model.intercept_:.3f}, b1 = {model.coef_[0]:.3f}")
# b0 ≈ 3, b1 ≈ 2 (跟真实 3 + 2x 吻合!)

2.2 残差分析: 检验模型假设

线性回归的 4 个假设 (教科书标准):

  1. 线性: y 和 x 是线性关系
  2. 独立: 残差互不相关 (Durbin-Watson)
  3. 正态: 残差 ~ N(0, σ²)
  4. 等方差: 残差方差恒定 (Homoscedasticity)
import matplotlib.pyplot as plt

y_pred = model.predict(x.reshape(-1, 1))
residuals = y - y_pred

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 1. 残差 vs 拟合值 (应随机散布)
axes[0].scatter(y_pred, residuals, alpha=0.6)
axes[0].axhline(0, color="red", linestyle="--")
axes[0].set_xlabel("拟合值"); axes[0].set_ylabel("残差")
axes[0].set_title("残差 vs 拟合值")

# 2. 残差 Q-Q 图 (应近似直线)
from scipy.stats import probplot
probplot(residuals, dist="norm", plot=axes[1])
axes[1].set_title("Q-Q 图 (正态性)")

# 3. 残差直方图
axes[2].hist(residuals, bins=15, edgecolor="black")
axes[2].set_title("残差分布")
plt.tight_layout(); plt.show()

# 数值检验
from scipy.stats import shapiro, bartlett
print(f"Shapiro 残差正态性: p = {shapiro(residuals).pvalue:.3f}")
# p > 0.05 → 残差正态, 假设成立

3. R²: 拟合优度

解释
0模型没解释任何方差
0.5解释一半 (凑合)
0.8解释大部分 (好)
1完美预测 (可疑, 可能过拟合)
print(f"R² = {model.score(x.reshape(-1, 1), y):.3f}")
# 0.85 左右, 拟合好

⚠️ R² 不是越多越好: 加任意特征都会让 R² 增加 (甚至噪声)! 修正版:调整 R² = 1 - (1-R²)(n-1)/(n-k-1), 惩罚"无意义特征"。

4. 预测区间 vs 置信区间

经常混!

区间估的是什么用途
均值 CIE(y | x) = β₀ + β₁x"x = 5 时, y 的平均是?"
预测 PI单个 y"x = 5 时, 下一个 y 是?"

PI 比 CI 宽 (因为多了一个"个体变异")。

from scipy.stats import t

# 计算预测区间
n = len(x)
x_mean = x.mean()
sse = np.sum(residuals**2)
s_e = np.sqrt(sse / (n - 2))  # 残差标准误
t_crit = t.ppf(0.975, df=n-2)

x_new = np.array([[5.0]])
y_pred = model.predict(x_new)[0]
# 预测区间
pi_se = s_e * np.sqrt(1 + 1/n + (5 - x_mean)**2 / np.sum((x - x_mean)**2))
pi = (y_pred - t_crit * pi_se, y_pred + t_crit * pi_se)
print(f"x=5 时预测值 = {y_pred:.2f}, 95% PI = [{pi[0]:.2f}, {pi[1]:.2f}]")

5. 相关 vs 回归: 别混

维度相关回归
对称r(x,y) = r(y,x)y = f(x) 不等于 x = f(y)
目的量化"关系强度"预测"未来值"
公式ry = b₀ + b₁x + ...
受 y, x 单位影响否 (标准化)是 (系数变)

6. 实战: 房价预测完整流程

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt

# 模拟房价数据
np.random.seed(42)
n = 200
size = np.random.uniform(50, 200, n)  # 面积 m²
rooms = (size / 30 + np.random.normal(0, 0.5, n)).astype(int)  # 房间数
age = np.random.uniform(0, 30, n)       # 房龄
price = (size * 0.5 + rooms * 5 - age * 0.3 +
         np.random.normal(0, 10, n))    # 万元

df = pd.DataFrame({"size": size, "rooms": rooms, "age": age, "price": price})

# 1. 探索性分析
print("相关系数:")
print(df.corr()["price"].round(3))

# 2. 拟合简单回归
X = df[["size"]]
y = df["price"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = LinearRegression().fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"\n单变量: R² = {r2_score(y_test, y_pred):.3f}, RMSE = {np.sqrt(mean_squared_error(y_test, y_pred)):.2f}")

# 3. 多元回归
model2 = LinearRegression().fit(df[["size", "rooms", "age"]], y)
y_pred2 = model2.predict(df[["size", "rooms", "age"]])
print(f"多变量: R² = {r2_score(y, y_pred2):.3f}")

# 4. 系数解释
print("\n回归系数:")
for name, coef in zip(["size", "rooms", "age"], model2.coef_):
    print(f"  {name:6s}: {coef:+.3f}")
# size: +0.50 (每 +1 m², 房价 +0.5 万)
# rooms: +5.0 (每多 1 房间, +5 万)
# age: -0.3 (每老 1 年, -0.3 万)

7. 回归在 ML 中的"血统"

经典统计等价 ML 概念
简单线性回归1 层线性神经网络
多元线性回归多元线性回归 (sklearn)
残差loss 值
最小二乘MSE loss
1 - MSE/Var(y)
显著检验特征重要性
预测区间不确定性估计 (贝叶斯回归)
岭回归L2 正则化
LassoL1 正则化 (特征选择)

💡 学习路径: 学完这章, 下一章学多元回归, 再下章学正则化 (Lasso/Ridge), 你就已经在 ML 入门了。

8. 简单 vs 多元回归: 何时用?

场景简单回归多元回归
1 个自变量-
多个自变量, 都相关-
控制混杂-✅ (协变量)
预测未来✅ (1 变量)✅ (多变量预测更准)
解释因果⚠️ (难)⚠️ (更难)

9. Python 实战: 完整回归诊断

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from scipy import stats

def full_regression_diagnosis(X, y, feature_names=None):
    """完整回归诊断: 拟合 + 残差 + 异常值 + 显著检验"""
    if feature_names is None:
        feature_names = [f"X{i}" for i in range(X.shape[1])]
    
    model = LinearRegression().fit(X, y)
    y_pred = model.predict(X)
    residuals = y - y_pred
    
    print("="*60)
    print(f"R² = {r2_score(y, y_pred):.4f}, 调整 R² = {1 - (1-r2_score(y, y_pred))*(len(y)-1)/(len(y)-X.shape[1]-1):.4f}")
    print(f"系数: {dict(zip(feature_names, model.coef_.round(4)))}")
    print(f"截距: {model.intercept_:.4f}")
    print("="*60)
    
    # 残差诊断
    print(f"\n残差诊断:")
    print(f"  均值 = {residuals.mean():.4f} (应≈0)")
    print(f"  标准差 = {residuals.std(ddof=1):.4f}")
    print(f"  Shapiro 正态性 p = {stats.shapiro(residuals).pvalue:.4f}")
    
    # 异常值检测
    infl = model.predict(X)  # 占位
    student_resid = residuals / residuals.std(ddof=1)
    outliers = np.abs(student_resid) > 3
    print(f"  异常值 (|z| > 3): {outliers.sum()} 个")
    
    # 系数显著检验 (近似 t 检验)
    n, k = X.shape
    mse = np.sum(residuals**2) / (n - k - 1)
    X_with_intercept = np.column_stack([np.ones(n), X])
    var_coef = mse * np.linalg.inv(X_with_intercept.T @ X_with_intercept).diagonal()
    se = np.sqrt(var_coef)
    t_stats = np.concatenate([[model.intercept_], model.coef_]) / se
    p_vals = 2 * (1 - stats.t.cdf(np.abs(t_stats), df=n-k-1))
    
    print(f"\n系数显著检验:")
    print(f"  {'变量':15s} {'系数':>10s} {'SE':>10s} {'t':>10s} {'p':>10s}")
    for name, b, s, t_v, p_v in zip(
        ["截距"] + list(feature_names),
        np.concatenate([[model.intercept_], model.coef_]),
        se, t_stats, p_vals):
        sig = "***" if p_v < 0.001 else ("**" if p_v < 0.01 else ("*" if p_v < 0.05 else ""))
        print(f"  {name:15s} {b:10.4f} {s:10.4f} {t_v:10.3f} {p_v:10.4f} {sig}")
    
    return model

# 测试
np.random.seed(42)
X = np.column_stack([
    np.random.normal(0, 1, 100),
    np.random.normal(0, 1, 100),
    np.random.normal(0, 1, 100),
])
y = 1 + 0.5 * X[:, 0] + 0.3 * X[:, 1] + np.random.normal(0, 0.5, 100)

full_regression_diagnosis(X, y, ["X1", "X2", "X3"])

10. 小结

你学到了关键点
Pearson r-1 到 1 量化线性关系, 但 ≠ 因果
最小二乘找最拟合直线, 闭式解
4 个假设线性、独立、正态、等方差
解释方差比例, 调整 R² 惩罚多余特征
残差分析Q-Q 图 + Shapiro 检验
预测区间估单个值, 比 CI 宽
跟 ML 关系线性回归 → 神经网络 → 全 ML 监督学习

11. 习题

  1. 10 个数据点 (x, y): (1,2), (2,4), (3,5), (4,4), (5,5), (6,7), (7,8), (8,8), (9,10), (10,9)

    • 算 Pearson r
    • OLS 拟合 y = b₀ + b₁x
    • R²?
    • x=11 时, y 的预测值 + 95% 预测区间
  2. np.random.seed(0); x = np.random.uniform(0, 10, 30); y = 0.5*x² + np.random.normal(0, 2, 30) 生成数据

    • 拟合线性回归, 报告 R²
    • 拟合二次回归 (y = b₀ + b₁x + b₂x²), 报告 R²
    • 哪个更好? 为什么?
👉 查看参考答案
  1. 计算:

    • r ≈ 0.99 (强正相关)
    • b₁ ≈ 0.83, b₀ ≈ 1.33
    • R² ≈ 0.97
    • 预测 x=11: ŷ ≈ 10.46; PI ≈ [9.0, 11.9]
  2. 线性 R² ≈ 0.85, 二次 R² ≈ 0.96。二次更好, 因为真实关系是二次。 教训: 画散点图! 线性关系不是唯一可能。

12. 下一章


📚 本章来源: 改编自 Triola《基础统计学》第 14 版 第 10 章 10-1、10-2、10-3 节, 加入 ML 视角。

章末小测验

检验你对《相关与线性回归》的掌握程度。

1

以下关于 Pearson 相关系数 r 的说法中,哪一项是正确的?

2

线性回归的四个基本假设中,哪一项是关于残差的?

3

以下关于 R² 的说法中,哪一项是正确的?

4

以下关于预测区间(PI)和置信区间(CI)的说法中,哪一项是正确的?

5

在简单回归和多元回归的应用场景中,以下哪一项更适合使用多元回归?

讨论区(0)

加载评论中...