行莫
行莫
发布于 2025-11-24 / 30 阅读
0
0

线性回归详解

线性回归详解

引言

想象一下,你要预测一个学生的成绩:

  • 方法1:只看学习时间,时间越长成绩越好(简单方法)
  • 方法2:看学习时间、复习次数、作业完成度等多个因素(复杂方法)

线性回归就像这两种方法——通过找到输入和输出之间的线性关系,来预测未知的结果。

本文将用生动的类比、详细的数学原理和丰富的可视化,带你深入理解线性回归:

  • 什么是线性回归?
  • 一元线性回归和多元线性回归
  • 如何找到最佳拟合线?
  • 线性回归的应用场景
  • 在机器学习中的实际应用

第一部分:什么是线性回归?

线性回归的直观理解

**线性回归(Linear Regression)**是一种通过拟合一条直线(或超平面)来预测连续数值的方法。

类比理解:

  • 就像用尺子画一条最接近所有点的直线
  • 就像找到"最佳趋势线"
  • 就像用一条线总结数据的规律

生活中的例子

例子1:房价预测

  • 输入:房屋面积(平方米)
  • 输出:房价(万元)
  • 关系:面积越大,房价越高(线性关系)

例子2:学习时间与成绩

  • 输入:每天学习时间(小时)
  • 输出:考试成绩(分)
  • 关系:学习时间越长,成绩越好(线性关系)

例子3:广告投入与销售额

  • 输入:广告投入(万元)
  • 输出:销售额(万元)
  • 关系:广告投入越多,销售额越高(线性关系)

线性回归的数学表示

一般形式:
$$
y = w_1 x_1 + w_2 x_2 + ... + w_n x_n + b
$$

其中:

  • $y$:预测值(输出)
  • $x_1, x_2, ..., x_n$:特征(输入)
  • $w_1, w_2, ..., w_n$:权重(参数)
  • $b$:偏置(截距)

一元线性回归(最简单):
$$
y = wx + b
$$

其中:

  • $w$:斜率
  • $b$:截距

类比理解:

  • $w$:就像"每增加1个单位,输出增加多少"
  • $b$:就像"起点值"
  • 线性关系:就像"成比例增长"

线性回归的目标

**目标:**找到最佳的 $w$ 和 $b$,使得预测值尽可能接近真实值。

数学表示:
$$
\min_{w,b} \sum_{i=1}^{n} (y_i - (wx_i + b))^2
$$

类比理解:

  • 就像找到一条直线,使得所有点到直线的距离平方和最小
  • 就像找到"最佳拟合线"

第二部分:一元线性回归(Simple Linear Regression)

什么是一元线性回归?

一元线性回归是最简单的线性回归形式,只有一个输入特征和一个输出。

数学表示:
$$
y = wx + b + \epsilon
$$

其中:

  • $x$:输入特征
  • $y$:输出值
  • $w$:权重(斜率)
  • $b$:偏置(截距)
  • $\epsilon$:误差项

预测函数:
$$
\hat{y} = wx + b
$$

可视化示例:学习时间与成绩

让我们用 matplotlib 可视化一元线性回归:

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

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成示例数据:学习时间与成绩
np.random.seed(42)
study_hours = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# 真实关系:成绩 = 10 * 学习时间 + 60 + 噪声
true_scores = 10 * study_hours + 60 + np.random.normal(0, 5, len(study_hours))

# 创建线性回归模型
model = LinearRegression()
model.fit(study_hours.reshape(-1, 1), true_scores)

# 预测
predicted_scores = model.predict(study_hours.reshape(-1, 1))

# 计算评估指标
r2 = r2_score(true_scores, predicted_scores)
mse = mean_squared_error(true_scores, predicted_scores)

# 创建图形
fig, ax = plt.subplots(1, 1, figsize=(12, 8))

# 绘制数据点
ax.scatter(study_hours, true_scores, color='blue', s=100, alpha=0.6, 
           label='实际成绩', zorder=5, edgecolors='black', linewidth=1.5)

# 绘制拟合直线
x_line = np.linspace(0, 11, 100)
y_line = model.predict(x_line.reshape(-1, 1))
ax.plot(x_line, y_line, 'r-', linewidth=3, label=f'拟合直线: y = {model.coef_[0]:.2f}x + {model.intercept_:.2f}', zorder=3)

# 绘制残差线(从点到直线的距离)
for i in range(len(study_hours)):
    ax.plot([study_hours[i], study_hours[i]], [true_scores[i], predicted_scores[i]], 
            'g--', alpha=0.5, linewidth=1, zorder=2)

# 标注关键点
ax.plot(study_hours, predicted_scores, 'ro', markersize=8, alpha=0.7, 
        label='预测值', zorder=4)

