线性回归算法

一、什么是线性回归

定义
线性回归(Linear Regression)是一种用于预测连续值的监督学习算法,用来建立特征和标签之间的线性关系。它的目标是找到一个线性公式,让模型的预测结果尽量接近真实值。

举个例子:
假如有了以下身高和体重数据。已知播仔的 身高,预测播仔的 体重 就是回归问题。

图片

找出一条尽可能经过多个点或者距离每个点比较近的一条线这条线就是规律(算法)

图片

线性回归的分类

  • 一元线性回归:只有一个特征参与预测,公式通常写成 $y = wx + b$
  • 多元线性回归:有多个特征共同参与预测,公式通常写成 $y = w_1x_1 + w_2x_2 + \cdots + w_nx_n + b$
  • 本质区别:前者只有一个自变量,后者有多个自变量,但本质上都是在建模线性关系

应用场景

  • 根据房屋面积、楼层、地段等特征预测房价
  • 根据广告投入、活动力度等特征预测销量
  • 根据学习时长、练习次数等特征预测考试成绩
  • 根据温度、湿度、风速等特征预测某个连续型指标
图片

二、线性回归原理

在一元线性回归里,常见公式是:

$$ Y = kX + b $$

它表示:已知特征 X,就可以通过斜率 k 和截距 b 算出预测结果 Y

如果是多个特征一起预测,就会变成多元线性回归,图片里的公式可以写成:

$$ h(x) = w_1x_1 + w_2x_2 + w_3x_3 + \cdots + b $$

进一步简写后,也常写成:

$$ h(x) = w^T x + b $$

其中:

  • x_1, x_2, x_3, ... 表示不同特征
  • w_1, w_2, w_3, ... 表示每个特征对应的权重
  • b 表示偏置
  • h(x) 表示模型根据输入特征算出来的预测结果

当然这只是实现逻辑,实际不会只根据两条数据手动计算,而是让 API 根据训练数据自动求出最优结果。

图片

要让线性回归真正学出规律,本质上要解决两个问题:

  • 怎么知道预测得准不准
  • 参数该往哪边调,才能让预测越来越准
线性回归流程图片

衡量标准:损失函数

用来衡量“预测和真实值到底差了多少”,这个标准就是损失函数。

可以先用“考试扣分”来理解损失函数:

  • 考了 20 分,相当于离满分还差 80
  • 考了 50 分,相当于离满分还差 50
  • 考了 70 分,相当于离满分还差 30

分数和满分之间差多少,就像模型预测值和真实值之间差多少。 损失函数概念

  • 预测值和真实值之间的差值(误差) ,把这种“差多少”量化成一个数的函数,也叫代价函数、目标函数
  • 核心目标:让损失尽可能小,也就是让预测结果尽量接近真实值

在线性回归里,本质上就是找到一条直线或一个超平面,让整体误差最小。

回归问题中常见的损失函数

  1. 平方误差和最小(也常叫最小二乘法)
    就是把每个样本的误差先平方,再全部加起来。
$$ \sum_{i=1}^{m}\left(h(x^{(i)}) - y^{(i)}\right)^2 $$
  1. 均方误差(MSE):误差平方和取平均值
    它比最小二乘法多除以了样本数,更适合直接比较不同数据集上的误差大小。
$$ \frac{1}{m}\sum_{i=1}^{m}\left(h(x^{(i)}) - y^{(i)}\right)^2 $$
  1. 平均绝对误差(MAE):误差绝对值和取平均值
    它不做平方,而是直接看“平均差了多少”,理解起来更直观。
$$ \frac{1}{m}\sum_{i=1}^{m}\left|h(x^{(i)}) - y^{(i)}\right| $$

其中:

  • m:样本数量
  • $h(x^{(i)})$:第 $i$ 个样本的预测值
  • $y^{(i)}$:第 $i$ 个样本的真实值

导数、偏导与优化思路

知道“差多少”以后,下一步就是想办法让这个差值越来越小。

导数是什么

  • 导数可以理解成“斜率”或“变化快慢”。
  • 如果把损失函数想成一座山,导数就是在看你脚下这块地是往上走还是往下走,有多陡。
  • 导数大于 0,说明往右走整体在变大;导数小于 0,说明往右走整体在变小;导数等于 0,通常说明可能走到了平一点的位置。

