从零实现线性回归

极简线性回归:概念、数学与Python实现

本文将从概念、数学推导与动手实现三个方面,极简地呈现线性回归模型的核心知识。

  1. 什么是线性回归?
  2. 线性回归的数学原理
  3. 从0用 Python 代码训练一个线性回归模型

完整代码查看本仓库或查看notion网页

1. 什么是线性回归?

线性回归是一种 监督学习 算法,用于估计因变量 y y y 与一个或多个自变量 x x x 之间的线性关系。
简单线性回归仅含一个自变量,模型形式为

y = β 0 + β 1 x + ε , y = \beta_0 + \beta_1 x + \varepsilon, y=β0+β1x+ε,

其中 β 0 \beta_0 β0 为截距, β 1 \beta_1 β1 为斜率, ε \varepsilon ε 为误差项。
多元线性回归则推广到多自变量,记作

y = X β + ε , \mathbf{y} = \mathbf{X}\boldsymbol\beta + \boldsymbol\varepsilon, y=Xβ+ε,

其中 X \mathbf{X} X 是设计矩阵, β \boldsymbol\beta β 是待估参数向量。

常见应用:预测房价、销售额或生物测量值等,既可用于 预测,也可用于 解释 自变量对响应变量变化的贡献。

2. 线性回归的数学原理

2.1 最小二乘损失函数

我们以训练集 { ( x i , y i ) } i = 1 n \{(x_i,y_i)\}_{i=1}^n {(xi,yi)}i=1n 为例,定义 均方误差(MSE) 作为目标函数:

J ( β 0 , β 1 ) = 1 2 n ∑ i = 1 n ( y i − ( β 0 + β 1 x i ) ) 2 . J(\beta_0,\beta_1) = \frac{1}{2n} \sum_{i=1}^n \bigl(y_i - (\beta_0 + \beta_1 x_i)\bigr)^2. J(β0,β1)=2n1i=1n(yi(β0+β1xi))2.

该函数对参数的导数即梯度,用于后续优化。

2.2 梯度下降法

β 0 , β 1 \beta_0,\beta_1 β0,β1 求偏导,并以学习率 α \alpha α 迭代更新:

β 0 ← β 0 − α ∂ J ∂ β 0 , β 1 ← β 1 − α ∂ J ∂ β 1 , \begin{aligned} \beta_0 &\leftarrow \beta_0 - \alpha \frac{\partial J}{\partial \beta_0},\\ \beta_1 &\leftarrow \beta_1 - \alpha \frac{\partial J}{\partial \beta_1}, \end{aligned} β0β1β0αβ0J,β1αβ1J,

其中

∂ J ∂ β 0 = − 1 n ∑ i = 1 n ( y i − ( β 0 + β 1 x i ) ) , ∂ J ∂ β 1 = − 1 n ∑ i = 1 n ( y i − ( β 0 + β 1 x i ) ) x i . \frac{\partial J}{\partial \beta_0} = -\frac{1}{n} \sum_{i=1}^n \bigl(y_i - (\beta_0 + \beta_1 x_i)\bigr),\quad \frac{\partial J}{\partial \beta_1} = -\frac{1}{n} \sum_{i=1}^n \bigl(y_i - (\beta_0 + \beta_1 x_i)\bigr)x_i. β0J=n1i=1n(yi(β0+β1xi)),β1J=n1i=1n(yi(β0+β1xi))xi.

此方法适用于大规模特征或无闭式解时。

2.3 正态方程(闭式解)

对于多元情况,可直接求解使 MSE 最小的参数:

β = ( X ⊤ X ) − 1 X ⊤ y . \boldsymbol\beta = (\mathbf{X}^\top \mathbf{X})^{-1} \mathbf{X}^\top \mathbf{y}. β=(XX)1Xy.

该公式称为 正态方程,在特征数较少时比梯度下降更高效。

3. 从0用 Python 代码训练一个线性回归模型

完整代码查看本仓库

3.1 导入必要库并生成模拟数据

首先,我们需要导入用于数值计算的 numpy 和用于可视化的 plotly。通过设置随机种子保证实验可复现,然后生成一组符合线性关系的数据( y = 4 + 3 x + 噪声 y = 4 + 3x + \text{噪声} y=4+3x+噪声),并用散点图展示数据分布。

import numpy as np
import plotly.graph_objects as go

np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