# 设置坐标轴
ax.set_xlabel('学习时间(小时)', fontsize=14, weight='bold')
ax.set_ylabel('考试成绩(分)', fontsize=14, weight='bold')
ax.set_title('一元线性回归:学习时间 vs 考试成绩', fontsize=16, weight='bold')
ax.legend(loc='best', fontsize=12)
ax.grid(True, alpha=0.3)

# 添加统计信息
info_text = f'拟合方程: y = {model.coef_[0]:.2f}x + {model.intercept_:.2f}\n'
info_text += f'R² 得分: {r2:.4f}\n'
info_text += f'均方误差 (MSE): {mse:.2f}\n'
info_text += f'斜率 (w): {model.coef_[0]:.2f}\n'
info_text += f'截距 (b): {model.intercept_:.2f}'

ax.text(0.02, 0.98, info_text, transform=ax.transAxes, 
        fontsize=11, verticalalignment='top',
        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))

plt.tight_layout()
plt.savefig('simple_linear_regression.png', dpi=300, bbox_inches='tight')
plt.show()

print(f"拟合方程: y = {model.coef_[0]:.2f}x + {model.intercept_:.2f}")
print(f"R² 得分: {r2:.4f}")
print(f"均方误差: {mse:.2f}")

最小二乘法(Least Squares Method)

最小二乘法是求解线性回归参数的标准方法。

目标函数(损失函数):
$$
L(w, b) = \sum_{i=1}^{n} (y_i - (wx_i + b))^2
$$

求解过程:

  1. 对 $w$ 求偏导:
    $$
    \frac{\partial L}{\partial w} = -2\sum_{i=1}^{n} x_i(y_i - wx_i - b) = 0
    $$

  2. 对 $b$ 求偏导:
    $$
    \frac{\partial L}{\partial b} = -2\sum_{i=1}^{n} (y_i - wx_i - b) = 0
    $$

  3. 求解得到:
    $$
    w = \frac{\sum_{i=1}^{n}(x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n}(x_i - \bar{x})^2}
    $$

$$
b = \bar{y} - w\bar{x}
$$

其中 $\bar{x}$ 和 $\bar{y}$ 分别是 $x$ 和 $y$ 的均值。

可视化:最小二乘法的原理

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成数据
np.random.seed(42)
x = np.array([1, 2, 3, 4, 5, 6, 7, 8])
y = 2 * x + 3 + np.random.normal(0, 1, len(x))

# 计算不同w和b的损失
w_range = np.linspace(0, 4, 50)
b_range = np.linspace(0, 6, 50)
W, B = np.meshgrid(w_range, b_range)

# 计算损失函数
Loss = np.zeros_like(W)
for i in range(len(w_range)):
    for j in range(len(b_range)):
        y_pred = W[j, i] * x + B[j, i]
        Loss[j, i] = np.sum((y - y_pred)**2)

# 找到最小值
min_idx = np.unravel_index(np.argmin(Loss), Loss.shape)
w_opt = W[min_idx]
b_opt = B[min_idx]

# 创建图形
fig = plt.figure(figsize=(16, 6))

# 左图:损失函数3D图
ax1 = fig.add_subplot(121, projection='3d')
surf = ax1.plot_surface(W, B, Loss, cmap='viridis', alpha=0.8, 
                       linewidth=0, antialiased=True)
ax1.scatter([w_opt], [b_opt], [Loss[min_idx]], color='red', 
           s=200, marker='*', label='最优点')
ax1.set_xlabel('权重 w', fontsize=12)
ax1.set_ylabel('偏置 b', fontsize=12)
ax1.set_zlabel('损失函数值', fontsize=12)
ax1.set_title('损失函数3D图(最小二乘法)', fontsize=14, weight='bold')
ax1.legend()

# 右图:等高线图
ax2 = fig.add_subplot(122)
contour = ax2.contour(W, B, Loss, levels=20, cmap='viridis')
ax2.clabel(contour, inline=True, fontsize=8)
ax2.scatter([w_opt], [b_opt], color='red', s=200, marker='*', 
           label=f'最优点 (w={w_opt:.2f}, b={b_opt:.2f})', zorder=5)
ax2.set_xlabel('权重 w', fontsize=12)
ax2.set_ylabel('偏置 b', fontsize=12)
ax2.set_title('损失函数等高线图', fontsize=14, weight='bold')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('least_squares_visualization.png', dpi=300, bbox_inches='tight')
plt.show()

print(f"最优权重 w: {w_opt:.4f}")
print(f"最优偏置 b: {b_opt:.4f}")
print(f"最小损失值: {Loss[min_idx]:.4f}")

代码实现:手动实现一元线性回归

import numpy as np
import matplotlib.pyplot as plt