偏导数是什么

  • 偏导数就是“多个变量时,只盯一个变量看它的影响”。
  • 比如线性回归里有多个参数:w1w2b,那就先固定其他参数,只看 w1 变一点时,损失会怎么变,这就是对 w1 求偏导。
  • 你也可以把它理解成:面前有很多旋钮,每次只拧一个,看结果是变好还是变差。

它在回归里有什么用

  • 我们的目标是让损失函数尽量小
  • 偏导数能告诉我们:每个参数应该往哪边调,损失才会降下来。
  • 所以后面的梯度下降,本质上就是根据偏导数的方向,一点点把参数调到更合适的位置。

如何找到最小的损失位置

  • 正规方程法:像“一次算出答案”,直接求出参数。
  • 梯度下降法:像“边走边找答案”,一点点往更小的损失方向调整参数,数据量大时更常用。

正规方程法

展开看正规方程法的优缺点和公式
  • 正规方程法可以理解成:不一小步一小步试,而是直接用公式把最优参数算出来。
  • 它的目标也是让损失尽量小,只是走的是“直接解方程”这条路。

优点

  • 不需要设置学习率。
  • 不需要一轮一轮迭代。
  • 一般也不需要先对特征做缩放处理。

缺点

  • 当特征很多、数据很大时,计算会越来越慢。
  • 如果 $(X^T X)$ 不可逆,就没法直接按这个公式求解,这时通常要换别的方法。

一句话总结

  • 正规方程法更适合中小规模数据;数据量很大或特征很多时,通常更常用梯度下降法。

最常见的写法是:

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

可以先这样理解:

  • X:特征数据表,也就是输入数据
  • y:真实结果
  • $X^T$:$X$ 的转置,也就是行列互换
  • $\theta$:模型最后算出来的一组参数

如果你前面习惯把模型写成 $w^T x + b$,这里可以把它理解成:
把偏置 $b$ 也并进参数向量 $\theta$ 里了,同时额外补一列恒为 1 的特征,所以公式里看起来没有单独写 b

你不用一开始就死记这个公式,先记住它的核心意思就行:
把一堆训练数据丢进去,直接算出一组最合适的参数,让预测结果尽量接近真实值。

梯度下降法

沿着当前点梯度(导数)的相反方向,以小步长迭代调整参数,逐步逼近函数的最小值点。

  • 梯度下降法可以理解成“下山找最低点”。
  • 现在你站在山坡上,但看不到整座山的全貌,只能根据脚下哪里更陡,判断下一步往哪边走。
  • 每走一步,位置都会更低一点;不断重复后,就会慢慢靠近谷底。

图片

在线性回归里:

  • 山的高度,可以理解成损失函数的大小。
  • 谷底,就是损失最小的位置。
  • 下山的方向,就是让参数继续变得更合适的方向。

它是怎么做的

  1. 先随便给参数一个初始值。
  2. 计算当前位置的损失有多大。
  3. 看看参数往哪边调,损失会下降得更快。
  4. 朝这个方向走一小步。
  5. 重复上面几步,直到损失降得差不多为止。

怎么理解“梯度”

  • 梯度可以理解成“当前位置最陡的上坡方向”。
  • 那梯度下降就是反着它走,也就是往更低的地方走。
  • 在单变量函数里,你也可以把它先理解成某一点的导数,也就是那一点的斜率。
  • 在多变量函数里,梯度可以理解成“很多个偏导数组成的方向信息”,它会告诉我们往哪边变得最快。

图片

梯度下降公式怎么理解

$$ \theta_j := \theta_j - \alpha \frac{\partial}{\partial \theta_j} J(\theta) $$
  • 每一次迭代,都会先看当前点的梯度,再用它去更新当前参数。
  • 可以把它理解成:新参数 = 旧参数 - 学习率 × 梯度($W_1 = W_0 - I_r \cdot \text{grad}$)
  • $\theta$:模型参数,比如线性回归里的权重 $w$ 和偏置 $b$。
  • $J(\theta)$:损失函数,也就是当前这组参数到底“错了多少”。
  • $\frac{\partial}{\partial \theta_j} J(\theta)$:第 $j$ 个参数对应的偏导数,表示它往哪边调会让损失变化得最快。

