同一个 y = 3x + 2 的问题,人推公式一步得到斜率 3.0147, 机器用梯度下降走 200 步得到 3.0146。两条路殊途同归。 区别在于:公式能写出来的时候人赢,写不出来的时候只剩机器那条路。
上一节讲了历史,这一节讲原理的最小内核。 后面所有模型——从线性回归到几千亿参数的 Transformer——用的都是这一个循环,只是规模和形状不同。
一、两条路:推公式,或者试错
给定一批带噪声的点,要找出背后的直线。 人走的路是推导:最小二乘有闭式解,把数代进去一步算完,得到斜率 3.0147、截距 2.0152。 机器走的路是试错:先随便猜一个 w 和 b,算出预测值,看差多少,往差得少的方向挪一点, 重复两百次,得到 3.0146、2.0152。 两条路的结果差在第四位小数——因为它俩解的是同一个优化问题,一个用解析解,一个用迭代解。 那为什么不总是用解析解?因为它要求「误差对参数的导数能写成式子并反解出来」。 线性回归能,多层神经网络不能。能解的时候没人愿意迭代,解不出来的时候迭代是唯一的路。
二、参数是什么
拿一条直线 y = wx + b 来说,w 和 b 就是参数。它们不是人想出来的,是从数据里定出来的。 换成一个九次多项式,参数就是十个系数。 换成一个两层神经网络,参数是两层矩阵里的每个数,几百到几十亿个。 不管多少个,性质一样:一组数定了,函数就定了。 Demo 里那个 sin(3x) 加 0.3cos(7x) 的目标函数,人手写不出闭式解,但可以用点去拟合: 一次多项式误差 0.470,九次压到 0.071。九次多项式只有十个参数,这十个参数就是「学到的规律」。 所以「模型」这个词没那么玄:它就是一个形状固定、参数待定的函数。训练就是找那组参数。
三、为什么大部分问题写不出公式
因为真实世界的映射关系太复杂,而且往往没有解析形式。 「这张图里有没有猫」这个函数的定义域是几百万维的像素空间,值域是两个字——你要怎么写出它的解析式? 还有一层:就算写得出,参数也解不出来。 线性回归的闭式解要算一个矩阵的逆,矩阵一大,计算量按三次方涨,几百亿参数根本算不动。 所以工业界的默认做法是:只要能定义「预测得准不准」,就用迭代去逼近,不去求解析解。 这也解释了为什么「损失函数」在设计模型时那么重要——它是迭代唯一的指路牌。
四、学习的三步循环
第一步前向:把输入代进当前参数,算出预测值。 第二步算误差:拿预测值和真值比,得到一个数(比如均方误差)。 第三步改参数:算误差对每个参数的导数,按导数的反方向挪一小步。 这三步重复几千几万次。 Demo 里那个梯度下降就是最朴素的版本:均方误差从 7.368 一路压到 0.047,两百步。 整个深度学习的复杂度,全在「怎么算导数」和「怎么挪」这两件事上。反向传播解决第一件, 各种优化器(SGD、Adam)解决第二件。 记住这个循环,后面第八章讲神经网络、第九章讲调参,你都能把它套回这三步——因为真的就是这三步。
五、动手 Demo:人推公式 vs 机器试错,同一个答案两条路
先造 80 个带噪声的点,真值斜率 3、截距 2。 然后走两条路:一条用最小二乘的闭式解一步算出,一条用梯度下降走 200 步。 最后换一个写不出公式的目标函数,看两条路还剩几条。
x = rg.uniform(-1, 1, 80)
y = 3.0 * x + 2.0 + rg.normal(0, 0.25, 80)
# ① 人推公式:最小二乘的闭式解
A = np.stack([x, np.ones_like(x)], axis=1)
sol = np.linalg.lstsq(A, y, rcond=None)[0]
# ② 机器试错:梯度下降两百步
w, b = 0.0, 0.0
for _ in range(200):
g = w * x + b - y
w -= 0.08 * 2.0 * float((g * x).mean())
b -= 0.08 * 2.0 * float(g.mean())
print(sol, w, b)
实跑输出:
--------------------------------------------------------------------
核心逻辑:能推出公式的就推公式,推不出来的才交给数据
--------------------------------------------------------------------
任务:从 80 个带噪声的点里,找出 y = 3x + 2 这条线。
做法 斜率 w 截距 b 步数 过程
-------------------------------------------------------
人推公式(最小二乘) 3.0147 2.0152 0 一次算完
机器学(梯度下降) 3.0146 2.0152 200 一步步挪
两条路都落在真值 3 和 2 上,差在第四位小数。**同一个答案,一个靠推导,一个靠试错。**
梯度下降把均方误差从 7.368 压到 0.047。
那有公式为什么还要学?因为这个形状能写成公式,绝大多数真问题写不出来。
模型 要调的参数个数 训练集均方根误差
---------------------------------------------
1 次多项式 2 0.4704
3 次多项式 4 0.2097
9 次多项式 10 0.0710
目标函数是 sin(3x) 加 0.3cos(7x),**这个式子人手写不出来**,但你可以拿一堆点去拟合它。1 次误差 0.470,9 次压到 0.071。
1 次那行不是「人写不出来」,是**这条直线描述不了这个形状**——参数少到装不下目标。
**参数就是公式里待定的那些数。** 深度学习和上面这行多项式拟合没有本质区别:只是参数从 10 个变成几十亿个,函数从多项式换成一层层矩阵乘。
两条路的答案:闭式解 3.0147 / 2.0152,梯度下降 3.0146 / 2.0152。 差在第四位小数,来自迭代次数不够。 多跑几百步还能再靠近,但永远差一点点——迭代法的精度是用步数换的。 注意学习率 0.08 这个数:太大会震荡甚至发散,太慢走不完。 这就是「调参」这件事最原始的样子,第八章还会遇到它。 第二段那个 sin(3x) 加 0.3cos(7x) 的函数,人手写不出闭式解,但机器能拟合:一次误差 0.470, 九次 0.071。参数从 2 个加到 10 个,误差降了六倍多。 要说清的是:九次多项式那 10 个参数不是「规律」,是「拟合」。 它在这 80 个点上贴得很好,换一批点就可能崩——这正是下一节要讲的「训练不等于学对」。
30 秒实验
- 把学习率从 0.08 改成 0.2,两百步内误差会震荡;改成 0.01,两百步走不完。 这在二维线性问题上有闭式解兜底,在神经网络里没有兜底——这就是调参为什么让人头疼。
- 把梯度下降的步数从 200 加到 2000,看系数能不能追上闭式解的四位小数。 你会发现它越来越近,但收敛速度是几何式的,前 50 步降得快,后面几乎不动。
- 把多项式从九次换到十五次,误差还会再降一点——但这是第二段那个「过拟合」的入口。 先记住这个信号,下一节专门算。
常见误区
- 「AI 是让机器像人一样思考」。工程上没人这么实现。 实际做法是定义一个可微的函数、给它一堆数据、让它自己调参数。 「像不像人」不是评价指标,「准不准」才是。
- 「公式能推出来就别用机器学了」。对,但前提是推得出来。 现实里绝大多数任务的映射关系没有解析式,这就是深度学习的生存空间。
- 「参数是模型的『知识』」。更准确地说,参数是在训练数据上压缩出来的统计规律。 它不含「知识」这种实体,所以数据里没有的东西它也不会有。
自测三问(点开对答案)
1. 为什么梯度下降能用,最小二乘的闭式解在神经网络里用不了? —— 闭式解要求能把「误差对参数求导并令其为零」这个方程组解出来。 线性模型能,多层网络加非线性激活之后解不出来,只能迭代逼近。 2. 参数多就一定更好吗? —— 在这一节的例子里是(2 个到 10 个,误差从 0.470 降到 0.071),但那是**训练集误差**。 参数多到一定程度会开始记噪声,测试集误差反而涨——下一节算这个临界点。 3. 三步循环里,哪一步最容易出错? —— 第二步「算误差」。选错损失函数,模型会朝着错的方向优化。比如分类问题用均方误差,梯度会饱和; 用交叉熵才正常。上一节说「训练就是调参数」,这一节把它拆成了三步循环。下一节追问一句:调完之后怎么知道自己调对了?
- ☐ (本节完,下一节 1.1.4 什么是模型,模型是怎么训练的)