class SimpleLinearRegression:
    """一元线性回归"""
    
    def __init__(self):
        self.w = None  # 权重
        self.b = None  # 偏置
    
    def fit(self, X, y):
        """
        训练模型
        
        参数:
            X: 输入特征(一维数组)
            y: 输出值(一维数组)
        """
        X = np.array(X)
        y = np.array(y)
        
        # 计算均值
        x_mean = np.mean(X)
        y_mean = np.mean(y)
        
        # 计算权重 w
        numerator = np.sum((X - x_mean) * (y - y_mean))
        denominator = np.sum((X - x_mean)**2)
        self.w = numerator / denominator
        
        # 计算偏置 b
        self.b = y_mean - self.w * x_mean
        
        return self
    
    def predict(self, X):
        """
        预测
        
        参数:
            X: 输入特征
        
        返回:
            预测值
        """
        return self.w * np.array(X) + self.b
    
    def score(self, X, y):
        """
        计算R²得分
        
        参数:
            X: 输入特征
            y: 真实值
        
        返回:
            R²得分
        """
        y_pred = self.predict(X)
        ss_res = np.sum((y - y_pred)**2)
        ss_tot = np.sum((y - np.mean(y))**2)
        return 1 - (ss_res / ss_tot)

# 使用示例
np.random.seed(42)
X = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
y = 2 * X + 3 + np.random.normal(0, 1, len(X))

# 创建模型
model = SimpleLinearRegression()
model.fit(X, y)

# 预测
y_pred = model.predict(X)

# 评估
r2 = model.score(X, y)

print(f"权重 w: {model.w:.4f}")
print(f"偏置 b: {model.b:.4f}")
print(f"R²得分: {r2:.4f}")

第三部分:多元线性回归(Multiple Linear Regression)

什么是多元线性回归?

多元线性回归有多个输入特征,用于预测一个输出值。

数学表示:
$$
y = w_1 x_1 + w_2 x_2 + ... + w_n x_n + b + \epsilon
$$

矩阵形式:
$$
\mathbf{y} = \mathbf{X}\mathbf{w} + \mathbf{b} + \boldsymbol{\epsilon}
$$

其中:

  • $\mathbf{X}$:特征矩阵($n \times m$,$n$ 个样本,$m$ 个特征)
  • $\mathbf{w}$:权重向量($m \times 1$)
  • $\mathbf{y}$:输出向量($n \times 1$)
  • $\mathbf{b}$:偏置(标量)

生活中的例子

例子:房价预测(多因素)

  • 特征1:房屋面积(平方米)
  • 特征2:房间数量(间)
  • 特征3:距离市中心距离(公里)
  • 特征4:楼层(层)
  • 输出:房价(万元)

关系:
$$
\text{房价} = w_1 \times \text{面积} + w_2 \times \text{房间数} + w_3 \times \text{距离} + w_4 \times \text{楼层} + b
$$

可视化示例:多特征回归

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from mpl_toolkits.mplot3d import Axes3D

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成示例数据:房价预测
np.random.seed(42)
n_samples = 100

# 特征:面积、房间数
area = np.random.uniform(50, 200, n_samples)
rooms = np.random.uniform(1, 5, n_samples)

# 真实关系:房价 = 0.5*面积 + 10*房间数 + 50 + 噪声
price = 0.5 * area + 10 * rooms + 50 + np.random.normal(0, 5, n_samples)

# 创建模型
X = np.column_stack([area, rooms])
model = LinearRegression()
model.fit(X, price)

# 预测
price_pred = model.predict(X)
r2 = r2_score(price, price_pred)

# 创建3D图形
fig = plt.figure(figsize=(16, 6))

# 左图:3D散点图和拟合平面
ax1 = fig.add_subplot(121, projection='3d')

# 绘制数据点
scatter = ax1.scatter(area, rooms, price, c=price, cmap='viridis', 
                     s=50, alpha=0.6, edgecolors='black', linewidth=0.5)

# 创建拟合平面
area_range = np.linspace(area.min(), area.max(), 20)
rooms_range = np.linspace(rooms.min(), rooms.max(), 20)
Area_grid, Rooms_grid = np.meshgrid(area_range, rooms_range)
Price_grid = model.coef_[0] * Area_grid + model.coef_[1] * Rooms_grid + model.intercept_

# 绘制拟合平面
ax1.plot_surface(Area_grid, Rooms_grid, Price_grid, alpha=0.3, 
                color='red', label='拟合平面')

ax1.set_xlabel('房屋面积(平方米)', fontsize=12)
ax1.set_ylabel('房间数量(间)', fontsize=12)
ax1.set_zlabel('房价(万元)', fontsize=12)
ax1.set_title('多元线性回归:3D可视化', fontsize=14, weight='bold')
plt.colorbar(scatter, ax=ax1, label='房价')