学习率是什么

  • $\alpha$ 就是学习率,也可以理解成每次下山迈多大一步。
  • 学习率太大,可能一下跨过谷底,来回震荡。
  • 学习率太小,又会走得很慢,训练时间变长。
  • 实际训练里,它控制的是“每次参数更新的步子大小”。
  • 在很多入门例子里,常见起步值可以先试 0.001 ~ 0.01,再根据训练效果调整。

为什么公式里是减号

  • 因为梯度指向的是“上升最快”的方向。
  • 我们想让损失下降,所以要朝反方向走,也就是用减号。

梯度下降法的分类

不同梯度下降方法的核心区别,在于每次更新参数时,用多少样本来计算梯度

  1. 全梯度下降(FGD):每次用全部样本算梯度,方向最稳,但计算最慢。
  2. 随机梯度下降(SGD):每次只用 1 个样本更新,速度快,但抖动也最大。
  3. 小批量梯度下降(Mini-Batch):每次用一小批样本,比如 3264128 条,速度和稳定性折中,实际最常用
  4. 随机平均梯度下降(SAG):不只看当前梯度,还会参考前面算过的梯度,整体更平滑,但启动偏慢。

局部最低点

  • 局部最低点,就是在某个局部范围里已经很低,但还不一定是全局最低的位置;在复杂的非凸问题里,梯度下降确实可能卡在这里。
  • 但在线性回归里,常见平方损失通常是凸问题,所以它一般不是主要问题,更常见的是学习率不合适、收敛慢,或特征没缩放导致训练不稳定。

图片

看懂公式常用的几个概念

这部分不是一开始必须死记的,只是为了后面看公式不发懵。
基础数据表示

  • 标量:一个单独的数,比如 70 分、1.75 米。
  • 向量:一组按顺序排好的数,可以理解成“一行数据”或“一列数据”。
  • 矩阵:很多行很多列的数表,可以理解成“多条数据拼在一起”。

转置是什么

  • 转置就是把列向量变成行向量,或者把行向量变成列向量,常写成 $x^T$。

矩阵乘法怎么理解

  • 矩阵加减要求两个矩阵的行列数一样。
  • 矩阵乘法要求前一个矩阵的列数,等于后一个矩阵的行数。
  • 如果 $A \in \mathbb{R}^{m \times n}$,$B \in \mathbb{R}^{n \times d}$,那么 $AB \in \mathbb{R}^{m \times d}$。
  • 例如 2×5 @ 5×3 = 2×3,所以结果的行列数可以看“最外层”。
  • 矩阵乘法不满足交换律A @ B 能乘,不代表 B @ A 也能乘;就算都能乘,结果通常也不一样。

三、线性回归实战

LinearRegression 基础用法

  • sklearn.linear_model.LinearRegression():普通线性回归模型,适合中小规模数据
  • fit(X, y):用训练数据拟合模型,求出最优权重和偏置
  • predict(X):根据输入特征预测结果
  • coef_:查看模型学到的权重
  • intercept_:查看模型学到的偏置

代码示例:

# 导包 此处导入线性回归API
from sklearn.linear_model import LinearRegression

# 1.准备数据  x:特征(身高)  y:标签(体重)
x_train = [[160], [166], [172], [174], [180]]
y_train = [56.3, 60.6, 65.1, 68.5, 75]
x_test = [[176]]
# 2.准备模型
model = LinearRegression()
# 3.模型训练
# TODO 训练线性回归目的是什么? 找线性规律(最优的k和b)
model.fit(x_train, y_train)
# 训练后打印k和b
print(f"最优的斜率:k是{model.coef_}")  # [0.92942177]
print(f"最优的偏置:b是{model.intercept_}")  # -93.27346938775517
print('=================================')
# 4.模型预测
# TODO 方式1: 手动套入公式
y_pred1 = model.coef_[0] * x_test[0][0] + model.intercept_
print(f"手动计算结果:{y_pred1}")
# TODO 方式2: 使用模型自带的预测方法
y_pred2 = model.predict(x_test)
print(f"模型预测结果:{y_pred2}")

打印:

最优的斜率:k是[0.92942177]
最优的偏置:b是-93.27346938775517
=================================
手动计算结果:70.30476190476188
模型预测结果:[70.3047619]

