面向 AI 的线性方程:把一个公式变成几何直线
学习一个二元线性方程如何变成一条直线,测试其上的点,处理垂直情形,并读懂线性分类器的得分等值线。
一个小型分类器为两个特征赋予一个得分:
特征对 、 和 看起来不同,但它们得到的得分相同:
这不是三个互不相关的巧合。它们位于同一条几何直线上,而且这条直线上的每个点都得到零分。学完本课后,你将能够把二元线性方程变成一条直线,生成并测试解,并在一个线性分类器的得分等值线中识别出同样的几何结构。
学习成果: 将二元线性方程转换为一条直线,并测试解。
本课直接承接第 9 课:面向 AI 的方程,在那里方程变成了筛选解集的约束。这里允许的值是 中的数对,几何图形让我们能够看见这个集合。第 10 课:逐行阅读 AI 推导提供了配套习惯:每次移项都必须精确保留同一批点。
官方的 Mathematics for Machine Learning 配套网站将线性方程组放在线性代数的开端。其第 2.1 节解释了几何视角:二元一次方程表示一条直线,而一个方程组询问这些直线在哪里相交。本课聚焦于这条基础的单条直线。文中的例子、代码、视觉组件和练习题均为原创,下一课将加入第二个方程。
一个方程描述一个位置
考虑一般的二元方程
其中 和 是实数坐标,、 和 是固定的实数系数。当 和 只以一次幂出现、彼此不相乘,并且只与固定数字相乘时,这个方程就是线性的。
假设 。从两边减去 ,再除以已知的非零数 :
这就是斜截式 。斜率为 :在约束保持不变时, 增加一个单位会使 改变 个单位。纵轴截距为 ,它在 时取得。
整理后的公式不只是让绘图更方便。它显露出一个自由选择。任选实数 ,方程就会计算出恰好一个匹配的 。随着 连续变化,解点描出的是一条直线,而不是有限个点的集合。
一条算例直线揭示斜率、截距和点
来看
这里 、、。由于 非零,解出 是合法的:
这条直线的斜率为 。如果 增加 , 就减少 。这个成对的变化使左侧保持不变,因为
残差把这个真值测试变成一个数字:
直线上的点残差为 。非零残差既表示测试失败,也带有符号:正值意味着加权和高于 ,负值意味着低于 。
| 候选点 | 代入 | 残差 | 判定 |
|---|---|---|---|
| 在线上 | |||
| 在线上 | |||
| 在线上 | |||
| 不在线上 |
两个不同的点足以画出这条直线,但它们不是完整的解集。这个受控视觉组件让缺失的点族变得直观。
| 量 | 当前值 | 计算方式 |
|---|---|---|
| x 坐标 | 2 | 由范围控件选择 |
| y 坐标 | 3 | (12 − 3 × 2) ÷ 2 |
| 残差 | 0 | 3 × 2 + 2 × 3 − 12 |
首次由服务器渲染的状态是 ,图表下方的表格包含同样的计算。这个直线不需要 JavaScript 才能表达它的数学含义;水合过程增加了选择另一个 并观察匹配的 变化的能力。控件使用固定的四分之一单位步长来便于检查,而数学参数可以是任意实数。
一个参数描述整条直线
写出几个点可以暗示规律,但一个参数可以一次描述所有点。令 为任意实数,并设
这个族中的每个数对都是解,因为
反向也很重要。任何满足 的数对也必须满足 。令 ,这个数对就属于参数化族。两个方向合在一起说明:参数化包含直线上的每个点,且不包含直线外的点。
这就是为什么绘图窗口从来不会包含整个数学对象。像素和采样坐标是有限的;直线则是方程所规定的无限解集。
NumPy 生成点;原始方程验证这些点
代码应当映照代数过程。NumPy 对 linspace 的文档说明,它会在指定区间上返回均匀间隔的样本。生成几个 值,计算匹配的 值,然后测试原始约束,而不是盲信整理后的公式:
import numpy as np
import matplotlib.pyplot as plt
# 3x + 2y = 12 -> y = (12 - 3x) / 2
x = np.linspace(-2.0, 6.0, num=5)
y = (12.0 - 3.0 * x) / 2.0
residual = 3.0 * x + 2.0 * y - 12.0
print("x:", x)
print("y:", y)
print("residual:", residual)
print("on line:", np.isclose(residual, 0.0, rtol=0.0, atol=1e-12))
candidates = np.array([[2.0, 3.0], [2.0, 4.0], [4.0, 0.0]])
candidate_residual = 3.0 * candidates[:, 0] + 2.0 * candidates[:, 1] - 12.0
print("candidate residual:", candidate_residual)
print(
"candidate on line:",
np.isclose(candidate_residual, 0.0, rtol=0.0, atol=1e-12),
)
plt.plot(x, y, label="3x + 2y = 12")
plt.plot(candidates[:, 0], candidates[:, 1], "o", label="tested points")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.show()
执行输出为:
x: [-2. 0. 2. 4. 6.]
y: [ 9. 6. 3. 0. -3.]
residual: [0. 0. 0. 0. 0.]
on line: [ True True True True True]
candidate residual: [0. 2. 0.]
candidate on line: [ True False True]
np.isclose用明确的容差比较浮点值。这里 rtol=0.0 和 atol=1e-12 意味着“接受绝对值最多为 的残差”。这是针对该例规模所作的数值工程决策,而不是对方程成立的符号证明。证明来自代数;代码检查实现是否与代数一致。
绘图调用遵循 Matplotlib 的 plot 约定:提供的数组给出直线节点的水平和垂直坐标。更密集的绘图会显得更平滑,但不会创造更多数学解。
分类器等值线是带有特征名称的同一个方程
回到开头的得分:
这里 和 是输入特征, 和 是固定权重, 是截距。得分等值线收集所有得到某个选定得分的特征对。零得分等值线满足
这是一条直线。从 移动到 时, 增加 , 减少 ,得分保持不变:
这个形式并不是为图示临时发明的。官方的 scikit-learn LinearSVC 示例展示了由特征矩阵乘以学习到的系数再加截距得到决策函数。scikit-learn 的 RidgeClassifier 文档还说明,其二分类置信度得分与到分隔超平面的带符号距离成比例,并记录了高于零的得分会预测其第二个类别。
对于这个教学分类器,采用明确的规则
在这个约定下,零等值线就是决策边界:跨过它会改变预测类别。一侧的点得分为正,另一侧的点得分为负。直线本身并不指定平局规则或类别名称;这些属于预测约定。原始得分也不会自动成为概率。
| 特征对 | 得分计算 | 得分 | 按所述规则的结果 |
|---|---|---|---|
| 类别 B,位于边界上 | |||
| 类别 A | |||
| 类别 B |
除以零揭示遗漏的情形
“ 是一条直线”这句话需要一个条件: 和 不能同时为零。解出 则需要更强的局部条件 。
| 系数情形 | 解集 | 几何结果 |
|---|---|---|
| 非垂直直线 | ||
| , | 且任意实数 | 垂直直线 |
| , | 且任意实数 | 水平直线 |
| , | 每个 | 整个平面,不是一条直线 |
| , | 无解 | 空集,不是一条直线 |
例如, 简化为 。每个点 都成立,所以这条直线是垂直的。尝试除以 来生成 并不是一条笨拙的路线,而是未定义的操作。垂直直线没有有限斜率,因为它的 坐标不变,而 可以变化。
还有一个需要避免的实际误解。两个绘出的点可以确定一条非退化直线,但通过两次测试并不能证明任意代码都表示预期方程。交换过系数的代码仍可能把这两个特定点放在预期位置。为几个有意选取的点(包括一个直线外点)计算残差,并保留代数推导作为真值来源。
再加一个约束,直线就会变成交点
一个方程留下一个自由坐标:选择 ,直线就确定 ;对于垂直直线,则选择 ,而 保持不变。这种自由度就是单个方程通常有无穷多个坐标对解的原因。
下一课会对同一个数对施加第二个方程。解届时必须同时位于两条直线上。这里建立的点在线上测试会变成交点测试,几何图形将揭示一个方程组为什么可能有一个解、无解,或有一整条公共直线。
检查你的理解
问题 1
将 4x + 2y = 8 转换为斜截式。写出斜率和两个坐标轴截距,并展示每一步。
显示分步解答
由于 的系数是非零数 ,用可逆步骤隔离 :
将 与 比较可得斜率 ,纵轴截距 ,因此一个截距是 。对于横轴截距,令 :
另一个截距是 。两者都满足原始方程: 和 。
问题 2
检验点 (3, 1)、(1, 3) 和 (0, 4) 是否满足 2x + y = 7。分别计算残差,并将其分类为在线上或不在线上。
显示分步解答
使用残差 :
只有 的残差为零,所以它在线上。另两个点不在线上。它们的负残差表示加权和 和 低于要求的值 。
问题 3
使用一个实参数参数化 x − 3y = 6 的所有解,然后证明集合相等的两个方向。
显示分步解答
令 ,其中 任意。解出 得
所以提出的族为
首先,列出的每个数对都成立,因为
其次,原方程的任意解 都满足 。令 ,就能把该解写成 。第一个方向排除了直线外的点;第二个方向确保没有遗漏任何解。
问题 4
一名学生将 5x + 0y = 10 重写为 y = (10 − 5x) / 0,并得出方程没有图像的结论。诊断错误并给出正确的解集。
显示分步解答
尝试的整理除以 的系数,而这个系数为零。除以零没有定义,因此这一步不能保留解集。
直接从原方程出发:
这个方程不限制 。其解集是
即垂直直线 。图像存在;只是无法使用具有有限斜率的斜截式。
问题 5
分类 0x + 0y = 0 和 0x + 0y = 4 的解集。解释为什么两个方程都不描述一条单独的直线。
显示分步解答
对于每个实数对 ,
因此,第一个方程的解集是整个 ,即整个坐标平面。它没有筛选出一条直线。
第二个方程简化为 ,对任何赋值都为假。其解集是空集 。它根本不包含直线。这些情形说明,直线的定义要求 或 至少一个非零。
问题 6
对于得分 s(x₁, x₂) = −x₁ + 2x₂ − 4,找出零得分等值线,并使用正得分为类别 A、其他得分为类别 B 的规则分类 (0, 3)、(2, 3) 和 (4, 3)。
显示分步解答
令得分为零并解出 :
这条直线是零得分等值线。现在计算三个点:
按照所述约定, 是类别 A,而 和 是类别 B。点 恰好位于边界上。不同的平局规则可能会给零得分点分配不同类别,因此方程本身不能决定该标签。
问题 7
调试这项 NumPy 检查:on_line = (3 * x + 2 * y == 12)。写出一个考虑容差的残差检查,并解释它能证明什么、不能证明什么。
显示分步解答
先计算残差,然后使用根据计算规模选择的容差将它与零比较:
residual = 3.0 * x + 2.0 * y - 12.0
on_line = np.isclose(residual, 0.0, rtol=0.0, atol=1e-12)当 时,它会接受该元素。浮点运算后的舍入可能留下很小的非零残差,因此这种方法避免要求逐位相等。
结果测试的是给定的数值数组和这个容差。它不能证明对每个实参数值都有符号恒等式 ,而且过大的 atol 可能错误地接受直线外的点。推导建立了公式;数值检查有助于发现实现错误。
资料来源
- Mathematics for Machine Learning companion website
- Mathematics for Machine Learning book PDF
- NumPy documentation: numpy.linspace
- NumPy documentation: numpy.isclose
- Matplotlib documentation: matplotlib.pyplot.plot
- scikit-learn documentation: RidgeClassifier
- scikit-learn example: Plot the support vectors in LinearSVC