# 右图:预测值 vs 真实值
ax2 = fig.add_subplot(122)
ax2.scatter(price, price_pred, alpha=0.6, s=50, edgecolors='black', linewidth=0.5)
ax2.plot([price.min(), price.max()], [price.min(), price.max()], 
         'r--', linewidth=2, label='完美预测线')
ax2.set_xlabel('真实房价(万元)', fontsize=12)
ax2.set_ylabel('预测房价(万元)', fontsize=12)
ax2.set_title(f'预测值 vs 真实值 (R² = {r2:.4f})', fontsize=14, weight='bold')
ax2.legend()
ax2.grid(True, alpha=0.3)

# 添加信息
info_text = f'拟合方程:\n'
info_text += f'房价 = {model.coef_[0]:.2f}×面积 + {model.coef_[1]:.2f}×房间数 + {model.intercept_:.2f}\n\n'
info_text += f'R²得分: {r2:.4f}\n'
info_text += f'面积权重: {model.coef_[0]:.2f}\n'
info_text += f'房间数权重: {model.coef_[1]:.2f}\n'
info_text += f'偏置: {model.intercept_:.2f}'

ax2.text(0.05, 0.95, info_text, transform=ax2.transAxes, 
        fontsize=10, verticalalignment='top',
        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))

plt.tight_layout()
plt.savefig('multiple_linear_regression.png', dpi=300, bbox_inches='tight')
plt.show()

print(f"拟合方程: 房价 = {model.coef_[0]:.2f}×面积 + {model.coef_[1]:.2f}×房间数 + {model.intercept_:.2f}")
print(f"R²得分: {r2:.4f}")

矩阵形式求解

最小二乘法的矩阵解:

$$
\mathbf{w} = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{y}
$$

代码实现:

import numpy as np

def multiple_linear_regression(X, y):
    """
    多元线性回归(矩阵解法)
    
    参数:
        X: 特征矩阵 (n_samples, n_features)
        y: 目标向量 (n_samples,)
    
    返回:
        w: 权重向量
        b: 偏置
    """
    # 添加偏置列(全1列)
    X_with_bias = np.column_stack([np.ones(len(X)), X])
    
    # 计算权重:w = (X^T X)^(-1) X^T y
    w = np.linalg.inv(X_with_bias.T @ X_with_bias) @ X_with_bias.T @ y
    
    # 分离偏置和权重
    b = w[0]
    weights = w[1:]
    
    return weights, b

# 示例
np.random.seed(42)
X = np.random.randn(100, 3)
y = 2 * X[:, 0] + 3 * X[:, 1] - X[:, 2] + 5 + np.random.normal(0, 0.1, 100)

weights, bias = multiple_linear_regression(X, y)
print(f"权重: {weights}")
print(f"偏置: {bias}")

特征重要性可视化

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成数据
np.random.seed(42)
n_samples = 100

# 特征:面积、房间数、距离、楼层
area = np.random.uniform(50, 200, n_samples)
rooms = np.random.uniform(1, 5, n_samples)
distance = np.random.uniform(1, 20, n_samples)
floor = np.random.uniform(1, 30, n_samples)

# 真实关系
price = (0.5 * area + 10 * rooms - 2 * distance + 0.5 * floor + 
         50 + np.random.normal(0, 5, n_samples))

# 创建模型
X = np.column_stack([area, rooms, distance, floor])
feature_names = ['面积', '房间数', '距离', '楼层']
model = LinearRegression()
model.fit(X, price)

# 创建图形
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 绘制每个特征与房价的关系
for idx, (ax, feature, name) in enumerate(zip(axes.flat, X.T, feature_names)):
    ax.scatter(feature, price, alpha=0.5, s=30, edgecolors='black', linewidth=0.5)
    
    # 绘制拟合线(单特征)
    z = np.polyfit(feature, price, 1)
    p = np.poly1d(z)
    x_line = np.linspace(feature.min(), feature.max(), 100)
    ax.plot(x_line, p(x_line), "r--", linewidth=2, 
           label=f'权重: {model.coef_[idx]:.2f}')
    
    ax.set_xlabel(f'{name}', fontsize=12)
    ax.set_ylabel('房价(万元)', fontsize=12)
    ax.set_title(f'{name} vs 房价', fontsize=13, weight='bold')
    ax.legend()
    ax.grid(True, alpha=0.3)

plt.suptitle('多元线性回归:各特征与房价的关系', fontsize=16, weight='bold')
plt.tight_layout()
plt.savefig('feature_importance.png', dpi=300, bbox_inches='tight')
plt.show()

