线性回归是研究因变量y和自变量x之间数量上相互依存的线性关系。在机器学习中自变量x为样本特征,因变量y为目标值。比如在预测房价的机器学习任务中,每个样本x表示与房价有关的各种特征,而y为相对应的房屋价格。根据每个样本中特征的个数分为:
- 简单线性回归:每个样本只有一个特征;
- 多元线性回归:每个样本中有多个特征;
本文主要介绍每个样本拥有一个特征的简单线性回归,
简单线性回归
机器学习是从有限的观测数据中学习(或"猜测")出具有一般性的规律,并可以将总结出来的规律推广应用到未观测样本上。对于一个机器学习任务来说,首先确定其输入空间
和输出空间
,不同的机器学习任务的主要区别在于输出空间的不同,在回归任务中
。
输入空间
和输出空间
构成一个样本空间。对于样本空间中的样本
,假定
和
之间的关系可以通过一个未知的真实映射函数
来描述。机器学习的目标是找到一个模型来近似真实映射函数
。不过由于我们不知道真实的映射函数
的具体形式,因而只能根据经验来假设一个函数集合
,称为假设空间(Hypothesis Space),然后通过观测其在训练集
上的特性,依据一些学习准则和优化算法选择一个理想的函数(也称为假设)
来近似真实映射函数
,其中
。
通过上面的描述总结出简单线性回归模型的三个特性:
- 每个样本只有一个特征;
- 输出空间
;
- 假设空间
;
简单线性回归的假设空间为所有的
的函数集合,不同参数
和
组合表示不同的函数。换句话说,简单线性回归的假设空间为所有不同参数
和
组合表示函数的集合。简单线性回归的目的找到能够近似真实的映射函数
的函数
,即通过一些学习准则和优化算法找到最好的参数
和
。
本文着重介绍每个样本拥有一个特征的简单线性回归,因此在横坐标表示特征,纵坐标表示目标值的二维坐标系中,简单线性回归的假设空间为所有可能的直线,我们需要找到一条能够拟合样本的直线。
我们知道两个点确定一条直线,如果已知直线上任意两个样本点,可以通过联立方程组的形式求出能够拟合这两个样本点的直线方程,这个过程和简单线性回归的过程相近。简单线性回归的假设空间
,简单来说
,其中参数
可以理解为直线的斜率(Slope),参数
为直线的偏置(Bias)。
已知直线上的任意两个点
,代入方程联立方程组即可求出对应的参数
的值。
假设直线上任意的两个点为
,代入上式即可得:
上式的二元一次方程组可以通过消元法轻松计算出
和
的值,
,这种能够通过严格公式推导出来的精确解称为解析解。
那么对于简单线性回归,我们只需要采样任意两个在直线上的样本点就可以通过联立二元一次方程组的方式找出最理想的函数
。不过通常我们采样的样本点存在观测误差,此时通过两个样本点确定的直线方程可能会带来较大的估计偏差。下图采样的两个红色的数据点由于具有观测误差,所以求解出来的参数
和理想的
存在较大的偏差。
▲两个点确定一条直线可能会有估计偏差
由于观测误差的影响,采样的两个样本点为
,此时确定的是红色标识的直线,显然和方程
之间有很大的估计偏差。为了减少观察误差的影响,可以通过采样多组样本点的集合
,通过多组样本点找出一条"最好"的直线。
▲多组样本点
简单线性回归的损失函数
由于观测误差的存在,我们不能使用两点确定一条直线的方法来找到最优的函数,不过我们采集了多个样本点集合
,这些样本点虽然含有观测误差等噪声,不过这些样本点整体和真实的直线趋势相吻合。因此即使可能不存在一条能够穿过所有数据点的直线,我们也能够找到一条能够尽可能拟合样本点集合
的比较好的直线。
如何来衡量"好"和"不好"呢?一个很自然的想法就是求出当前模型的所有数据点的预测值
与真实值
之间的差的平方和作为总误差
:
被称为损失函数,通过一些优化算法找到一组参数
和
使得
最小,
即为寻找的最理想的函数
:
其中
表示采样的数据集个数。这种直接计算预测值与真实值之间的差的平方的误差计算方法称为(Mean Squared Error, 简称MSE)。
代码语言:javascript复制def mse(w, b, X, y):
"""
根据当前w,b参数计算MSE
:param w:当前w参数的值
:param b:当前b参数的值
:param X:样本特征
:param y:真实值
:return:当前w,b参数的MSE值
"""
totalError = 0
try:
# 遍历所有样本点
for i in range(len(X)):
x = X[i]
y = y[i]
# 累加所有样本点的误差值
totalError = (y - (w * x b)) ** 2
# 将累加的误差值求平均得到MSE
return totalError / float(len(X))
except:
return float("inf")
简单线性回归的优化方法
对于没有观测误差的样本点,我们可以使用消元法直接求解出参数
和
的精确解,这种通过严格公式推导出的精确解被称为解析解。如果样本点具有观测误差(当然真实情况或多或少会有一些样本点存在观测误差),依然通过解析解来求解参数
和
的精确解显然是不可能的。我们只能去借助一些数值方法进行优化来得到一个近似的数值解,这些优化方法有很多,最常用的是梯度下降法,梯度下降法的具体公式如下:
其中
表示待优化的参数,
为学习率用于决定梯度下降移动的步长,
为函数的梯度。梯度下降法的原理非常简单,梯度方向是函数增长最快的方向,因此梯度的反方向就是函数下降最快的方向,梯度下降法就是利用这一点每一次都迭达一个小步长,不断向当前点函数下降最快方向迭代,直到找到函数的极小值点。
现在可以利用梯度下降法来求解参数
和
的值。这里需要最小化的函数为
:
待优化的参数有两个
和
,按照梯度下降算法的公式可以得到下面两个更新迭代的公式:
接下来只需要计算出
和
两个偏导数代入公式(3.3)即可更新迭代参数
和
:
由于样本拥有一个特征,因此求导并不难,只需要了解复合函数的求导法则
,同理可得:
代码语言:javascript复制def step_gradient(w_current, b_current, X, y, lr):
"""
计算误差函数在所有点上的导数,并更新w,b
:param w_current: 当前的参数w
:param b_current: 当前的参数b
:param X:样本特征
:param y:真实值
:param lr:学习率
:return:更新后的参数w,b
"""
# 计算所有样本的梯度
w_gradient = 0
b_gradient = 0
N = float(len(X)) # 样本总个数
for i in range(N):
x = X[i]
y = y[i]
# 误差函数对w的导数,参考公式(3.4)
b_gradient = (2/ N) * ((w_current * x b_current) - y)
# 误差函数对b的导数,参考公式(3.5)
w_gradient = (2 / N) * ((b_current * x b_current) - y)
# 根据梯度下降算法更新参数w,b
new_w = w_current - (lr * w_gradient)
new_b = b_current - (lr * b_gradient)
return [new_w, new_b]
对所有数据集的所有样本训练一次称为一个Epoch,通常需要循环迭代足够多的次数,此时循环迭代num_iterations个Epoch。
代码语言:javascript复制def gradient_descent(X, y, starting_w, starting_b, lr, num_iterations):
"""
更新num_iterations个Epoch后的w,b
:param X: 样本特征
:param y: 真实值
:param starting_w: 初始的参数w
:param starting_b: 初始的参数b
:param lr: 学习率
:param num_iterations: 迭代的次数
:return: 返回最后一次梯度下降算法后的参数w, b
"""
w = starting_w
b = starting_b
# 迭代更新num_iterations次参数更新
for step in range(num_iterations):
# 计算一次梯度下降算法更新参数
w, b = step_gradient(w, b, X, y, lr)
loss = mse(w, b, X, y)
if step % 100 == 0:
print(f"iteration{step}, loss:{loss}, w:{w}, b:{b}")
return [w, b] # 返回最后一次的w, b
参考:
- 《TensorFlow深度学习》
- 《神经网络与深度学习》