机器学习1-一元线性回归

125 阅读4分钟

一元线性回归

想象你是一名出租车司机,记录了三位乘客的打车数据:

  • 乘客1:乘车 3公里,支付 8元
  • 乘客2:乘车 5公里,支付 10元
  • 乘客3:乘车 6公里,支付 11元

你发现他们支付的费用和乘车距离之间存在一种“魔法规律”——总费用等于距离的 1 倍再加上5元的起步费用,用公式表示就是y=f(x)=a*x+b(𝑎=1,𝑏=5)。

如果乘客4打车,她需要乘车 4公里,你很快就能算出:他需要支付9元! ——这就是回归分析,通过已知规律预测未知结果。

image.png

这种只涉及一个自变量(距离x)和一个因变量(费用y),且之间存在线性关系就是一元回归

最小二乘法

但现实世界的规律往往不像这个例子这么完美。假如你来到一个新的城市,这里的出租车费用和距离的关系如下图分布.

image.png 你可以看出他们之间有一些规律,可又无法通过一条直线覆盖所有的点.这是你只能尽量画出一条线让这条线离大多数的点都不太远,该用什么规则来定义不太远呢.

  1. 最先想到的方法是用每个点到直线的垂直距离的和:位于直线上方的点距离为,位于直线下方的点距离为,然后求每个点到直线距离的总和,但是这种方法一个致命问题:正负距离会相互抵消!

  2. 那我们优化一下,用每个点到直线的垂直距离的绝对值的和:这解决了第一种方案的问题,却无法区分均匀偏差与极端偏离,如果有3个点到2条直线的垂直距离分别是(2,2,-2)和(0,0,6)他们的绝对值求和都是6,但很明显第二组的偏差更大.

  3. 为了更好的表达这种大误差,我们采用每个点到直线的垂直距离的平方的和,通过非线性放大惩罚大偏差.

  4. 若使用点到直线的垂直距离(形式为(axi+byi+c)2a2+b2\frac{\sum (a x_i + b y_i + c)^2}{a^2 + b^2}),目标函数会因分母的非线性项 a2+b2{a^2 + b^2}变得复杂,计算成本显著增加。实践中更优的方案是用点到直线的竖直距离,它舍弃了几何对称性,但换来了:

闭合解的存在:通过方程直接计算 a 和 b;

统计合理性:假设误差仅存在于因变量(如打车费用),自变量(如距离)精确可控;

计算高效性:复杂度低,适合大规模数据。

这就是最小二乘法,它的的核心思想是最小化预测值与实际值之间的误差平方和。具体步骤如下:

  • 定义误差

对于每个数据点 (xi,yi)(x_i, y_i),预测值为 y^i=axi+b\hat{y}_i = a x_i + b,误差为: ei=yiy^ie_i = y_i - \hat{y}_i

  • 计算平方误差

平方误差为: ei2=(yi(axi+b))2e_i^2 = (y_i - (a x_i + b))^2

  • 总平方误差

对所有数据点求和,得到总平方误差(Cost Function): S(a,b)=i=1n(yi(axi+b))2S(a, b) = \sum_{i=1}^n (y_i - (a x_i + b))^2

  • 最小化总平方误差

通过对 S(a,b)S(a, b) 关于 aabb 的偏导数设置为零,求解出最优的 aabb

求解a和b

对于简单线性回归(只有一个自变量),可以通过解析解直接计算出 aabb 的值。公式如下:

斜率 aa 的计算公式: a=nxiyi(xi)(yi)nxi2(xi)2 a = \frac{n \sum x_i y_i - (\sum x_i)(\sum y_i)} {n \sum x_i^2 - (\sum x_i)^2}

截距 bb 的计算公式: b=yiaxinb = \frac{\sum y_i - a \sum x_i}{n}

其中: - nn 是样本数量, - xy\sum xy 是所有 xxyy 的乘积之和, - x\sum xy\sum y 分别是所有 xxyy 的和, - x2\sum x^2 是所有 xx 的平方和。 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

image.png