# 绘制权重条形图
fig, ax = plt.subplots(1, 1, figsize=(10, 6))
colors = ['green' if w > 0 else 'red' for w in model.coef_]
bars = ax.barh(feature_names, model.coef_, color=colors, alpha=0.7, edgecolor='black')
ax.axvline(x=0, color='black', linestyle='--', linewidth=1)
ax.set_xlabel('权重值', fontsize=12)
ax.set_title('特征权重(正权重表示正相关,负权重表示负相关)', fontsize=14, weight='bold')
ax.grid(axis='x', alpha=0.3)

# 添加数值标签
for i, (bar, w) in enumerate(zip(bars, model.coef_)):
    ax.text(w + (0.1 if w > 0 else -0.1), i, f'{w:.2f}', 
           va='center', ha='left' if w > 0 else 'right', fontsize=11, weight='bold')

plt.tight_layout()
plt.savefig('feature_weights.png', dpi=300, bbox_inches='tight')
plt.show()

第四部分:线性回归的评估指标

常用评估指标

1. 均方误差(Mean Squared Error, MSE)

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

特点:

  • 值越小越好
  • 对大误差敏感(平方项)

2. 均方根误差(Root Mean Squared Error, RMSE)

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

特点:

  • 与目标值同单位
  • 更容易解释

3. 平均绝对误差(Mean Absolute Error, MAE)

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

特点:

  • 对异常值不敏感
  • 更容易理解

4. R²得分(决定系数)

$$
R^2 = 1 - \frac{\sum_{i=1}^{n}(y_i - \hat{y}i)^2}{\sum{i=1}^{n}(y_i - \bar{y})^2}
$$

特点:

  • 取值范围:[0, 1]
  • 1 表示完美拟合
  • 0 表示模型不比均值预测好

可视化:评估指标对比

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成数据
np.random.seed(42)
X = np.linspace(0, 10, 50)
y = 2 * X + 3 + np.random.normal(0, 2, len(X))

# 训练模型
model = LinearRegression()
model.fit(X.reshape(-1, 1), y)
y_pred = model.predict(X.reshape(-1, 1))

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

# 创建图形
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 1. 预测值 vs 真实值
ax1 = axes[0, 0]
ax1.scatter(y, y_pred, alpha=0.6, s=50, edgecolors='black', linewidth=0.5)
ax1.plot([y.min(), y.max()], [y.min(), y.max()], 'r--', linewidth=2, label='完美预测')
ax1.set_xlabel('真实值', fontsize=12)
ax1.set_ylabel('预测值', fontsize=12)
ax1.set_title(f'预测值 vs 真实值 (R² = {r2:.4f})', fontsize=13, weight='bold')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 2. 残差图
ax2 = axes[0, 1]
residuals = y - y_pred
ax2.scatter(y_pred, residuals, alpha=0.6, s=50, edgecolors='black', linewidth=0.5)
ax2.axhline(y=0, color='r', linestyle='--', linewidth=2)
ax2.set_xlabel('预测值', fontsize=12)
ax2.set_ylabel('残差(真实值 - 预测值)', fontsize=12)
ax2.set_title('残差图(理想情况:随机分布在0附近)', fontsize=13, weight='bold')
ax2.grid(True, alpha=0.3)

# 3. 评估指标对比
ax3 = axes[1, 0]
metrics = ['MSE', 'RMSE', 'MAE']
values = [mse, rmse, mae]
colors_bar = ['skyblue', 'lightgreen', 'lightcoral']
bars = ax3.bar(metrics, values, color=colors_bar, alpha=0.7, edgecolor='black', linewidth=1.5)
ax3.set_ylabel('误差值', fontsize=12)
ax3.set_title('评估指标对比(越小越好)', fontsize=13, weight='bold')
ax3.grid(axis='y', alpha=0.3)

# 添加数值标签
for bar, val in zip(bars, values):
    height = bar.get_height()
    ax3.text(bar.get_x() + bar.get_width()/2., height,
             f'{val:.2f}', ha='center', va='bottom', fontsize=11, weight='bold')

# 4. R²得分可视化
ax4 = axes[1, 1]
# 计算各部分
ss_res = np.sum((y - y_pred)**2)
ss_tot = np.sum((y - np.mean(y))**2)
ss_reg = ss_tot - ss_res

# 绘制饼图
sizes = [ss_reg, ss_res]
labels = [f'解释的方差\n({ss_reg:.2f})', f'未解释的方差\n({ss_res:.2f})']
colors_pie = ['lightgreen', 'lightcoral']
explode = (0.05, 0.05)

ax4.pie(sizes, explode=explode, labels=labels, colors=colors_pie, 
        autopct='%1.1f%%', shadow=True, startangle=90)
