@ha7ch/stanford-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,147 @@
1
+ # 机器学习(Machine Learning) · 第 3 课:亲手写一个梯度下降拟合器,看"学习"两个字到底在算什么
2
+
3
+ > source: https://cs229.stanford.edu/ | https://cs229.stanford.edu/syllabus-new.html | https://see.stanford.edu/Course/CS229 | 类型:实验课(原创实操) | 前置:cs229/02-classification-and-overfitting
4
+ > 教法遵循 references/pedagogy.md(写成代码样式):像课本一样把概念讲透、给足,不盘问、不当门槛。
5
+
6
+ ## 一句话本质(开场钩子)
7
+ 机器学习里所谓的"学习",拆开看根本没有魔法:就是一个函数举着一把叫"梯度"的尺子,反复量出"我现在错得有多离谱、该往哪边挪",然后挪一点点、再量一次——今天你不调库,亲手把这把尺子做出来。
8
+
9
+ ## 为什么要在乎(钩子展开)
10
+ "模型学到了规律"这句话你大概听过一百遍,但它其实是个很容易让人误解的说法——好像模型真的"理解"了什么。上一节我们聊过拟合,看到的是模型在训练集和验证集上表现的分化;这节课我们要往下钻一层:那个让模型"变强"的动作,具体到计算机里,到底是哪几行代码在跑?如果你从没亲手写过,"学习"这个词对你来说就永远是个黑箱按钮——调个 `model.fit()`,等它转完,出来一堆权重,你不知道那几秒钟里到底发生了什么。这节课我们不调 `sklearn`、不调任何机器学习库,从一张白纸开始,用不到 30 行 `numpy` 代码,亲手把"学习"这个过程跑一遍、看一遍。跑完你会得到一个很具体的认知:所谓训练,就是一个循环——算一次错得多离谱,算一次该往哪边挪,挪一点点,再来一遍。这就是这门课接下来讲的逻辑回归、神经网络、几乎所有模型训练的共同底盘。
11
+
12
+ ## 核心概念(逐个讲透,内容给足)
13
+
14
+ ### 先说清楚这节课的设计思路
15
+ 我们选一个全场最简单的任务:线性回归——用一条直线去拟合一堆点。选它不是因为它多重要,而是因为它**足够简单到你能亲眼验证对错**:我们自己造数据的时候,会提前"藏"起一组真实参数(比如"每公里 2.5 元、起步价 8 元"),训练结束后直接拿这组真实参数和模型学出来的参数对答案。这种"出题人自己知道标准答案"的设计,在真实数据集上是做不到的——真实世界里没人告诉你房价和面积之间"真正"的系数是多少。用一个我们自己造、自己知道答案的小任务,才能让你清清楚楚地看到:模型是不是真的从数据里"学"对了。
16
+
17
+ 也正因为这样,这节课刻意不碰任何 `.fit()` 这样的一行式调用。梯度下降的价值不在于这个名字本身,而在于它是一个**极其通用的优化套路**——线性回归只是最好验证的练习场,之后你会看到逻辑回归、神经网络用的是同一套循环,只是模型和损失函数换了。
18
+
19
+ ### 步骤 1:造一份自己知道"标准答案"的数据集
20
+ **要做什么**:假装我们在收集打车数据——每一趟车记录"里程(公里)"和"车费(元)"。写一个函数,用一个我们自己设定好的真实关系(每公里 2.5 元、起步价 8 元)加上一点随机噪声(模拟堵车、绕路这些现实里的波动),生成 40 条模拟打车记录。
21
+
22
+ ```python
23
+ import numpy as np
24
+ rng = np.random.default_rng(7)
25
+
26
+ # 真实世界里没人会告诉你这两个数字,但因为数据是我们自己造的,
27
+ # 我们提前"埋"下真实参数,训练完之后拿来对答案。
28
+ true_w, true_b = 2.5, 8.0 # 每公里 2.5 元,起步价 8 元
29
+
30
+ def make_taxi_data(n):
31
+ distance = rng.uniform(1, 20, size=n) # 1~20 公里的行程
32
+ noise = rng.normal(0, 1.5, size=n) # 堵车、绕路带来的随机波动
33
+ fare = true_w * distance + true_b + noise
34
+ return distance, fare
35
+
36
+ X, y = make_taxi_data(40)
37
+ ```
38
+
39
+ **为什么这样做**:真实数据集从来不会告诉你"生成它的公式"是什么,你只能靠模型去猜。但作为第一次亲手训练,你需要一个能验证对错的靶子——所以我们反过来操作:自己定好真实公式,生成数据,然后"忘掉"这个公式,只把 `X, y` 交给模型去猜,最后再拿模型猜出来的参数和 `true_w, true_b` 对比。
40
+
41
+ **做完应该观察到什么**:打印几对 `(X[0], y[0])`,你会看到类似"距离 12.3 公里、车费 39.8 元"这样的记录——大致符合"12.3×2.5+8≈38.75"再加一点噪声。这一步只是让你确认:数据长得像真实世界里会出现的样子,而不是一堆没有规律的随机数。
42
+
43
+ ### 步骤 2:从零写模型和损失函数——不调库,自己定义"错得多离谱"
44
+ **要做什么**:写一个只有两个参数 `w`(斜率/单价)和 `b`(截距/起步价)的线性模型,用它对每条数据做预测;再写一个损失函数,衡量"模型预测的车费"和"真实车费"差多远,同时算出这两个参数各自该往哪个方向调整(也就是梯度)。
45
+
46
+ ```python
47
+ def predict(w, b, X):
48
+ return w * X + b
49
+
50
+ def mse_loss(w, b, X, y):
51
+ y_hat = predict(w, b, X)
52
+ error = y_hat - y
53
+ loss = np.mean(error ** 2) # 均方误差:算"错得多离谱"
54
+ dw = np.mean(2 * error * X) # 损失对 w 的梯度
55
+ db = np.mean(2 * error) # 损失对 b 的梯度
56
+ return loss, dw, db
57
+ ```
58
+
59
+ 这里唯一值得写下来的公式是损失函数本身:
60
+
61
+ ```
62
+ loss(w, b) = 平均( (w·距离 + b − 真实车费)² )
63
+ ```
64
+
65
+ **为什么这样做**:均方误差(MSE)做的事很直白——预测和真实值差得越远,误差平方之后被放大得越厉害,逼着模型格外在意那些错得离谱的点。而 `dw`、`db` 这两行是这节课真正的核心:它们告诉你,如果你把 `w` 或 `b` 往正方向挪一点点,损失会变大还是变小。梯度下降的全部智慧,就浓缩在"往梯度的反方向走"这一句话里——因为梯度指向损失变大最快的方向,你当然要往它的反方向走,损失才会变小。
66
+
67
+ **做完应该观察到什么**:随便代入几个 `w, b`(比如 `w=0, b=0`),你会看到 `loss` 是一个很大的正数——因为预测全是 0,和真实车费差得很远;`dw` 和 `db` 也会是明显偏离 0 的数字,指出参数该往哪边挪。这一步本身还看不出"学习",它只是把"错得多离谱"和"该往哪挪"这两件事变成了能算出数字的代码。
68
+
69
+ ### 步骤 3:写梯度下降循环——亲眼看两个随机数字被数据"拉"向真相
70
+ **要做什么**:把 `w` 和 `b` 都初始化成 0(也就是"模型对这个世界一无所知,瞎猜"),然后反复执行同一个循环:算一次损失和梯度,把参数往梯度反方向挪一小步,重复 500 次,每隔 50 次打印一下当前的损失和参数。
71
+
72
+ ```python
73
+ w, b = 0.0, 0.0 # 从"什么都不知道"开始
74
+ lr = 0.01 # 学习率:每一步迈多大
75
+ for it in range(500):
76
+ loss, dw, db = mse_loss(w, b, X, y)
77
+ w -= lr * dw
78
+ b -= lr * db
79
+ if it % 50 == 0:
80
+ print(it, "loss=", round(loss, 3), "w=", round(w, 3), "b=", round(b, 3))
81
+
82
+ print("学出来的参数:", round(w, 3), round(b, 3))
83
+ print("真实参数: ", true_w, true_b)
84
+ ```
85
+
86
+ **为什么这样做**:这个循环——算损失、算梯度、挪参数、再来一遍——就是"训练"这个动作在计算机里的真身。没有更多了。它不理解"打车"是什么意思,也不知道"里程"和"车费"是什么概念,它只是在不断根据数字上的误差,机械地调整另外两个数字。
87
+
88
+ **做完应该观察到什么**:打印出来的 `loss` 会随着迭代逐渐变小,`w` 会从 0 一路爬向接近 2.5,`b` 会从 0 一路爬向接近 8.0——也就是我们在步骤 1 里悄悄藏起来的那两个真实参数。**这一刻就是这节课最关键的体感**:两个原本被瞎猜成 0 的数字,只靠反复看数据、算误差、挪一点点,自己"走"到了正确答案附近,没有人把公式告诉过模型。这就是"学习"两个字在计算机层面最朴素、最真实的样子。
89
+
90
+ ### 步骤 4:调学习率——体会"学习"是有节奏感的,不是越用力越好
91
+ **要做什么**:把上面的循环包成一个函数,分别用三个不同的学习率跑一遍(比如 `lr=0.1`、`lr=0.01`、`lr=0.0001`),对比它们各自的收敛过程。
92
+
93
+ ```python
94
+ def train(lr, iters=500):
95
+ w, b = 0.0, 0.0
96
+ for it in range(iters):
97
+ loss, dw, db = mse_loss(w, b, X, y)
98
+ w -= lr * dw
99
+ b -= lr * db
100
+ return w, b, loss
101
+
102
+ for lr in [0.1, 0.01, 0.0001]:
103
+ w, b, loss = train(lr)
104
+ print("lr=", lr, "-> final loss=", round(loss, 3), " w=", round(w, 3), " b=", round(b, 3))
105
+ ```
106
+
107
+ **为什么这样做**:`lr`(learning rate,学习率)决定的是"每一步迈多大"。这个数字选不好,前面步骤 3 里那种优雅的收敛就不会发生——这一步是想让你亲眼见识两种失败模式,而不是只知道"学习率是个超参数"这句空话。
108
+
109
+ **做完应该观察到什么**:`lr=0.1` 这种偏大的学习率,很可能让 `loss` 不降反升甚至变成一个大得离谱甚至 `nan` 的数字——参数在最优点附近来回乱跳,一步迈太远,反而越走越偏,这叫**发散**。`lr=0.0001` 这种偏小的学习率,`loss` 会降,但降得极其缓慢,跑完 500 轮 `w, b` 可能还远远没有靠近真实参数——这叫**收敛太慢**,不是训练错了,只是给它的时间不够。中间那个 `lr=0.01`,才是这份数据上那个"稳步、较快地" 把 loss 压下去的甜点区间。学习率没有一个万能的正确数值,它需要你根据"训练过程中的表现"去试出来——这也是为什么后面课程会专门讲怎么系统地调这些超参数。
110
+
111
+ ### 步骤 5(进阶,选做):和数学直接解出来的答案对个账
112
+ **要做什么**:线性回归这个问题恰好有一个可以直接用线性代数算出来的精确解(不需要迭代),叫正规方程(normal equation)。写几行代码直接解出来,和步骤 3 里梯度下降跑 500 轮"迭代逼近"出来的结果做个对比。
113
+
114
+ ```python
115
+ def normal_equation(X, y):
116
+ X_b = np.column_stack([X, np.ones_like(X)]) # 拼上一列 1,对应 b
117
+ w_b = np.linalg.solve(X_b.T @ X_b, X_b.T @ y)
118
+ return w_b # [w, b]
119
+
120
+ print("解析解:", normal_equation(X, y))
121
+ ```
122
+
123
+ **为什么这样做**:这一步是想让你亲眼确认——梯度下降不是"另一种投机取巧的答案",它一步步逼近的,正是数学上能精确算出来的那同一个最优解。线性回归凑巧简单到可以直接解方程,但等这门课往后讲到逻辑回归、神经网络,损失函数会变得复杂到**根本没有解析解**——那时候,梯度下降这套"迭代着一步步逼近"的笨办法,就是我们手里唯一能用的武器。今天你先在一个"能对答案"的场景里,亲眼确认这套笨办法是可信的。
124
+
125
+ **做完应该观察到什么**:`normal_equation` 算出来的 `[w, b]` 应该和步骤 3 里梯度下降跑到后期收敛出的 `w, b` 非常接近(小数点后一两位的误差都算正常,因为数据里有噪声、迭代次数也有限)。两条完全不同的路——一条是硬解方程,一条是反复试错——最后走到了同一个地方,这本身就是对"梯度下降到底靠不靠谱"最有说服力的验证。
126
+
127
+ ## 带学生读 source
128
+ CS229 官方课程大纲 https://cs229.stanford.edu/syllabus-new.html 把"监督学习设定、线性回归"列为整门课最早讲的核心主题之一——今天你亲手写的这套流程,就是这个主题在代码层面的落地。如果想看这套内容最初是怎么被公开讲授的,Stanford Engineering Everywhere 上还留着 Andrew Ng 当年主讲这门课的完整原始录像 https://see.stanford.edu/Course/CS229,可以找到线性回归和梯度下降那几讲对着看——但记住,看视频是补理论框架用的,真正的体感要靠你今天亲手跑出那两个从 0 爬向真实参数的数字才能长在身上。
129
+
130
+ ## 金句(引用时标出处)
131
+ 本课暂无可靠可引用的原话金句——不编造。
132
+
133
+ ## 讲完这节,你应该能答上(可选巩固 · 别逐题盘问、别当门槛)
134
+ 1. **Q:用大白话说,梯度下降在训练的每一步到底在算什么、往哪个方向挪参数?**
135
+ - 好答案信号:能说出每一步先用当前参数算一次预测和真实值的误差,再算出这个误差对每个参数的梯度(也就是"往哪边挪损失会变大"),然后往梯度的**反方向**挪一小步——不是瞎猜、不是随机试探,是每一步都有方向依据。
136
+ 2. **Q:学习率调得太大和太小,分别会出现什么现象?**
137
+ - 好答案信号:太大——loss 可能不降反升、甚至发散成很离谱的数字,因为每一步迈得太远,越过了最优点还继续往外冲;太小——loss 确实在降,但降得极慢,给同样的迭代次数根本走不到位。
138
+ 3. **Q:线性回归这种本来就有解析解(能直接解方程)的问题,为什么还要大费周章讲梯度下降?**
139
+ - 好答案信号:能提到线性回归只是一个"方便验证对错"的练习场,很多更复杂的模型(逻辑回归、神经网络)的损失函数根本没有解析解可解,梯度下降是那时候唯一通用的优化武器;今天在一个能对答案的场景里验证过它靠谱,之后遇到没有标准答案可对的场景才敢信它。
140
+
141
+ ## 常见误解(听到就纠正)
142
+ - ❌ "机器学习的'学习'意味着模型理解了数据背后的含义" → 今天写的这十几行代码就是拆穿这句话最好的证据:模型不知道"里程"和"车费"是什么,它只是在反复执行"算误差、算梯度、挪参数"这个纯数字循环,没有任何理解发生,"学习"只是这个机械循环跑多了之后的结果。
143
+ - ❌ "学习率当然是越小越安全,大不了多跑几轮" → 太小的学习率不叫安全,叫训练几乎停滞;合适的学习率是能让 loss 稳定、较快下降的那个区间,选得过于保守往往意味着你要多花几十倍的计算时间才能到达同一个终点,这在真实项目里就是实实在在的成本。
144
+ - ❌ "只要让梯度下降跑得足够久,参数一定会收敛到全局最优" → 对线性回归这种损失函数是一个规整"碗形"(凸函数)的问题确实成立,跑得足够久几乎总能到底部;但很多更复杂的模型损失函数坑坑洼洼,梯度下降可能陷进一个局部低洼就走不出来了——这也是这门课后面讲到优化和神经网络训练时会重新捡起来的难题。
145
+
146
+ ## 收尾 & 排下一课
147
+ 这节课你从一张白纸开始,亲手写出了"学习"这个词在计算机里的真身:造一份自己知道答案的数据、定义模型和损失、写一个梯度下降循环、调学习率感受收敛的节奏、最后拿解析解对了账。这套"算误差—算梯度—挪参数"的循环,接下来整门课都会反复出现,只是模型和损失函数会越换越复杂。下一课默认按课程大纲往前走,进入逻辑回归、Newton's method、感知机这些分类相关的方法(对应官方大纲里"Logistic regression, Newton's method, perceptron, generalized linear models"这一块)——你会发现分类问题用的还是同一套梯度下降底盘,只是换了个损失函数。如果你今天对"调学习率""对解析解"这类实操格外来劲、想多玩一会儿再往前走,也完全可以先在这节课的代码上多试几组数据分布、多跑几种学习率,把这套手感练得更扎实一点,再进入下一课。
@@ -0,0 +1,72 @@
1
+ # 机器学习(Machine Learning) · 第 4 课:神经网络为什么现在才"火"——从 CS229 到深度学习的桥
2
+
3
+ > source: [CS229 官方主页](https://cs229.stanford.edu/) | [CS229 Syllabus 与逐讲课程表](https://cs229.stanford.edu/syllabus-new.html) | [Stanford Engineering Everywhere:Andrew Ng 讲授的原版 CS229 公开课](https://see.stanford.edu/Course/CS229) | 类型:延伸阅读 | 前置:`cs229/03-lab-gradient-descent-from-scratch`
4
+ > 教法遵循 `references/pedagogy.md`:像课本一样把概念讲透、给足,不盘问、不当门槛。
5
+
6
+ ## 一句话本质(开场钩子)
7
+ **神经网络不是最近才被发明的——它在 CS229 的课程表里,一直和线性回归、逻辑回归、SVM 排在同一张表格里,是"标准监督学习工具箱"里普通的一员;真正让它在过去十几年"火"起来的,从来不是数学变了,而是数据、算力、和工程架构三样东西追上来了。**
8
+
9
+ ## 为什么要在乎(钩子展开)
10
+ 上一课你亲手写了梯度下降:定义一个 loss,算它对参数的导数,沿着负梯度方向一步步挪动参数,直到收敛。你当时训练的可能是一个线性模型,几行代码、几十个参数、一眨眼就能跑完。你可能会问:ChatGPT 背后那种几千亿参数的神经网络,是不是靠一套完全不同的、更"高级"的数学?
11
+
12
+ 答案是:不是。它用的还是你刚写过的那同一套东西——loss、gradient、update rule。只是链子拉长了(很多层堆叠)、规模变大了(参数从几十个变成几千亿个)、还换了几样更适合并行计算的架构零件。CS229 的课程表里,"Neural networks: basics and training (backpropagation)" 就紧跟在逻辑回归、广义线性模型(GLM)之后,前面还有 SVM、高斯判别分析——这个排位本身就在告诉你一件事:神经网络在这门课的视角里,从来就不是一个孤立的黑科技,而是这条监督学习谱系上自然长出来的下一格。这节课就是要把这条线接上:CS229 教的"地基",是怎么一步步长成今天铺天盖地的深度学习的。
13
+
14
+ ## 核心概念(逐个讲透,内容给足)
15
+
16
+ ### 1. 反向传播不是新东西——它是 CS229 里一个标准小节
17
+ **是什么**:backpropagation(反向传播)是训练神经网络时用来算梯度的算法,本质是链式法则(chain rule)的系统性应用——网络有很多层,你要算 loss 对第一层参数的导数,就得把梯度一层一层从输出端"传"回输入端,每传一层就乘上那一层的局部导数。它作为一套可实用的训练算法,在学术界已经流传了几十年,早就是教科书级别的标准内容。
18
+
19
+ **为什么重要**:很多人对"深度学习革命"的直觉误解是"我们发现了训练神经网络的新数学"。真相恰好相反——反向传播这套算法几十年没有本质变化,CS229 至今仍然把它当作和逻辑回归的梯度下降、SVM 的对偶问题并列的一个标准知识点来教,编号排在 "Logistic regression, Newton's method, perceptron, generalized linear models" 之后。这说明学界一直清楚:训练一个神经网络在数学原理上,和训练一个逻辑回归模型没有本质区别,都是"定义一个可导的 loss,用梯度往下走"。
20
+
21
+ **怎么用/落到哪**:你上一课手写的梯度下降循环——forward 算一遍预测、算 loss、backward 算梯度、update 参数——放到一个五层、五十层、还是五百层的网络上,循环体的骨架完全一样,唯一的区别是 backward 那一步要多算好几层链式法则。今天所有深度学习框架(PyTorch、TensorFlow)自动帮你做的"autograd",本质就是把这套链式法则自动化、批量化了。
22
+
23
+ **例子**:你写的线性回归梯度下降大概是这样一个循环:"预测 → 算 loss → 算梯度 → 参数减去 学习率×梯度 → 重复"。一个三层神经网络的训练循环,代码结构和这个几乎一模一样,只是"算梯度"那一步从一行导数公式,变成了从最后一层往第一层链式传递三次导数——道理没变,步骤变多了。
24
+
25
+ ### 2. 从线性回归到神经网络:一条连续的谱系,不是断层
26
+ **是什么**:如果你把一个 logistic regression(逻辑回归)模型拆开看,它其实就是"输入 → 一层线性加权求和 → 一个非线性函数(sigmoid)→ 输出"。而一个神经网络,就是把这个结构**重复堆叠**:输入先经过一层"线性加权 + 非线性函数",output 不直接当结果,而是喂给下一层再做一次同样的事,如此重复很多层,最后一层才输出结果。
27
+
28
+ **为什么重要**:这条谱系关系解释了为什么 CS229 会把 "Gaussian discriminant analysis, Naive Bayes"、"generalized linear models(指数族)"、"neural networks" 排在相邻的位置——它们都是同一个大问题(怎么用一个可训练的函数去拟合输入到输出的映射)的不同解法,复杂度依次递增。逻辑回归是"零隐藏层"的神经网络;给它加一层带非线性激活函数的隐藏层,它就变成了最简单的多层感知机(MLP);再加更多层、换更适合处理图像/文本结构的层类型(卷积层、注意力层),就变成了你熟悉的 CNN、Transformer。理解了这条连续谱系,你就不会把"深度学习"当成一个和"机器学习基础"割裂的全新学科,而会看到它是同一套原理往深、往宽长出来的自然结果。
29
+
30
+ **怎么用/落到哪**:这也是为什么 CS229 的课程表要先讲透 GLM、指数族这些"浅"模型——它们不是"过时的老古董",而是深度学习每一层内部到底在做什么的最小构件。你理解了逻辑回归里 sigmoid 函数在干什么,就理解了神经网络任意一层里非线性激活函数在干什么,只是重复了很多次、拼在了一起。
31
+
32
+ **例子**:一个手写数字识别的神经网络,第一层可能在学"识别边缘和简单笔画",这一步单独拎出来看,就是一堆并联的逻辑回归在各自判断"这块像素区域像不像一条边";第二层把第一层的输出当作新的输入特征,学"笔画怎么拼成一个圈或一个勾";最后一层再把这些拼好的模式当特征,做一次总的分类判断——本质上是好几层逻辑回归级联在一起,层层把原始像素抽象成越来越高级的概念。
33
+
34
+ ### 3. "现在"才火的真正原因:数据、算力、架构,三样都变了
35
+ **是什么**:如果反向传播的数学几十年没变,神经网络的雏形(多层感知机)在上世纪就已经存在,那为什么深度学习是最近十几年才爆发的?真正变化的是三样和"数学原理"无关的东西:一是数据规模——互联网让海量标注数据(图片、文本、语音)第一次变得触手可及;二是算力——GPU 最初是为游戏图形渲染设计的并行硬件,恰好神经网络里最耗时的矩阵乘法天然适合并行计算,二者一拍即合;三是架构工程——像卷积层(专治图像的局部/平移不变结构)、后来的注意力机制(专治长序列依赖)这类针对具体数据结构设计的层类型,让"更深"的网络第一次能被有效训练出来,而不是层数一多梯度就消失或炸掉。
36
+
37
+ **为什么重要**:这个视角能纠正一个很常见的错觉——好像深度学习是靠某个天才灵光一现的数学突破才火起来的。实际情况更像是"三块积木同时到位":数学(反向传播)等了几十年没变,是数据和硬件条件先追上来了,接着架构工程师才想明白怎么设计出能吃下这些数据、跑满这些算力的网络结构。这也是为什么你能在同一门 CS229 课程表里,同时看到"Neural networks: basics and training"和后面紧跟的"Bias-variance tradeoff, regularization, model/feature selection"——课程始终把神经网络当作要遵守偏差-方差权衡这套基本规律的普通模型来讲,而不是当作一套跳出规律之外的魔法。
38
+
39
+ **怎么用/落到哪**:当你以后听到"某某新模型突破了 XX 记录",可以习惯性地拆开问一句:这次突破主要来自更多数据、更大算力,还是真的换了新架构?大部分时候答案是前两者的组合,而底层训练用的仍然是你已经学过的梯度下降加反向传播。
40
+
41
+ **例子**:一个放在 1990 年代的神经网络研究者,手上有反向传播的完整数学、有 MNIST 这类小数据集,但既没有互联网规模的数据,也没有 GPU 那种能并行算矩阵乘法的硬件——他能训练一个几层的小网络做手写数字识别,但没有任何现实条件让他训练出一个千亿参数的模型。缺的从来不是公式,是数据和算力这两块拼图。
42
+
43
+ ### 4. CS229 是地基,CS231n / CS224n 是深度学习在具体领域的展开
44
+ **是什么**:CS229 的课程表把神经网络基础训练放进整个监督学习框架里,只占一讲的篇幅;紧接着还要讲树模型、无监督学习(K-means、EM、PCA)、强化学习(MDP、Q-learning)等一整套不局限于深度学习的方法论。斯坦福没有把"深度学习"这整个领域塞进 CS229 里越讲越细,而是把它拆成了专门的课:处理图像的卷积网络细节,拆到了 CS231n;处理语言的 Transformer、预训练、对齐这一整条线,拆到了 CS224n。
45
+
46
+ **为什么重要**:这个课程体系设计本身就是一种教学判断——先用 CS229 把"任何一个机器学习模型,不管多复杂,本质都在解一个'定义 loss、用梯度找最优参数'的优化问题"这个地基打牢,再让学生带着这套通用框架,分头扎进某个具体领域,学那个领域里让深度学习真正能打的架构细节(CNN 的卷积核、Transformer 的注意力)。如果没有 CS229 这一层地基,直接跳进 CS231n 或 CS224n,会知其然不知其所以然——知道"这样写代码能训出模型",但说不清为什么这套训练循环本身是合理的。
47
+
48
+ **怎么用/落到哪**:如果你在这个平台上同时能看到 cs229、cs231n、cs224n 这几门课的讲义,现在就该明白它们不是三门互相独立的课,而是一条主干(CS229)加两条分支(CS231n 管视觉、CS224n 管语言)。学习顺序上,CS229 打的地基(梯度下降、偏差-方差、正则化、神经网络基础)会在后面两门课里被反复直接用到,不会重新从零讲一遍。
49
+
50
+ **例子**:CS231n 会告诉你卷积层为什么比全连接层更适合处理图像(参数共享、局部感受野),但它不会重新教你梯度下降是什么、loss function 是什么——因为那是 CS229 已经打好的地基。CS224n 同理,它会花大量篇幅讲 Transformer 的 self-attention 具体怎么算,但训练这个 Transformer 用的仍然是反向传播加梯度下降,这部分默认你从 CS229 就已经懂了。
51
+
52
+ ## 带学生读 source
53
+ 去 [CS229 官方主页](https://cs229.stanford.edu/) 看一眼课程简介和当前的开课信息,再点进 [Syllabus 页面](https://cs229.stanford.edu/syllabus-new.html) 把整整 20 讲的主题列表从头扫一遍——你会发现 "Neural networks: basics and training (backpropagation)" 前后紧挨着的,全是逻辑回归、SVM、树模型这些"经典"方法,这份排位顺序本身就是这节课最好的证据。如果想看这门课最早、最有历史感的版本,[Stanford Engineering Everywhere 上还留着 Andrew Ng 亲自讲授的原版 CS229 公开课](https://see.stanford.edu/Course/CS229)(20 讲视频全公开)——那是很多人认识"机器学习"这个学科的起点,也正是深度学习浪潮爆发之前,这门课的原始样貌。
54
+
55
+ ## 金句(引用时标出处)
56
+ 本课暂无可靠可引用的原话金句——不编造。
57
+
58
+ ## 讲完这节,你应该能答上(可选巩固 · 别逐题盘问、别当门槛)
59
+ 1. **Q:反向传播是深度学习时代才发明的新算法吗?**
60
+ - 好答案信号:能说清反向传播的核心是链式法则,早已是几十年历史的标准算法,CS229 一直把它当作和逻辑回归梯度下降并列的常规知识点来教,不是深度学习浪潮才冒出来的新数学。
61
+ 2. **Q:逻辑回归和神经网络是什么关系?**
62
+ - 好答案信号:能说出逻辑回归相当于"零隐藏层"的神经网络(一层线性加权 + 一个非线性函数),神经网络是把这个结构重复堆叠很多层,两者是同一条谱系上的不同复杂度,不是两个不相关的东西。
63
+ 3. **Q:为什么神经网络"最近十几年"才火,而不是它刚被提出的时候?**
64
+ - 好答案信号:能提到三个真正变化的因素——海量数据(互联网时代才有)、并行算力(GPU 恰好适合矩阵运算)、以及针对具体数据结构设计的架构(卷积层、注意力机制),而不是笼统地说"技术进步了"或以为数学本身变了。
65
+
66
+ ## 常见误解(听到就纠正)
67
+ - ❌"神经网络是深度学习时代才发明的全新技术" → 多层感知机和反向传播这套数学几十年前就已经存在,CS229 至今仍把它当作监督学习工具箱里的标准一员来教,紧跟在逻辑回归、GLM 之后教授,说明它一直被视为这条谱系上自然的下一步,不是凭空冒出来的黑科技。
68
+ - ❌"深度学习是靠某个天才想出了更聪明的数学公式才突破的" → 真正的转折点主要来自数据规模(互联网时代的海量数据)和算力(GPU 并行计算恰好匹配神经网络的矩阵运算),架构工程(卷积层、注意力机制)是让更深的网络能被有效训练的关键拼图,但训练用的底层算法(梯度下降 + 反向传播)几十年没有本质变化。
69
+ - ❌"CS229 是'老派机器学习',跟今天的深度学习没什么关系,得跳过去直接学 CS231n/CS224n 才实用" → CS229 里的梯度下降、loss function、偏差-方差权衡、正则化这些框架,是 CS231n、CS224n 里所有深度学习内容默认你已经会的地基,跳过它直接学后两门课,容易变成"会调包但说不清为什么这样训练是合理的"。
70
+
71
+ ## 收尾 & 排下一课
72
+ 这节课把"神经网络为什么现在才火"这个常见疑问拆开讲透了:数学(反向传播)没变,变的是数据、算力、架构这三块拼图,而 CS229 的课程表本身——把神经网络紧挨着逻辑回归、GLM、SVM 排在一起——就是这条谱系连续性最直接的证据。到这里,CS229 这条主线的地基部分基本讲完了,默认下一步可以往两个方向走:一是继续往 CS229 后半段深挖(偏差-方差权衡、正则化、树模型集成,或者跳到无监督/强化学习那几块),二是如果你已经觉得这块地基站得住了,可以直接转去 CS231n 或 CS224n,看这套框架具体是怎么在视觉和语言这两个领域里长出 CNN 和 Transformer 的。如果你更想先把手感练扎实,也可以回头把 03 课的梯度下降从零实现再多跑几组超参数,把地基踩得更实再往上走。
@@ -0,0 +1,91 @@
1
+ # 卷积神经网络与视觉识别(CNN for Visual Recognition) · 第 1 课:计算机怎么"看"一张图:像素、卷积,以及为什么全连接网络不够用
2
+
3
+ > source: `https://cs231n.stanford.edu/`|`https://cs231n.github.io/`|`https://cs231n.stanford.edu/schedule.html` | 类型:讲授(概念) | 前置:无
4
+ > 教法遵循 `references/pedagogy.md`:像课本一样把概念讲透、给足,不盘问、不当门槛。
5
+
6
+ ## 一句话本质(开场钩子)
7
+ **一张照片对你来说是"一只猫",对计算机来说,从头到尾都只是一个塞满数字的方框——它从没"看见"过猫,它只是在做数字运算;而卷积(convolution),就是让这堆数字运算第一次真正"认得出"空间结构的那个关键设计。**
8
+
9
+ ## 为什么要在乎(钩子展开)
10
+ 你手机相册自动把猫的照片分到一起、自动驾驶汽车认出前面是行人还是路灯、医院的片子被 AI 提前标出可疑病灶——这些背后都是同一件事:让机器从一堆像素里"看懂"东西。斯坦福官网上介绍这门课时说得很直接:
11
+
12
+ > "Computer Vision has become ubiquitous in our society, with applications in search, image understanding, apps, mapping, medicine, drones, and self-driving cars."(计算机视觉已经渗透进社会的方方面面——搜索、图像理解、各类应用、地图、医疗、无人机、自动驾驶。)—— [CS231n 官网](https://cs231n.stanford.edu/)
13
+
14
+ 这门课(CS231n,斯坦福"深度学习计算机视觉"课,前身叫"卷积神经网络与视觉识别",由 Fei-Fei Li、Justin Johnson 等人主讲)整整 10 周,从最基础的图像分类一路讲到 Transformer、扩散模型。但所有这一切的起点,都是今天这一课要讲透的问题:**图片进了计算机之后到底是什么东西,以及为什么"随便搭一个神经网络"处理不好它,非得发明卷积不可。** 这不是选修的背景知识——后面每一节 CNN 架构、每一个视觉模型,都是在这个起点上盖房子。
15
+
16
+ ## 核心概念(逐个讲透,内容给足)
17
+
18
+ ### 1. 图片在计算机眼里:一堆数字,仅此而已(语义鸿沟)
19
+
20
+ **是什么**:你眼里的一张猫的照片,在计算机里是一个三维的数字方块——宽 × 高 × 3(红、绿、蓝三个颜色通道)。比如一张常见的小图 32×32 像素,就是 32×32×3 = 3072 个数字,每个数字是 0~255 之间的一个亮度值。没有"猫耳朵""胡须"这种概念,只有一堆整数。
21
+
22
+ **为什么重要**:这就是计算机视觉里常说的"语义鸿沟"(semantic gap)——人类看图片看的是语义(这是猫、在草地上、朝左看),计算机拿到的却只是原始数值,而且这堆数值极其脆弱:换个光照、挪个角度、猫换个姿势、甚至只是拍摄时手抖了一下,底层的这 3072 个数字几乎全部会变,但"这是一只猫"这件事丝毫没变。整个计算机视觉要解决的核心矛盾,就是怎么从这种低层、易变的数值表示,稳定地推出高层、不变的语义结论。
23
+
24
+ **怎么用 / 落到哪**:任何一个视觉模型,第一步都是"吃进"这个数字方块,最后一步都是"吐出"一个语义结论(比如"猫"这个类别、或者一个框出猫所在位置的矩形)。中间这一大坨,就是这门课接下来 10 周要教的东西。
25
+
26
+ **具体例子**:经典的 CIFAR-10 数据集里,一张 32×32×3 的小图可能标签是"猫";同一只猫换个姿势拍一张,底层的 3072 个像素值可能有一大半都不一样了,但对人来说,"这是猫"这个判断没有变过。模型要学的,正是这种"数值大变、语义不变"的映射关系。
27
+
28
+ ### 2. 最直观但错的做法:把图片拉平塞进全连接网络
29
+
30
+ **是什么**:既然图片就是一串数字,最朴素的想法是:把这 3072 个数字拉成一条长向量,直接接一个全连接层(每个输出神经元都和输入的每一个数字连一条带权重的边),像处理表格数据那样处理它。
31
+
32
+ **为什么重要(这里是本课真正的矛盾焦点)**:这个做法在数学上完全可行——全连接网络理论上能拟合任意函数——但在图像上会撞上两个致命问题:
33
+
34
+ - **参数爆炸**。哪怕是一张不算大的 200×200 彩色图,展平后就是 200×200×3 = 120,000 个数。如果第一层隐藏层只放 1000 个神经元、做全连接,光这一层的权重数量就是 120,000 × 1000 = 1.2 亿个参数。图片稍微大一点、网络稍微深一点,参数量直接失控,训练又慢又容易过拟合。
35
+ - **丢掉了空间结构,没有平移不变性**。拉平之后,原本"相邻"的像素在向量里可能离得很远,网络完全不知道哪些数字在图上原本是挨着的。更麻烦的是:如果同一只猫在图片里往右挪了几个像素,拉平后的向量几乎是另外一串完全不同的数字——网络在左上角学到的"识别猫耳朵"的那套权重,对挪到右下角的猫耳朵完全不认识,得从头再学一遍。也就是说,全连接网络没有"平移不变性"(translation invariance):一个特征无论出现在图片的哪个位置,都得单独学一遍。
36
+
37
+ **怎么用 / 落到哪**:正是这两个问题,直接导致了卷积的发明——它不是锦上添花的优化,而是"图像这种数据不能不这么处理"的结构性必需品。
38
+
39
+ **具体例子**:想象你训练一个全连接网络去认猫耳朵,它在图片左上角看过足够多带猫耳朵的样本、学会了识别;但测试时来了一张猫耳朵长在图片正中间的照片,全连接网络里对应"正中间"那批像素的权重从没被这样训练过,识别效果会明显打折——这在人类看来很荒谬(耳朵挪个位置又不会变成别的东西),但对全连接网络来说,位置就是身份的一部分。
40
+
41
+ ### 3. 卷积怎么工作:局部感受野 + 权重共享
42
+
43
+ **是什么**:卷积层不再让每个输出神经元看整张图,而是让它只看图片里一小块局部区域(比如 3×3 或 5×5 的小窗口),这个小窗口叫**卷积核 / 滤波器(filter / kernel)**。这个滤波器带着一组权重,在整张图上从左到右、从上到下滑动,每滑到一个位置,就把窗口里的像素值和滤波器权重做一次点积(对应位置相乘再求和),得到一个数,所有位置滑一遍,就拼出一张新的二维图,叫**特征图(feature map)**。用最简单的伪代码写一遍这个过程:
44
+
45
+ ```
46
+ for 每个位置 (i, j) in 输出网格:
47
+ output[i, j] = sum(图像局部窗口(i, j) 中每个像素 × 对应权重) + 偏置
48
+ ```
49
+
50
+ 关键的两个设计点:
51
+ - **局部连接(local receptive field)**:每个输出只依赖一小块局部输入,而不是像全连接层那样依赖全图——这直接把参数量从"和整张图大小成正比"砍到"只和滤波器大小成正比"。一个 5×5 的滤波器不管图有多大,永远只有 25 个权重(加一个偏置)。
52
+ - **权重共享(weight sharing)**:这个滤波器在图片的每一个位置用的是**同一组权重**,而不是每个位置单独学一套。这解决了上面全连接网络的两个问题:参数量骤降(一个滤波器整张图共用,不随图片尺寸膨胀),同时天然带来平移不变性——一个学会识别"竖直边缘"的滤波器,不管这个边缘出现在图片左上角还是右下角,用的都是完全相同的权重,识别能力自动"复制"到每个位置。
53
+
54
+ **为什么重要**:这就是为什么这门课以前叫"卷积神经网络"——局部连接 + 权重共享这两条设计,是让神经网络第一次真正"适配"图像这种有空间结构的数据的关键一步,而不是把图像硬塞进一个为表格数据设计的全连接结构里。
55
+
56
+ **怎么用 / 落到哪**:卷积层是几乎所有经典视觉模型(AlexNet、VGG、ResNet,这些是这门课后面架构专题会讲到的内容)的基本构件;实际使用时还有 stride(滤波器每次滑动的步长)、padding(在图片边缘补零,控制输出尺寸)这些细节,属于"怎么算"上更具体的工程参数,直觉上先记住:滤波器越小、滑得越密,抓的细节越精细;步长越大,输出图越小、计算越快。
57
+
58
+ **具体例子**:一个训练好的 3×3 滤波器,权重大致长这样:中间一列是正数、两侧一列是负数,这种权重组合在做点积时,遇到"左边暗、右边亮"的竖直边缘会输出一个很大的正值,遇到均匀区域输出接近 0——这其实就是在学"竖直边缘探测器",而它不需要在图片的每个位置都重新学一遍,同一组权重扫过全图就够了。
59
+
60
+ ### 4. 一层不够,层层堆叠:从边缘到部件到物体的特征层级
61
+
62
+ **是什么**:单独一个卷积层能抓到的东西很有限——大多是边缘、颜色斑块这类非常局部、非常低级的图案。真正的威力在于**把很多卷积层堆叠起来**:前一层的输出(特征图)作为后一层的输入,一层一层往上叠。
63
+
64
+ **为什么重要**:随着层数加深,网络能"看到"的有效范围(感受野)会越来越大,能表达的图案也从简单变复杂——直觉上大致是:浅层学会识别边缘、颜色、简单纹理;中间层把这些低级图案组合成更复杂的局部结构,比如眼睛的轮廓、轮子的形状;深层再把这些局部结构组合成完整的物体概念,比如"猫脸""汽车"。这种从底层到高层逐渐抽象的特征层级,是深度学习在视觉任务上远胜传统手工设计特征方法的核心原因——网络自己学会了该抓什么样的图案,而不是工程师凭经验去设计。
65
+
66
+ **怎么用 / 落到哪**:这也是"深度"这个词在"深度学习"里的字面意思——层数越多、层级特征越丰富。CS231n 后续专门有一节课叫"Image Classification with CNNs (Convolution and Pooling)",会具体讲卷积层之间怎么配合池化(pooling,一种降采样、压缩特征图尺寸的操作)搭出一整套可训练的网络;再往后的"CNN Architectures"那节课会讲 AlexNet、VGG、ResNet 这些经典结构,本质上都是"怎么把卷积层堆得更深、更高效"的不同答案。
67
+
68
+ **具体例子**:一个训练好的图像分类网络,如果你把中间某一层的特征图可视化出来,会发现浅层的图确实长得像边缘检测的结果(一堆线条、轮廓),而越往深层的特征越抽象、越难用肉眼直接解读,但分类效果反而越好——这正是因为它已经不再是"像素级的图案",而是"语义级的概念"。
69
+
70
+ ## 带学生读 source
71
+ 建议直接去读 [CS231n 官网](https://cs231n.stanford.edu/) 的课程简介,感受一下这门课的整体调性和覆盖范围;如果想看这门课怎么系统组织"图像分类 → 卷积 → 经典架构"这条主线的笔记,可以翻一下配套笔记站 [cs231n.github.io](https://cs231n.github.io/),里面按 Module 0/1/2 把 kNN、线性分类器、反向传播、CNN、迁移学习这些主题拆解得很清楚,是这门课最常被引用的公开学习资料之一。
72
+
73
+ ## 金句(引用时标出处)
74
+ - "Computer Vision has become ubiquitous in our society, with applications in search, image understanding, apps, mapping, medicine, drones, and self-driving cars."(计算机视觉已经渗透进社会的方方面面——搜索、图像理解、各类应用、地图、医疗、无人机、自动驾驶。)—— [CS231n 官网](https://cs231n.stanford.edu/)
75
+ - "This course is a deep dive into the details of deep learning architectures with a focus on learning end-to-end models for these tasks, particularly image classification."(这门课深入探讨深度学习架构的细节,重点是为这些任务——尤其是图像分类——学习端到端的模型。)—— [CS231n 官网](https://cs231n.stanford.edu/)
76
+
77
+ ## 讲完这节,你应该能答上(可选巩固 · 别逐题盘问、别当门槛)
78
+ 1. **Q:为什么把图片拉平塞进全连接网络效果不好,哪怕理论上它能拟合任意函数?**
79
+ - 好答案信号:能同时说出两点——参数量会随图片尺寸/网络规模爆炸式增长,以及拉平之后丢失了空间局部性,导致网络没有平移不变性(同一个特征换个位置就得重新学一遍),而不是只笼统说"太慢了"。
80
+ 2. **Q:卷积核(filter)到底在干什么?为什么同一个滤波器要在整张图上滑动、用同一组权重?**
81
+ - 好答案信号:能讲清楚"局部感受野"(每次只看一小块窗口)和"权重共享"(全图共用同一组权重)这两个设计点,并且能说出这两点分别怎么解决了参数爆炸和平移不变性的问题。
82
+ 3. **Q:CNN 为什么要堆叠很多层卷积,而不是一层卷积就够?**
83
+ - 好答案信号:能讲出"浅层学边缘/纹理,深层学部件/物体"这种特征从低级到高级逐层抽象的直觉,而不是只说"层数多效果好"。
84
+
85
+ ## 常见误解(听到就纠正)
86
+ - ❌"卷积核是工程师手工设计好、写死的,跟 PS 里的边缘检测滤镜一个意思。" → 训练开始前,卷积核的权重其实是随机初始化的,是靠反向传播从训练数据里自己学出来的——网络自己摸索出"该用什么样的滤镜才能把猫和狗分开",不是人预先规定好的规则。这也是为什么后面会专门讲反向传播。
87
+ - ❌"权重共享把参数变少了,模型的能力也就跟着变弱了。" → 权重共享减少的只是"参数数量",不是"表达能力上限"。正因为不用给图片的每个位置单独学一套边缘检测器,同样的参数预算才能省下来堆更多层、学更丰富的特征层级——这恰恰是 CNN 在图像任务上反超全连接网络的原因,不是打折版本。
88
+ - ❌"卷积是 CNN 独有的、全新发明的数学运算。" → 卷积这个数学运算在信号处理和传统图像处理里已经用了几十年(比如经典的模糊、锐化、边缘检测滤镜都是手工设计的卷积核)。CNN 真正的创新不是"发明卷积",而是让机器自己去学习卷积核该长什么样,并且把很多层卷积堆叠起来,变成一个整体可训练的端到端系统。
89
+
90
+ ## 收尾 & 排下一课
91
+ 今天把"图片是什么、全连接为什么不够用、卷积怎么解决这两个问题"这条主线讲透了——这是后面所有 CNN 架构课的地基,记得进度打个勾。下一课默认按课程结构往下走:**图像分类的第一批具体算法——k-近邻(k-NN)和线性分类器(SVM / Softmax)**,也就是在真正上卷积网络之前,先看看"最简单能跑起来的分类器长什么样、损失函数怎么定义"。如果你更想先动手感受"训练一个网络是怎么跑起来的",也可以调整顺序先跳去讲反向传播和优化那节,回头再补线性分类器——跟着你的兴趣走就行。
@@ -0,0 +1,73 @@
1
+ # 卷积神经网络与视觉识别(CNN for Visual Recognition) · 第 2 课:卷积核、感受野与池化——一步步搭出一个会「看」的网络
2
+
3
+ > source: https://cs231n.stanford.edu/ | https://cs231n.github.io/ | https://cs231n.stanford.edu/schedule.html | 类型:讲授(概念) | 前置:`cs231n/01-how-computers-see-images`
4
+ > 教法遵循 `references/pedagogy.md`:像课本一样把概念讲透、给足,不盘问、不当门槛。
5
+
6
+ ## 一句话本质(开场钩子)
7
+ CNN 的核心洞察就一句话:**不用给每个像素单独学一套权重,而是用一小块可滑动、可复用的「模式探测器」(卷积核)扫过整张图,再靠堆叠层数让每个神经元逐步「看到」更大范围的原图(感受野),中间穿插池化把探测结果压缩成对位置不那么敏感的摘要**——这三件事拼起来,就是一个能识别图片的网络。
8
+
9
+ ## 为什么要在乎(钩子展开)
10
+ 上一课我们讲了图片在计算机眼里就是一个数字网格,一张 32×32×3 的小图拉直了就是 3072 个数。如果直接用一个全连接的线性分类器,相当于给这 3072 个数每一个都学一个独立权重——图一大(比如 200×200 的真实照片),参数量直接爆炸,而且更糟的是:**它把「相邻像素之间的空间关系」全丢了**,一个像素挪到别的位置,对分类器来说跟换了张完全不相关的图没区别。
11
+
12
+ 想象你在训练一个网络识别「猫的尖耳朵」。耳朵可能出现在图片左上角,也可能出现在右下角——但本质上你想让网络用**同一个**「尖耳朵探测器」去认它们,而不是为每个可能出现的位置单独学一遍「这里如果出现尖耳朵形状就是猫」。这就是卷积要解决的问题:**参数共享 + 保留空间结构**。这节课就是把「卷积核怎么滑」「网络看到的范围怎么一层层变大」「为什么要做池化」这三件事一次讲透——讲完你就有了拆解任何一张 CNN 架构图的底层直觉。
13
+
14
+ ## 核心概念(逐个讲透,内容给足)
15
+
16
+ ### 1. 卷积核(filter / kernel):一个可滑动的模式探测器
17
+ **是什么**:一个很小的权重矩阵——常见的是 3×3 或 5×5(如果输入是彩色图,还要乘上通道数,比如 5×5×3)。它在整张图上按固定步长(stride)滑动,每滑到一个位置,就把这个小窗口里的像素值和卷积核的权重逐一相乘再求和,输出一个数字。
18
+
19
+ **为什么重要**:这个小矩阵在图像的**所有位置上共用同一组权重**,术语叫 parameter sharing。这一件事同时解决了两个痛点:一是参数量大幅下降——不管原图多大,一个卷积核的权重数量是固定的(比如 3×3×3 只有 27 个数 + 1 个偏置);二是带来了平移不变性的雏形——同一个模式(比如「一条竖直边缘」)不管出现在图的哪个角落,都会被同一个卷积核以同样的方式识别出来,网络不需要为每个位置单独学一遍。
20
+
21
+ **怎么用/怎么算**:卷积核滑过整张输入图像后,每个滑动位置输出一个数字,这些数字按原来的空间排布拼成一张新的二维网格,叫**特征图**(feature map / activation map)。一个卷积层通常不会只放一个卷积核,而是并排放很多个(比如 32 个、64 个),每个核在训练中会学着去响应不同的模式——有的对边缘敏感,有的对某种颜色过渡敏感,有的对特定纹理敏感。这样一层卷积的输出就是一摞(比如 32 张)特征图叠在一起。
22
+
23
+ **一个具体例子**:设想一个训练好的 3×3 卷积核,权重大致是 `[[-1,-1,-1],[0,0,0],[1,1,1]]`——上面一排是负数,下面一排是正数,中间是零。这个核滑到「上暗下亮」的水平边缘位置时,负值乘暗像素、正值乘亮像素,加起来会得到一个很大的正数;滑到图像平坦区域(上下亮度差不多)时,输出接近 0。把它滑过整张图,输出的特征图上凡是有水平边缘的地方都会「亮」起来——这就是一个边缘探测器。CNN 从不需要人工去设计这样的核,它是靠反向传播、从大量带标签的训练图片里自动学出来的。
24
+
25
+ ### 2. 感受野(receptive field):这个神经元到底「看到」了原图多大一块
26
+ **是什么**:网络某一层里的一个神经元(或者说某张特征图上的一个点),它的数值最终是由原始输入图像上**哪一块区域**共同决定的,这块区域就是它的感受野。第一层卷积的神经元感受野很直观——一个 3×3 卷积核对应的感受野就是原图上 3×3 那一小块像素。
27
+
28
+ **为什么重要**:单靠一层卷积,感受野太小,只能捕捉局部的细小纹理和边缘,看不到「整只猫」这种大范围的模式。但把很多层卷积堆叠起来后,情况会变化:后面层的一个神经元,看到的是前一层输出特征图上的一小块(比如 3×3),而前一层那 3×3 里的每一个点,本身又各自对应着原图上的一小块——层层往回追溯、层层叠加,越往深处的神经元,感受野在原图上覆盖的范围就越大。这解释了 CNN 为什么要做「深」:不是单纯为了堆参数,而是让感受野随深度增长,从而让浅层捕捉边缘纹理这类局部特征,中间层组合出部件级模式(眼睛、轮子),深层组合出接近整体物体(脸、汽车)的表示。
29
+
30
+ **怎么用/落到哪**:这个直觉在你以后看任何 CNN 架构图时都用得上——看深度,就该联想到「感受野在往外扩」,而不只是「层数多=模型强」。
31
+
32
+ **一个具体例子**:两层 3×3 卷积堆叠时,第二层某个神经元的感受野不是 3×3,而是 5×5——因为它看的是第一层输出特征图上 3×3 那一块,而那 3×3 块里的每个点各自又各看了原图上的 3×3,边界互相叠加后一共覆盖了原图 5×5 的范围。堆到十层左右,感受野就能轻松覆盖几十甚至上百像素——这就是深层网络能「看得远、看得整体」的数学根源。
33
+
34
+ ### 3. 池化(pooling):把探测结果压缩,换来对位置的容忍度
35
+ **是什么**:在卷积层之间(或之后)插入一个下采样操作,最常见的是 max pooling——用一个小窗口(比如 2×2)在特征图上滑动,每个窗口只保留其中的最大值、丢掉其余的,输出一张空间尺寸更小的特征图(长宽通常直接减半)。
36
+
37
+ **为什么重要**:有两层作用。第一是**降维**:特征图变小了,后面层要处理的数据量和参数量都跟着下降,训练推理都更快,也一定程度上缓解过拟合。第二个作用更关键——**对小幅位置偏移的容忍度**:即便「猫耳朵」这个模式在图片里挪动了几个像素,只要它挪动后还落在同一个池化窗口内,max pooling 取到的最大值往往还是同一个响应,输出的高层特征不会因为物体轻微移动就整个变了样、导致误判。
38
+
39
+ **怎么用/落到哪**:一个典型 CNN 的基本模块是「卷积层 → 激活函数(ReLU)→(有时)池化层」,这个模块反复堆叠若干次;每经过一次池化,特征图的空间尺寸变小、但卷积核(通道)数量通常增多——也就是越往深处走,看的范围越大,但表示越抽象、空间上的精确位置信息越粗糙。到网络最后几层,特征图已经被压得很小,这时候展平(flatten)接上全连接层去做最终分类。
40
+
41
+ **一个具体例子**:假设某层卷积输出了一张 4×4 的特征图,用 2×2、步长 2 的 max pooling 处理,会把它切成 4 个不重叠的 2×2 小块,每块只留最大值,输出变成 2×2——尺寸直接减半,数据量降到四分之一,代价是「这个区域里具体哪个像素最亮」这种精确位置信息被丢弃了,换来的是「这个区域出现过一次强响应」这个更抽象、也更不容易被噪声干扰的信号。
42
+
43
+ ### 4. 把三者拼起来:一个能识别的网络长什么样
44
+ **是什么**:CNN 的经典结构就是反复堆叠「卷积 → 激活 →(有时)池化」这个模块——前面几层学习局部的边缘、颜色、纹理;中间层把局部模式组合成部件级别的表示;到最后几层,特征已经足够抽象、感受野也足够大(基本能覆盖整张输入图),这时把特征图展平,接一到两层全连接层,最后用 softmax 输出每个类别的概率——这其实和第一课讲的线性分类器是同一件事,只是这次喂给分类器的不再是原始像素,而是 CNN 自己学出来的高层特征。
45
+
46
+ **为什么重要**:这是从「感知」到「判断」的完整链路——卷积核负责发现局部模式并复用参数,感受野的逐层扩张负责把局部信息一步步组合成对整张图的理解,池化负责压缩信息、换取对位置扰动的鲁棒性,最后的全连接层 + softmax 负责把这些高层特征映射到具体类别上。CS231n 后面几讲(AlexNet / VGG / ResNet)讲的都是「怎么把这个基本模块堆得更深、更巧妙、训得动」,但骨架就是这一课讲的三件事。
47
+
48
+ **怎么用/落到哪**:以后看到任何一张 CNN 架构图,都可以用这套直觉去拆它:看到卷积层,想「局部模式 + 参数共享」;看到网络变深,想「感受野在变大、表示在变抽象」;看到池化,想「降维 + 对位置的容忍度」。
49
+
50
+ **一个具体例子**:一个简化的识别流程大致是——第一层卷积核对图像里的边缘、颜色边界产生响应;池化一次后,第二层卷积核在这些边缘特征上组合出更复杂的形状,比如尖耳朵的轮廓、条纹纹理;再池化、再卷积,更深的层可能对「两只尖耳朵 + 胡须 + 圆眼睛」这种部件组合产生响应;最后展平接全连接层,网络在「猫」这个类别上输出最高的概率。整个过程没有人告诉网络「耳朵长什么样」——全部是靠大量带标签的猫/非猫图片,通过反向传播自动学出每一层卷积核该长成什么样。
51
+
52
+ ## 带学生读 source
53
+ 想看这套「卷积 → 感受野 → 池化」的直觉在斯坦福课上具体怎么排课、CNN 这一块在第几讲展开,可以翻一下官方课程表 https://cs231n.stanford.edu/schedule.html ;想找更系统的文字讲义(哪怕只挑卷积层、池化层那几节读),去配套笔记站 https://cs231n.github.io/ 。
54
+
55
+ ## 金句(引用时标出处)
56
+ - "This course is a deep dive into the details of deep learning architectures with a focus on learning end-to-end models for these tasks, particularly image classification." —— https://cs231n.stanford.edu/
57
+ - "During the 10-week course, students will learn to implement and train their own neural networks and gain a detailed understanding of cutting-edge research in computer vision." —— https://cs231n.stanford.edu/
58
+
59
+ ## 讲完这节,你应该能答上(可选巩固 · 别逐题盘问、别当门槛)
60
+ 1. **Q:为什么卷积核要在整张图上共享同一组参数,而不是给每个位置单独学一套权重?**
61
+ - 好答案信号:能同时说出参数量会爆炸、以及更关键的一点——同一个模式(比如边缘)不管出现在图的哪个位置,都该被同一个探测器认出来,而不只是停在「省内存」这一层。
62
+ 2. **Q:为什么 CNN 要堆很多层,而不是一层卷积核直接怼到底?**
63
+ - 好答案信号:提到感受野随深度增长——浅层看到的只是局部小范围(边缘/纹理),只有堆够层数,深层神经元的感受野才能覆盖到足以识别「部件」乃至「整体物体」的范围。
64
+ 3. **Q:max pooling 除了让网络算得更快,还解决了什么问题?**
65
+ - 好答案信号:提到对小幅位置偏移的容忍度——特征稍微挪了位置,只要还落在同一个池化窗口内,输出基本不变,而不只是停在「降维」这一层。
66
+
67
+ ## 常见误解(听到就纠正)
68
+ - ❌"卷积核是人工设计好的,就像 PS 里的锐化/模糊滤镜。" → 传统图像处理里的滤镜确实是人工设计的固定权重,但 CNN 里的卷积核权重是随机初始化后,靠反向传播从训练数据里自动学出来的——没人告诉它「去学一个边缘探测器」,那只是训练之后自然浮现的结果。
69
+ - ❌"感受野的扩张全靠池化层,没有池化感受野就不会变大。" → 感受野扩张的根本原因是「层数堆叠」这件事本身——哪怕不做任何池化、单纯堆更多层卷积,感受野也会一层层变大;池化确实会加速这个扩张过程(因为它压缩了空间尺寸),但两者是配合关系,不是因果关系。
70
+ - ❌"卷积核越大、通道数越多,网络就越强。" → 核变大、通道变多确实能表达更复杂的模式,但代价是参数量和计算量陡增。后面几课会讲到(AlexNet/VGG/ResNet 这条演化线),更常见的进步方向反而是用更小的核堆更多层——比如用两层连续的 3×3 卷积去换一个 5×5 卷积同样大小的感受野,但参数更少、非线性更多。
71
+
72
+ ## 收尾 & 排下一课
73
+ 这节把 CNN 的三个地基概念——卷积核、感受野、池化——一次讲透了,下次看到任何一张 CNN 架构图,都应该能顺着「局部模式 → 感受野变大 → 压缩换鲁棒性」这条线把它拆开看懂。默认下一课往「CNN Architectures」方向走,讲 AlexNet / VGG / ResNet 这条真实架构演化线、以及 Batch Norm 和迁移学习怎么让深网络训得动、用得起来;如果你更想先动手跑一个能识别图片的小 CNN 感受一下这三件事怎么落到代码里,也可以告诉我,我们可以先插一节实操再回来讲架构演化。
@@ -0,0 +1,162 @@
1
+ # 卷积神经网络与视觉识别(CNN for Visual Recognition) · 第 3 课:亲手训练一个迷你分类器,看过拟合真实发生的样子
2
+
3
+ > source: https://cs231n.github.io/ | 类型:实验课(原创实操) | 前置:cs231n/02-cnn-core-intuition
4
+ > 教法遵循 references/pedagogy.md(写成代码样式):像课本一样把概念讲透、给足,不盘问、不当门槛。
5
+
6
+ ## 一句话本质(开场钩子)
7
+ 这节课不是听课,是亲手把一个分类器从"能考好"训练成"只会背答案"——过拟合不是一个你需要记住的定义,是你会亲眼在两条曲线上看着它发生的那一刻。
8
+
9
+ ## 为什么要在乎(钩子展开)
10
+ 你大概率已经能背出"过拟合"的教科书定义:模型在训练集上表现很好,但换成没见过的数据就掉链子。但背定义和亲眼见过是两回事——就像"心动过速"这四个字谁都认识,可医生看心电图曲线、和你自己心慌意乱地摸着脉搏,是完全不同层次的理解。这节课我们不读书,直接跑代码:不到十分钟,你会亲手养出一个从"学得挺好"变成"开始死记硬背"的分类器,亲眼看着 train 曲线一路冲上 100%、val 曲线却在某一刻掉头往下。这个画面你会记一辈子——以后不管你在 CIFAR-10 上训练 ResNet,还是拿公司业务数据微调随便什么模型,脑子里冒出来的第一反应都会是今天看到的这两条线,而不是一句干巴巴的"要小心过拟合"。
11
+
12
+ ## 核心概念(逐个讲透,内容给足)
13
+
14
+ ### 先说清楚这节课的设计思路
15
+ 上一节我们讲了卷积和池化的直觉,但这节课**故意不用 CNN**——原因很实际:从零手写一个卷积层需要铺垫太多张量操作的细节,会把你的注意力从"训练这件事本身"上拉走。而"训练/验证/过拟合"这套节奏,跟你用的是最简单的线性分类器还是一个 50 层的 ResNet,**逻辑完全一样**:都是看两条曲线、都是分三份数据、都是同一套刹车手段。所以这节课我们用全场最简单的模型——一个线性分类器——把模型复杂度降到最低,把全部注意力放在"训练动态"这一件事上。等你真正开始训练 CNN 时,会发现今天看到的现象原封不动地复现,只是曲线换了个模型产生。
16
+
17
+ 也正因为这样,我们不去下载 CIFAR-10、不折腾数据预处理——那是另一件事,会稀释这节课想让你看到的东西。我们自己写几行代码,程序化生成一批几十像素的"迷你图片",几秒钟就能把整个训练流程跑完、重跑、调参、再跑。
18
+
19
+ ### 步骤 1:造一份自己的迷你图像数据集
20
+ **要做什么**:写一个函数,程序化生成两类 6×6 的小灰度图——一类画一个"十字",一类画一条"斜线",每张图再叠加一点随机噪声(模拟真实图片里的噪声/形变),然后展平成 36 维向量。分别生成训练集(60 张)、验证集(20 张)、测试集(20 张)。
21
+
22
+ ```python
23
+ import numpy as np
24
+ rng = np.random.default_rng(42)
25
+
26
+ def make_image(kind, size=6, noise=0.3):
27
+ img = np.zeros((size, size))
28
+ mid = size // 2
29
+ if kind == "cross":
30
+ img[mid-1:mid+1, :] = 1.0
31
+ img[:, mid-1:mid+1] = 1.0
32
+ else: # "diag"
33
+ for i in range(size):
34
+ img[i, i] = 1.0
35
+ if i + 1 < size:
36
+ img[i, i+1] = 1.0
37
+ img += rng.normal(0, noise, size=(size, size))
38
+ return img.flatten()
39
+
40
+ def make_dataset(n_per_class):
41
+ X, y = [], []
42
+ for label, kind in enumerate(["cross", "diag"]):
43
+ for _ in range(n_per_class):
44
+ X.append(make_image(kind))
45
+ y.append(label)
46
+ X, y = np.array(X), np.array(y)
47
+ idx = rng.permutation(len(y))
48
+ return X[idx], y[idx]
49
+
50
+ X_train, y_train = make_dataset(30) # 60 张
51
+ X_val, y_val = make_dataset(10) # 20 张
52
+ X_test, y_test = make_dataset(10) # 20 张
53
+ ```
54
+
55
+ **为什么这样做**:真实图像分类的数据管线(下载、解压、归一化、划分)本身就是一大块工作量,会喧宾夺主。用几行代码"造"出来的假图片,本质上和真实图片对模型来说没区别——都是一串数字向量,模型不知道也不关心这些数字是不是来自一张真的照片。
56
+
57
+ **做完应该观察到什么**:打印 `X_train.shape` 应该是 `(60, 36)`——60 张样本,每张 36 个像素值拉平后的向量。这就是"一张图片对模型而言长什么样"最直观的体感:不是一张图,是一个高维向量。
58
+
59
+ ### 步骤 2:从零写一个最小的分类器(线性 + softmax + 梯度下降)
60
+ **要做什么**:写一个只有一层的线性分类器:给每张图算出属于"十字类"和"斜线类"的两个分数,用 softmax 把分数变成概率,用交叉熵衡量"预测概率"和"真实标签"差多远,再用梯度下降一步步调整参数。
61
+
62
+ ```python
63
+ D, C = X_train.shape[1], 2 # 36 维输入,2 个类别
64
+ W = rng.normal(0, 0.01, size=(D, C))
65
+ b = np.zeros(C)
66
+
67
+ def softmax_loss(W, b, X, y, reg=0.0):
68
+ scores = X @ W + b
69
+ scores -= scores.max(axis=1, keepdims=True) # 数值稳定
70
+ probs = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True)
71
+ N = X.shape[0]
72
+ loss = -np.log(probs[np.arange(N), y]).mean() + reg * np.sum(W * W)
73
+ dscores = probs.copy()
74
+ dscores[np.arange(N), y] -= 1
75
+ dscores /= N
76
+ dW = X.T @ dscores + 2 * reg * W
77
+ db = dscores.sum(axis=0)
78
+ return loss, dW, db
79
+
80
+ def accuracy(W, b, X, y):
81
+ return (np.argmax(X @ W + b, axis=1) == y).mean()
82
+ ```
83
+
84
+ **为什么这样做**:这就是这门课里"图像分类"最原始的样子——一个矩阵乘法把像素映射成分数,softmax 把分数变成概率,损失函数量化"错得有多离谱",梯度告诉参数该往哪个方向挪一点。CNN 无非是在这个骨架前面加了卷积层去提取更好的特征,训练这件事的核心逻辑完全不变。
85
+
86
+ **做完应该观察到什么**:这一步本身还看不出什么——它只是把"分类器"这个词变成了 20 行能跑的代码。真正的戏在下一步。
87
+
88
+ ### 步骤 3:只训练、不设防——亲眼看过拟合发生的过程
89
+ **要做什么**:只用训练集反复迭代很多轮(比如 2000 轮),不加任何正则化、不设停止条件,每隔几十轮打印一次 train 和 val 的准确率。
90
+
91
+ ```python
92
+ lr = 0.5
93
+ for it in range(2000):
94
+ loss, dW, db = softmax_loss(W, b, X_train, y_train, reg=0.0)
95
+ W -= lr * dW
96
+ b -= lr * db
97
+ if it % 200 == 0:
98
+ print(it,
99
+ "train_acc", round(accuracy(W, b, X_train, y_train), 3),
100
+ "val_acc", round(accuracy(W, b, X_val, y_val), 3))
101
+ ```
102
+
103
+ **为什么这样做**:故意不加任何"刹车",就是要让过拟合毫无保留地暴露出来,你才能亲眼确认它长什么样,而不是靠脑补。
104
+
105
+ **做完应该观察到什么**:前几百轮,train_acc 和 val_acc 会一起涨——模型在学真正有用的规律(十字有横竖两条线,斜线是对角线)。但因为训练集只有 60 张、模型参数却有 72 个(36×2 的 W 加 2 个 b),继续训练下去,train_acc 会一路顶到 1.0(100% 背下训练集),而 val_acc 会在某一轮附近停滞、甚至开始往下掉。**两条曲线之间那条越拉越开的缝,就是过拟合的真实样子**——不是某个数字很差,而是"模型在训练集上还在变强,在没见过的数据上却已经不再变强了"。
106
+
107
+ ### 步骤 4:两个刹车——早停与 L2 正则化
108
+ **要做什么**:加两件事重跑一遍——① 训练时记录每次的 val_loss,一旦连续几次不再下降就提前停止(early stopping);② 训练时给损失函数加一个 `reg` 惩罚项(上面代码里已经预留了 `reg` 参数),逼着权重不能长得太大。
109
+
110
+ ```python
111
+ def train(reg, patience=5):
112
+ W = rng.normal(0, 0.01, size=(D, C)); b = np.zeros(C)
113
+ best_val, wait = np.inf, 0
114
+ for it in range(2000):
115
+ loss, dW, db = softmax_loss(W, b, X_train, y_train, reg=reg)
116
+ W -= lr * dW; b -= lr * db
117
+ val_loss, _, _ = softmax_loss(W, b, X_val, y_val, reg=reg)
118
+ if val_loss < best_val:
119
+ best_val, wait = val_loss, 0
120
+ else:
121
+ wait += 1
122
+ if wait >= patience:
123
+ break
124
+ return W, b, it
125
+ ```
126
+
127
+ **为什么这样做**:L2 正则化的直觉很简单——如果不加惩罚,模型完全可以靠某几个像素上的极端权重去死记硬背训练集里的噪声("第 17 号像素等于 0.62 就是十字类"这种没有泛化性的规律);给权重加一个"不许长太大"的惩罚,就是逼模型放弃这种投机取巧的捷径,转而找更平滑、更站得住脚的决策边界。早停则更直接——既然 val 曲线告诉你模型从哪一轮开始"不再学新东西、只在死记硬背",那就在那一刻喊停,不用等它彻底训练"完成"。
128
+
129
+ **做完应该观察到什么**:加了这两个刹车之后,train_acc 不再冲到 100%(甚至可能只到 90% 出头),但 train_acc 和 val_acc 之间的缝明显变窄了,val_acc 往往还比刚才不设防那版更高。**这才是关键认知**:一个"看起来训练得不够满"的模型,可能比一个"训练集分数完美"的模型更好用——因为它更能扛得住没见过的数据。
130
+
131
+ ### 步骤 5:test set 的纪律——只碰一次
132
+ **要做什么**:前面所有调参(要不要加正则化、正则化力度多大、什么时候早停)都只看 val_acc。等你选定了一版你最满意的配置,**只在这一刻**,把它拿到 test 集上跑一次,报告这个数字,然后收工。
133
+
134
+ **为什么这样做**:如果你在调参过程中反复看 test 集表现、根据它调整策略,test 集就悄悄变成了第二个 val 集——你其实是在拿"没见过的数据"当训练信号用,最后报出来的分数会虚高,你等于是在自己骗自己。val 集就是给你"边看边调"用的;test 集的唯一使命,是在所有决定都已经做完之后,给出一个诚实的、不受调参过程污染的最终答案。
135
+
136
+ **做完应该观察到什么**:test_acc 应该和你在步骤 4 里看到的 val_acc 大致接近(毕竟 val 和 test 是用同一套规则生成的同分布数据)。如果 test_acc 明显低于 val_acc,通常说明调参时不知不觉"看了太多次" val,把模型调得对 val 集本身有点过拟合了——这也是一种更隐蔽的过拟合,值得记在心里。
137
+
138
+ ## 带学生读 source
139
+ 如果想看这套"线性分类器 + softmax + 训练动态"背后更系统的讲法(包括更多分类器变体、更细的优化算法对比),可以去读 https://cs231n.github.io/ 上对应的课程笔记——但记住,笔记上的公式和代码是给你搭理论框架用的,真正的体感要靠你自己跑出那两条曲线才能长在身上。
140
+
141
+ ## 金句(引用时标出处)
142
+ > "Through multiple hands-on assignments and the final course project, students will acquire the toolset for setting up deep learning tasks and practical engineering tricks for training and fine-tuning deep neural networks."
143
+ > —— CS231n 官网,https://cs231n.stanford.edu/
144
+
145
+ > "During the 10-week course, students will learn to implement and train their own neural networks and gain a detailed understanding of cutting-edge research in computer vision."
146
+ > —— CS231n 官网,https://cs231n.stanford.edu/
147
+
148
+ ## 讲完这节,你应该能答上(可选巩固 · 别逐题盘问、别当门槛)
149
+ 1. **Q:为什么要把数据分成 train / val / test 三份,而不是两份就够?**
150
+ - 好答案信号:能讲出 test 是"最终只能用一次的裁判"——如果调参时反复看 test 表现,test 就失去了检验泛化能力的意义;val 才是那份可以在训练过程里反复看、反复调的数据。
151
+ 2. **Q:什么信号能让你判断模型正在过拟合,而不是还在正常学习?**
152
+ - 好答案信号:能提到要同时看 train 和 val 两条曲线——train 还在变好、val 已经停滞或变差,两者之间的差距持续拉大,而不是只盯着某一个孤立的数字下结论。
153
+ 3. **Q:L2 正则化为什么能缓解过拟合?(不用写公式,讲直觉就行)**
154
+ - 好答案信号:能说出"给权重加一个不许长太大的惩罚,逼模型放弃靠个别极端权重去死记硬背训练样本噪声的捷径,转而学更平滑、更通用的决策边界"这层意思。
155
+
156
+ ## 常见误解(听到就纠正)
157
+ - ❌ "训练集准确率越高,说明模型越好" → 训练集准确率冲到 100% 很多时候恰恰是过拟合的信号,不是模型更强了,而是模型把训练集背下来了;判断模型好不好,永远要看它在没参与训练的数据(val/test)上的表现。
158
+ - ❌ "只要 loss 还在往下降,训练就没问题" → 要同时盯两条线:train loss 一直降是正常的,但如果 val loss 已经开始掉头往上,那才是该警惕的信号,只看训练集这一条线是看不出过拟合的。
159
+ - ❌ "调参时顺手看看 test 集表现,分数不好就调回去,反正只是看看" → 这就是"偷看考卷":test 集只能在所有超参数都定型之后碰一次,调参过程一律只用 val,否则 test 分数会被间接过拟合,失去作为"诚实检验"的意义。
160
+
161
+ ## 收尾 & 排下一课
162
+ 这节课你亲手跑通了图像分类最核心的训练节奏——造数据、写分类器、放任它过拟合、再用早停和正则化把它拉回来、最后诚实地碰一次 test。这套节奏之后不管模型换成什么都不会变。下一课默认往"CNN 架构"方向走,把这节用的线性分类器换成真正带卷积层的网络,你会发现今天看到的两条曲线原封不动地复现;如果你更想先补优化算法(SGD/Momentum/Adam 这些"怎么让训练更快更稳"的手段),也可以调整顺序先讲那块,再回来上 CNN 架构。