这个实验要弄清一件事:给定一堆数据,机器是怎么把一条直线的两个参数一点一点调到最合适的位置的。全程不写代码,通过拖动、点击和观察完成。
我们手上有 100 个点,想用一条直线 y = w·x + b 去穿过它们。只要定下 w(斜率)和 b(截距)这两个数,直线就定下来了。左边那张图的横纵坐标就是这两个数,所以左图上任意一个位置,都对应右图里的一条具体直线。
误差的定义是把每个点的偏差先平方再平均,写出来是这样:
误差 = 平均值[ (w·x + b − y)² ]括号里是「直线给出的预测值」减去「真实值」。平方是为了让偏高和偏低都算错,而且偏得越多罚得越重。
把这个式子展开,里面 w 和 b 的最高次数都是 2 次,而且平方项的系数是正的。二次函数的图像是抛物线,两个变量的二次函数图像就是一个碗。所以这个误差面只有一个最低点,不存在走错方向掉进别的坑里的问题。
课件第 2 章第 17 页那个式子 (1/N)‖y−Xθ‖²,说的就是这里的误差。
因为误差是个碗,碗底那一点的坡度必然是零。把「对 w 的坡度 = 0」和「对 b 的坡度 = 0」两个条件列出来,是一个两个未知数、两个方程的方程组,解出来就是最优的 w 和 b。这就是课件第 2 章第 24 页那个 θ*=(XᵀX)⁻¹Xᵀy 在只有两个参数时的样子。
问题是:真实的模型有几百万个参数,那个方程组根本解不动。所以才需要后面这些一步一步往下走的办法。
上一节是靠手感找最低点,很快就会发现手感不太靠得住。有没有办法在任意一点上算出「往哪边走是下坡」?有,而且这个方向可以精确算出来,它叫梯度。
一、所有箭头都指向中间那个最低点附近,但不是直直指过去,而是垂直于脚下那条等高线。
二、越靠近最低点,箭头越短。到了最低点,箭头长度就是零,这也是判断「到没到底」的依据。
一个方向上的坡度,就是「往这个方向挪一点点,误差会变多少」。数学上把它叫做偏导数。这个实验里两个方向的坡度分别是:
w 方向坡度 = 2 × 平均值[ (预测值 − 真实值) × x ]两个式子长得几乎一样,区别只在于 w 的那个多乘了一个 x。原因很直观:改变 b 是把整条直线平移,对每个点的影响一样大。改变 w 是把直线转一下,x 越大的点被甩得越远,所以要按 x 加权。
把这两个数拼成一对,就是梯度。梯度指向上坡最快的方向,所以下山要往它的反方向走,箭头画的就是反方向。
有了方向,剩下的事就是反复做同一件事:算出脚下的坡度,往反方向挪一小步,再算一次。这句话写成式子就是课件第 2 章第 26 页那一行,整个第 2 章最该记住的一行。
新位置 = 旧位置 − 学习率 × 坡度因为算出来的坡度只是脚下这一点的坡度,它只在很小的范围内可信。就像你站在山坡上,能看清脚边的坡度,但看不见远处的地形。一次跨太远,很可能踩到一个坡度完全不同的地方,反而越走越高。这就是下一节要看的现象。
真实的模型面对的地形远比这个碗复杂,所以「小步试探、反复修正」几乎是唯一可行的办法。
学习率是这个算法里唯一要人来定的数。定小了走不到,定大了会飞出去。这一节要做的事是:自己扫一遍学习率,把结果一个个点在右边那张图上,看它到底长什么形状。先手动试七八个值,试出感觉了再用自动扫描对照。
一、一个太小的学习率,40 步之后还离底很远。
二、一个正好的,几步就到底。
三、一个刚好开始发散的临界值。把它精确到小数点后两位,这个数要写进实验报告。
一个一个往上加太慢。用二分法:先找一个肯定能到底的值(比如 0.5)和一个肯定发散的值(比如 2.0),取它们的中点 1.25 试一次。
如果 1.25 发散,说明临界值在 0.5 和 1.25 之间,就把上界改成 1.25,再取中点 0.875 试。如果 0.875 能到底,就把下界改成 0.875,再取中点。
每试一次,可能的范围就缩小一半。从 1.5 的宽度缩到 0.01 只需要试七八次。这个办法在任何「单调分界」的问题上都能用,比逐个试省力得多。
假设你在碗的左壁上,坡度指向右下。步子小的时候,你落在碗底偏左的位置,下一步再往右一点,慢慢收敛。
步子一大,你会直接越过碗底冲到右壁,而且落点比原来的左壁更高。下一步从右壁往左冲,又比刚才更高。每一次都比上一次更远,误差就这样滚雪球式地放大,几步之后数值大到算不下去。
这个临界值不是随便定的,它由碗的陡峭程度决定。碗越陡(数据的尺度越大),能用的学习率就越小。这也是为什么真实项目里几乎总要先把数据标准化,把碗的形状调圆一点,学习率才好定。
| 学习率 | 最终误差 | 结果 |
|---|
前面每走一步,都是把全部 100 条数据的坡度算出来求平均,这叫全批量。也可以只随机抽几条来算,抽到的这几条不一定代表全体,方向会有偏差,但算得快得多,这叫小批量。到底哪个划算?
按「走了几步」来比是不公平的。全批量走一步要算全部 100 条数据的坡度,小批量走一步只算几条。同样的计算资源,小批量能走多得多的步数。
切换到按计算量比那一档再看一次,两条曲线的相对位置会变。这才是工程上真正关心的比较方式。
批量拉到最左边,就是每看一条数据就走一步。这时候方向几乎是乱的,轨迹会抖成一团毛线。但它照样能大致走到底,因为虽然每一步都不准,几百步的平均方向还是对的。
再把批量拉到 100,赭色的轨迹会和蓝色完全重合,因为这时小批量就是全批量。中间那一段(大概 4 到 30)是实际最常用的区间。
顺手在右图里对比一下:批量 1 的曲线在「按更新次数」这一档看起来很差,在「按计算量」这一档往往反而最靠左。
在这个实验里误差面是个碗,只有一个底,抖动纯属浪费。但真实的模型误差面坑坑洼洼,有很多局部的小坑。走得太平稳的算法一掉进小坑就出不来了,而带抖动的算法有机会被自己的随机性甩出小坑,继续往下找。
所以实际训练几乎不用全批量。常见的批量大小是 32、64、128 这些数,既保留了一点随机性,又能让显卡一次算一批,不浪费算力。
下一个实验里那个香蕉形的函数,就会让你看到「走不出去」是什么样子。
| 批量 | 更新次数 | 总计算量 |
|---|
前面所有的图都建立在同一批 100 条数据上。可这 100 条只是从现实世界里抽出来的一个样本,换一批数据,误差面就会变形,最低点也会挪位置。这一节要看的是:我们辛辛苦苦走到的那个最低点,到底是真的最优,还是只是这批数据的最优。
样本量拉到 20、噪声拉到 1.5,连点十次换数据,看那团点散成什么样。
再把样本量拉到 400、噪声拉到 0.1,重新清空后连点十次。两团点的大小差别非常明显。
我们能算的只有经验风险,也就是在手上这批数据上的平均误差。真正想让它小的是期望风险,也就是在全世界所有数据上的平均误差。
右图那团点的中心接近真值,说明方法本身没错。那团点的散开程度,就是「只用一个样本去估计真相」这件事本身带来的不确定性。数据越多、噪声越小,这团点就越紧。
第一,训练误差降到很低不代表模型好,只代表它在这批数据上表现好。所以必须留一部分数据出来,专门用来检验,这就是第 7 章会讲的训练集与测试集划分。
第二,如果换一批数据模型参数就大变,说明数据量相对于模型的复杂度太少了。要么补数据,要么把模型改简单,要么加约束限制参数乱跑。课件第 2 章第五节的正则化就是干这件事的。
下面的题目都没有标准答案,写你自己看到的现象和想到的解释。前面各节做过的操作会自动汇总进报告,不用手抄数据。
| 学习率 | 40 步后误差 | 判定 |
|---|---|---|
| 未记录 | ||
| 批量 | 更新次数 | 总计算量 |
|---|---|---|
| 未记录 | ||