ax4.set_title(f'R²得分 = {r2:.4f}\n(解释的方差比例)', fontsize=13, weight='bold')

plt.suptitle('线性回归评估指标可视化', fontsize=16, weight='bold')
plt.tight_layout()
plt.savefig('regression_metrics.png', dpi=300, bbox_inches='tight')
plt.show()

print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"R²: {r2:.4f}")

第五部分:线性回归的应用场景

1. 预测问题

应用场景:

  • 房价预测:根据面积、位置、房间数等预测房价
  • 销量预测:根据广告投入、季节、促销等预测销量
  • 股票预测:根据历史数据预测股价(简单模型)
  • 需求预测:根据历史数据预测未来需求

示例:销量预测

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成示例数据:广告投入与销量
np.random.seed(42)
advertising = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
# 真实关系:销量 = 5 * 广告投入 + 100 + 噪声
sales = 5 * advertising + 100 + np.random.normal(0, 10, len(advertising))

# 训练模型
model = LinearRegression()
model.fit(advertising.reshape(-1, 1), sales)

# 预测未来
future_ads = np.array([110, 120, 130])
future_sales = model.predict(future_ads.reshape(-1, 1))

# 可视化
fig, ax = plt.subplots(1, 1, figsize=(12, 7))

# 绘制历史数据
ax.scatter(advertising, sales, color='blue', s=100, alpha=0.6, 
          label='历史数据', zorder=5, edgecolors='black', linewidth=1.5)

# 绘制拟合线
x_line = np.linspace(0, 140, 100)
y_line = model.predict(x_line.reshape(-1, 1))
ax.plot(x_line, y_line, 'r-', linewidth=3, label='拟合直线', zorder=3)

# 绘制预测数据
ax.scatter(future_ads, future_sales, color='green', s=150, marker='*', 
          label='预测数据', zorder=6, edgecolors='black', linewidth=2)

# 添加预测线
for ad, sale in zip(future_ads, future_sales):
    ax.plot([ad, ad], [0, sale], 'g--', alpha=0.5, linewidth=1)
    ax.text(ad, sale + 10, f'{sale:.0f}', ha='center', fontsize=10, 
           weight='bold', bbox=dict(boxstyle='round', facecolor='lightgreen', alpha=0.7))

ax.set_xlabel('广告投入(万元)', fontsize=13, weight='bold')
ax.set_ylabel('销量(件)', fontsize=13, weight='bold')
ax.set_title('销量预测:广告投入 vs 销量', fontsize=15, weight='bold')
ax.legend(fontsize=12)
ax.grid(True, alpha=0.3)

# 添加信息
info_text = f'拟合方程: 销量 = {model.coef_[0]:.2f} × 广告投入 + {model.intercept_:.2f}\n\n'
info_text += f'预测结果:\n'
for ad, sale in zip(future_ads, future_sales):
    info_text += f'  广告投入 {ad}万元 → 预测销量 {sale:.0f}件\n'

ax.text(0.02, 0.98, info_text, transform=ax.transAxes, 
        fontsize=11, verticalalignment='top',
        bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))

plt.tight_layout()
plt.savefig('sales_prediction.png', dpi=300, bbox_inches='tight')
plt.show()

2. 关系分析

应用场景:

  • 影响因素分析:哪些因素对结果影响最大?
  • 相关性分析:两个变量之间是否存在线性关系?
  • 趋势分析:数据的变化趋势是什么?

3. 异常检测

应用场景:

  • 数据质量检查:识别偏离预测值的数据点
  • 异常值检测:找出不符合规律的数据

第六部分:线性回归在机器学习中的作用

线性回归在机器学习中的地位

1. 基础算法

  • 线性回归是机器学习的基础算法
  • 许多复杂算法的基础(如神经网络)
  • 理解线性回归有助于理解其他算法

2. 基准模型

  • 作为其他模型的对比基准
  • 如果线性回归效果好,说明问题可能比较简单
  • 如果线性回归效果差,可能需要更复杂的模型

3. 可解释性强

  • 权重和偏置有明确的物理意义
  • 容易理解和解释
  • 适合需要解释的场景

在Scikit-learn中的应用

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

# 加载数据(示例:波士顿房价数据集)
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing()
X, y = housing.data, housing.target

# 数据划分
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_train_pred = model.predict(X_train_scaled)
y_test_pred = model.predict(X_test_scaled)

# 评估
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)
test_mse = mean_squared_error(y_test, y_test_pred)

print(f"训练集 R²: {train_r2:.4f}")
print(f"测试集 R²: {test_r2:.4f}")
print(f"测试集 MSE: {test_mse:.4f}")
print(f"\n特征权重:")
for i, (name, weight) in enumerate(zip(housing.feature_names, model.coef_)):
    print(f"  {name}: {weight:.4f}")
