寻找结构 — 从「任意一条线」到「站得最开的一条线」¶
这一章讲三件事: 感知机找到的解「能用但不好用」,怎么用一个叫「最大间隔」的 挑选标准把它修成「好用」;一个一行都没写错的分类器,为什么在一个干净数据集上 照样训练失败(答案是数据准备,不是算法);以及全书最重要的一次视角切换—— 数据不是一张表,而是某个看不见的机制留下的痕迹,学习是从痕迹里把机制近似出来。 读完你会拿到原书前 3 章的机制顶点,以及机器学习实践的第一课。
1. 从第 1 章的两个问题出发¶
第 1 章末尾,感知机学会了 AND 和 OR,风光得很。但留下了两个问题:
- 它找到的线,是「能分开就行」的线。 AND 的训练数据可以被无数条线分开, 感知机碰到哪条算哪条——对新数据好不好,它一概不问;
- 有些数据根本不存在那条线。 数据不能用一条直线分开时,感知机会在错误之间 永远震荡,这不是调参能救的。
本章的修法顺序值得注意:先修第一个问题,而且不换模型、不换算法框架,只换「要哪条线的标准」; 第二个问题本章只给诊断(XOR),真正的解法(非线性模型)留给第 6、8 章。 中间还会插一段全书最实用的排错实录,最后把「数据是什么」这个问题连根拔起。
原书给本章的题眼是 George Box 的那句名言:「所有模型都是错的,有些是有用的。」33 这句话在本章的落地方式:模型从来不完美,所以问题从来不是「找到完美解」, 而是「在无数不完美的解里,按一个明白的标准挑最好的」。
2. 顶层全景:这条推导链怎么走¶
感知机的两个问题
│
├─ 问题一:解太多、不挑好坏
│ └─ 修法:给「挑线」定一个标准 → 离两边最近的点都最远(最大间隔)
│ └─ 实现:支持向量分类器 = hinge loss + L2 正则 + 梯度下降
│
├─ 问题二:数据分不开(XOR)
│ └─ 本章只诊断;解药(非线性模型)在后面的章节
│
└─ 实践插曲:算法没错、数据也没错,训练照样失败
└─ 根因:特征量纲差太多 → 归一化 + 打乱
└─ 认知升级:数据 = 生成过程的痕迹;学习 = 逼近那个过程
下面逐段走。主线例子是第 6 节那次教科书级的失败与修复, 它把「特征缩放」「数据准备」从口头禅变成你亲眼见过的东西。
3. 局限一:能分开的线太多,该要哪条?¶
一个反直觉的事实:有无穷多条线都「对」¶
把 AND 数据画在平面上,能分开圈和三角的线有多少条? 无穷多条。 任何斜率合适、位置夹在两组点中间的线都行34。
初看这像好事:题很简单嘛。问题只在你把没见过的新数据放进来那一刻显形: 擦着数据边缘过的线,新点稍微越过旧边界就被判错; 站在两组点正中间的线,新数据抖一抖也还在自己这边35。
所以「哪条线」不是无所谓的事。我们要的是对新数据也稳的那条。
一个朴素的想法:站得最开的那条¶
什么样的线最稳?离两组点里离它最近的那些点都最远的那条—— 正好骑在两组点正中间、两边都留出最大空隙的线。 这个标准叫最大间隔(maximal margin),按这个标准挑线的分类器叫 最大间隔分类器36。
关键认识:它没有变强——还是线性分类器,还是一条直线。 变的是一样抽象的东西,原书叫它归纳偏置(inductive bias): 当多个解释同样说得通时,算法偏好哪一个。37
| 算法 | 归纳偏(偏好什么) |
|---|---|
| 感知机 | 随便,哪条能分开都行 |
| 最大间隔分类器 | 非要那条站得最开的 |
归纳偏置是机器学习里被低估的一个概念:模型的「聪明」很多时候不在它能表达什么, 而在它不表达什么——预先砍掉一大堆「也能解释数据、但我们不相信」的解。 第 5 节会看到,砍的方式就是往目标函数里加一项。
为什么不同的线泛化能力不同:一个演示¶
用一组具体的数把这个直觉钉死(这些数是为演示编的,不是书里的真实数值):
假设猫的体重都在 2.0 公斤上下、狗的体重都在 6.0 公斤上下,两组相差约 4。
线 A:骑在正中间(离最近的猫和狗都约 2.0)
线 B:擦着狗群边缘(离最近的狗只有 0.2)
现在来一只 6.4 公斤的「大狗」:
线 A:它离狗的典型值 6.0 有 0.4,仍在线 A 的狗侧 → 判对。
线 B:0.4 的偏移越过了那条擦边的线 → 被判成猫 → 判错。
新数据带着噪声,天然会超出旧数据的边界一点。 边界和数据之间的「缓冲带」越宽,容忍这种抖动的余地就越大——这就是「站得最开」 之所以等于「对新数据最稳」的原因1。
支持向量:整条线只由几个点决定¶
先认识本章最重要的名词。支持向量(support vectors)是训练数据里 离决策边界最近的那批点;最大间隔的线,恰好就是离两类支持向量等距的那条38。
为什么叫「支持」?因为这些点撑着整条边界的位置: 把它们挪走,线就要重画;把别的点随便挪,线纹丝不动。 一条由 400 个点训练出来的线,真正说了算的可能只有三四个点。
这个性质实用价值极大:它意味着训练完成后,大批数据可以扔掉, 模型只需要记住那几个支持向量——不过本书走的是梯度下降实现,没有利用这个性质。
4. 局限二:XOR,以及「模型类的表达力」¶
最小的反例¶
XOR(异或):两个输入恰好一个为真时输出真。四个点画出来: 两个圈、两个三角,棋盘式交错39。
现在请你拿尺子比着画一条直线,让同类的点都在同一侧。 画不出来——这不是你手笨,这样的线不存在39。 感知机在 XOR 数据上的表现是确定性——同样的输入永远得到同样的输出——的: 不管迭代多少次、初值怎么设, 永远收敛不了,测试永远红40。
这个失败和第 6 节那次失败性质完全不同,必须分清:
| 第 6 节的失败 | XOR 的失败 | |
|---|---|---|
| 根因 | 数据准备(量纲) | 模型类表达不了 |
| 修法 | 归一化,立刻好 | 换模型类(非线性),本章无解 |
| 判据 | 算法对了但走不稳 | 对的线压根不存在 |
「模型类的表达力」这个词值得带走:每类模型(线性、神经网络……)有一个它能画出的 边界的集合;数据不在这个集合里,算法再努力也没用。 第 1 章收敛定理的前提 (线性可分)说的就是这件事。
把 XOR 的四个点画出来¶
XOR 的数据(−1/1 编码后)照抄:
| A | B | A XOR B |
|---|---|---|
| 1 | 1 | −1 |
| 1 | −1 | 1 |
| −1 | 1 | 1 |
| −1 | −1 | −1 |
画到平面上:两个 1 点 (1,−1)、(−1,1) 斜对角相望, 两个 −1 点 (1,1)、(−1,−1) 占着另外两个角2。 任何一条直线都会把至少一对同类拆散——直线族里根本不存在解, 这不是「还没找到」,是「不存在」3。
但有一条折线可以(书里的图 2-4 就画了那么一条):两段直线绕开对角的点。 这个图就是「出路一」的全部内容预告:允许模型画折线、画曲线, XOR 立刻变得可解——代价是模型类要升级,第 6 章(kernel)和第 8 章(神经网络)走这条路4。
两条出路(本书各管一条)¶
出路一:学更复杂的边界(曲线、折线)——需要更 express 的模型,第 6、8 章(核方法、神经网络)走这条41。
出路二:数据近似线性、但做不到完美分开时,放弃「全对」的要求, 改求「错得尽量少、间隔尽量大」——允许少量错误,换取整体更稳。 这就是接下来支持向量分类器走的那条,也是本章的主线41。
很多实际问题(比如反欺诈:「这笔账单有 87% 的概率不会被支付」)本来就带着不确定性, 「允许不完美」不是妥协,是终于对上了问题的真实形状42。
5. 支持向量分类器:把「挑线标准」写成目标函数¶
这一节是本章的机制核心,原书从硬间隔一路推到梯度下降的更新代码,我们照单接住。
先做一个省事的改造:把偏置藏进输入¶
第 1 章的感知机有一个单独的偏置 b。从本章起,作者把它藏进向量:
输入末尾补一个恒为 1.0 的数:[x₁, x₂] → [x₁, x₂, 1.0]
权重向量加长一位: [w₁, w₂] → [w₁, w₂, w₃]
加权和 = w₁x₁ + w₂x₂ + w₃·1.0 = w₁x₁ + w₂x₂ + w₃
原来的偏置 b,现在就是 w₃——用一位权重顶掉了单独一个参数。
这叫增广(augment)技巧:代码里从此没有单独的 bias 成员,
但 w₃ 干的就是偏置的活43。好处是更新规则可以对整个向量一视同仁,
不用给偏置单独写一行(后面会看到一个例外:L2 正则要绕开它)。
从感知机到间隔分类器:trait 不换,struct 换新¶
一个容易被略过的设计美感:支持向量分类器没有动 Classifier trait 的一个字母——
还是 train 和 classify 两个签名;换掉的只有 struct(MarginalClassifier)
和它们背后的实现5。
这就是第 1 章「契约先于实现」眼光的回报:换一个算法,动的是内部,
行为契约原封不动。 测试的写法也因此几乎不变——数据、训练、断言四个输入,
只是 new 的参数从迭代遍数变成了 (η, λ, 遍数),断言的预期输出从 OR 换成 AND6。
对比着看两个 struct:
pub struct PerceptronClassifier { weights, bias, iterations } // 第 1 章
pub struct MarginalClassifier { weights, eta, lambda, iterations } // 第 2 章
偏置成员消失了(藏进权重末位),多了两个超参数——数据结构的差异, 就是算法差异的目录。
硬间隔:理想情况的数学样子¶
先把「站得最开」翻译成数学。原书写的是(记号照原书):
两个部件分开读:
右边是约束「全部分对,且间隔够 1」。 回忆 wᵀx = 0 是边界;wᵀx > 0 判正类。 乘上标签 y(1 或 −1)后,「y(wᵀx) 为正」就统一表示「判对了」。 要求 ≥ 1 是说:判对之外,还要离边界至少 1 的间隔44。 (这个 1 不是几何里的 1 厘米,是归一化后的间隔单位——固定它为 1, 才能把「求间隔最大」转交给左边的最小化。)
左边是目标「在这些合格的 w 里,挑 ‖w‖ 最小的」。 ‖w‖ 是权重向量到原点的欧氏距离,勾股定理的推广: ‖w‖² = Σwᵢ²(全部权重平方求和)45。
为什么小 ‖w‖ = 大间隔?原书给了点到边界的距离公式(照抄):
分子已经被约束固定成 ≥ 1,那么分母 ‖w‖ 越小,距离越大—— 最小化 ‖w‖² 就是最大化最小间隔46。
硬间隔约束的逐个读法¶
约束式 y(wᵀx) ≥ 1 值得逐字再读一遍,因为它是软间隔的前身:
wᵀx是加权和;wᵀx = 0是边界,> 0判正;- 乘上标签 y:若 y = 1,要求加权和 ≥ 1;若 y = −1,要求加权和 ≤ −1—— 一个式子统一表达「每个点都要判对,而且不许贴着边界站」7;
- 那个 1:原书特意解释,它不是几何距离,是归一化后的间隔单位—— 把间隔的尺度固定住,才能用「最小化 ‖w‖」来求「最大化间隔」8。
一句话:这个约束就是「支持向量站在距离恰好为 1 的地方」的代数写法, 最优解时,至少各有一个点恰好满足等号——它们就是支持向量。
软间隔:把「必须全对」改成「错了要罚」¶
硬间隔有个实际麻烦:它是个约束优化问题(求最小,但要满足一堆约束), 教科书的标准解法(二次规划)作者明确声明不在本书范围,也不是机器学习里的常用做法47。
替代做法是一次小而关键的改写:把约束挪进目标函数,变成对违反行为的惩罚:
两个部件,各管一件事:
合页损失(hinge loss):对每一个例子算 max(0, 1 − y·wᵀx)。
间隔够 1(判对且站得开)→ 罚 0;间隔不足 1 → 按缺口罚48。
注意它不追求全对:分错的点罚得重,但只要其他点挣得回来,总体照样可以接受。
这就是「软间隔」(soft-margin)——对「硬要求全分对」的松手。
L2 正则项(λ/2 · ‖w‖²):沿用上一步「小权重 = 大间隔」的几何, 只不过现在是罚出来的而不是约束出来的;λ(lambda)控制罚多重49。 它还有个专门效果叫权重衰减(weight decay):每一步都让权重朝零缩一点, 压制那些「对拟合有帮助但站不稳」的大权重解。
这个 λ 和第 1 章的迭代次数一样,是超参数——人定的,从数据里学不到。
hinge loss 的三种处境:拿数走一遍¶
max(0, 1 − y·wᵀx) 这个式子对「一个点」只有三种态度,各配一个数(数为演示编的):
① 间隔充足: y = 1, wᵀx = 1.8 → 1 − 1.8 = −0.8 → max 取 0
「分对了,还站得远,不罚。」
② 分对但太近: y = 1, wᵀx = 0.3 → 1 − 0.3 = 0.7 → 罚 0.7
「分对了,但站在缓冲带里,按比例罚。」
③ 分错了: y = 1, wᵀx = −0.5 → 1 − (−0.5) = 1.5 → 罚 1.5
「站到了对面,罚得比 ② 更重,但仍是按缺口罚,不是一票否决。」
对比感知机的「错了才罚、罚也不定量」,合页损失把「好多少、差多少」连续化了—— 这正是梯度下降能吃它的原因:有连续的变化,才有「往哪降」的方向。
硬约束到软惩罚:一张翻译表¶
把「硬间隔」和「软间隔」并排,改动其实只有一处:「必须」变成了「罚」9。
| 硬间隔(教科书) | 软间隔(本书实现) | |
|---|---|---|
| 对间隔不足的点 | 不允许存在(约束) | 按缺口罚(合页损失) |
| 对分错的点 | 不允许存在(约束) | 罚得更重(缺口 > 1) |
| 数据不可分时 | 问题无解 | 照样能解:拿折中 |
| 求解方法 | 二次规划 | 梯度下降 |
| 机器学习主流 | 理论经典 | 实践常用 |
这张表解释了作者那个看似偷懒的选择:「二次规划不在本书范围内」之外, 他还说了一句话——软间隔「也不是机器学习里的常用做法」指的硬约束路线; 实际上整个深度学习时代,所有训练都是软目标的梯度下降, 硬约束求解反而成了小众。选软间隔,不是妥协,是站在了实践的主路上10。
梯度下降:怎么把「降低损失」变成「朝哪改数」¶
现在有目标函数了,问题是:往哪个方向改 w,损失下降得最快?
答案借自微积分:梯度(gradient)。一个函数对参数的梯度是一个向量, 它的方向是函数值上升最快的方向,长度是上升得有多快50。 所以——朝梯度的反方向走一小步,函数就在局部下降:
这个方法叫梯度下降(gradient descent),原书强调: 它是训练绝大多数现代神经网络的基础优化技术50。
软间隔目标的两个部件各自求梯度(式子照原书):
∇(λ/2·‖w‖²) = λ·w —— 正则项:推着 w 朝 0 缩
∇hinge = { −y·x 若 y(wᵀx) < 1 —— 间隔被违反时:朝「补回间隔」的方向推
{ 0 若 y(wᵀx) ≥ 1 —— 间隔满足时:这一项不管
合页损失的梯度是分段函数,这在代码里就是一个 if:间隔 violated 才套用合页更新51。
落成代码:两个循环,各管一项¶
原书的更新函数照单接住(加了注释):
fn update(&mut self, input: &Vec<f32>, label: f32, score: f32) {
let margin = label * score;
for j in 0..self.weights.len() - 1 { // 第一循环:权重衰减
self.weights[j] -= self.eta * self.lambda * self.weights[j];
} // (正则项,注意绕开了最后一维——那是藏起来的偏置)
if margin < 1.0 { // 第二循环:合页更新
for j in 0..self.weights.len() {
self.weights[j] += self.eta * label * input[j];
}
}
}
和第 1 章感知机的更新并排看,差别一目了然:
| 感知机 | 支持向量分类器 | |
|---|---|---|
| 什么时候更新 | 判错才动 | 间隔不足 1 就动(判对但站得近也算) |
| 更新方向 | 误差 e 的方向 | 梯度的反方向 |
| 步长 | 固定为 e 的大小 | η 控制,每个超参数可调 |
| 额外动作 | 无 | 每步先朝 0 缩一点(权重衰减) |
和感知机一样,新参数需要新超参数:new(eta, lambda, iterations)——
学习率——每一步迈多大——、正则强度、迭代遍数,三个都是人定的52。
权重为什么从 0.7 起步¶
训练方法的第一行是个不起眼的细节:self.weights = vec![0.7; num_features + 1]——
全部权重初始化成 0.7(不是 0)11。
为什么不是零?原书没有解释,但和第 1 章的教法连起来看就通了: 零起步的感知机第 0 遍会一路输出 −1(第 7 节「开局」小节), 从非零起步,第一遍就有正有负,损失曲线从第一天就有起伏可看。 这个选择服务的是「让训练过程可见」的教学目标,不是算法的必需—— 又一次说明:超参数的默认值背后,常常是意图而不是定理。
train 的完整形态:损失曲线是怎么画出来的¶
更新函数之外,train 方法还承担「记录训练过程」的活儿(代码照原书):
fn train(&mut self, x_train: Vec<Vec<f32>>, y_train: Vec<f32>) {
self.weights = vec![0.7; x_train[0].len() + 1]; // 0.7 起步,教材选择
let mut loss_history = Vec::new();
for iteration in 0..self.iterations {
let mut iteration_loss = 0.0;
for (input, label) in zip(&x_train, &y_train) {
let aug_input = self.augment_with_bias(input);
let score = self.forward(&aug_input);
iteration_loss += self.loss(*label, score);
self.update(&aug_input, *label, score);
}
let penalty = Self::l2_penalty(&self.weights, self.lambda);
let total_loss = iteration_loss / x_train.len() as f32 + penalty;
loss_history.push(total_loss); // 每遍存一个点
println!("Iteration {}: loss = {:.4}", iteration, total_loss);
if iteration % 5 == 0 { /* 画边界图 */ }
}
plot_loss_history(&loss_history); // 收尾:画损失曲线
}
每一遍做三件事:累加这一遍每个例子的合页损失(第 21 行)、照常更新、
然后把「平均损失 + L2 惩罚」存进 loss_history12。
训练结束,plot_loss_history 把这串点画成曲线——
第 5 节说的「损失降到约第 25 遍企稳」,读的就是这条曲线13。
注意一个和第 1 章感知机的区别:这里每个例子都更新,不管对错—— 就算间隔满足,正则项的衰减那一半也在走14。 「只在犯错时动」是感知机的特例,「每步都朝目标函数下降的方向动」才是通用形态。
前向与增广的完整走查¶
classify 的完整链路只有三行,值得逐行走一遍(代码照原书):
fn forward(&self, input: &Vec<f32>) -> f32 {
self.weights.iter().zip(input.iter())
.map(|(w, x)| w * x)
.sum()
}
fn classify(&self, input: Vec<f32>) -> f32 {
let x_aug = self.augment_with_bias(&input); // [x₁, x₂] → [x₁, x₂, 1.0]
if self.forward(&x_aug) > 0.0 { 1.0 } else { -1.0 }
}
和感知机的 forward 相比,这段代码换了一种「乘法再相加」的写法:
zip 把权重和输入配成对,map 逐对相乘,sum 收尾15——
功能一模一样,但这是 Rust 迭代器风格的惯用写法,第 4 章会以此为基础搭自己的线性代数库。
拿一个具体输入走一遍(数为演示编的):输入 (身高 30, 体重 2.5),增广后
[30, 2.5, 1.0];假设权重 [0.8, −1.5, −20](藏着的偏置是最后一位):
加权和 = 30×0.8 + 2.5×(−1.5) + 1.0×(−20) = 24 − 3.75 − 20 = 0.25 > 0 → 判 1(狗)。
注意那位 −20:它唯一的功能就是把整条线从原点推开——这就是「藏起来的偏置」在干活。
η 和 λ 各自管什么:两个旋钮的手感¶
超参数不是摆设——它们决定优化怎么走。原书的代码里两个旋钮各有明确分工16:
η,也就是学习率——每一步迈多大,管步幅。 太大:更新越过谷底,损失来回震荡甚至发散; 太小:每一步谨小慎微,200 遍迭代可能还没走到头。 第 6 节排错故事里,作者换了好几组 η 都没救回训练——说明那个失败的锅不在步长上, 这本身就是排错信息。
λ(正则强度)管「站得开」值多少钱。
λ 大:权重的收缩力压过拟合的冲动,边界趋于保守、平滑;
λ 小:合页损失说了算,模型更愿意贴着数据走。
注意 λ 是加在除偏置外的权重上的——代码里 0..self.weights.len() - 1
那个 - 1 就是在绕开增广进来的偏置维17。
判断(我们的,不是书里的): 这两个旋钮的默认值(
0.05/0.01) 在原书里也是「试出来的」而不是推出来的——和别的书里那些试出来的默认值是同类。 实操的手感是:先认默认值是经验值,再用验证集(第 3 章预告的技术)把它校准。 如果错,会错在: 对这两个超参数的敏感性会随数据集而变,别人的默认值搬家后可能失灵。
这一节的机制,一条主线收拢¶
解太多? → 定标准:要站得最开的线
站得最开? → 写成目标:hinge(罚间隔不足) + L2(罚权重大)
怎么最小化? → 梯度下降:沿最陡下降方向挪一小步,η 定步长
数据分不开? → 软间隔本来就不追求全对
第 1 章感知机的「拍脑袋更新」,到这里变成了「对一个明白写出的目标函数做系统优化」—— 学习从技巧变成了优化问题。 这是通向神经网络(第 8 章反向传播)的最后一块跳板。
6. 主线例子:一个没写错的算法,怎么失败的¶
这一节是原书最精彩的一段实录,也是全书最实用的一课。 情节按原书顺序讲:作者先栽了三个跟头,每个都排除了一个错误嫌疑,最后才命中真凶。
场景之前:为什么先跑一个「干净的」数据集¶
第 6 节的实验其实分两步,第一步常被略过:在造 noisy 数据之前, 作者先拿 20 个「故意做成可分」的猫狗例子做了次预演18。
这是工程上叫 sanity check(健全性检查)的做法: 引入新复杂因素(noisy、不可分)之前,先确认系统在简单情形下表现符合预期。 如果干净数据上都失败了(后来真的发生了),问题一定出在更基础的地方—— 算法实现、超参数、数据准备——而不是「噪声太难」这种说不清的理由。
这次预演炸出了全书最实用的排错课,是歪打正着的教学设计: 先怀疑实现、再怀疑超参数、最后才看数据,三个跟头排成了一条教训。
场景:一个「故意做成可分」的猫狗数据集¶
作者拿出 20 个例子:猫、狗各 10 只,特征是两个——身高(厘米)、体重(公斤),
数据集被刻意构造成线性可分53。分类器还是上一节那个支持向量分类器,
参数 MarginalClassifier::new(0.05, 0.01, 50)54。
按第 1–5 节的所有理论,这应该顺利通过。实际:50 遍迭代后,边界是错的55。
三个跟头¶
跟头一:怀疑实现。 作者的第一反应是算法写错了。 他仔细复查了损失计算、更新规则,确认优化逻辑正确—— 「几个小时后,我确信不是支持向量分类器实现的问题。」56
跟头二:怀疑超参数。 学习率太小?正则太强? 换了几组 η 和 λ 的组合——问题照旧57。
跟头三:怀疑顺序。 看数据时发现:例子排了序,前 10 个全是猫,后 10 个全是狗。 凭经验(和理论)他知道训练前要打乱(shuffle)数据,改了——还是不行58。
真凶:特征的量纲。 最后才轮到看特征本身的数值:
| 特征 | 猫 | 狗 | 数值范围(约) |
|---|---|---|---|
| 身高 | 平均 25 厘米 | 平均 48 厘米 | 0 到几十 |
| 体重 | 平均 2 公斤 | 平均 6 公斤 | 0 到十几 |
两个特征的量纲差着好几倍,更新时数值大的特征主导了权重的走向, 优化在误差面上横冲直撞,收敛无从谈起59。
机制特写:为什么大数值特征会主导更新¶
「量纲不同会不稳」是一句结论,把它拆成机制看才记得住(例子里的数为演示编的):
梯度下降的更新,分量 = 学习率 × 梯度分量;而合页损失的梯度分量含 x 本身:
∇ = −y·x (间隔被违反的那一项)
身高特征(几十的量纲): x₁ = 40 → 这一项推动权重改动的幅度 ∝ 40
体重特征(个位的量纲): x₂ = 5 → 这一项推动权重改动的幅度 ∝ 5
更新时,身高的权重一步挪 8 倍于体重的权重 → 优化轨迹被身高「拽着走」,
体重的权重还没走到位,身高方向已经来回震荡。
归一化把两个特征压进同一区间后,x₁ ≈ x₂ 的量级,
两个权重按相近的步幅推进,轨迹平稳19。
这个机制还解释了为什么「部分有效」的偏方会骗人:第 6 节里, 作者只打乱顺序时问题依旧——因为顺序影响的是例子到访的先后, 而主导更新的量级差还在;归一化拿走了量级差,才治本。
复盘的正确姿势:从这一次失败里提炼流程¶
把第 6 节的排错过程抽象出来,就是一套可携带的检查清单—— 下次任何学习算法训练失败,按这个顺序排查(从便宜到昂贵):
作者的亲身经历恰好是反着走的:实现查了几小时,超参数调了几轮,最后才看数据20。 越贵的嫌疑犯排得越靠前,这是直觉;越便宜的嫌疑犯常常是真凶,这是经验。 把「看数据」挪到第一位,是这一节最实际的收获。
还要记住这条失败给出的判据:同样的算法,同一组超参数, 归一化之前不收敛、之后收敛——差异只能来自数据准备。 以后判断「要不要归一化」时,不用再翻书,回想这只猫狗数据集就够了。
打乱为什么有用:顺序也是一种信息¶
第 6 节的排错里,「数据排了序」是第一个被发现的嫌疑犯: 前 10 个全是猫、后 10 个全是狗21。为什么这会坏事? 机制不难想(解释为我们的补充,原书只给了结论):
顺序访问时,更新规则连续 10 次只看到「猫侧」的反馈:
权重被一路推向「全是猫」的方向;
接着 10 次全是狗,又整个推回来——优化在两端之间来回震荡,
像只会走极端的钟摆。
打乱之后,猫狗交替出现,每次更新拿到的反馈方向混杂,
权重走向的是「同时满足两边」的中庸位置。
对感知机这类逐点更新的算法,顺序的影响尤其直接; 一次过一遍全量数据的算法对此不那么敏感,但「打乱」依然是无成本的保险。 第 3 章讲训练/验证划分时还会回到它。
修复:归一化,一个公式¶
拿「狗的体重」走一遍(这两个数是为演示编的,不是书里的真实数值): 设数据集里体重最小 1.5 公斤、最大 12 公斤,一只 6 公斤的狗:
6 公斤在「1.5 到 12」之间偏中间,归一化后落在 0 附近——量纲信息没了, 位置信息全保留。身高同样处理。两个特征从此在同一个数值尺度上竞争权重, 更新稳了——「voilá,算法收敛了」60。
把这一节折成四句话(原书自己的总结)¶
- 对的算法也会失败。 出事时,嫌疑犯常常不在实现里,而在数据或数据准备里61;
- 特征缩放要紧。 量纲不同的特征会让优化不稳、收敛失败61;
- 数据准备是学习的一部分。 打乱、归一化不是做饭前的洗菜,是菜本身61;
- 排错的顺序是门手艺: 先实现、再超参数、最后数据——这恰恰是最贵到最便宜的顺序, 作者替你付过了学费,顺序倒过来试能省几个小时。
7. 数据生成过程:学习到底是什么¶
失败修复完,原书把机器开到最大马力:换一份故意 noisy 的数据, 逼你把「学习」的定义整个换掉。
自己造一个「世界」¶
这次不自选数字,而是写一个造数据的函数,模拟真实世界的样子:
-
猫狗的身高、体重各服从一个正态分布(也叫高斯分布): 一种「大多数个体挤在均值附近、离均值越远越少」的钟形分布62;
-
两个参数定它的形状:均值——平均值,分布的中心——和标准差——散布宽度, 标准差大,数据摊得开62。
书里给的参数(照抄)63:
| 均值·身高 | 标准差·身高 | 均值·体重 | 标准差·体重 | |
|---|---|---|---|---|
| 狗 | 48 cm | 15 | 6 kg | 3 |
| 猫 | 25 cm | 4 | 2 kg | 0.8 |
注意狗的散布比猫大好几倍——稍后看图时这点会很重要。
随机数用固定种子(代码里是 42),每次跑造出同一批 400 个点(两类各 200),
可复现64。采样用 Rust 的 rand / rand_distr 两个 crate——
这是原书「核心自造、外围借用」原则的一次兑现:造数据靠库,学数据靠自己65。
采样是怎么回事:一只猫的诞生过程¶
gen_examples 函数的每一次调用,都在模拟「世界上凭空出现一只猫」(数值为演示编的):
调用:gen_examples(200, 均值身高 25, 身高标准差 4, 均值体重 2, 体重标准差 0.8, 标签 −1, rng)
内部:先造两个正态分布
身高 ~ 钟形,中心 25,散布 ±4 → 大多落在 21–29,偶尔 17 或 33
体重 ~ 钟形,中心 2, 散布 ±0.8 → 大多落在 1.2–2.8,偶尔 0.4 或 3.6
然后重复 200 次:
从身高分布抽一个数(比如 26.3),从体重分布抽一个数(比如 1.9),
打上「猫」的标签,放进数据集。
狗用同样流程,参数换成 48/15 和 6/3。最后两类合并、用同一个随机源打乱,
固定种子(seed_from_u64(42))保证每次跑得到同一批点——
可复现是科学实验的入场券,造数据也一样22。
两个分布参数的差别会写在图上:狗的标准差(15 和 3)比猫(4 和 0.8)大好几倍, 所以狗的云团又矮又宽、猫的又紧又小;两组云一旦交叠,线性可分就不存在了23。
一个会被问到的追问:为什么狗的分布要设得那么散¶
看参数表时读者常会问:猫的标准差是 4 和 0.8,狗却是 15 和 3, 为什么故意把狗造得那么「散」? 原书没有直接回答,但图和叙述里埋着答案24:
如果两个分布都又紧又远(比如猫 25±1、狗 48±1),它们几乎不重叠,
数据集又退化成「可分」的玩具题——第 6 节那 20 个例子的翻版。
把狗的标准差放大 → 云团变宽 → 必然伸进猫的云里 → 不可分。
散布参数就是这场实验的「难度旋钮」:标准差之比,大致决定了重叠带有多宽, 也就决定了任何分类器都要交多少「不可避免的错」。这和第 5 节 hinge loss 平台的高度 是同一个量的两种看法。
训练时假装不知道¶
关键安排:造数据的函数对训练完全保密。
分类器拿到的只是 400 个 (身高, 体重, 类别) 样本,
像真实场景一样,它对背后的分布一无所知66。
这张图(书里的 Figure 2-9)和 AND/OR 真值表有本质区别:
- 真值表:数据即函数,四行说完一切,没有未知;
- 这批样本:数据是痕迹,真正的函数(那个正态分布的机制)你从没见过。
一个容易忽略的安排:训练时只知道样本¶
把第 7 节的实验设计单独拎出来,因为它模拟了真实世界的关键约束:
学习算法永远不可能看到生成机制,它只有痕迹。25 这正是真实场景的复刻:你给模型一万张猫狗照片,它不知道「猫」这个物种的 生物学分布参数——它只有照片。本章的合成分布,就是真实机制的替身演员。
这个安排还顺便定义了什么叫「学得好」: 分类器最后画出的边界,和真实分布(生物学意义上的猫狗分界)的接近程度, 就是学习的成绩单——原书的结论是:边界抓住了主导结构, 「大多数猫在一边,大多数狗在另一边,间隔反映了两个分布真实的重叠程度」26。
训练结果:loss 不再归零,而这正是对的¶
分类器(eta=0.0005, lambda=0.01, 200 遍)跑起来,两件事发生:
- 训练损失不再降到零,而是稳定在一个正的数值上67。 原因直白:有些区域猫狗重叠,任何一条线都必然错掉一些点; 损失曲线的平台,就是「不可避免的错」的高度。
- 边界不完美,但形状对了:绝大多数猫在一边、狗在另一边, 间隔的宽度恰好反映了两个分布真实的重叠程度68。
这就是软间隔存在的意义的最直观演示:目标不是零错误, 是在「间隔尽量大」和「错误尽量少」之间,拿一个最好的折中。
把三件事摆在一起:本章学到的知识结构¶
第 5–7 节看似三个独立话题(间隔分类器、数据准备、生成过程), 其实是一根藤上的三个瓜:
任何一个环节掉了,另两个都立不住:数据若是一张死表,「鲁棒」就无从谈起; 目标若坚持完美,不可分数据上就无解;数据不配好,对的算法也到不了终点。 原书把这三层分别藏在三个小节里,我们自己把它们拧成了一股—— 这就是「数据是痕迹」这个视角真正的承重方式。
认知升级:学习的正式定义¶
把这一节和第 1 章拼起来,原书给出了它对「学习」的完整定义:
我们收集的每个数据集背后,都有一个生成机制(生物的、物理的、经济的、社会的), 它在不断产生带着结构和噪声的观测。我们永远看不到机制本身,只看到它吐出来的样本。 学习,就是从这个机制的痕迹里把它近似出来69。
由此,全书的判据成形:
- 能完美分开的玩具题(AND/OR、可分猫狗)→ 检验「机制被完整恢复了」;
- 分不开的真实数据 → 完美分离本来就不存在于机制里,目标换成 「鲁棒近似」:近似到足够做预测,而不是精确到复刻真相70。
原书这一句值得原样带走:「那条边界最初是生物学画出来的;我们的分类器只是从样本里把它恢复了。」68
判断(我们的,不是书里的): 「数据 = 生成过程的痕迹」这个框架, 比它表面看起来更能打——它顺手解释了机器学习的几大日常困惑: 为什么训练集准确率 100% 的新模型未必好(它可能把痕迹背下来了,机制一点没碰到, 这就是过拟合);为什么「更多数据」经常比「更好的算法」管用(痕迹多了,机制的影子更清楚)。 书里没展开这两层,但骨架确实是这个框架。 如果错,会错在: 不是所有任务都有清晰的「单一生成机制」—— 比如「这张照片美不美」,机制可能是一群审美各异的人;把框架套得太死会忘记这一点。
为什么「换个更好的特征」不能解决一切¶
本章最后替读者问了一个问题:既然身高体重会重叠, 换更本质的特征(比如 DNA 信息)不就能完美分类了吗?为什么不这么干? 作者的答案很实在:很多时候是拿不到(测 DNA 太贵)、维度太高(几十亿像素)、 或者根本不知道哪个变量在起作用71。
所以机器学习的真正功课不是「找到完美特征」,而是:
用可计算的形式,给这个复杂世界建一个够用的近似,并且这个近似扛得住数据里的噪声。
后面 12 章(未放出)要做的就是不断换更强的模型类,去近似更复杂的机制——这是全书路线图在认知层面的总纲。
8. Rust 侧:所有权不是规则,是设计¶
本章最后三分之一篇幅突然换台,去讲 Rust 的所有权(ownership)。 官方文档称它为「Rust 最独特的特性」72。这一段值得逐层吃透, 因为它是「Rust 教机器学习」交叉点上最锋利的一课。
从一个编译错误讲起¶
作者故意写了一段「有毛病」的代码:train 方法里,把输入向量
input 传给 forward,然后接着打印 input。编译,报错:
逐字翻译这个报错:input 的类型是 Vec<f32>,
forward 的签名是 fn forward(&self, input: Vec<f32>)——
参数没有 &,意思是「把这个向量整个给我」。
Rust 管这个动作叫move(所有权转移):从这一刻起,input 归 forward 管,
原来的那个变量作废;你再去打印一个作废的变量,编译器当场拦住73。
之前所有代码没踩这个坑,是因为一直写的 &Vec<f32>(注意那个 &):
那是借用——看一眼,东西还是你的,不转移74。
三条规则,一个内存观¶
Rust 的所有权就三条规则:
- 每个值都有一个属主(owner);
- 同一时刻只能有一个属主;
- 属主离开作用域,值就被销毁(drop)——内存自动收回75。
对比别的语言的管理方式,差异才显出来:
| 语言的做法 | 谁负责回收内存 | 代价 |
|---|---|---|
| 垃圾回收(GC,Java/Python/Go) | 运行时定期扫 | 停顿不可控、内存占用高 |
| 手动管理(C/C++) | 程序员 | 忘了收=泄漏;早收=崩溃 |
| Rust 的所有权 | 编译器按规则检查 | 写代码时被规则约束 |
Vec<f32> 的数据存在堆里(堆:程序运行时动态要的大块内存)。
所有权一转移,「负责销毁这块堆内存」的责任也跟着转移——
这就是 move 的实质:不是复制,是连「善后责任」一起过户75。
三条规则背后:一块内存在谁手里¶
所有权的三条规则管的是「一块内存在任何时刻归谁、寿终谁负责收尸」。 用本章的代码就能画出完整的户口图:
main 函数里: 数据集 x_train 出生在栈上,它的元素存在堆里
│
train(x_train) │ move:所有权从 main 过户给 train
▼
train 内部: x_train 是这一片堆内存的户主;
逐例子 &input 借用给 forward(看一眼就还)
train 返回后:x_train 的户主离场 → 堆内存自动收回
「自动收回」是 Rust 和 C 的分水岭:C 里忘记 free 就是内存泄漏, free 早了就是悬空的指针(谁用谁崩);Rust 里回收跟着属主走, 属主什么时候离场是编译器静态算好的,运行时零成本27。
和 GC 语言的分水岭则在「什么时候收」: GC 是运行时定期巡逻,发现没人引用才收(有停顿、有额外内存开销); Rust 是编译期就写好了回收时刻表,运行时照表执行28。 代价是:写代码时就得想清楚归属——本章那个 E0382 报错,就是思考不全时编译器把你拦下的样子。
点睛:所有权是设计工具¶
这是原书在本章最后的落点,也是最值得带走的一层。
回看数据在训练流程里的路径:
main 函数里造出数据集 → classifier.train(x_train, y_train) → 所有权交给分类器
→ train 内部:forward(&input) 只借用,逐个算分 → 训练结束,数据完成使命
train 的签名故意写成接受所有权(不加 &),内部的计算却只借用。
这个不对称是有含义的设计决策,作者自己点破:
它在类型层面声明:数据集一旦被交给分类器训练,它的使命就完成了。 训练之后需要的是模型,不是原始数据。所有权帮我们把「谁对什么负责、负责多久」 写进了代码的签名里76。
「把所有权当编译器形式就错过了它的大部分价值」——原书这句是本节的题眼77。
fn train(&mut self, x: Vec<Vec<f32>>) 这个签名,本身就是一句设计文档:
「我会拿走这些数据、我会被训练改变、我用完就丢。」
Clone:编译器递过来的另一条路¶
那个 E0382 报错的末尾,编译器其实给了三条出路,其中一条是:
help: consider cloning the value if the performance cost is acceptable
| self.forward(input.clone())
clone 是显式复制:调一次,造一份一模一样的数据交给被调方, 原变量不受影响29。它不是默认行为(Rust 里赋值和传参默认 move), 而是一张「我愿意付复制的代价」的申请单——性能成本写进了代码, 谁读谁知道。
三条出路并排:
| 方案 | 写法 | 代价 |
|---|---|---|
| 借用 | &input |
无;但对方只能用不能留 |
| 克隆 | input.clone() |
一份内存复制 |
| 移交 | input |
原变量作废 |
本书的实现选借用(内部计算)和移交(train 吃数据集),把 clone 留给
「确实需要两份」的场景——这也是性能意识的体现:默认不复制,复制要申请。
一个具体的设计决策:train 接受所有权,forward 只借用¶
把代码里的签名并排看,设计意图一览无余:
fn train(&mut self, x_train: Vec<Vec<f32>>, y_train: Vec<f32>) // 要所有权
fn forward(&self, input: &Vec<f32>) -> f32 // 只借
fn classify(&self, input: Vec<f32>) -> f32 // 要所有权
三种签名,三种宣告:
train要所有权:数据给我,练完它的使命就结束了——你 main 里那份不能再用了 (编译器会保证你不去用);forward借用:内部看一眼就算,东西还是调用者的;classify也要所有权:对新样本,模型接管它,用完即弃30。
这套不对称不是编译器的任性,是意图的显式书写:谁负责什么、负责到哪一刻, 全部写在类型签名上,新人读代码不用猜,编译器帮你守住31。 对比 Python:这些约定只能写在文档字符串里,靠自觉;Rust 里它们不可违反。
判断(我们的,不是书里的): 这一节是全书「语言服务机制理解」的最佳案例, 值得单独带走。所有权的价值不只是内存安全,而是把「责任的时空范围」变成签名的一部分。 当你再看到任何语言的函数签名时,都可以问一句:这个参数进来之后归谁、活多久? 能回答这个问题,函数签名的设计水平就上了一档。 如果错,会错在: 把所有权当银弹是有代价的——某些天然共享的数据结构 (比如图)在所有权模型里要用
Rc/Arc这类引用计数绕路,绕路的成本会回升; 本章教的「训练一次性消费数据」恰是最适合所有权的情形,别当成普遍经验。
9. 作者的判断与证据¶
有实证支撑的:
- 归一化排错的全过程(怀疑实现→怀疑超参数→怀疑顺序→命中量纲)是作者亲录的, 每个环节在原文都有对应叙述56575860;
- 软间隔训练「分开之后还继续优化到约第 25 遍才停」——有损失曲线图为证78;
- noisy 数据上「loss 稳定于正数」与「边界反映重叠程度」——同样来自实跑的图6768。
作者的立场性断言(无法实证,看信不信):
- 「硬间隔的标准解法不在本书范围内,也不是机器学习里的常用做法」——前半句是事实声明, 后半句「不是常用做法」是有争议的判断(学术与工业界视任务而定);
- 「归一化后 voilá,收敛」的叙事把功劳全给了归一化——实际上打乱(跟头三)也是必要条件之一, 只是单归一化那一次恰好过了;读者复现时若只归一化不打乱,可能仍失败。
我们替读者标出的空白:
- 为什么 L2 正则的代码要绕开增广的偏置维?书里没有解释。 补充(不在书里,来自通用知识):偏置是常数项,不衰减它才方便——否则边界会被无谓地拉向原点。
- 有个行话叫贝叶斯误差——「贝叶斯」是人名,十八世纪的概率学家—— 它说的是数据重叠导致的、任何模型都消不掉的错误率。本章的实践已经摸到了它 (loss 平台的高度),但原书只是预告「下一章详谈」79,本章没有给它名字和形式化。
- 打乱与归一化的相对贡献没有拆开:原书的叙事是「先打乱,仍失败;再归一化,成功」, 读者容易得出「归一化才是唯一解」的印象;严格说,两个都是必要条件, 只是那个数据集上归一化碰巧是最后补上的那块拼图32。
判断(我们的,不是书里的): 这本书对 Rust 的使用是「克制的工具主义」—— 语言特性只在服务机制理解时登场,没有为 Rust 而 Rust 的炫技。 已放出章节里最好的例子就是所有权:它不是作为「内存管理技术」教的, 而是作为「把责任写进签名」的设计工具教的,这恰好是它在机器学习代码里最真实的角色。 如果错,会错在: 如果后续章节(尤其第 4 章的类型系统、第 12 章的性能优化) 转向「Rust 本位」的写法——为展示语言特性而增加机制复杂度—— 我们对「克制工具主义」的判断就要撤回一半。
10. 边界与局限¶
- XOR 没有在本章解决。 诊断完了,药方(非线性模型、核方法、神经网络)在尚未放出的第 6、8 章。 读到本章为止的读者要清楚:线性方法的天花板你已经摸到了,翻越它的工具还没发货。
- 实现的 SVM 不能少存数据。 教科书 SVM 可以只存几个支持向量, 这种「大部分位置留空、只存关键几项」的存法叫稀疏;本书的梯度下降版本是 每个权重都挨个更新的普通存法,支持向量在这里只是几何解释,不是计算优化。
- 归一化的演示只覆盖了 min-max 一种。 更常用的 z-score(减均值除标准差)本书没提; 第 3 章预告会展开数据准备79。
- 「数据生成过程」的论证靠一份程序造的假数据支撑。 真实世界的机制远比两个正态分布复杂; 用程序造的假数据讲认知框架是干净的,但别把它当成真实数据行为的完整画像。
- 所有权一节是引子。 作者明说「不会在这里穷尽所有权,随书推进再补」80;
生命周期(
&的详细规矩)被推迟到第 3 章。 - Early release 的通病本书处处有:简介的路线图里还留着「(cf. 13)」这类编辑痕迹, 个别图注、段落还是初稿状态81。
11. 可带走的(合上这章,你应该带走了)¶
- 感知机的缺陷分两种:解太多(要换归纳偏置)和表达力不够(要换模型类)—— 修法完全不同,先诊断再动手。
- 归纳偏置 = 多个解释同样通时的挑选标准;「最大间隔」就是最著名的一种。
- 支持向量:离边界最近的点;它们撑着整条边界,挪动它们线才重画。
- 支持向量分类器 =
hinge loss(罚间隔不足)+λ/2·‖w‖²(罚权重大,即 L2 正则), 梯度下降更新:w ← w − η·∇L;感知机的拍脑袋更新由此变成系统优化。 - 软间隔的意义:数据分不开时,目标自动从「全对」降级为「折中最优」。
- 算法没错也会失败:排错顺序建议反过来——先看数据准备(打乱、归一化),再看超参数,最后才查实现。
- 归一化公式
x' = 2(x−x_min)/(x_max−x_min) − 1:把每个特征压进 [−1,1],量纲消失、相对关系保留。 - 「数据 = 生成过程的痕迹;学习 = 逼近那个机制」——全书认知框架,顺便解释了过拟合和「数据比算法值钱」。
- 噪声导致的 loss 平台不是失败,是数据重叠的真相在损失曲线上的高度。
- Rust 的所有权:一个属主、move 过户、出作用域销毁;把
train写成接受所有权, 是把「数据训练完即完成使命」写进类型签名——所有权是设计工具。 - 移植性是这个暗线的收益:trait、测试、迭代器、所有权,没有一样是你看完就还给这本书的—— 它们在任何 Rust 项目里都站在同样的位置。学机器学习顺路拿走的语言资产, 比表面上的「双料」还多一层。
12. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| George Box 名言 | Chapter 2. The Search for Structure | text/06-ch02-chapter-2-the-search-for-structure.txt:12(搜「All models are wrong」) |
| 数据是过程留下的痕迹(章题眼) | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:21(搜「visible trace of an underlying process」) |
| 解不唯一,无穷多条线 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:39(搜「infinitely many separating lines」) |
| 新数据面前不同的线表现不同 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:43(搜「behave very differently」) |
| 最大间隔分类器的标准 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:49(搜「maximal-margin classifier」) |
| 归纳偏置的定义 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:51(搜「inductive bias」) |
| 支持向量的定义 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:59(搜「support vectors」) |
| XOR 数据与不可分 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:65(搜「XOR」);:92(搜「never converges to a solution」) |
| 两条出路(非线性 / 容忍错误) | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:96(搜「more complex functions」);:98(搜「87% probability」) |
| hinge loss 的三种处境 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:219(搜「if your point is correctly classified」) |
| 间隔分类器的测试 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:137(搜「it_classifies_and_inputs_with_marginal」) |
| 增广技巧藏偏置 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:113(搜「hide the bias」) |
| 硬间隔目标 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:191(搜「min」)——原式为「min𝐰12‖𝐰‖2」,约束 y(wᵀx)≥1 |
| ‖w‖² 即勾股推广 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:197(搜「Pythagoras」) |
| 点到边界距离公式 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:205(搜「distance between a point and the decision boundary」) |
| 软间隔目标与 hinge loss | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:217(搜「soft-margin maximization」);:219(搜「hinge loss」) |
| 二次规划不在范围内 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:213(搜「quadratic programming」) |
| 梯度的两个性质 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:240(搜「steepest increase」) |
| 梯度下降的地位 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:242(搜「basis of gradient descent」) |
| 两个梯度式 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:244(搜「λ*w」);:248(搜「−y𝐱」) |
| 更新规则 w ← w − η∇L | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:252(搜「η∇L」) |
| 权重衰减与合页更新两个循环 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:275(搜「weight decay」) |
| 分开之后仍继续优化(约 25 遍) | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:321(搜「around iteration 25」) |
| 二十例猫狗数据集(故意可分) | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:353(搜「twenty examples」) |
| 失败的边界 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:357(搜「something is wrong」) |
| 排错三跟头(实现/超参/顺序) | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:361(搜「After a few hours」);:363(搜「hyperparameters eta and lambda」);:365(搜「ordered, first all cats then all dogs」) |
| 归一化公式 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:367(搜「minimum and maximum values」) |
| 生成数据的参数 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:449(搜「average height: 48 cm」) |
| 打乱数据集 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:486(搜「data.shuffle」) |
| 训练配置(生成数据) | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:519(搜「MarginalClassifier::new(0.0005」) |
| 四条经验教训 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:377(搜「Correct algorithms can still fail」) |
| 正态分布与均值/标准差 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:433(搜「Gaussian distribution」);:435(搜「standard deviation」) |
| 猫狗分布参数 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:449(搜「average height: 48 cm」) |
| rand / rand_distr 两个 crate | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:459(搜「two Rust crates」) |
| 训练时忽略生成过程 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:397(搜「deliberately ignore」) |
| noisy 数据 loss 不降零 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:523(搜「stabilizes around a positive value」) |
| 边界反映分布重叠 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:527(搜「inherent overlap」) |
| 「边界由生物学画出」 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:535(搜「drawn first by biology」) |
| 鲁棒近似作为目标 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:537(搜「robust approximation」) |
| 为什么不能总换更好的特征 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:653(搜「billions of pixels」) |
| E0382 移动后借用报错 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:569(搜「borrow of moved value」) |
| 所有权三条规则 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:621(搜「There can only be one owner」) |
| 所有权是设计工具 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:629(搜「design tool」);:643(搜「communicate intent」) |
| 权重从 0.7 起步 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:285(搜「vec![0.7」) |
| sanity check 的动机 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:333(搜「sanity check」) |
| 所有权一节是引子 | 同上 | text/06-ch02-chapter-2-the-search-for-structure.txt:629(搜「will be adding to it」) |
-
出处:「Chapter 2. The Search for Structure」第 43 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:43,搜「goes a little beyond that boundary」)与第 45 段(搜「generalizes better」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 73–78 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:74,搜「1,1,-1」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 92 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:92,搜「not expressive enough」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 94 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:94,搜「non-linear decision boundary」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 106 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:106,搜「pub struct MarginalClassifier」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 137 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:137,搜「it_classifies_and_inputs_with_marginal」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 193 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:193,搜「multiplying by y」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 195 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:195,搜「normalized margin」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 213 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:213,搜「replace the constraints」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 213 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:213,搜「not the common approach in machine learning」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 285 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:285,搜「vec![0.7」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 283–304 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:287,搜「let mut loss_history」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 315 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:315,搜「plot_loss_history」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 275 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:275,搜「applies weight decay」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 170 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:170,搜「zip(input.iter())」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 119 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:119,搜「η (eta) controls」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 275 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:275,搜「excluding the bias」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 333 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:333,搜「sanity check」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 367 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:367,搜「larger magnitudes dominate the gradient」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 361 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:361,搜「My initial instinct」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 365 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:365,搜「ordered, first all cats then all dogs」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 478 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:478,搜「seed_from_u64」)与第 410 段(搜「gen_examples」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 499 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:499,搜「much sparser」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 499 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:499,搜「much sparser for both heights and weights」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 391 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:391,搜「only its realizations」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 527 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:527,搜「dominant structure」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 625 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:625,搜「responsibility for freeing」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 615 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:615,搜「garbage collection」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 592 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:592,搜「consider cloning the value」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 633 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:633,搜「classifier.train」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 643 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:643,搜「design decision」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 365 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:365,搜「shuffling the data before training」)与第 367 段(搜「voilá」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 12 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:12,搜「All models are wrong」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 39 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:39,搜「infinitely many separating lines」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 43 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:43,搜「behave very differently」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 49 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:49,搜「maximal-margin classifier」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 51 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:51,搜「inductive bias」)与第 53 段(搜「resolves the ambiguity」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 59 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:59,搜「support vectors」)与第 61 段(搜「equidistant from the support vectors」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 65 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:65,搜「exclusive or」)与第 69 段(搜「simply not possible」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 92 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:92,搜「never converges to a solution」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 96 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:96,搜「more complex functions of the data」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 98 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:98,搜「87% probability」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 113 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:113,搜「hide the bias without losing its function」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 193 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:193,搜「margin should be at least one」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 197 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:197,搜「Pythagoras」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 205 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:205,搜「distance between a point and the decision boundary」)与第 209 段(搜「making the ‖𝐰‖2 as small as possible」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 213 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:213,搜「quadratic programming」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 219 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:219,搜「hinge loss」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 221 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:221,搜「norm of the weights」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 240 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:240,搜「steepest increase」)与第 242 段(搜「basis of gradient descent」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 254 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:254,搜「piecewise definition」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 119 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:119,搜「η (eta) controls」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 353 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:353,搜「twenty examples」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 349 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:349,搜「MarginalClassifier::new(0.05, 0.01, 50)」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 357 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:357,搜「something is wrong」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 361 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:361,搜「After a few hours」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 363 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:363,搜「hyperparameters eta and lambda」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 365 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:365,搜「ordered, first all cats then all dogs」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 367 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:367,搜「feature scaling」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 367 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:367,搜「voilá」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 377 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:377,搜「Correct algorithms can still fail」)。 ↩↩↩ -
出处:「Chapter 2. The Search for Structure」第 433 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:433,搜「Gaussian distribution」)与第 435 段(搜「standard deviation」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 449 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:449,搜「average height: 48 cm」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 493 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:493,搜「fn generate_cats_and_dogs_data」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 459 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:459,搜「rand and rand_distr」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 397 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:397,搜「deliberately ignore the generating process」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 523 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:523,搜「stabilizes around a positive value」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 527 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:527,搜「inherent overlap」)与第 535 段(搜「drawn first by biology」)。 ↩↩↩ -
出处:「Chapter 2. The Search for Structure」第 391 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:391,搜「approximating the unknown process」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 537 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:537,搜「robust approximation」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 653 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:653,搜「billions of pixels」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 545 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:545,搜「Rust’s most unique feature」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 597 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:597,搜「value borrowed here after move」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 611 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:611,搜「only a borrow occurs」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 617 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:617,搜「compile time」)与第 619–623 段(三条规则)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 643 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:643,搜「communicate intent」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 629 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:629,搜「design tool」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 321 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:321,搜「around iteration 25」)。 ↩ -
出处:「Chapter 2. The Search for Structure」第 100 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:100,搜「treated in detail in the next chapter」)。 ↩↩ -
出处:「Chapter 2. The Search for Structure」第 629 段(
text/06-ch02-chapter-2-the-search-for-structure.txt:629,搜「will be adding to it as the book progresses」)。 ↩ -
出处:「Introduction: Programs that Learn」第 107 段(
text/04-fm-introduction-programs-that-learn.txt:107,搜「cf. 13」)——简介路线图处的编辑痕迹。 ↩