回归(Regression)是机器学习中的一种基本方法,属于有监督学习(Supervised Learning),其目标是通过分析自变量与因变量之间的关系,建立一个数学模型来预测因变量的值。这种技术通常用于预测分析,时间序列模型以及发现变量之间的因果关系。通常使用曲线或直线来拟合数据点,目标是使曲线到数据点的距离差异最小。
线性回归、多项式回归、正则化线性回归是机器学习中常用的回归分析方法。
-
线性回归(Linear Regression):用直线拟合数据关系;
-
多项式回归(Polynomial Regression):用曲线拟合复杂关系;
-
正则化线性回归(Regularized Linear Regression):通过加约束来防止模型过于复杂。
✅ 一、线性回归的基本概念
1.1 模型定义
1.1.1 样本特征集
单个样本有 个特征,表示为
为列向量
,即
(样本特征空间
)
共有 个样本,则样本特征集表示为
为矩阵,每一行代表一个样本,即:
1.1.2 样本标签集
单个样本有一个标签为 ,
个样本的标签表示为
为列向量
(简单起见,假设标签只有一维)
1.1.3 线性回归函数
对于每一个标签 ,线性回归函数为:(
为预测的标签,
为真实的标签)
其中系数用列向量表示为
因此,对于每一个样本,线性回归方程可以用矩阵表示:
则对于 n 个样本,有:
使用矩阵表示,令 为
维列向量,则对于整个样本空间,有:
当样本特征空间 | 当样本特征空间 |
---|---|
![]() | ![]() |
1.2 目标函数(损失函数)
大部分机器学习算法都有误差,我们需要通过显性的公式来描述这个误差,并将这个误差优化到最小值,这个显性的公式即为损失函数。
对于每个样本,真实的值为 ,预测的值为
,则误差(残差)
。
则对于 个样本,总误差(残差平方和SSE:Sum of Squares for Error)为:
总误差为 的函数,取
是为了便于求导(求梯度)。
则目标优化式为:
1.3 评估指标
除了残差平方和,还有其他评估指标用于评价回归模型的准确性。
1.3.1 均方误差(平均残差MSE:Mean Squared Error)
误差平方的平均数。为了消除数据集规模对残差平方和的影响,把每个误差平方后取平均:
n 为样本数
1.3.2 R-square 判定系数(
)
判定系数测度了回归直线对观测数据的拟合程度,它的计算需要用到组间误差平方和和离差平方和。
-
样本总数:
-
真实值:
-
预测值:
-
真实均值:
名称 | 定义 |
---|---|
离差平方和 (SST: Total sum of squares) | |
组间平方和 (SSR: Sum of squares of the regression) | |
均方误差(平均残差MSE:Mean Squared Error) |
对于最小二乘线性回归(含截距项),有经典分解
证明:
其中 。
最小二乘正规方程(见2.1)保证 且
,因此交叉项:
判定系数 的两种等价定义
从分解式 SST=SSR+SSE 立即得到
-
;
-
当模型无解释力时
,则 SSR=0,
=0;
-
当模型完美拟合时
,则 SSE=0,
=1;
-
的取值范围是 [0,1]。
越接近1,回归直线和个观测点越接近,回归直线的拟合程度就越好;反之,越接近0,拟合程度越差。
💡 二、线性回归的求解方法
2.1 最小二乘法(正规方程)
2.1.1 推导
则求导为:
求梯度的一些结论:
的梯度为
为标量,其转置
的梯度也为
的梯度为
因为 ,则
为凸函数,则 的局部极小值即为全局极小值
当 取极小值时,
,即:
2.1.2 适用条件
-
当样本数量
大于样本空间的维数
,并且输入矩阵
满足列满秩,则
可逆。
-
若
满足可逆,则也满足正定,因此我们找到的临界点(通过求解梯度为零)是最小的。
-
如果
为非满秩矩阵,则
不可逆。使用正规方程解法则需要对
进行正则化。
2.2 梯度下降法(Gradient Descent)
2.2.1 梯度下降法的通用框架
-
待最小化目标:
-
参数向量:
-
更新规则(批量梯度下降):其中
为学习率(每次迭代时
的值可以改变),
为梯度
举例:使用梯度下降法求函数 F(x)=x^2−3 取最小值时 x 的值
import numpy as np
# 目标函数
def f(x):
return x**2 - 3
# 梯度
def grad(x):
return 2 * x
# 超参数
alpha = 0.1 # 学习率
x = 4.0 # 初始点
tol = 1e-6 # 收敛阈值
max_iter = 1000 # 最大迭代次数
# 记录过程
path = [x]
for k in range(max_iter):
x_new = x - alpha * grad(x)
if abs(x_new - x) < tol:
break
x = x_new
path.append(x)
print(f"迭代次数: {k+1}")
print(f"近似最小值点 x* ≈ {x:.6f}")
print(f"近似最小值 F(x*) ≈ {f(x):.6f}")
控制台输出: 迭代次数: 62 近似最小值点 x* ≈ 0.000005 近似最小值 F(x*) ≈ -3.000000
超参数不同,控制台输出不同,例如:
# 超参数
alpha = 0.6 # 学习率
x = 3.0 # 初始点
控制台输出: 迭代次数: 11 近似最小值点 x* ≈ 0.000000 近似最小值 F(x*) ≈ -3.000000
-
超参数影响:
-
不同的学习率将决定训练结果的质量。如果学习率过小,那么收敛过程会很慢,如果学习率过大,那么可能会导致不收敛,甚至远离。
-
初始点的选取也将影响结果。初始点选取的不好则容易陷入局部最优解,选取的好则能很快找到全局最优解。
-
2.2.2 线性回归中的梯度下降法
-
迭代方程
线性回归中的损失函数:
梯度为:
则梯度下降法的迭代方程为:
表示经过第 t 轮迭代得到的
值。由于每一次迭代,
都会根据所有样本的值进行更新,所以这种方法也被称为批次梯度下降法(Batch Gradient Descent)。
-
时间复杂度
假设有 n 个样本,d 个特征,那么每轮迭代的时间复杂度为 O(np),若经过 l 轮达到收敛,则总的复杂度为 O(npl)
-
学习率
的选择
如何选择学习率 的值是一个重要的问题。如果
取值太小, 那么靠近局部最小值的速度会很慢;如果
取值太大,那么可能难以足够接近局部最小值。所以在实际运用中,需依靠经验或实验来选取
的值。
-
停机准则
-
最大迭代数:对于算法设置一个最大的迭代次数T,当算法的迭代代数达到这个最大值的时候,算法会停止。
-
改进低于阈值:提前设定一个阈值
,当算法中在新的一次迭代后,系数与前一代相比的改进低于这个阈值时,算法就会提前停止。如何计算系数改进程度需要按照实际要求来选择。比如,对于系数
,用
来判断是否提前停止算法。
-
损失函数值:利用损失函数值来作为停机准则。因为我们的 目的是最小化损失函数值,那么当损失函数值很接近于 0 的时候,算法就可以停止了。比如针对第 t 代更新后的系数
, 如果
≤10^{−5} 就停止算法。
-
上述的三种停机准则可通用于任何迭代的机器学习算法。但是针对具体的模型或算法,需要进行特别的设计。
-
2.2.3 几种变体
随机梯度下降法
梯度下降算法(GD)最大的问题是运算负担很重,在批量梯度下降中,我们将 作为一个整体,每一次迭代时计算这个整体的梯度
。显然,当样本数量 n 较大的时候,计算梯度的时间开销会很大。
在随机梯度下降中,我们每一次迭代更新 ,都只考虑用其中一个样本对应的损失函数的梯度
来近似
。
则:(注意 是标量,其转置为本身,
为标量)
上图体现了批量梯度下降与随机梯度下降的梯度更新的区别。显然随机梯度下降的曲线波动更大,因为在每一步只考虑了一个样本,然而整体的趋势与批量梯度下降相同。除此之外,由于随机梯度下降每一次更新的幅度较大,算法比较容易避开局部最优值,所以随机梯度下降最终的预测结果可能会更准确。
小批量梯度下降(Mini-Batch GD)
随机梯度下降存在的一个问题是不稳定。这是因为我们每次 更新系数时只考虑一个样本,这种“短视”的做法会让我们的算法过分关注一个样本,而不是考虑多个样本的平均效应。最原始的批量梯度下降从整体上考虑了所有数据的信息,然而它的运算效率比较低。因此为了在运算效率和算法稳定性之间作出权衡,我们可以使用小批量梯度下降。
我们将数据分为一共 L 个块 ,其中每一块的样本数量为
。然后在更新系数时综合考虑一个块内的数据。这时损失函数可以表示为:
这样一来,在迭代中的每一代,算法遍历每一个数据块 ,利用这一块数据来计算梯度。这样系数的更新公式就变为:
其中 是样本块
中样本对应的标签。小批量梯度下降算法的伪代码如Algorithm3所示。
一般来说,在实际使用小批量梯度下降时,我们选择使得每一块的样本数都相同,即。特别地,当 B=1 时就是随机梯度下降算法。数据块的大小 B 是实现小批量梯度下降时所需要调整的参数。如果 B 较小,那么算法会收敛很快,但是不稳定;反之如果 B 较大,那么算法的收敛会比较慢但是会更稳定。因此 B 的选择是在算法效率和稳定性之间的权衡。 为了提高CPU或者GPU运算的效率,块大小 B 经常设置为2的次方,比如32,64,128⋯⋯。
复杂度分析:
梯度下降的计算主要集中在更新 时的梯度计算。先考虑随机梯度下降算法。对于随机梯度下降,梯度的计算只需要向量乘法运算,因此每一次更新
只需要
的时间复杂度。因此随机梯度下降算法总体的时间复杂度为
。
然后考虑小批量梯度下降这种一般情况。针对每一块样本 ,计算梯度所需的是 B 乘法运算。这一步所需的时间复杂度为
。在每一次迭代中要针对总共 L 个样本块进行上述的系数更新。因此小批量梯度下降算法的时间复杂度为
。因为
,小批量梯度下降和批量梯度下降算法的时间复杂度其实是相同的。但是如果我们把更新一次
当做算法的一步,那么小批量梯度下降算法在每一步中的时间成本要少于批量梯度下降(
)。这种特性使得小批量梯度下降更适用于样本数量不断变化的场景,比如在线学习。
在线学习
之前我们考虑的情况都是以获得了全部样本为前提的:利用获得的所有样本来预测系数
。然而有些应用中,我们可能暂时无法一次性获得所有样本,或者样本数量是无限的。比如说对于股票预测,每一天都会有新的数据产生而且理论上来说数据量是无限的。这种情况下数据往往以数据流的形式存在,因此当每次获得新的样本时,我们需要对模型参数(比如线性回归中的
)进行调整。对于在线学习,批量梯度下降并不是一个好的选择,因为每当有新的数据输入,我们就需要将新的数据合并入现有的数据集,并用合并后的数据集来重新计算梯度以更新系数。随着时间的增长,数据集会越来越大,每次有新的数据输入时,更新系数的时间成本会非常大。相比较而言,小批量梯度下降更适合在线学习。针对一个提前设定的数据块大小 B,如果新加入的数据数量达到 B,我们就用这些数据来更新系数。因此,根据数据流来每次调整系数只需要用到一小部分数据,而不是现有的整个数据集。
2.3 牛顿法
2.3.1 基本思想
在凸优化中,牛顿法用迭代和不断逼近的思想来求解一个比较复杂的方程的根。牛顿法的核心思想是对函数的一阶泰勒展开求解。假设我们有一个函数 ,我们需要求解
。我们在点
处将函数进行一阶展开,得到:
将 代入可得:
我们这里计算得到的 x 只是对方程根的近似,但是 x 比 要更接近方程根。因此我们可以通过迭代的方式,在这个近似解 x 处一阶展开然后更新近似解的值。这样我们拥有一个迭代解方程根的公式:
2.3.2 用于线性回归
我们已经知道了牛顿法的根本思路:利用迭代方法逼近最优解以解 。
回顾线性回归,我们希望预测一个系数 来最小化函数
。而损失函数的最小值出现在
的位置。所以对于线性回归,我们可以求牛顿法求方程
的根。牛顿法解线性回归问题的系数迭代公式:
其中:
-
是梯度向量;
-
是 Hessian 矩阵(二阶导数矩阵);
-
是第
次迭代的参数估计。
对于线性回归损失函数:
其梯度与 Hessian 分别为:
代入牛顿法公式得:
结论:牛顿法在线性回归中 一步收敛 到最优解,但需计算 Hessian 逆,时间复杂度为
,不适用于大规模数据。
2.3.3 牛顿法 vs 梯度下降法
特性 | 梯度下降法 | 牛顿法 |
---|---|---|
使用信息 | 一阶导数 | 一阶+二阶导数 |
收敛速度 | 较慢 | 较快(二次收敛) |
计算复杂度 | ||
是否需调参 | 需设置学习率 | 无需学习率 |
是否适合大规模数据 | 适合 | 不适合 |
✍️ 三、线性回归的Python实现
3.1 手动实现梯度下降算法
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 随机生成100个样本点,每个样本点有1个特征,特征值是x,标签值是y
np.random.seed(0)
x = 2 * np.random.rand(100, 1)
y = 4 + 3 * x + np.random.randn(100, 1)
# 初始化参数
w = 0
b = 0
learning_rate = 0.1
n_iterations = 1000
# 梯度下降
for i in range(n_iterations): # 1000次迭代
y_pred = w * x + b
dw = -(2/len(x)) * np.sum(x * (y - y_pred))
db = -(2/len(x)) * np.sum(y - y_pred)
w = w - learning_rate * dw
b = b - learning_rate * db
# 输出最终参数
print(f"手动实现的斜率 (w): {w}")
print(f"手动实现的截距 (b): {b}")
# 可视化手动实现的拟合结果
y_pred_manual = w * x + b
plt.scatter(x, y)
plt.plot(x, y_pred_manual, color='green')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Manual Gradient Descent Fit')
plt.show()
控制台输出:
手动实现的斜率 (w): 2.968467510701028
手动实现的截距 (b): 4.222151077447219
3.2 Scikit-learn库的运用
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 随机生成100个样本点,每个样本点有1个特征,特征值是x,标签值是y
np.random.seed(0)
x = 2 * np.random.rand(100, 1)
y = 4 + 3 * x + np.random.randn(100, 1)
# 创建线性回归模型
model = LinearRegression()
# 拟合模型
model.fit(x, y)
# 输出模型的参数
print(f"斜率 (w): {model.coef_[0][0]}")
print(f"截距 (b): {model.intercept_[0]}")
# 预测
y_pred = model.predict(x)
# 可视化拟合结果
plt.scatter(x, y)
plt.plot(x, y_pred, color='red')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Linear Regression Fit')
plt.show()