print(f"偏置: {model.intercept_:.4f}")

线性回归的局限性

1. 线性假设

  • 假设输入和输出之间存在线性关系
  • 对于非线性关系效果差

2. 对异常值敏感

  • 异常值会显著影响拟合结果
  • 需要使用鲁棒回归方法

3. 多重共线性问题

  • 特征之间高度相关时,权重不稳定
  • 需要使用正则化方法

改进方法

1. 多项式回归

  • 将特征进行多项式变换
  • 可以拟合非线性关系

2. 正则化回归

  • Ridge回归:L2正则化
  • Lasso回归:L1正则化
  • Elastic Net:L1+L2正则化

3. 鲁棒回归

  • 对异常值不敏感
  • 使用Huber损失等

第七部分:实际案例:完整的线性回归项目

案例:学生成绩预测

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

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 生成模拟数据
np.random.seed(42)
n_students = 200

data = {
    'study_hours': np.random.uniform(1, 10, n_students),
    'homework_completed': np.random.uniform(0, 100, n_students),
    'attendance': np.random.uniform(70, 100, n_students),
    'previous_score': np.random.uniform(60, 90, n_students)
}

# 真实关系:成绩 = 5*学习时间 + 0.3*作业完成度 + 0.2*出勤率 + 0.4*上次成绩 + 20 + 噪声
df = pd.DataFrame(data)
df['score'] = (5 * df['study_hours'] + 
               0.3 * df['homework_completed'] + 
               0.2 * df['attendance'] + 
               0.4 * df['previous_score'] + 
               20 + 
               np.random.normal(0, 5, n_students))

# 准备数据
X = df[['study_hours', 'homework_completed', 'attendance', 'previous_score']]
y = df['score']

# 划分训练集和测试集
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_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# 评估
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)
test_rmse = np.sqrt(mean_squared_error(y_test, y_test_pred))
test_mae = mean_absolute_error(y_test, y_test_pred)

# 创建综合可视化
fig = plt.figure(figsize=(18, 12))

# 1. 特征与目标的关系
for idx, feature in enumerate(X.columns, 1):
    ax = fig.add_subplot(3, 3, idx)
    ax.scatter(X_train[feature], y_train, alpha=0.5, s=30, label='训练集')
    ax.scatter(X_test[feature], y_test, alpha=0.5, s=30, label='测试集', marker='x')
    ax.set_xlabel(feature, fontsize=11)
    ax.set_ylabel('成绩', fontsize=11)
    ax.set_title(f'{feature} vs 成绩', fontsize=12, weight='bold')
    ax.legend(fontsize=9)
    ax.grid(True, alpha=0.3)

# 2. 预测值 vs 真实值(训练集)
ax = fig.add_subplot(3, 3, 5)
ax.scatter(y_train, y_train_pred, alpha=0.6, s=50, edgecolors='black', linewidth=0.5)
ax.plot([y_train.min(), y_train.max()], [y_train.min(), y_train.max()], 
        'r--', linewidth=2)
ax.set_xlabel('真实成绩', fontsize=11)
ax.set_ylabel('预测成绩', fontsize=11)
ax.set_title(f'训练集:预测值 vs 真实值 (R² = {train_r2:.4f})', fontsize=12, weight='bold')
ax.grid(True, alpha=0.3)

# 3. 预测值 vs 真实值(测试集)
ax = fig.add_subplot(3, 3, 6)
ax.scatter(y_test, y_test_pred, alpha=0.6, s=50, edgecolors='black', linewidth=0.5, color='green')
ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 
        'r--', linewidth=2)
ax.set_xlabel('真实成绩', fontsize=11)
ax.set_ylabel('预测成绩', fontsize=11)
ax.set_title(f'测试集:预测值 vs 真实值 (R² = {test_r2:.4f})', fontsize=12, weight='bold')
ax.grid(True, alpha=0.3)

# 4. 残差图
ax = fig.add_subplot(3, 3, 7)
residuals = y_test - y_test_pred
ax.scatter(y_test_pred, residuals, alpha=0.6, s=50, edgecolors='black', linewidth=0.5)
ax.axhline(y=0, color='r', linestyle='--', linewidth=2)
ax.set_xlabel('预测成绩', fontsize=11)
ax.set_ylabel('残差', fontsize=11)
ax.set_title('残差图', fontsize=12, weight='bold')
ax.grid(True, alpha=0.3)

