随笔
最小二乘法:用 Python 拟合一条直线
从残差平方和出发推导直线拟合公式,并用可运行的 Python 示例画出观测点、拟合直线和残差。
引用本文
APA
Zhang, C. (2026, November 13). 最小二乘法:用 Python 拟合一条直线. Chester Zhang. https://chesterzhang.site/zh/writing/least-squares
GB/T 7714
Chester Zhang. 最小二乘法:用 Python 拟合一条直线[EB/OL]. (2026-11-13)[2026-11-13]. https://chesterzhang.site/zh/writing/least-squares.
BibTeX
@online{zhang2026least,
author = {Zhang, Chester},
title = {最小二乘法:用 Python 拟合一条直线},
year = {2026},
month = nov,
date = {2026-11-13},
organization = {Chester Zhang},
url = {https://chesterzhang.site/zh/writing/least-squares},
urldate = {2026-11-13},
}给定一组点,怎样找一条最能代表它们的直线?最小二乘法把这个问题变成了一个可计算的目标。我们用五个观测点推导公式,再用 Python 算出直线并画出残差。
把“最贴近”写成一个目标
设第 个观测点为 ,拟合直线为 。在同一个 处,观测值与拟合值之间的竖直差 称为残差。
(1)
最小二乘法选择让 最小的斜率和截距。平方后,正负残差不会抵消。较大的残差也会得到更大的惩罚,因此离群点可能明显拉动拟合直线。
为什么拟合直线经过样本中心
记 和 为样本均值。把误差拆成中心化部分和常数部分,再利用中心化变量的和为零,可以消去交叉项。
(2)
固定 时,第一项不变,第二项在 时达到零。所以拟合直线必经过 。把这个截距代回去,对 求导并令导数为零,得到
(3)
只要 不全相同,分母就为正,剩下的是开口向上的二次函数,所以得到唯一的全局最优解。若所有 都相同,数据只能确定在这个横坐标处的最佳预测值 ,无法单独确定斜率和截距。
用 Python 拟合并画出残差
点击代码块的运行按钮。散点是观测值,实线是拟合直线,虚线段标出各点的竖直残差。代码直接计算上面的公式,并打印残差平方和。
import matplotlib.pyplot as plt
x = [0, 1, 2, 3, 4]
y = [1.0, 2.1, 2.7, 4.2, 5.1]
n = len(x)
x_mean = sum(x) / n
y_mean = sum(y) / n
sxx = sum((xi - x_mean) ** 2 for xi in x)
if sxx == 0:
raise ValueError("At least two distinct x values are required.")
sxy = sum((xi - x_mean) * (yi - y_mean) for xi, yi in zip(x, y))
slope = sxy / sxx
intercept = y_mean - slope * x_mean
fitted = [slope * xi + intercept for xi in x]
residuals = [yi - fit for yi, fit in zip(y, fitted)]
sse = sum(residual ** 2 for residual in residuals)
print(f"Slope: {slope:.3f}")
print(f"Intercept: {intercept:.3f}")
print(f"Sum of squared residuals: {sse:.4f}")
fig, ax = plt.subplots(figsize=(6.4, 3.8), layout="constrained")
ax.plot(x, fitted, color="#1d4ed8", label="Least-squares line")
ax.vlines(x, fitted, y, colors="#b45309", linestyles="--",
linewidth=2, label="Residuals")
ax.scatter(x, y, color="#0f172a", zorder=3, label="Observations")
ax.set(xlabel="x", ylabel="y", title="Least-squares fit")
ax.grid(alpha=0.18)
ax.legend(loc="upper left")这组数据得到 ,残差平方和为 。可以把最后一个观测值从 改成 再运行一次,观察右侧的点如何把斜率拉高。拟合优度只说明直线对这组数据的贴合程度。用它预测观测范围之外的值时,还要判断线性关系是否有理由继续成立。