fig = go.Figure(
    data=[
        go.Scatter(
            x=X.flatten(),
            y=y.flatten(),
            mode='markers',
            opacity=0.7,
            name='数据点'
        )
    ]
)
fig.update_layout(
    title="模拟数据分布",
    title_x=0.5,
    xaxis_title="x",
    yaxis_title="y"
)
fig.show()

运行结果图如下:
在这里插入图片描述

3.2 正态方程解法

正态方程是线性回归的解析解。核心公式为:

β = ( X T X ) − 1 X T y \beta = (X^T X)^{-1} X^T y β=(XTX)1XTy
正确的实现代码如下:

def normal_equation(X, y):
    X_b = np.c_[np.ones((len(X), 1)), X]
    theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
    return theta_best

theta = normal_equation(X, y)
print(f"求解得到参数:截距 = {theta[0][0]:.2f}, 斜率 = {theta[1][0]:.2f}")

运行上述代码,可以得到以下结果:求解得到参数:截距 = 4.22, 斜率 = 2.77

3.3 梯度下降法

梯度下降是一种常用的优化算法,适合大规模数据。我们实现了 gradient_descent 函数,迭代更新参数,并记录每次的损失(均方误差)

def gradient_descent(X, y, lr=0.1, n_iters=1000):
    X_b = np.c_[np.ones((len(X),1)), X]
    theta = np.zeros((2,1))
    m = len(X_b)
    losses = []
    for i in range(n_iters):
        gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
        theta -= lr * gradients
        loss = np.mean((X_b.dot(theta) - y) ** 2)
        losses.append(loss)
    return theta, losses

theta_gd, losses = gradient_descent(X, y, lr=0.05, n_iters=100)
print(f"梯度下降得到参数:截距 = {theta_gd[0][0]:.2f}, 斜率 = {theta_gd[1][0]:.2f}")

运行上述代码,可以得到以下结果:梯度下降得到参数:截距 = 4.04, 斜率 = 2.93
可视化损失曲线:
在这里插入图片描述

可视化损失曲线代码如下:

fig_loss = go.Figure()
fig_loss.add_trace(go.Scatter(
    y=losses,
    mode='lines',
    name='训练损失(MSE)'
))
fig_loss.update_layout(
    title="梯度下降损失曲线",
    title_x=0.5,
    xaxis_title="迭代次数",
    yaxis_title="均方误差(MSE)"
)
fig_loss.show()

3.4 可视化拟合结果

将正态方程和梯度下降得到的参数分别画出拟合直线,与原始数据点对比。
绘制拟合曲线的代码如下:

X_new = np.array([[0],[2]])
y_pred_ne = theta[0] + theta[1] * X_new
y_pred_gd = theta_gd[0] + theta_gd[1] * X_new

fig = go.Figure()
fig.add_trace(
    go.Scatter(
        x=X.flatten(),
        y=y.flatten(),
        mode='markers',
        opacity=0.5,
        name='数据点'
    )
)
fig.add_trace(
    go.Scatter(
        x=X_new.flatten(),
        y=y_pred_ne.flatten(),
        mode='lines',
        name='正态方程拟合',
        line=dict(color='red')
    )
)
fig.add_trace(
    go.Scatter(
        x=X_new.flatten(),
        y=y_pred_gd.flatten(),
        mode='lines',
        name='梯度下降拟合',
        line=dict(color='blue', dash='dash')
    )
)
fig.update_layout(
    title="线性回归拟合结果",
    title_x=0.5,
    xaxis_title="x",
    yaxis_title="y"
)
fig.show()

可视化拟合曲线:
在这里插入图片描述

3.5 计算均方误差

用 sklearn.metrics.mean_squared_error 计算两种方法的预测结果与真实值之间的均方误差,量化模型性能。

from sklearn.metrics import mean_squared_error

y_pred_ne_all = np.c_[np.ones((100,1)), X].dot(theta)
y_pred_gd_all = np.c_[np.ones((100,1)), X].dot(theta_gd)

mse_ne = mean_squared_error(y, y_pred_ne_all)
mse_gd = mean_squared_error(y, y_pred_gd_all)

print(f"MSE (正态方程) = {mse_ne:.2f}, MSE (梯度下降) = {mse_gd:.2f}")

运行上述代码,可以得到以下结果:MSE (正态方程) = 0.81, MSE (梯度下降) = 0.82

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值