# 5. 特征权重
ax = fig.add_subplot(3, 3, 8)
colors = ['green' if w > 0 else 'red' for w in model.coef_]
bars = ax.barh(X.columns, model.coef_, color=colors, alpha=0.7, edgecolor='black')
ax.axvline(x=0, color='black', linestyle='--', linewidth=1)
ax.set_xlabel('权重值', fontsize=11)
ax.set_title('特征权重', fontsize=12, weight='bold')
ax.grid(axis='x', alpha=0.3)
for bar, w in zip(bars, model.coef_):
    ax.text(w + (0.1 if w > 0 else -0.1), bar.get_y() + bar.get_height()/2, 
           f'{w:.2f}', va='center', ha='left' if w > 0 else 'right', 
           fontsize=10, weight='bold')

# 6. 评估指标
ax = fig.add_subplot(3, 3, 9)
metrics = ['R²', 'RMSE', 'MAE']
values = [test_r2, test_rmse, test_mae]
colors_bar = ['skyblue', 'lightgreen', 'lightcoral']
bars = ax.bar(metrics, values, color=colors_bar, alpha=0.7, edgecolor='black', linewidth=1.5)
ax.set_ylabel('值', fontsize=11)
ax.set_title('评估指标', fontsize=12, weight='bold')
ax.grid(axis='y', alpha=0.3)
for bar, val in zip(bars, values):
    height = bar.get_height()
    ax.text(bar.get_x() + bar.get_width()/2., height,
           f'{val:.2f}', ha='center', va='bottom', fontsize=10, weight='bold')

plt.suptitle('学生成绩预测:完整的线性回归分析', fontsize=16, weight='bold')
plt.tight_layout()
plt.savefig('complete_regression_project.png', dpi=300, bbox_inches='tight')
plt.show()

# 打印结果
print("="*60)
print("线性回归模型结果")
print("="*60)
print(f"\n拟合方程:")
print(f"成绩 = {model.coef_[0]:.2f}×学习时间 + {model.coef_[1]:.2f}×作业完成度 + "
      f"{model.coef_[2]:.2f}×出勤率 + {model.coef_[3]:.2f}×上次成绩 + {model.intercept_:.2f}")
print(f"\n评估指标:")
print(f"  训练集 R²: {train_r2:.4f}")
print(f"  测试集 R²: {test_r2:.4f}")
print(f"  测试集 RMSE: {test_rmse:.2f}")
print(f"  测试集 MAE: {test_mae:.2f}")
print(f"\n特征重要性:")
for name, weight in zip(X.columns, model.coef_):
    print(f"  {name}: {weight:.4f}")
print(f"偏置: {model.intercept_:.4f}")

第八部分:总结与最佳实践

线性回归的核心要点

  1. 简单而强大:线性回归虽然简单,但在很多场景下效果很好
  2. 可解释性强:权重和偏置有明确的物理意义
  3. 计算效率高:训练和预测都很快
  4. 作为基准:可以作为其他模型的对比基准

使用线性回归的步骤

  1. 数据准备

    • 收集数据
    • 数据清洗
    • 特征选择
  2. 模型训练

    • 划分训练集和测试集
    • 训练模型
    • 调整参数
  3. 模型评估

    • 计算评估指标
    • 分析残差
    • 检查假设
  4. 模型应用

    • 进行预测
    • 解释结果
    • 持续监控

最佳实践

  1. 数据预处理

    • 处理缺失值
    • 标准化特征
    • 处理异常值
  2. 特征工程

    • 选择相关特征
    • 处理多重共线性
    • 创建新特征(如多项式特征)
  3. 模型验证

    • 使用交叉验证
    • 检查过拟合
    • 分析残差
  4. 模型改进

    • 尝试正则化
    • 使用多项式回归
    • 考虑非线性模型

常见问题与解决方案

问题1:线性关系假设不成立

  • 解决方案:使用多项式回归或非线性模型

问题2:特征之间存在多重共线性

  • 解决方案:使用Ridge或Lasso回归

问题3:存在异常值

  • 解决方案:使用鲁棒回归方法

问题4:特征数量很多

  • 解决方案:使用Lasso回归进行特征选择

结语

线性回归是机器学习中最基础、最重要的算法之一:

  • 简单易懂:概念直观,容易理解
  • 应用广泛:适用于各种预测和分析场景
  • 基础算法:是理解其他复杂算法的基础
  • 实用性强:在实际项目中经常使用

通过本文的学习,你应该能够:

  1. 理解线性回归的基本原理
  2. 掌握一元和多元线性回归
  3. 知道如何评估线性回归模型
  4. 在实际项目中应用线性回归

记住:线性回归虽然简单,但非常实用。在很多场景下,简单的线性回归可能比复杂的模型效果更好!


参考文献

  1. Scikit-learn 官方文档: https://scikit-learn.org/stable/modules/linear_model.html
  2. Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning
  3. James, G., et al. (2013). An Introduction to Statistical Learning

评论