TheAlgorithms/Python 中局部加权线性回归(LWLR):从高斯加权代价函数到 NumPy 闭式解实现
局部加权线性回归(Locally Weighted Linear Regression,LWLR,又称 Local Regression)是一种非参数机器学习算法,它不依赖一组全局固定的模型参数,而是针对每个查询点单独求解局部回归系数。本文以仓库中 machine_learning/local_weighted_learning/README.md 的理论讲解为主线,结合 local_weighted_learning.py 的 NumPy 实现,完整覆盖术语定义、加权代价函数推导、数值算例、算法步骤,以及源码级实现细节与运行方式。
与普通线性回归的对比:问题从何而来
普通线性回归是一种监督学习算法,用于计算输入(X)与输出(Y)之间的线性关系。根据 README 的术语约定:
number_of_features(i):特征数量;number_of_training_examples(m):训练样本数量;output_sequence(y):输出序列;- :预测点;
- :代价函数。
普通线性回归的流程分两步:
- 训练阶段:计算 以最小化代价
- 预测阶段:对给定查询点 x,返回 。
这种"先训练一次、再处处复用同一组 "的机制,隐含了一个前提假设:数据点之间存在全局线性关系。一旦 x 与 y 之间的关系是非线性的(README 中给出了非线性数据的示意图场景),固定的全局参数就无法贴合局部弯曲趋势。此时需要非参数算法:不基于固定参数集做预测,而是让参数 针对每个查询点 x 单独计算。
这就是 LWLR 的核心思想:在计算 时,对离查询点 x 更近的样本赋予更高权重,离得更远的样本权重更低。
加权代价函数与高斯权重
将权重 引入代价函数后得到 LWLR 的目标:
其中:
- 是训练点 的非负权重;
- 离查询点 x 近的 , 较大;
- 离查询点 x 远的 , 较小。
一个典型的高斯权重函数为:
其中 是带宽(bandwidth)参数,控制 随与 x 距离增长的衰减速率。可以把它想象成一条以预测点为中心的钟形曲线:样本离中心越远,权重越低; 控制钟形曲线的宽度—— 越大,曲线越宽,远处样本仍保留较高权重,拟合结果越"平滑"; 越小,权重衰减越快,拟合越贴近近邻点、越容易受局部噪声影响。
README 中的数值算例(复算验证)
README 给出的算例:查询点 ,训练点 ,另一点按 README 的代入过程 取值为 3.0(README 正文将其写作 ,与其后代入的 3 不符,属于原文笔误,下文以代入值 3.0 为准),带宽 :
于是代价函数变为:
结论与 README 一致:权重随 x 与 间距离的增大指数级下降, 的预测误差对总代价的贡献也随之指数级下降。上面的两个数值已用 NumPy 实际复算,与 README 给出的 0.9802 / 0.000335 完全吻合。
LWLR 的完整算法步骤
继承 README 的描述,LWLR 的每一步都发生在预测时刻:
- 对每个查询点 x,计算权重 ;
- 计算 以最小化加权代价 ;
- 预测输出:对查询点 x 返回 。
与普通回归"训练一次、预测多次"不同,LWLR 每次预测都要对全部 m 个训练点求解一次加权最小二乘,因此它是典型的时间换拟合灵活性的非参数方法。
源码实现:NumPy 闭式解与函数结构
local_weighted_learning.py 将上述理论落地为四个核心函数(weight_matrix、local_weight、local_weight_regression、load_data/plot_preds),文件头部 docstring 直接给出了加权最小二乘的闭式解:
其中 X 是设计矩阵,y 是响应向量,W 是对角权重矩阵,权重采用与 README 相同的高斯形式 , 为欧几里得范数(2-范数)。源码还特别注明:LOESS(Locally Estimated Scatterplot Smoothing)等其它局部加权回归通常使用不同的权重函数,本实现选用的是高斯权重。
weight_matrix:构造查询点周围的权重矩阵
见 local_weighted_learning.py#L38-L67:
def weight_matrix(point: np.ndarray, x_train: np.ndarray, tau: float) -> np.ndarray:
m = len(x_train) # Number of training samples
weights = np.eye(m) # Initialize weights as identity matrix
for j in range(m):
diff = point - x_train[j]
weights[j, j] = np.exp(diff @ diff.T / (-2.0 * tau**2))
return weights
- 输入
point是预测点,x_train是训练 x 值矩阵,tau为带宽; - 从源码结构看,实现用
np.eye(m)初始化后仅填充对角线元素,这正是 为对角权重矩阵的体现: 只乘在 自身的残差平方上,样本之间不产生交叉权重项; - 注意当 远离预测点时, 会下溢为 0(如 doctest 中距离较远的样本权重直接为
0.0,最近的样本权重也只有1.4e-207量级),这与"权重指数级衰减"的理论预期一致。
local_weight:对单个查询点求解局部系数 β
见 local_weighted_learning.py#L70-L99:
def local_weight(point, x_train, y_train, tau):
weight_mat = weight_matrix(point, x_train, tau)
weight = np.linalg.inv(x_train.T @ weight_mat @ x_train) @ (
x_train.T @ weight_mat @ y_train.T
)
return weight
两行核心运算精确对应闭式解 :x_train.T @ weight_mat @ x_train 即 ,对其求逆后再乘 。函数名沿用了文档中"局部权重(local weights)"的叫法,返回的向量就是针对该查询点的局部 (文档中的 )。
local_weight_regression:对每个点做"即查即算"的预测
见 local_weighted_learning.py#L102-L127:
def local_weight_regression(x_train, y_train, tau):
y_pred = np.zeros(len(x_train)) # Initialize array of predictions
for i, item in enumerate(x_train):
y_pred[i] = np.dot(item, local_weight(item, x_train, y_train, tau)).item()
return y_pred
这个循环正是 LWLR 步骤"对给定查询点 x 返回 "的代码化:对训练集中的每一个点依次当作查询点,重新计算局部 ,再做内积 np.dot(item, β) 得到预测值。doctest 用 3 个 2 维样本验证了这一点:输入 [[16.99, 10.34], [21.01, 23.68], [24.59, 25.69]] 与响应 [[1.01, 1.66, 3.5]]、 时,输出 array([1.07173261, 1.65970737, 3.50160179])。
数据加载与可视化
load_data(local_weighted_learning.py#L130-L148)从 seaborn 内置数据集读取数据,并通过 np.column_stack((one, x_data)) 在特征矩阵最左列拼接一列全 1——即截距项,保证局部模型形如 ,这也解释了为何 doctest 中预测值能平滑地穿过样本附近而非过原点。plot_preds(local_weighted_learning.py#L151-L174)按 x 排序后绘制散点加黄色粗线,展示"局部线性曲线"随数据走势弯曲的非参数特性。
运行方式与依赖
文件底部以 __main__ 块先跑 doctest,再用 seaborn 的 tips 数据集做完整演示(local_weighted_learning.py#L177-L185):
if __name__ == "__main__":
import doctest
doctest.testmod()
# Demo with a dataset from the seaborn module
training_data_x, total_bill, tip = load_data("tips", "total_bill", "tip")
predictions = local_weight_regression(training_data_x, tip, 5)
plot_preds(training_data_x, predictions, total_bill, tip, "total_bill", "tip")
运行前提与注意事项:
- pyproject.toml 声明
requires-python = ">=3.14",且numpy、matplotlib、pandas均在其dependencies中; - 需要额外说明的是,
load_data在函数内部import seaborn as sns,而 seaborn 并未出现在 pyproject.toml 的依赖列表中,因此运行该演示前需自行确保环境装有 seaborn,否则load_data("tips", ...)会抛出 ImportError; - 演示中带宽取 ,这是一个相对较大的带宽:对
total_bill量级为 10~50 的数据而言,远处样本仍保有可观权重,画出的局部回归线相对平滑;若把tau调小(如 doctest 所用的 0.6),权重衰减极快,曲线会更贴近近邻点。调整tau是观察"带宽—平滑度"权衡最直接的实验; - 文件头部的 doctest(
weight_matrix、local_weight、local_weight_regression)随doctest.testmod()在模块导入测试(pyproject.toml 中 pytest 配置了--doctest-modules)时自动执行,可作为对闭式解实现的快速正确性校验; - 该模块在 DIRECTORY.md 的目录索引中登记为 "Local Weighted Learning",入口即 machine_learning/local_weighted_learning/local_weighted_learning.py。
复杂度与适用边界
从源码结构看,local_weight_regression 对每个查询点调用一次 local_weight,后者需做 权重矩阵构造与一次矩阵求逆,因此单个查询点的计算开销随训练集规模近似为 (求逆主导),整体预测为 量级——这正是非参数局部回归"存储所有训练数据、每次查询现场求解"的固有代价。它的适用场景因而是中小规模、关系不明或明显非线性、且不需要持久化模型参数的探索性回归任务;反之,若数据量大、需要快速批量预测或可部署的固定模型,普通线性回归或显式基函数扩展通常更合适。
小结
本文对应的仓库材料可归纳为:理论文档 machine_learning/local_weighted_learning/README.md 给出了术语、加权代价函数、高斯权重公式与手算数值例;实现文件 machine_learning/local_weighted_learning/local_weighted_learning.py 用 NumPy 实现了 闭式解、对角权重矩阵构造、逐点预测循环,以及基于 seaborn tips 数据集的可视化演示。读懂这两个文件,即可完整掌握 LWLR "按距离指数加权、逐查询点求解局部线性模型"的算法全貌,并能通过调节带宽 直观理解非参数回归中平滑度与局部贴合度的权衡。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00