fit():只做“学习/计算规则参数”这一件事,不修改原始数据。比如线性回归里学斜率 k 和截距 b、标准化里学均值和标准差,都是“算出规律”,不改变输入数据本身。
fit_transform() = fit() + transform(),这类写法主要出现在 StandardScaler 这种“先学规则、再改数据格式”的预处理器里,不是 LinearRegression() 这个模型本身常用的方法。

回归模型评估方法

  • MAE:平均绝对误差,表示模型平均差了多少,最直观。
  • MSE:均方误差,会把误差平方,所以对大误差更敏感。
  • RMSE:均方根误差,是 MSE 再开根号,数值单位和原始结果一致,更容易直观理解。
  • 这三个指标通常都是越小越好,说明预测结果越接近真实值。

下面这个梯度下降案例里,也会顺手把这 3 个指标跑一遍,这样你能把“训练”和“评估”连起来看。

SGDRegressor:梯度下降实现

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor,LinearRegression
from sklearn.metrics import mean_absolute_error,mean_squared_error,root_mean_squared_error
# TODO 1.准备数据
# todo 1.1 获取原始数据
data_url = "http://lib.stat.cmu.edu/datasets/boston"
# 读取波士顿房价原始数据:
# - `sep="\s+"` 表示按一个或多个空白字符分隔
# - `skiprows=22` 表示跳过前 22 行说明文字
# - `header=None` 表示原始数据没有表头
raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
# 这个数据集比较特殊:一条样本被拆成了两行存放
# - 第 1 行提供前半部分特征
# - 第 2 行提供后半部分特征,且最后一列是房价标签
# `np.hstack(...)` 的作用就是把两行特征横向拼成一条完整样本
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
# 取出每条样本第二行的第 3 列,作为最终预测目标 `target`
target = raw_df.values[1::2, 2]
# print(f"特征:{data}")
# print(f"标签:{target}")
# todo 1.2 数据切割
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=42)
# todo 1.3 特征的标准化数据
ss = StandardScaler()
new_x_train = ss.fit_transform(X_train) # 训练集用fit_transform()训练并转换
new_x_test = ss.transform(X_test) # 测试集只能用transform()转换,因为前面训练集已经训练了模型计算了相关内容
# TODO 2.准备模型(正规方程或者梯度下降)
# invscaling: 动态调整学习率    constant:固定学习率
# model = LinearRegression()
model = SGDRegressor(loss="squared_error",learning_rate="constant",eta0=0.01) # 梯度下降模型
# TODO 3.模型训练
model.fit(new_x_train,y_train)
print(f"训练后k权重参数:{model.coef_}")
print(f"训练后b偏置参数:{model.intercept_}")
# TODO 4.模型预测
y_pred = model.predict(new_x_test)
# TODO 5.模型评估 (误差值越小越好)
print(f"平均绝对误差:{mean_absolute_error(y_test,y_pred)}") # 平均绝对误差:3.157712560608428
print(f"均方误差:{mean_squared_error(y_test,y_pred)}") # 均方误差:23.00412271141051
print(f"均方根误差:{root_mean_squared_error(y_test,y_pred)}") # 均方根误差:4.796261326430255
扩展:这里的$k$和$b$是斜率和截距吗?

对,可以这么理解,但更准确一点要分场景说。

  • 在这两行里:

    • model.coef_权重参数
    • model.intercept_偏置项
  • 如果是一元线性回归,公式是 y = kx + b

    • coef_ 就相当于 斜率 k
    • intercept_ 就相当于 截距 b
  • 但你这里这个案例不是一元,而是多特征回归,更接近:

    • y = w1x1 + w2x2 + ... + wnxn + b
    • 所以 model.coef_ 不是一个单独的斜率,而是一组权重 [w1, w2, ..., wn]
    • model.intercept_ 才是那一个偏置/截距 b
  • 所以最准确的说法是:

    • coef_ = 各个特征的权重
    • intercept_ = 截距(偏置)
    • 只有在“只有一个特征”时,coef_ 才能直接叫“斜率”

你现在这段 Boston 案例里,打印出来的 k权重参数 这个写法其实没问题,比直接叫“斜率”更准确。