一元线性回归
想象你是一名出租车司机,记录了三位乘客的打车数据:
- 乘客1:乘车 3公里,支付 8元
- 乘客2:乘车 5公里,支付 10元
- 乘客3:乘车 6公里,支付 11元
你发现他们支付的费用和乘车距离之间存在一种“魔法规律”——总费用等于距离的 1 倍再加上5元的起步费用,用公式表示就是y=f(x)=a*x+b(𝑎=1,𝑏=5)。
如果乘客4打车,她需要乘车 4公里,你很快就能算出:他需要支付9元! ——这就是回归分析,通过已知规律预测未知结果。
这种只涉及一个自变量(距离x)和一个因变量(费用y),且之间存在线性关系就是一元回归。
最小二乘法
但现实世界的规律往往不像这个例子这么完美。假如你来到一个新的城市,这里的出租车费用和距离的关系如下图分布.
你可以看出他们之间有一些规律,可又无法通过一条直线覆盖所有的点.这是你只能尽量画出一条线让这条线离大多数的点都不太远,该用什么规则来定义不太远呢.
-
最先想到的方法是用每个点到直线的垂直距离的和:位于直线上方的点距离为正,位于直线下方的点距离为负,然后求每个点到直线距离的总和,但是这种方法一个致命问题:正负距离会相互抵消!
-
那我们优化一下,用每个点到直线的垂直距离的绝对值的和:这解决了第一种方案的问题,却无法区分均匀偏差与极端偏离,如果有3个点到2条直线的垂直距离分别是(2,2,-2)和(0,0,6)他们的绝对值求和都是6,但很明显第二组的偏差更大.
-
为了更好的表达这种大误差,我们采用每个点到直线的垂直距离的平方的和,通过非线性放大惩罚大偏差.
-
若使用点到直线的垂直距离(形式为),目标函数会因分母的非线性项 变得复杂,计算成本显著增加。实践中更优的方案是用点到直线的竖直距离,它舍弃了几何对称性,但换来了:
闭合解的存在:通过方程直接计算 a 和 b;
统计合理性:假设误差仅存在于因变量(如打车费用),自变量(如距离)精确可控;
计算高效性:复杂度低,适合大规模数据。
这就是最小二乘法,它的的核心思想是最小化预测值与实际值之间的误差平方和。具体步骤如下:
- 定义误差
对于每个数据点 ,预测值为 ,误差为:
- 计算平方误差
平方误差为:
- 总平方误差
对所有数据点求和,得到总平方误差(Cost Function):
- 最小化总平方误差
通过对 关于 和 的偏导数设置为零,求解出最优的 和 。
求解a和b
对于简单线性回归(只有一个自变量),可以通过解析解直接计算出 和 的值。公式如下:
斜率 的计算公式:
截距 的计算公式:
其中: - 是样本数量, - 是所有 和 的乘积之和, - 和 分别是所有 和 的和, - 是所有 的平方和。 python实现代码如下
original_x = [[10.0], [8.0], [13.0], [9.0], [11.0], [14.0], [6.0], [4.0], [12.0], [7.0], [5.0]]
original_y = [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]
sum_x = sum(x[0] for x in original_x)
print(f"x求和 = {sum_x:.2f}")
sum_y = sum(original_y)
print(f"y求和 = {sum_y:.2f}")
sum_x_squared = sum(x[0]**2 for x in original_x)
print(f"x平方和 = {sum_x_squared:.2f}")
sum_xy = sum(original_x[i][0]*original_y[i] for i in range(len(original_x)))
print(f"xi*yi和 = {sum_xy:.2f}")
a = (len(original_x)*sum_xy- sum_x*sum_y)/(len(original_x)*sum_x_squared-sum_x**2)
print(f"a = {a:.2f}")
b = (sum_y-a*sum_x)/(len(original_x))
print(f"b = {b:.2f}")
# 预测Y值
y_pred = a * 7.5 + b
print(f"7.5 的预测值是 {y_pred:.2f}")
执行结果如下
x求和 = 99.00
y求和 = 82.51
x平方和 = 1001.00
xi*yi和 = 797.60
a = 0.50
b = 3.00
7.5 的预测值是 6.75
python为我们封装了简单的接口
from sklearn.linear_model import LinearRegression
# X和Y 形成一次函数 y=ax+b
original_x = [[10.0], [8.0], [13.0], [9.0], [11.0], [14.0], [6.0], [4.0], [12.0], [7.0], [5.0]]
original_y = [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]
# 创建一个线性模型
model = LinearRegression()
# 输入x和y开始运算 获得模型
model.fit(original_x, original_y)
a = model.coef_[0] # 斜率
b = model.intercept_ # 截距
# 打印结果
print(f"a = {a:.2f}, b = {b:.2f}")
# 新的x值
pred_x = [[7.5]]
# 预测Y值
y_pred = model.predict(pred_x)
print(f"7.5 的预测值是 {y_pred[0]:.2f}")
执行结果如下
a = 0.50, b = 3.00
7.5 的预测值是 6.75