Lecture 4 · 非线性模型:决策树、集成与神经网络 · Trees, Ensembles & Neural Networks

Machine Learning · Lecture 04。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:Stanford CS229 Autumn 2018(Andrew Ng)Lectures 10–12(Decision Trees and Ensemble Methods; Introduction to Neural Networks; Backprop & Improving Neural Networks),CS229 main notes(Andrew Ng & Tengyu Ma)的深度学习一章(Ch. 7),以及 CS229 的 decision trees / ensemble methods 补充讲义。 CS229 当前学期(Summer 2026)的课表需要 Stanford 登录才能看,所以本讲以公开的讲义和 2018 年的录像为准。 配套代码 Code:code/lecture04/(numpy;3 个工具模块 + 5 个脚本,最长的约 23 秒,其余都在 12 秒以内) 预计阅读时间:2.5–3 小时(含运行代码)。这是一份复习讲义:假设你见过这些模型,重点放在”为什么这样设计”和”它们之间的联系”上。


0. 本讲导航 · How to read this lecture

0.1 Learning goals

After this lecture you should be able to:

  1. 解释决策树为什么用 Gini / entropy 而不用误分类率选切点(严格凹),以及树的三个性格:可解释、高方差、轴对齐。
  2. Derive the variance of an average of correlated predictors, ρσ2+1−ρBσ2\rho\sigma^2 + \frac{1-\rho}{B}\sigma^2, and use it to explain what bagging and random forests can and cannot fix.
  3. 把 AdaBoost 说成”指数损失上的前向分步加法建模”,把 gradient boosting 说成”函数空间里的梯度下降”,并说出 shrinkage、树深、损失函数各控制什么。
  4. Explain a neural network as learned features + a GLM head, write the backprop recursion from memory, and explain why computing all PP partial derivatives costs only a small constant times one forward pass.
  5. Say what initialization (symmetry, scale), optimizers and normalization each fix — and what the universal approximation theorem does not tell us.

0.2 默认你已经会的 / 本讲会省略的

默认你已经会 Assumed本讲省略或只给结论 Skipped
Lecture 1: GLMs, log loss / cross-entropy, GD and conditioning, “loss = −log-likelihood”CART 的代价复杂度剪枝(cost-complexity pruning)的完整算法
Lecture 3: the bias–variance decomposition, cross-validation(本讲只用到 bagging 需要的部分)Full proofs of the AdaBoost training-error bound and the margin bounds (key step only)
链式法则、矩阵乘法Matrix-calculus details of backprop(给出递推式和形状,不逐项推导)
You have seen MLPs, ReLU and Adam万能逼近定理的证明;BatchNorm / Adam 的理论;XGBoost 的工程细节

0.3 本讲地图 Map

§1  The big picture:三条通往非线性的路(手工特征 / 切分空间 / 学习特征)
§2  Decision trees:贪心递归划分 / 为什么不用误分类率 / 深度与过拟合 / 轴对齐 / 回归树    ← 代码 01
§3  Bagging & random forests:ρσ² + (1−ρ)σ²/B / 特征子采样 / OOB                      ← 代码 02
§4  Boosting:AdaBoost = 指数损失 + 前向分步 / 上界与 margin / gradient boosting       ← 代码 03
§5  Neural networks:GLM → MLP / XOR / 万能逼近与学出来的特征 / backprop               ← 代码 04、05
§6  Training neural networks:初始化 / 优化器 / 归一化(简述)                         ← 代码 04、05
§7–11 小结、常见误区、练习、延伸阅读、下一讲预告

0.4 Notation(本讲新增的符号)

符号Meaning
RR, NRN_R, pRp_Ra tree node (an axis-aligned box), the number of training points in it, its class proportions
I(p)I(p)impurity: Gini 1−∑kpk21 - \sum_k p_k^2, entropy −∑kpklog⁡pk-\sum_k p_k\log p_k, misclassification 1−max⁡kpk1 - \max_k p_k
BB, σ2\sigma^2, ρ\rhoensemble size, variance of one member’s prediction, correlation between two members
Fm(x)F_m(x), αt\alpha_t, εt\varepsilon_t, ν\nuboosting’s additive model after mm steps, weight and weighted error of weak learner tt, learning rate (shrinkage)
Wℓ,bℓ,Zℓ,AℓW_\ell, b_\ell, Z_\ell, A_\elllayer-ℓ\ell weights, bias, pre-activation Zℓ=Aℓ−1Wℓ+bℓZ_\ell = A_{\ell-1}W_\ell + b_\ell, activation Aℓ=g(Zℓ)A_\ell = g(Z_\ell), with A0=XA_0 = X
δℓ\delta_\ell∂J/∂Zℓ\partial J/\partial Z_\ell, the “error” that backprop sends backwards

代码的约定:神经网络部分和 numpy / PyTorch 一样把样本放在行上(Z=AW+bZ = AW + b,WW 的形状是 fan_in × fan_out);CS229 讲义用列向量 z=Wx+bz = Wx + b。两者差一个转置。

0.5 关于数字

讲义里所有实验数字都来自配套代码的实际输出(固定随机种子,同一台机器上重复运行逐字相同,python3 -W error 无警告)。数据都是合成的。有几处结果和”预想的故事”不一样(比如 random forest 并没有赢 bagging),讲义按实际结果写,代码 README 的”关于实验结果”逐条记录了。


1. The big picture · 三条通往非线性的路

第 1 讲的所有模型都是 g(θ⊤x)g(\theta^\top x)。真实世界的关系是非线性的,有三种基本办法:

RouteWhere the nonlinearity comes fromOptimizationExamples
Hand-crafted featuresa fixed ϕ(x)\phi(x) you choose (polynomials, kernels)convex (still a linear model)Lecture 1 §2.5, Lecture 2 (kernels, SVM)
Partition the input spacecut the space into boxes, one constant per boxgreedy (combinatorial)decision trees, random forests, gradient boosting
Learn the featuresϕW(x)\phi_W(x) has parameters, trained jointly with the headnon-convex, SGD + backpropneural networks

用第 1 讲的”三件套”看本讲:

  • Trees: hypothesis = piecewise-constant functions on axis-aligned boxes; loss = impurity (classification) or SSE (regression); optimizer = greedy top-down splitting.
  • Ensembles change the hypothesis class by combining trees: bagging averages many high-variance trees (attacks variance); boosting adds many weak trees sequentially (attacks bias). 这正是第 3 讲 bias–variance 分解的两半。
  • Neural networks: hypothesis = compositions gL(…g1(xW1+b1)… )g_L(\dots g_1(xW_1 + b_1)\dots); loss = the GLM NLL of the output head (Lecture 1 §4.7); optimizer = the SGD family, with gradients from backprop.

A fact worth keeping in mind: on tabular data (each column a different, meaningful feature) gradient boosting is still usually the strongest baseline; on raw structured signals (images, text, audio) networks that learn features win by a wide margin. This lecture explains the inductive bias behind each.


2. Decision trees · 决策树

2.1 The model: greedy recursive partitioning

A decision tree partitions the input space into axis-aligned boxes R1,…,RMR_1, \dots, R_M and predicts a constant in each:

h(x)=∑m=1Mcm 1{x∈Rm},cm={majority class (or class proportions) in Rmclassificationmean of y in Rmregressionh(x) = \sum_{m=1}^{M} c_m\, 1\{x \in R_m\}, \qquad c_m = \begin{cases}\text{majority class (or class proportions) in } R_m & \text{classification}\\ \text{mean of } y \text{ in } R_m & \text{regression}\end{cases}

Finding the globally optimal partition is NP-hard, so CART (Breiman et al. 1984) is greedy and recursive: at the root, try every feature jj and every threshold tt, split the region into {xj≤t}\{x_j \le t\} and {xj>t}\{x_j > t\}, and keep the split with the smallest weighted loss of the two children; recurse on each child until a stopping rule fires (maximum depth, minimum leaf size, the node is pure, or no split reduces the loss).

实现上的一个小技巧(代码 DecisionTree._best_split):把节点里的样本按某个特征排序后,“左边放前 ii 个样本”时左孩子的类别计数就是前缀和的第 ii 项,右孩子 = 总数 − 左边。所以一个特征上所有候选切点的代价,一次 cumsum 就全算出来了。

2.2 Why Gini / entropy rather than misclassification?

我们最终关心的是误分类率,为什么不直接用它选切点?Ng 在课上用的例子(下面的数字仿照 CS229 决策树补充讲义):父节点有 900 个正类、100 个负类,两个候选切分。代码 01_decision_trees.py 演示 1(b)(表中是 N⋅IN\cdot I,gain = 父节点 − 两个孩子之和):

criterion父节点 [900, 100]A:[700, 100] + [200, 0]gain AB:[400, 100] + [500, 0]gain B
misclassification100100 + 00100 + 00
Gini180175 + 05160 + 020
entropy(bits)469.0434.9 + 034.1361.0 + 0108.0

两刀都把一大块纯的正类分了出来,B 显然更有用;但所有节点的多数类都是正类,“预测多数类”犯的错永远是那 100 个负类,误分类率看不到任何进步。

The reason is concavity. The children’s class proportions average (weighted by size) to the parent’s: pparent=NLNpL+NRNpRp_{\text{parent}} = \frac{N_L}{N}p_L + \frac{N_R}{N}p_R (here 0.8×0.875+0.2×1.0=0.90.8 \times 0.875 + 0.2 \times 1.0 = 0.9). For any concave II, Jensen gives

N I(pparent)  ≥  NL I(pL)+NR I(pR),N\,I(p_{\text{parent}}) \;\ge\; N_L\,I(p_L) + N_R\,I(p_R),

so the gain is never negative. If II is strictly concave (Gini, entropy), equality holds only when pL=pRp_L = p_R: any split that changes the class proportions gets a positive gain. Misclassification 1−max⁡kpk1 - \max_k p_k is concave but piecewise linear — on p∈[0.5,1]p \in [0.5, 1] it is the straight line 1−p1 - p (demo 1(a): 0.5, 0.3, 0.1 at pp = 0.5, 0.7, 0.9, while Gini gives 0.5, 0.42, 0.18 and entropy 1, 0.881, 0.469) — so whenever both children land on the same linear piece (same majority class), the gain is exactly zero.

这不只是”灵敏度”的问题。贪心算法只看一步:如果眼前这一刀的 gain 是 0,它就不会切,也就永远看不到后面的好处。演示 1(c) 的”口袋”数据:正类集中在中心的小正方形 [0.35,0.65]2[0.35, 0.65]^2 里,任何一刀切出的半边正类都不占多数(训练 1000、测试 5000 个点,min_samples_leaf = 5):

criterion叶子数深度test acc正类 recall
misclassification100.902(= 全部预测负类)0.000
Gini42130.9620.689
entropy44130.9600.667

用误分类率建树,根节点上每一刀的 gain 都是 0,树根本没长出来。Gini / entropy 看到”正类在某一半里更集中了”,一路切下去把口袋挖了出来。实践中 Gini 和 entropy 几乎总是选出同样的树;Gini 不用算 log,是 CART / sklearn 的默认。误分类率仍然适合用来剪枝和评估 —— 那时我们关心的正是最终的错误率。

和第 1 讲 §3.4 的对照:分类时我们不直接优化 0-1 损失,而是优化一个光滑的凸 surrogate(logistic、hinge);建树时也不直接用 0-1 损失,而是用一个严格凹的 surrogate。原因相通:0-1 损失”太平”,给不出前进的方向。

2.3 Depth is the complexity knob; trees are high-variance

Demo 2: the boundary is x2=0.5+0.3sin⁡(2πx1)x_2 = 0.5 + 0.3\sin(2\pi x_1) with 10% of the labels flipped (so the Bayes accuracy is 0.902); 300 training points.

max_depth12346810none
leaves2481533455562
train acc0.8030.8030.8470.8930.9300.9630.9831.000
test acc0.7430.7430.8380.8250.8170.8180.7830.784

另一个旋钮 min_samples_leaf 给出同样的形状(1 → 20 时测试准确率 0.784 → 0.841,40 时回落到 0.793)。几点观察:

  • 不限深度的树把每个叶子都切到纯为止,训练准确率 100% —— 它把 10% 的翻转标签也”背”了下来。这是 low bias / high variance 的典型。
  • 曲线不平滑(深度 1 和 2 完全一样,4~8 几乎持平):贪心建树是一个离散、不稳定的过程,训练集稍有变化,顶部的切点就可能完全不同,下面的整棵子树都跟着变。这种不稳定正是 §3 bagging 的原料。
  • 深度 3 的树只有 8 条规则,可以完整打印出来逐条检查(演示 2 末尾),这就是树”可解释”的含义。但准确率更高的树已经有几十条规则了 —— 可解释性和准确率在这里是冲突的。

2.4 The axis-aligned bias

演示 3,对角线边界 y=1{x2>x1}y = 1\{x_2 > x_1\}(无噪声),树不限深度,逻辑回归加 λ=10−4\lambda = 10^{-4} 的 L2(数据线性可分,第 1 讲 §3.5):

ntrainn_{\text{train}}3010030010003000
tree: leaves79202647
tree: test acc0.8730.8790.9550.9710.982
logistic: test acc0.9910.9860.9980.9940.996
tree after a 45° rotation: leaves / test acc2 / 0.9972 / 0.9942 / 0.9992 / 1.0002 / 1.000

Every split is perpendicular to a coordinate axis, so a diagonal boundary must be approximated by a staircase; finer stairs need more leaves, and each leaf needs data. Rotate the inputs by 45° and the same tree needs a single split. The difficulty is not that the boundary is complex — it is that it is not aligned with the features. Trees are not rotation-invariant.

但这个偏置也是优点,硬币的两面:

  • 树对每个特征的单调变换完全不变(取 log、换单位都不改变切分结果),所以不需要 feature scaling,混合了不同单位、不同分布的表格特征也能直接用。
  • 很多表格问题的真实结构就是”阈值规则”(年龄 > 65 且血压 > 140 ……),轴对齐正好匹配。
  • 反过来,图像的单个像素、音频的单个采样点几乎没有意义,有意义的是坐标的组合 —— 这类数据交给能学习特征组合的神经网络(§5)。Grinsztajn et al.(2022)系统比较过:在中等规模的表格数据上,树模型仍然普遍优于深度网络,原因之一就是表格数据里有很多不规则的、和坐标轴对齐的结构,以及很多无用的特征。

2.5 Regression trees

Same algorithm, different loss: each leaf predicts the mean of its yy‘s, and a split is scored by the total squared error of the two children, ∑i∈RL(yi−yˉL)2+∑i∈RR(yi−yˉR)2\sum_{i \in R_L}(y_i - \bar y_L)^2 + \sum_{i \in R_R}(y_i - \bar y_R)^2 (again computable for all thresholds with prefix sums of yy and y2y^2). The fitted function is a step function. Demo 4 uses Lecture 1’s sine data (50 training points, noise variance 0.09):

max_depth1234568none
leaves2481320254050
train MSE0.1750.1160.0650.0510.0390.0260.0060.000
test MSE0.1950.1550.1310.1350.1330.1430.1540.156

最好的单棵树(0.131)不如第 1 讲同样 50 个点的 LWR(最好 0.099;样本不同,只能粗略比较):用台阶去逼近一条光滑曲线天生吃亏,要么台阶粗(偏差),要么每级台阶的数据少(方差)。

2.6 Pros and cons of a single tree

优点 Strengths缺点 Weaknesses
可解释(浅树)高方差:数据稍变,树就大变
不需要 feature scaling;对单调变换不变轴对齐:斜的边界要很多叶子
天然处理混合类型特征、多分类、非线性和交互分段常数:光滑函数逼近得差,不能外推
预测快(O(depth)O(\text{depth}))贪心:可能错过”第一刀没收益、后面收益很大”的结构(§2.2 的口袋数据就差点被错过)

单棵树很少是最好的模型。它真正的价值在于做集成的零件:它的缺点(高方差;偏差和方差都能用深度调)恰好能被 §3 的平均和 §4 的叠加修补。


3. Bagging & random forests · 平均降方差

3.1 Averaging correlated predictors

Suppose T1,…,TBT_1, \dots, T_B are identically distributed predictions at a fixed xx, each with variance σ2\sigma^2 and pairwise correlation ρ\rho. Then

Var⁡(1B∑b=1BTb)=1B2[Bσ2+B(B−1)ρσ2]=ρσ2+1−ρBσ2.\operatorname{Var}\Big(\frac{1}{B}\sum_{b=1}^B T_b\Big) = \frac{1}{B^2}\Big[B\sigma^2 + B(B-1)\rho\sigma^2\Big] = \rho\sigma^2 + \frac{1-\rho}{B}\sigma^2 .

两个推论:

  • 偏差不变:平均的期望等于单个的期望。平均很多个同样有偏的模型,结果还是同样有偏。所以要平均的零件应该是 low bias, high variance 的 —— 不剪枝的深树正好如此(§2.3)。
  • 方差有地板:B→∞B \to \infty 时只剩 ρσ2\rho\sigma^2。模型之间越相关,平均的效果越差。

理想情况是在 BB 份独立的训练集上各训练一棵树,但我们只有一份数据。Bootstrap:从 nn 个样本里有放回地抽 nn 个,模拟”另一份训练集”。Bagging(bootstrap aggregating, Breiman 1996)= 在 BB 个 bootstrap 样本上各训练一棵树,回归取平均,分类取平均概率(或投票)。所有 bootstrap 样本都来自同一份训练集,所以树和树之间必然相关(ρ>0\rho > 0):训练集里的噪声是它们共有的,平均消不掉。

代码 02_bagging_forests.py 演示 1 直接量出了这些量:Friedman #1 回归(d=10d = 10,前 5 个特征有用,噪声方差 1),重复抽 20 个独立的训练集(每个 200 个点),每个训练集上训练 30 棵不限深度的 bootstrap 树。单棵树 σ2=10.07\sigma^2 = 10.07,两棵树的协方差 ρσ2=1.33\rho\sigma^2 = 1.33,ρ=0.132\rho = 0.132:

BB1251030
measured variance9.805.743.142.241.62
formula ρσ2+(1−ρ)σ2/B\rho\sigma^2 + (1-\rho)\sigma^2/B10.075.703.082.201.62
bias²3.663.733.823.653.65
MSE vs the true function13.989.777.126.015.35

(B=30B = 30 那一列按构造必然相等,因为 ρσ2\rho\sigma^2 就是用这 30 棵树估出来的;真正的检验是 B<30B < 30 的几列,误差都在几个百分点以内。偏差² 用的是减掉 s2/Rs^2/R 的无偏估计,否则只有 20 个训练集时会把单棵树的偏差高估约 0.5。)方差从 9.8 降到 1.6,偏差纹丝不动;30 棵树之后误差的大头已经是偏差了(3.65 vs 1.62)。

对照:bagging 一个稳定的模型。 同样的实验换成线性回归:偏差² 5.75,方差 0.394;bagging 之后偏差² 5.75,方差 0.414(甚至略升)。对一个(近似)线性的、稳定的估计量,bootstrap 平均基本还是它自己。Breiman 的结论是:bagging 帮助的是不稳定的学习器(树、神经网络),对稳定的(线性回归、大 kk 的 kNN)没用。

3.2 Random forests: decorrelate the trees

The floor is ρσ2\rho\sigma^2, so how do we lower ρ\rho? Bagged trees are correlated largely because they all use the same strong features — the top split tends to be the same in every tree. Random forests (Breiman 2001) change one thing: at each node, search for a split among only m<dm < d randomly chosen features. This forces different trees to use different features and lowers ρ\rho, at the price of weaker individual trees (higher σ2\sigma^2 and bias). Breiman suggested m≈d/3m \approx d/3 for regression and m≈dm \approx \sqrt d for classification.

Demo 2 repeats the simulation on two datasets and sweeps mm (“MSE vs f” is the 30-tree average against the true function):

datammσ2\sigma^2ρ\rhofloor ρσ2\rho\sigma^2bias²variance (B=30B = 30)MSE vs f
d=5d = 5 (all relevant)5 (bagging)8.410.1331.122.751.364.19
39.320.0870.812.821.103.98
113.830.0410.574.021.015.08
d=10d = 10 (half noise)10 (bagging)10.070.1321.333.651.625.35
314.590.0560.824.791.286.13
119.810.0300.598.091.239.39

机制完全如预期:mm 越小,ρ\rho 越小(0.132 → 0.030),地板越低(1.33 → 0.59)。但净效果并不总是好的 —— 这和”random forest 总比 bagging 好”的常见印象不同:

  • d=10d = 10 时,一半的特征是纯噪声,m=1m = 1 的节点有一半只能在噪声特征上切,偏差² 翻了一倍多;最好的反而是纯 bagging。ESL §15.3 有同样的结论:相关特征占比小时,小 mm 表现差。
  • d=5d = 5 时,m=3m = 3 只比 bagging 好一点(3.98 vs 4.19)。
  • 为什么好处有限?bagging 之后剩下的误差主要是偏差(d=10d = 10:3.65 vs 1.62),特征子采样最多只能省下方差的那一部分(就算把地板 1.33 压到 0),却会推高偏差。而且深树 + 有噪声的回归,bagging 的 ρ\rho 本来就只有 0.13。ρ\rho 很高的问题(几个强特征主导、每棵树的第一刀都一样)上,特征子采样的作用才大。

所以 mm 是一个要调的超参数(用 §3.3 的 OOB 误差调很方便),不是”越小越好”;sklearn 的 RandomForestRegressor 默认干脆用全部特征,也就是 bagging。特征子采样的另一个好处是快:每个节点只看 mm 个特征。

为什么 random forest 不会因为”树太多”而过拟合? 公式里 BB 只出现在方差项的分母上:加树只会让方差单调地降向地板,偏差不变。森林的复杂度由每棵树(深度、mm、叶子大小)决定,而不是由 BB 决定 —— 这和 boosting(§4)完全不同。

3.3 Out-of-bag error: free cross-validation

一个 bootstrap 样本不含某个特定样本的概率是 (1−1/n)n→e−1≈0.368(1 - 1/n)^n \to e^{-1} \approx 0.368。所以每个训练样本大约有 37% 的树没见过它,用这些树预测它,就得到一个”测试”预测。演示 3(Friedman #1,训练 300 个点,测试 5000 个点;实测每棵树的 OOB 比例 0.368):

B=5B = 5102550100200
train MSE (bagging)1.781.341.050.870.800.78
OOB MSE11.058.837.056.395.995.72
test MSE8.146.966.226.095.875.86
OOB MSE (RF, m=3m = 3)13.5211.227.677.166.656.58
test MSE (RF, m=3m = 3)7.757.226.846.626.616.64
  • 训练误差严重低估(0.78 vs 5.86):每棵树都长到纯,它见过的点几乎被背下来了。
  • BB 足够大时 OOB 误差非常接近测试误差(B=200B = 200:5.72 vs 5.86)。BB 小时偏悲观:每个点只有约 0.37B0.37B 棵 OOB 树,OOB 预测其实是一个”更小的森林”的预测。(B=5B = 5 时还有 25 个点一棵 OOB 树都没有,概率 0.6325≈10%0.632^5 \approx 10\%。)
  • OOB 就是免费的交叉验证(第 3 讲),可以拿来选 mm、叶子大小。它只对 bagging 类方法成立 —— boosting 的每棵树依赖之前所有的树,没有干净的”没见过”的样本。

4. Boosting · 叠加降偏差

4.1 The idea

Bagging 平均的是一群独立训练的、强的、高方差的模型。Boosting 反过来:串行地叠加一串弱的、高偏差的模型(比如 stump:只切一刀的树),每一个都专门去修补前面留下的错误。最终模型是加法的:

FM(x)=∑m=1Mαm hm(x).F_M(x) = \sum_{m=1}^{M} \alpha_m\, h_m(x).

AdaBoost(Freund & Schapire 1997),二分类,y∈{−1,+1}y \in \{-1, +1\}。初始化样本权重 wi=1/nw_i = 1/n,对 t=1,…,Tt = 1, \dots, T:

  1. 用权重 ww 训练弱学习器 hth_t(让加权误差 εt=∑iwi1{ht(xi)≠yi}\varepsilon_t = \sum_i w_i 1\{h_t(x_i) \ne y_i\} 最小);
  2. αt=12log⁡1−εtεt\alpha_t = \frac{1}{2}\log\frac{1 - \varepsilon_t}{\varepsilon_t} —— 越准的弱学习器说话越算数;
  3. wi←wi e−αtyiht(xi)w_i \leftarrow w_i\, e^{-\alpha_t y_i h_t(x_i)},再归一化:分错的样本权重乘 eαte^{\alpha_t},分对的乘 e−αte^{-\alpha_t}。

预测 sign⁡(FT(x))\operatorname{sign}(F_T(x))。这个算法最初是从在线学习(Hedge 算法)的角度设计的;为什么这三步”恰好”是这几个公式,要几年后才有一个干净的统计解释。

4.2 AdaBoost = forward stagewise additive modeling with exponential loss

Friedman, Hastie & Tibshirani (2000) observed that AdaBoost greedily minimizes the exponential loss L(F)=∑iexp⁡(−yiF(xi))L(F) = \sum_i \exp(-y_i F(x_i)), adding one term at a time and never revisiting old ones (forward stagewise):

(αt,ht)=arg⁡min⁡α,h∑iexp⁡(−yi[Ft−1(xi)+αh(xi)])=arg⁡min⁡α,h∑iwi(t) e−αyih(xi),wi(t)=e−yiFt−1(xi).(\alpha_t, h_t) = \arg\min_{\alpha, h}\sum_i \exp\big(-y_i[F_{t-1}(x_i) + \alpha h(x_i)]\big) = \arg\min_{\alpha, h}\sum_i w_i^{(t)}\,e^{-\alpha y_i h(x_i)}, \qquad w_i^{(t)} = e^{-y_i F_{t-1}(x_i)} .

The key step: with ε\varepsilon the weighted error of hh, the objective is (1−ε)e−α+εeα(1-\varepsilon)e^{-\alpha} + \varepsilon e^{\alpha} (times ∑iwi\sum_i w_i). For any fixed α>0\alpha > 0 it is increasing in ε\varepsilon, so the best hh is the one with the smallest weighted error; setting the derivative in α\alpha to zero gives α=12log⁡1−εε\alpha = \frac{1}{2}\log\frac{1-\varepsilon}{\varepsilon}, and the minimum value is 2ε(1−ε)2\sqrt{\varepsilon(1-\varepsilon)}. All three AdaBoost rules fall out.

Code 03_boosting.py, demo 1, checks this round by round on the circle data (first 3 of 6 rounds shown):

ttεt\varepsilon_tmax⁡∣wt−normalized e−yFt−1∣\max\lvert w_t - \text{normalized } e^{-yF_{t-1}}\rvertαt\alpha_t (formula)αt\alpha_t (numerical line search)L(Ft)/L(Ft−1)L(F_t)/L(F_{t-1})2εt(1−εt)2\sqrt{\varepsilon_t(1-\varepsilon_t)}
10.32700.3616500.3616500.9379880.937988
20.3824×10−194\times10^{-19}0.2395790.2395790.9719720.971972
30.3479×10−199\times10^{-19}0.3168350.3168350.9518250.951825

The other rounds agree just as well (the largest α\alpha gap, 2×10−82\times10^{-8}, is the line search’s own precision). So AdaBoost’s weights are simply each example’s share of the current exponential loss: points that keep being misclassified have very negative yFyF and exponentially large weight.

为什么是指数损失? 它是 margin t=yF(x)t = yF(x) 的函数,和第 1 讲 §3.4 的 logistic loss log⁡(1+e−t)\log(1 + e^{-t}) 是近亲:两者在 t→+∞t \to +\infty 时都趋于 0,总体最优解(population minimizer)都是 log-odds 的倍数 —— 指数损失的是 F⋆(x)=12log⁡P(y=1∣x)P(y=−1∣x)F^\star(x) = \frac{1}{2}\log\frac{P(y = 1\mid x)}{P(y = -1\mid x)}(练习 3)。区别在 t→−∞t \to -\infty 时:logistic loss 线性增长,指数损失指数增长,所以 AdaBoost 对分错得离谱的点(往往是标签噪声)反应过度 —— 和第 1 讲”平方损失怕离群点”是同一类问题。

4.3 Training error bound, margins, and overfitting

因为 1{yF≤0}≤e−yF1\{yF \le 0\} \le e^{-yF},训练误差被平均指数损失控制住,而后者每轮恰好乘以 Zt=2εt(1−εt)Z_t = 2\sqrt{\varepsilon_t(1-\varepsilon_t)}。令 γt=12−εt\gamma_t = \frac{1}{2} - \varepsilon_t(比随机猜好多少),用 1−x≤e−x1 - x \le e^{-x}:

1n∑i1{yiFT(xi)≤0}  ≤  ∏t=1T2εt(1−εt)  =  ∏t=1T1−4γt2  ≤  exp⁡(−2∑t=1Tγt2).\frac{1}{n}\sum_i 1\{y_i F_T(x_i) \le 0\} \;\le\; \prod_{t=1}^T 2\sqrt{\varepsilon_t(1-\varepsilon_t)} \;=\; \prod_{t=1}^T \sqrt{1 - 4\gamma_t^2} \;\le\; \exp\Big(-2\sum_{t=1}^T \gamma_t^2\Big).

只要每个弱学习器都比随机猜好一点点(γt≥γ>0\gamma_t \ge \gamma > 0,weak learning assumption),训练误差就指数级地降到 0。这就是 boosting 名字的来历:把”弱”学习器提升(boost)成”强”学习器(Schapire 1990 回答了 Kearns & Valiant 的问题:弱可学习 ⇒ 强可学习)。

演示 2:圆形边界(无噪声),300 个训练点,2000 轮 stumps(margin = yF(x)/∑tαt∈[−1,1]yF(x)/\sum_t\alpha_t \in [-1, 1]):

round11020508119650010002000
εt\varepsilon_t0.3430.3980.3750.3960.432–0.4530.4430.447
train err0.3430.1100.0570.0170–000
bound ∏Zt\prod Z_t0.9500.6580.4930.2670.170–0.0090.00060.0000
test err0.3820.1770.1080.0550.04460.04120.05580.05660.0570
min margin−1−0.165−0.097−0.0230.004–0.0310.0410.046

(第 196 轮是测试误差最低的一轮,代码没有打印那一轮的其他列。)

  • 单个 stump 的测试误差 0.382;stumps 的加权和是 g1(x1)+g2(x2)g_1(x_1) + g_2(x_2) 形状的加法模型,而圆 x12+x22<r2x_1^2 + x_2^2 < r^2 正好是加法的,所以 boosting 能做好。
  • εt\varepsilon_t 越来越接近 0.5:后面的 stump 面对的是被反复加权的”难”样本。训练误差本身不单调(第 81 轮到 0,第 100 轮又是 0.003),单调下降的只有上界。
  • 训练误差到 0 之后,测试误差先继续下降了一点(0.0446 → 0.0412),最小 margin 也一直在增大:训练点都分对了,AdaBoost 还在把它们推离边界。Schapire et al.(1998)的 margin 理论用这一点解释”boosting 为什么不怎么过拟合”。但这里的改善很小,之后测试误差又回升到 0.057,比训练误差刚到 0 时还差。
  • 加 10% 标签噪声后,测试误差在第 40 轮最低(0.171),到第 2000 轮升到 0.243,训练误差降到 0.023 —— 指数损失在拼命拟合翻转的标签(Dietterich 2000 的经典观察)。

结论:“AdaBoost 不会过拟合”是一个流传很广但不成立的说法;它只是在低噪声时过拟合得慢。轮数仍然要用验证集或 early stopping 选;有标签噪声时更应该换成对离群点温和的损失(logistic loss → LogitBoost / gradient boosting)。

4.4 Gradient boosting = gradient descent in function space

Generalize §4.2 to any differentiable loss L(y,F)L(y, F) (Friedman 2001). Treat the values (F(x1),…,F(xn))(F(x_1), \dots, F(x_n)) at the training points as the “parameters”; gradient descent would move along the negative gradient:

ri(m)=−∂L(yi,F)∂F∣F=Fm−1(xi)(pseudo-residuals),Fm=Fm−1+ν hm,hm≈arg⁡min⁡h∈trees∑i(ri(m)−h(xi))2.r_{i}^{(m)} = -\frac{\partial L(y_i, F)}{\partial F}\Big|_{F = F_{m-1}(x_i)} \quad(\text{pseudo-residuals}), \qquad F_m = F_{m-1} + \nu\, h_m, \quad h_m \approx \arg\min_{h \in \text{trees}} \sum_i \big(r_i^{(m)} - h(x_i)\big)^2 .

But a gradient defined only at the nn training points says nothing about other xx. So each step fits a small regression tree to the pseudo-residuals: the tree is “the direction in function space closest to the negative gradient that we can actually represent”, and it also generalizes the step to new inputs. (Optionally, each leaf’s value is then re-optimized for the true loss — a per-leaf line search.)

loss L(y,F)L(y, F)pseudo-residual −∂L/∂F-\partial L/\partial Fnoise model
12(y−F)2\frac{1}{2}(y - F)^2y−Fy - F (the ordinary residual)Gaussian
∣y−F∣\lvert y - F\rvertsign⁡(y−F)\operatorname{sign}(y - F)Laplace — robust
log⁡(1+e−y′F)\log(1 + e^{-y'F}), y∈{0,1}y \in \{0,1\}y−σ(F)y - \sigma(F)Bernoulli — Lecture 1’s “target − prediction” again
e−yFe^{-yF}y e−yFy\,e^{-yF}AdaBoost

Shrinkage. Demo 3, Friedman #1 (300 training, 5000 test points), depth-3 trees:

ν\nutest @10@100@300@1000best test MSEbest round
110.7011.2011.2011.209.797
0.35.423.503.493.493.48163
0.110.973.353.173.143.14996
0.0317.955.483.543.173.171000 (not converged)
  • ν=1\nu = 1:每棵树把残差(包括噪声)一口吃掉,第 7 轮之后就在拟合噪声了。
  • ν\nu 从 1 降到 0.1,最好的测试误差从 9.79 降到 3.14;ν=0.03\nu = 0.03 在 1000 轮时还在下降,补跑 3000 轮后最好也是 3.14 —— 收益递减,再小只是更慢。经验法则:ν≤0.1\nu \le 0.1,轮数用 early stopping 选。小步长把每一处修正分摊给很多棵树,相当于一种正则化 —— 和 GD 里”小学习率 + early stopping”是同一个道理(第 3 讲)。
  • 1000 轮时训练误差已经是 0,但 ν≤0.3\nu \le 0.3 时测试误差几乎不回升:过拟合是有的,只是很慢。
  • 同一份数据上 bagging(200 棵树)的测试 MSE 是 5.35。§3 说过 bagging 之后剩下的主要是偏差;boosting 用很多棵小树逐步修正残差,降的正是偏差。

树深 = 交互的阶数。 演示 4(同一份数据,ν=0.1\nu = 0.1,1000 轮)。最后一行是 x1,x2x_1, x_2 的混合差分 E∣F(a,b)−F(a,b′)−F(a′,b)+F(a′,b′)∣\mathbb{E}\lvert F(a,b) - F(a,b') - F(a',b) + F(a',b')\rvert,加法模型恰好为 0:

max_depth1(stumps)236
best test MSE3.843.083.145.07
x1x_1–x2x_2 interaction7×10−157\times10^{-15}0.770.961.23

stumps 的和是 ∑jgj(xj)\sum_j g_j(x_j),表示不了任何交互(混合差分是浮点误差),所以学不好 Friedman #1 里的 sin⁡(πx1x2)\sin(\pi x_1 x_2)。深度 kk 的树最多表示 kk 阶交互;这份数据最高是两两交互,深度 2 最好,深度 6 的树太灵活反而变差。经验:boosting 用浅树(4~32 个叶子),random forest 用深树 —— 一个靠”很多弱学习器的和”降偏差,一个靠”很多强学习器的平均”降方差,零件的选择正好相反。

换损失 = 换伪残差。 演示 5:正弦数据(200 个点)里有 20 个点加了 ±3~6 的大误差,深度 2、ν=0.1\nu = 0.1。和真实函数的 MSE:平方损失最好 0.169(第 22 轮),500 轮恶化到 1.552;绝对值损失最好 0.015,500 轮 0.017。第 1 讲 §2.4 的故事原样重演:选损失就是选噪声模型,不管模型是线性的、树的和,还是神经网络。

4.5 Bagging vs boosting

Bagging / random forestBoosting
零件深树(低偏差、高方差)浅树 / stumps(高偏差、低方差)
组合方式并行、独立、平均串行、每一步依赖前面、加权求和
主要降低方差偏差(也降一些方差)
加更多模型不会过拟合(方差单调降向 ρσ2\rho\sigma^2)会过拟合(慢);需要 early stopping
关键超参数mm、叶子大小ν\nu、轮数、树深
额外好处OOB 误差、易并行任意可导损失

现代的 XGBoost(Chen & Guestrin 2016)、LightGBM、CatBoost 都是 gradient boosting:用损失的二阶导做 Newton 式的叶子值、对叶子数和叶子值加正则、借鉴 random forest 的行 / 列子采样、高效的直方图切分。它们是表格数据上的默认强基线。


5. Neural networks · 学习特征

5.1 From GLM to MLP

Lecture 1 §4.7 said it already: a neural network is a learned feature map followed by a GLM head,

η=ϕW(x)⊤θ,ϕW(x)=g(…g(g(xW1+b1)W2+b2)…),\eta = \phi_W(x)^\top\theta, \qquad \phi_W(x) = g\big(\dots g(g(xW_1 + b_1)W_2 + b_2)\dots\big),

trained with the head’s NLL (identity + MSE, sigmoid + BCE, softmax + CE). If we froze ϕW\phi_W, this would be exactly Lecture 1 — a convex GLM on fixed features (§5.3 uses precisely this as a baseline). Training WW as well is what makes the features learned, and what makes the problem non-convex.

Why do we need the activation gg? Without it, xW1W2⋯WL=xWxW_1W_2\cdots W_L = xW is still linear — depth buys nothing. Common choices:

activationg(z)g(z)g′(z)g'(z)notes
sigmoidσ(z)\sigma(z)σ(1−σ)≤1/4\sigma(1-\sigma) \le 1/4vanishing gradients when saturated; not zero-centered. Today mostly in output layers and gates
tanhtanh⁡z\tanh z1−tanh⁡2z1 - \tanh^2 zzero-centered, still saturates
ReLUmax⁡(0,z)\max(0, z)1{z>0}1\{z > 0\}no saturation for z>0z > 0, cheap; units can “die” (z≤0z \le 0 for every input)
GELU / SiLUz Φ(z)z\,\Phi(z) / z σ(z)z\,\sigma(z)smoothcommon in Transformers (CS224N / CS336)

5.2 XOR, revisited

第 1 讲 §3.6 留下的悬念:Minsky & Papert(1969)指出单层感知机连 XOR 都学不了,第一波神经网络热潮随之退去。代码 04_mlp_backprop.py 演示 2 把这段历史跑一遍:

  • 感知机(第 1 讲的 perceptron):100 个 epoch,每个 epoch 犯错 3 或 4 次,从未停下,最终准确率 0.50。
  • 逻辑回归:由于对称性,θ=0\theta = 0 处的梯度恰好是 0,Newton 一步都不走,所有预测概率都是 0.5。任何线性分类器在这 4 个点上最多分对 3 个。
  • 一个隐藏层的 MLP [2,h,2][2, h, 2],Adam 训练 1000 步,每个 hh 用 20 个随机初始化,数成功的次数:
hh12348
tanh: solved0/2013/2017/2020/2020/20
ReLU: solved0/205/209/2015/2019/20
ReLU: all units dead7/202/200/200/201/20

三个要点:

  • h=1h = 1 永远失败:一个隐藏单元只给出 xx 的一个线性投影的单调函数,接上线性输出还是分不开。h=2h = 2 在理论上就够了,但 tanh 只成功了 13 次 —— 损失非凸,有局部最优和平台。ReLU 还有一种特有的失败:所有单元对 4 个输入的 zz 都 ≤0\le 0,梯度恒为 0,网络退化成常数(损失恰好 log⁡2\log 2)。
  • 加宽到 h=8h = 8 几乎总能成功:过参数化让优化变容易。表示 XOR 只需要 2 个单元,训练时多给一些就几乎不会卡住。这是深度学习里反复出现的现象,理论上仍不完全清楚。
  • 第一个解出 XOR 的 h=2h = 2 tanh 网络(种子 101)把两个标签为 0 的点映射到 (h1,h2)(h_1, h_2) 平面的同一个角落附近((−0.94,0.96)(-0.94, 0.96) 和 (−0.97,0.92)(-0.97, 0.92)),两个标签为 1 的点在另外两个角上((0.96,1.00)(0.96, 1.00) 和 (−1.00,−0.95)(-1.00, -0.95)):在隐藏空间里它们线性可分了,输出层(一个 softmax 回归)只需画一条直线。隐藏层学到的就是让问题变成线性的特征。

5.3 Universal approximation, and what learned features buy

Universal approximation (Cybenko 1989; Hornik 1991): a one-hidden-layer network with enough units can approximate any continuous function on a compact set to any accuracy. In 1-D the intuition is elementary: a ReLU network with hh hidden units is a piecewise-linear function with at most hh kinks, and piecewise-linear interpolants approximate any continuous function. Code 05_learned_features.py, demo 1, writes down the network that interpolates sin⁡(2πx)\sin(2\pi x) (unit kk = kink kk) and compares it with a same-sized network trained from random initialization (max error):

hh248163264
constructed network1.0000.2110.0700.0190.00480.0012
error × h2h^24.003.374.504.834.914.93
trained network1.1531.0960.2620.1610.0570.043
kinks inside [0, 1] after training2145818

The constructed error falls like 1/h21/h^2 (error × h2h^2 approaches the bound max⁡∣f′′∣/8=(2π)2/8≈4.93\max\lvert f''\rvert/8 = (2\pi)^2/8 \approx 4.93). But the theorem only promises existence: the trained network with h=64h = 64 is more than an order of magnitude worse (0.043), with many kinks wasted outside [0,1][0, 1]. In high dimensions, “placing kinks on a grid” needs exponentially many units. Universal approximation cannot explain why deep learning works — it says nothing about how many units are needed or whether GD finds them.

What, then, does learning features buy? Demo 2 is a controlled comparison on two interleaved spirals (400 training points), network [2,h,2][2, h, 2] with tanh, the same initialization for both models (W1,b1W_1, b_1 entries ∼N(0,s2)\sim\mathcal{N}(0, s^2)). The MLP trains everything; the random features (RF) model freezes the hidden layer and trains only the head — a convex logistic regression on fixed features, solved exactly with Lecture 1’s Newton method to give it the best possible chance.

hh51020502001000
MLP test acc0.7650.8750.9660.9680.9640.969
RF (s=3s = 3) test acc0.4680.6280.7470.9270.9630.971
RF (s=1s = 1) test acc0.5250.5970.5290.7260.8950.927
hidden layer moved, ∥ΔW1∥/∥W1(0)∥\lVert\Delta W_1\rVert/\lVert W_1^{(0)}\rVert2.291.892.312.131.470.87
  • 测试准确率第一次达到 0.9:MLP 需要 h=20h = 20,随机特征(s=3s = 3)需要 h=50h = 50,s=1s = 1 需要 h=1000h = 1000。学习特征省的是单元数:网络把每个单元的”等高线”挪到螺旋需要的地方,随机特征只能靠数量去碰。
  • 但随机特征不是没用:h=1000h = 1000 时它(0.971)和 MLP(0.969)不相上下。宽度 → ∞ 时,随机特征上的线性模型收敛到一个 kernel 方法(Rahimi & Recht 2007 —— 第 2 讲的 kernel trick 反过来用)。它的效果强烈依赖于随机分布的尺度 ss:s=1s = 1 的特征太平缓,同样 h=200h = 200 只有 0.895。选 ss 就像选 RBF kernel 的带宽;MLP 会自己把特征调到合适的陡峭程度。
  • 最后一行:网络越宽,隐藏层需要移动得越少(h≤50h \le 50 时约 2,h=1000h = 1000 时 0.87),越接近”随机特征 + 训练输出头”。这是 neural tangent kernel / lazy training 的观察(Jacot et al. 2018):无限宽的网络在某些参数化下等价于一个 kernel 方法。真正的 feature learning 发生在有限宽度、参数移动较大的时候。
  • 第 3 讲的 double descent 正是用这种随机特征模型演示的。

5.4 Backpropagation

The recursion. For the loss JJ of a minibatch, define δℓ=∂J/∂Zℓ\delta_\ell = \partial J/\partial Z_\ell (shape n×hℓn \times h_\ell). The output head gives the starting point — for softmax + CE (or identity + MSE, or sigmoid + BCE) it is the GLM gradient of Lecture 1 §4.4:

δL=1n(Y^−Y).\delta_L = \frac{1}{n}(\hat Y - Y).

Then, going backwards, each layer does

∂J∂Wℓ=Aℓ−1⊤ δℓ,∂J∂bℓ=1⊤δℓ,δℓ−1=(δℓ Wℓ⊤)⊙g′(Zℓ−1).\frac{\partial J}{\partial W_\ell} = A_{\ell-1}^\top\,\delta_\ell, \qquad \frac{\partial J}{\partial b_\ell} = \mathbf{1}^\top\delta_\ell, \qquad \delta_{\ell-1} = \big(\delta_\ell\,W_\ell^\top\big)\odot g'(Z_{\ell-1}).

读法:∂J/∂Wℓ\partial J/\partial W_\ell = “输入 × 误差”,和第 1 讲 GLM 的梯度 (h−y) x(h - y)\,x 是同一个形状 —— 每一层的权重都在做一个”以 Aℓ−1A_{\ell-1} 为特征的 GLM 更新”,只是它的”误差” δℓ\delta_\ell 是从上面传下来的。δℓ−1\delta_{\ell-1} 的公式就是链式法则:误差沿着权重传回上一层,再乘以激活函数在那里的局部斜率。g′g' 很小(sigmoid / tanh 饱和)时信号在这里消失,这是 §6.1 的伏笔。

Gradient checking. 演示 1 把手写的 backward 和中心差分逐个比较(网络 [3,5,4,3][3, 5, 4, 3],59 个参数):

activationrelative error ∥g−gnum∥/(∥g∥+∥gnum∥)\lVert g - g_{\text{num}}\rVert/(\lVert g\rVert + \lVert g_{\text{num}}\rVert)mismatched parameters
tanh8.3×10−108.3\times10^{-10}–
sigmoid1.3×10−91.3\times10^{-9}–
ReLU1.0×10−91.0\times10^{-9}–
ReLU, one input row set to 07.8×10−27.8\times10^{-2}b1b_1, b2b_2

最后一行不是 bug:全 0 的输入让第一层的 Z=b1=0Z = b_1 = 0,正好落在 ReLU 的折角上;它在第一层的输出也是 0,于是第二层的 Z=b2=0Z = b_2 = 0 也落在折角上。我们取 g′(0)=0g'(0) = 0,中心差分算出的是 1/21/2。梯度检查失败时,先看是不是有折角(ReLU、max、abs)。另外,logits 上的数值梯度和 (P−Y)/n(P - Y)/n 的差是 10−1010^{-10};装了 PyTorch 的话,autograd 和手写的 backward 一致到 5×10−175\times10^{-17} —— autograd 做的是同一件事:前向时记录计算图,反向时对每个节点套用链式法则。

Why backprop is cheap. The loss is a scalar function of PP parameters. Finite differences (or forward-mode autodiff) get one directional derivative per pass, so the full gradient costs ∼P\sim P forward passes. Reverse mode propagates ∂J/∂(⋅)\partial J/\partial(\cdot) from the single output back to everything at once, so the full gradient costs a small constant times one forward pass, independent of PP (the Baur–Strassen / Griewank–Walther “cheap gradient principle”; the deep-learning chapter of the CS229 main notes states it as an informal theorem). Demo 4 counts multiply-accumulates in the matrix products (batch 64):

networkparams PPbackward / forwardfinite differences / backprop
[784, 256, 10]203,5301.01202,256
[784, 256, 256, 10]269,3221.25239,043
[784, 1024, 1024, 1024, 10]2,913,2901.722,138,871
[64] + [512] × 8 + [10]1,877,0021.981,258,675

Each layer costs one matrix product forward and two backward (A⊤δA^\top\delta and δW⊤\delta W^\top; the first layer skips the second), so backward ≤ 2 × forward: the ratio is low (1.01) when the input layer dominates and close to 2 (1.98) for a deep stack of equal layers. “One training step ≈ 3 forward passes” is where CS336’s ”6×6 \times parameters ×\times tokens” training-cost estimate comes from (2 FLOPs per multiply-add). The price is memory: backward needs every layer’s Zℓ,AℓZ_\ell, A_\ell from the forward pass, which is where most training memory goes (activation checkpointing trades recomputation for memory).

Vectorization. Aℓ−1⊤δℓA_{\ell-1}^\top\delta_\ell 这一次矩阵乘法,等于把 nn 个样本各自的外积 a(i)δ(i)⊤a^{(i)}\delta^{(i)\top} 加起来(演示 4 验证两者差 3×10−173\times10^{-17})。数学上完全一样,但一次大的矩阵乘法能用上 BLAS / GPU 的并行。写神经网络代码的基本功就是把”对样本的循环”变成”多一个维度的矩阵运算”。


6. Training neural networks · 让网络训练得动(简述)

Ng 的第 12 讲后半段讲 “improving neural networks”。这一节只讲清楚每个技巧在解决什么问题,细节见 CS224N / CS336。

6.1 Initialization: break symmetry, keep the scale

Symmetry. 如果同一层的所有隐藏单元初始值相同,它们的输入相同、输出相同、收到的梯度也相同 —— 永远一模一样。演示 3(a):圆形边界,网络 [2,8,2][2, 8, 2]:

initializationtest accmax difference between hidden unitsrank of hidden output
random N(0,1/fan_in)\mathcal{N}(0, 1/\text{fan\_in})0.9647.168
all zeros0.52100
all W=0.5W = 0.50.60401
(reference: a single hidden unit)0.605––

全 0 时 tanh⁡(0)=0\tanh(0) = 0,输出层的梯度 A⊤δ=0A^\top\delta = 0;又因为 W2=0W_2 = 0,传回隐藏层的 δW2⊤\delta W_2^\top 也是 0 —— 除了输出偏置什么都学不动。全相同时 8 个单元训练 2000 步后仍然完全相同(差恰好是 0),等价于 1 个单元。随机初始化的首要目的是打破对称性。

Scale. ZℓZ_\ell 的每个元素是 fan_in 项之和,所以 Var⁡(Zℓ)≈fan_in⋅Var⁡(W)⋅E[Aℓ−12]\operatorname{Var}(Z_\ell) \approx \text{fan\_in}\cdot\operatorname{Var}(W)\cdot\mathbb{E}[A_{\ell-1}^2]。取 Var⁡(W)=gain2/fan_in\operatorname{Var}(W) = \text{gain}^2/\text{fan\_in},信号每过一层大约乘以 gain2⋅c\text{gain}^2 \cdot c,cc 是激活函数”保留”的比例(tanh 在 0 附近约 1,ReLU 砍掉一半是 1/2)。演示 3(b):20 层、每层 100 个单元:

activationgainstd, layer 1std, layer 10std, layer 20∥∂J/∂W1∥\lVert\partial J/\partial W_1\rVert∥∂J/∂W20∥\lVert\partial J/\partial W_{20}\rVert
tanh0.50.420.00080.00001.3×10−61.3\times10^{-6}1.0×10−61.0\times10^{-6}
tanh10.630.240.160.290.22
tanh20.800.730.73241.1
ReLU10.590.0290.00091.3×10−31.3\times10^{-3}1.1×10−31.1\times10^{-3}
ReLU2\sqrt 20.830.940.891.31.1
ReLU21.17309101.4×1031.4\times10^{3}1.1×1031.1\times10^{3}

太小:信号和梯度一层层消失;太大:tanh 饱和、越靠近输入梯度越大(tanh gain 2:24 vs 1.1),ReLU 直接爆炸。让 gain2c=1\text{gain}^2 c = 1 就是 Xavier / Glorot(tanh,gain 1)和 He / Kaiming(ReLU,gain 2\sqrt 2)初始化的全部想法。

6.2 Optimizers

第 1 讲 §2.2 的结论(步长上限 2/λmax⁡2/\lambda_{\max}、步数 ∝κ\propto \kappa)对神经网络同样成立,只是曲率还随位置变化。05_learned_features.py 演示 3:螺旋数据、[2,50,2][2, 50, 2] 网络、full batch,每个优化器扫 4 个学习率,取每个检查点上最好的训练损失:

optimizerstep 100step 300step 1000step 3000
GD0.6020.4570.0350.0148
momentum (β=0.9\beta = 0.9)0.1780.0680.0290.0155
Adam0.1310.0280.0100.0086
  • Momentum(v←βv+g, θ←θ−αvv \leftarrow \beta v + g,\ \theta \leftarrow \theta - \alpha v):在一致的方向上累积速度,在来回震荡的方向上抵消。有效步长约 α/(1−β)=10α\alpha/(1-\beta) = 10\alpha,所以它的最佳学习率比 GD 小一个数量级(0.3 vs 3)。
  • Adam:mm 是梯度的滑动平均,vv 是梯度平方的滑动平均,更新 θ←θ−α m^/(v^+ϵ)\theta \leftarrow \theta - \alpha\,\hat m/(\sqrt{\hat v} + \epsilon)。除以 v^\sqrt{\hat v} 相当于逐坐标的 feature scaling,对条件数不敏感,所以成了默认选择。
  • GD 的学习率很难选:最后最好的 lr = 3,前期损失却从 0.72 冲到 5.64,等网络走到曲率更小的区域才回落(“catapult / edge of stability”);到第 3000 步它基本追平了 momentum。
  • 训练损失更低 ≠ 测试更好:三者的测试准确率是 0.957 / 0.972 / 0.965。优化器决定多快到达低损失,泛化还取决于停在哪里(第 3 讲的隐式正则化)。实践中还要用 mini-batch(第 1 讲 §2.2 的 SGD 噪声)、learning-rate warmup + decay、AdamW(把 weight decay 和自适应步长解耦)。

6.3 Normalization and other tricks (pointers)

  • Input normalization:把输入特征标准化 —— 第 1 讲 §2.2 的条件数论证原样适用。(树不需要这一步,§2.4。)
  • BatchNorm / LayerNorm:在训练中持续地把每层的激活标准化,再乘以可学习的尺度和平移。§6.1 只能在初始化时把尺度调对,训练中权重一变就又偏了;归一化层在整个训练过程中维持它。Transformer 用 LayerNorm(CS224N / CS336)。
  • Residual connections:Aℓ=Aℓ−1+f(Aℓ−1)A_\ell = A_{\ell-1} + f(A_{\ell-1}),让梯度有一条不经过激活导数的”高速公路”,是训练几十上百层网络的关键。
  • Regularization:L2 / weight decay、dropout、data augmentation、early stopping —— 第 3 讲。dropout 可以看作对指数多个子网络做 bagging 的近似(§3 的思想又出现了)。

7. 小结 · Summary

modelsource of nonlinearityloss / criterionoptimizermainly reducesbiggest weakness
Decision treerecursive axis-aligned splitsGini / entropy; SSEgreedy top-down— (depth trades bias for variance)high variance; axis-aligned
Bagging / random forestaveraging many deep treessameparallel, bootstrap, feature subsamplingvariancecannot reduce bias; floor ρσ2\rho\sigma^2
AdaBoostweighted sum of stumpsexponential lossforward stagewise (exact line search)biassensitive to label noise
Gradient boostingsum of small treesany differentiable lossGD in function space + shrinkagebiassequential; tune ν\nu, rounds, depth
MLPlearned features ϕW(x)\phi_W(x)NLL of the GLM headSGD / Adam + backpropbias (via width and depth)non-convex; tune initialization, learning rate

Five takeaways:

  1. Strictly concave impurity makes greedy splitting sensitive to any change in class proportions; misclassification is piecewise linear and can stall the greedy search entirely.
  2. Averaging reduces variance down to a floor, ρσ2\rho\sigma^2; bias is untouched. Bagging needs unstable, low-bias base learners; random forests lower ρ\rho at the price of weaker trees, and whether that pays off depends on the data.
  3. Boosting is greedy optimization of a loss over an additive model: AdaBoost for exponential loss, gradient boosting for any differentiable loss. It attacks bias, it can overfit, and shrinkage + early stopping are its regularizers.
  4. A neural network = learned features + a GLM head. Backprop starts from y^−y\hat y - y and applies the chain rule layer by layer; reverse mode makes the whole gradient cost about one extra forward pass (plus memory).
  5. Representable ≠ learnable. Universal approximation guarantees existence only; whether SGD finds a good solution depends on initialization (symmetry, scale), width (over-parameterization helps), and the optimizer.

8. 常见误区 · Common pitfalls

  • “Gini vs entropy is an important choice.” They almost always pick the same splits (demo 1: 0.962 vs 0.960). The important choice is not using misclassification for growing.
  • “A tree with 100% training accuracy is a good tree.” It has memorized the noise (§2.3). Control depth / leaf size, or average many such trees.
  • “More trees in a random forest will overfit.” Adding trees only lowers the variance toward ρσ2\rho\sigma^2; complexity is set by each tree. (More boosting rounds can overfit.)
  • “Random forests always beat bagging.” Feature subsampling lowers ρ\rho but weakens each tree; with many irrelevant features it can lose (§3.2).
  • “Boosting doesn’t overfit.” It overfits slowly on clean data and clearly with label noise (§4.3).
  • “Bagging helps any model.” It does essentially nothing for stable learners like linear regression (§3.1).
  • “Universal approximation explains why deep learning works.” It says nothing about how many units are needed or whether SGD finds them (§5.3).
  • “A failing gradient check means a bug.” First check for kinks (ReLU at exactly 0, max, abs) (§5.4).
  • “Zero initialization is fine, the bias will break the symmetry.” Units that start identical stay identical forever (§6.1).

9. 练习 · Exercises

练习 1(Strict concavity and the gain). 二分类时令 pp 为正类比例。(a) 证明 Gini G(p)=2p(1−p)G(p) = 2p(1-p) 是严格凹的,并由此证明:只要 pL≠pRp_L \ne p_R,Gini gain 严格为正。(b) 对误分类率 E(p)=min⁡(p,1−p)E(p) = \min(p, 1-p),找出 gain = 0 的充要条件(假设 pL,pRp_L, p_R 都不等于 0.5)。

参考答案

(a) G′′(p)=−4<0G''(p) = -4 < 0。p=wpL+(1−w)pRp = w p_L + (1 - w)p_R,w=NL/N∈(0,1)w = N_L/N \in (0, 1);严格凹 ⇒ G(p)>wG(pL)+(1−w)G(pR)G(p) > wG(p_L) + (1-w)G(p_R) 当且仅当 pL≠pRp_L \ne p_R,两边乘以 NN 就是 gain >0> 0。(b) EE 在 [0,0.5][0, 0.5] 和 [0.5,1][0.5, 1] 上各是一段直线。若 pL,pRp_L, p_R 在 0.5 的同一侧,三点都在同一段直线上,加权平均的 EE 等于 EE 的加权平均,gain = 0;若在两侧,不等式严格成立,gain > 0。所以 gain = 0 ⇔ 两个孩子的多数类相同(§2.2 的两个切分都是这种情况)。

练习 2(How many trees?). 用 §3.1 的数字(σ2=10.07\sigma^2 = 10.07,ρ=0.132\rho = 0.132):(a) 无穷多棵树时方差是多少?(b) 要让方差离地板不超过 10%,至少需要多少棵树?(c) 如果 random forest 把 ρ\rho 降到 0.05 但 σ2\sigma^2 升到 14,地板变成多少?这说明了什么?

参考答案

(a) ρσ2=1.33\rho\sigma^2 = 1.33。(b) 需要 (1−ρ)σ2/B≤0.1ρσ2(1-\rho)\sigma^2/B \le 0.1\rho\sigma^2,即 B≥10(1−ρ)/ρ≈66B \ge 10(1-\rho)/\rho \approx 66。一般地,ρ\rho 越小,要”吃满”降方差的好处需要的树越多。(c) 0.05×14=0.700.05 \times 14 = 0.70,地板确实降低了;但这只是方差,单棵树变差往往还伴随偏差上升(§3.2 的表:d=10d = 10、m=3m = 3 时偏差² 从 3.65 升到 4.79),总误差不一定下降。

Exercise 3 (What does AdaBoost estimate?). Show that the population minimizer of the exponential loss, F⋆(x)=arg⁡min⁡FE[e−yF∣x]F^\star(x) = \arg\min_F \mathbb{E}[e^{-yF} \mid x], is 12log⁡P(y=1∣x)P(y=−1∣x)\frac{1}{2}\log\frac{P(y=1\mid x)}{P(y=-1\mid x)}. Compare with the logit of logistic regression. How would you turn AdaBoost’s F(x)F(x) into a probability?

Answer

Let p=P(y=1∣x)p = P(y = 1\mid x), so E[e−yF∣x]=p e−F+(1−p) eF\mathbb{E}[e^{-yF}\mid x] = p\,e^{-F} + (1-p)\,e^{F}. Setting the derivative to zero, −p e−F+(1−p) eF=0-p\,e^{-F} + (1-p)\,e^{F} = 0, gives e2F=p/(1−p)e^{2F} = p/(1-p), i.e. F⋆=12log⁡p1−pF^\star = \frac{1}{2}\log\frac{p}{1-p} — half the logit. Hence P(y=1∣x)≈σ(2F(x))P(y = 1\mid x) \approx \sigma(2F(x)). At the population level exponential loss and log loss agree up to this factor of 2; they differ on finite samples, where exponential loss punishes badly misclassified points exponentially (§4.2).

练习 4(Gradient boosting for classification). 对 y∈{0,1}y \in \{0, 1\}、FF = logit、损失 L=−[ylog⁡σ(F)+(1−y)log⁡(1−σ(F))]L = -[y\log\sigma(F) + (1-y)\log(1-\sigma(F))],(a) 求伪残差;(b) 若一个叶子 RR 里的值 γ\gamma 用一步 Newton 来定(从 γ=0\gamma = 0 出发最小化 ∑i∈RL(yi,Fi+γ)\sum_{i\in R} L(y_i, F_i + \gamma)),写出 γ\gamma。(c) 这和第 1 讲的哪个算法是同一个东西?

参考答案

(a) ∂L/∂F=σ(F)−y\partial L/\partial F = \sigma(F) - y,伪残差 ri=yi−σ(Fi)r_i = y_i - \sigma(F_i)。(b) 二阶导是 σ(F)(1−σ(F))\sigma(F)(1 - \sigma(F)),所以 γ=∑i∈Rri/∑i∈Rσ(Fi)(1−σ(Fi))\gamma = \sum_{i\in R} r_i \big/ \sum_{i\in R}\sigma(F_i)(1-\sigma(F_i))。(c) 第 1 讲 §3.7 的 Newton / IRLS:梯度是”目标 − 预测”,Hessian 的权重是 h(1−h)h(1-h)。区别只是这里的”参数”是一个叶子里的常数,而不是 θ\theta。这就是 XGBoost 用二阶导定叶子值的做法。

练习 5(Backprop by hand, and the symmetry argument). 两层网络 Z1=XW1+b1Z_1 = XW_1 + b_1,A1=tanh⁡(Z1)A_1 = \tanh(Z_1),Z2=A1W2+b2Z_2 = A_1W_2 + b_2,softmax + 平均 CE。(a) 写出四个参数梯度的表达式和形状。(b) 用归纳法证明:若初始时 W1W_1 的所有列相同、b1b_1 的所有元素相同、W2W_2 的所有行相同,则无论用 GD 训练多少步,这些性质都保持。(c) 若初始全为 0,证明 W1W_1 的梯度恒为 0。

参考答案

(a) δ2=(P−Y)/n\delta_2 = (P - Y)/n(n×Kn \times K);∂J/∂W2=A1⊤δ2\partial J/\partial W_2 = A_1^\top\delta_2(h×Kh \times K);∂J/∂b2=1⊤δ2\partial J/\partial b_2 = \mathbf{1}^\top\delta_2;δ1=(δ2W2⊤)⊙(1−A12)\delta_1 = (\delta_2W_2^\top)\odot(1 - A_1^2)(n×hn \times h);∂J/∂W1=X⊤δ1\partial J/\partial W_1 = X^\top\delta_1(d×hd \times h);∂J/∂b1=1⊤δ1\partial J/\partial b_1 = \mathbf{1}^\top\delta_1。(b) 若 W1W_1 的列、b1b_1 的元素相同,则 Z1Z_1、A1A_1 的所有列相同;W2W_2 的行相同 ⇒ δ2W2⊤\delta_2W_2^\top 的所有列相同 ⇒ δ1\delta_1 的列相同 ⇒ ∂J/∂W1\partial J/\partial W_1 的列相同、∂J/∂b1\partial J/\partial b_1 的元素相同;A1A_1 的列相同 ⇒ A1⊤δ2A_1^\top\delta_2 的行相同。一步 GD 后性质保持,归纳即得(Adam 是逐坐标的,同样保持)。(c) A1=tanh⁡(0)=0A_1 = \tanh(0) = 0 ⇒ ∂J/∂W2=0\partial J/\partial W_2 = 0,W2W_2 保持为 0 ⇒ δ1=0\delta_1 = 0 ⇒ ∂J/∂W1=0\partial J/\partial W_1 = 0。只有 b2b_2 在学(§6.1 的表:准确率 0.521)。

Exercise 6 (Forward vs reverse mode). A function f:RP→RKf: \mathbb{R}^P \to \mathbb{R}^K is a chain of elementary operations, and one evaluation costs CC. (a) Roughly what does the full Jacobian cost in forward mode and in reverse mode? (b) Training a network has P∼109P \sim 10^9, K=1K = 1: which mode? (c) Give an example where forward mode is the right choice.

Answer

(a) One forward-mode pass gives one directional derivative JvJv (a column of the Jacobian), so the full Jacobian needs PP passes, about P⋅CP\cdot C. One reverse-mode pass gives u⊤Ju^\top J (a row), so it needs KK passes, about K⋅CK\cdot C, plus memory for the stored intermediates. (b) With K=1K = 1, a single reverse pass gives the whole gradient at a constant multiple of CC — that is backprop (§5.4’s table). (c) Few inputs and many outputs: e.g. the sensitivity of many outputs to one scalar (a learning rate, a physical constant), or a Jacobian–vector product (half of a Hessian–vector product in some second-order methods).

代码相关的练习(“动手改一改”)见 code/lecture04/README.md。


10. 延伸阅读 · Further reading

  • CS229 main notes (Ng & Ma), the deep-learning chapter (Ch. 7): from GLMs to networks, backprop (column-vector convention), vectorization. The main source for §5.
  • CS229 decision trees / ensemble methods notes and the Autumn 2018 lectures 10–12 (YouTube): Ng’s own treatment of trees, bagging, boosting and networks.
  • Hastie, Tibshirani & Friedman, The Elements of Statistical Learning: §9.2 (trees), §8.7 (bagging), Ch. 10 (boosting, including AdaBoost = exponential loss), Ch. 15 (random forests, including the ρσ2\rho\sigma^2 analysis and the effect of irrelevant features on small mm).
  • Breiman, “Bagging Predictors”, 1996; Breiman, “Random Forests”, 2001; Breiman, Friedman, Olshen & Stone, Classification and Regression Trees, 1984.
  • Freund & Schapire, “A Decision-Theoretic Generalization of On-Line Learning and an Application to Boosting”, 1997; Schapire, Freund, Bartlett & Lee, “Boosting the Margin”, 1998; Friedman, Hastie & Tibshirani, “Additive Logistic Regression: A Statistical View of Boosting”, 2000; Friedman, “Greedy Function Approximation: A Gradient Boosting Machine”, 2001; Chen & Guestrin, “XGBoost”, 2016.
  • Grinsztajn, Oyallon & Varoquaux, “Why do tree-based models still outperform deep learning on tabular data?”, NeurIPS 2022 Datasets & Benchmarks.
  • Rumelhart, Hinton & Williams, “Learning representations by back-propagating errors”, 1986; Griewank & Walther, Evaluating Derivatives (the standard reference on automatic differentiation).
  • Glorot & Bengio, 2010 (Xavier init); He et al., 2015 (He init); Kingma & Ba, “Adam”, 2015; Ioffe & Szegedy, “Batch Normalization”, 2015.
  • Cybenko, 1989 (universal approximation); Rahimi & Recht, “Random Features for Large-Scale Kernel Machines”, 2007; Jacot, Gabriel & Hongler, “Neural Tangent Kernel”, 2018.
  • Goodfellow, Bengio & Courville, Deep Learning, Ch. 6 (feedforward networks and backprop) and Ch. 8 (optimization).
  • 同目录的 CS224N / CS336:Transformer、LayerNorm、AdamW、学习率调度、训练成本的 6ND 估算、activation checkpointing。

11. 下一讲预告 · Next lecture

Lecture 5:无监督学习 —— k-means、EM、PCA 与 ICA(Unsupervised learning)。

到目前为止每个样本都有标签 yy。决策树用标签把输入空间切成一块一块;如果没有标签,还能切吗?k-means 做的就是这件事 —— 它在失真函数上做坐标下降,结果是一个 Voronoi 划分。Replace “each point belongs to exactly one cluster” with “each point belongs to each cluster with some probability” and you get Gaussian mixtures and the EM algorithm: a general method that alternately maximizes a lower bound on the likelihood (the ELBO), with k-means as its “hard” special case. Then dimensionality reduction: PCA finds the directions of maximum variance (equivalently, minimum reconstruction error), and ICA solves the cocktail-party problem — and we will see why Gaussian sources cannot be separated. Finally, §5 called a network’s hidden layers “learned features”; at the end of Lecture 5 we will see that learning such features from huge unlabeled datasets (self-supervised learning) is the starting point of today’s foundation models.

← 返回课程目录