📘 第1章 人工智能的过去、现在和未来
📄 一句话概括

前两次热潮靠一个信念推着走:只要推理机更聪明,智能就会自己长出来。第三次热潮换了个信念: 不去设计智能,去喂给它足够多的例子。 这一节把「足够多」拆成三样——数据、算力、算法,然后挨个砍一刀。 结论是这三样相乘,不是相加:任何一样接近零,结果就接近零。

📝 本节和前面几节的关系

上一节讲到 2012 年 AlexNet 把错误率从 26.2% 压到 15.3%,第三次热潮从学术话题变成产业。 这一节解释那一下为什么发生在 2012 年,而不是 1992 年或 2002 年。 三要素里「算法」这一条,第 9 章还要专门讲;这里只量它对结果的影响有多大。

一、2012 年那一下,到底变了什么

先说清楚一个常见误解:2012 年的网络结构不是新的。卷积、池化、ReLU、Dropout, 这些零件在 1980 年代末到 1990 年代就都有了,LeCun 1989 年就用卷积网络认手写数字, 1998 年的 LeNet-5 已经能读支票上的数字。 变的是三样外部条件同时越过了门槛。

要素 1990 年代 2012 年
数据 MNIST 6 万张就算大 ImageNet 1400 万张
算力 CPU 串行,训多层要几周 两块 GTX 580,训五六天
算法 sigmoid 为主,深层训不动 ReLU 加 Dropout,八层直训
这三样不是并列关系,是互相开门的关系。数据多了,模型才敢变大;模型大了,才需要 GPU;
GPU 快了,才敢试更深的网络。任何一条断掉,另外两条都推不动。
所以「为什么是 2012 年」有一个很朴素的答案:那一年这三扇门第一次同时开着。早五年,数据不够;
晚五年,别人已经做完了。

二、AI、机器学习、深度学习是三层套娃

这三个词天天一起出现,但它们不是同义词,是包含关系。 而且每一层的「人给什么」完全不同——这才是关键。

层次 人给什么 机器自己产出什么 代表
规则系统 规则(IF-THEN) 结论 MYCIN、XCON
机器学习 特征 + 标签 判别边界 SVM、随机森林
深度学习 原始数据 + 标签 特征 + 判别边界 AlexNet、ResNet
看第二列,从上到下,人要做的事在减少,机器要做的事在增加。规则系统里人得把判断逻辑全写出来;
机器学习里人只需要告诉它「看哪些量」,判断逻辑交给它找;深度学习里连「看哪些量」都不用说了。
这张表也解释了两次寒冬的位置。第一次寒冬卡在「规则写不完」,第二次寒冬卡在「知识抄不完」。
两次都是因为人还在第二列里干活。

三、从「教规则」到「给例子」:一个具体任务

拿车牌识别举例,两条路的分岔一眼就看清。 规则法要串四步:找边缘(用哪种算子)、找矩形(宽高比多少算车牌)、切字符(怎么处理粘连)、 模板匹配(字体变形怎么办)。 每一步都要人做一次决定,每一步的决定都只对某几种情况有效。换个省份的牌照、换个阴天的光照, 四步里总有一两步要重调。 深度学习的做法是一句话:给它十万张标好的车牌图,让它自己学。人只决定网络多大、训多久。 特征不用设计,它在第一层到第三层自己长出来。 这两种做法的差别不在效果,在成本结构。 规则法的成本是「每一种例外都要人再写一条」,随情形种类线性增长; 深度学习的成本是「标注数据」,一次投入,之后边际成本接近零。 这就是为什么规则系统在窄领域能活、在宽领域必死。窄领域的例外是有限的,宽领域的例外是无穷的。

四、三条线:感知、认知、决策

第三次热潮之后,AI 的应用大致分成三束。分开看,才能看清每一束各自卡在哪。

线 做什么 卡在哪 标志事件
感知 把信号变成类别 基本解决,剩长尾 2015 ResNet 错误率 3.57%
认知 把符号变成符号 会说话,不保证真 2016 神经翻译全面普及
决策 在环境里选动作 开放环境不敢放 2016 AlphaGo 胜李世石
这三束的难度是递增的,但递增的原因不在算法。感知任务有标准答案,错误可以逐张数;
认知任务的「对」有多个版本;决策任务的代价要过很久才知道。
换句话说:越往后,越难知道自己错了。这才是第三束最难的地方,
也是第 8 章「强化学习」要正面处理的问题。

五、这一节要量的三个数

上面讲的都是叙述。接下来用一个实验把「三样缺一不可」量出来。 任务是认 8 乘 8 的七段数码管数字图,十类, 训练和测试都带 ±2 像素的随机平移和一个翻转像素——同一个数字每次出现的位置都不一样, 这逼着模型去学形状而不是记位置。 三样东西各给一个旋钮:每类给多少张(数据)、隐层多宽(表达力)、跑多少步(算力)、 激活函数是 ReLU 还是线性(边界能不能弯)。基准全开,然后一次只拧一个旋钮。 最后把三个「时代」的典型配置放进同一张表。1958 年的感知器、1986 年的反向传播、 2012 年的深度网络,用同一份数据、同一个训练循环,只换配置。

六、动手 Demo:数据、算力、算法,挨个砍一刀

网络是两层隐层加一个 softmax,全部用 numpy 手写。H.mlp_fit 的 hs 传空元组就是线性分类器, 传 (16,8) 就是窄网络,act 传 'lin' 就把激活换成恒等——三个旋钮都在同一个函数签名里。 标准化按训练集的均值和标准差算,再套到测试集上,不然测试集的统计量会漏进训练过程。

JIT, NOISE = 2, 1             # 训练和测试都带 ±2 像素平移 + 1 个翻转像素
Xte, yte = H.make_data(80, jitter=JIT, noise=NOISE, seed=99)

def run(n, hs, act, steps):
    Xtr, ytr = H.make_data(n, jitter=JIT, noise=NOISE, seed=0)
    A, B = H.standardize(Xtr, Xte)      # 按训练集的均值和标准差换算
    m = H.mlp_fit(A, ytr, hs, act, steps=steps)
    return H.npar(m), H.mlp_acc(A, ytr, m, act), H.mlp_acc(B, yte, m, act)

CFG = ((u'全开',    80, (64,32), 'relu', 1500),
       (u'只砍数据',  10, (64,32), 'relu', 1500),
       (u'只砍宽度',  80, (16,8),  'relu', 1500),
       (u'只砍激活',  80, (64,32), 'lin',  1500),
       (u'只砍步数',  80, (64,32), 'relu',  100),
       (u'全砍',     10, (16,8),  'lin',   100))

for tag, n, hs, act, steps in CFG:
    p, a, b = run(n, hs, act, steps)
    print(tag, p, a, b)

实跑输出:

--------------------------------------------------------------------
三样东西——数据、算力、算法
--------------------------------------------------------------------

==============================================================



[1] 同一个任务,三样东西各占一头



  任务是认 8 乘 8 的七段数码管数字图,十类,

  训练和测试都带 ±2 像素随机平移 + 1 个翻转像素,测试集 800 张。

  这不是个容易的任务:同一个数字每次出现的位置都不一样。



    数据    每类给多少张    样本不够,规则就学成巧合

    算法    网络长什么样    线性只能切直线,切不开就永远切不开

    算力    能跑多少步      步子不够,梯度还没走到山脚就停了



  下面把三样挨个砍一刀,其余两样不动。



[2] 基准、只砍一样、全砍



    配置        每类  隐层     激活   参数    训练    测试    秒

    全开        80    64/32   relu   6570    1.0000  0.9275  3.58

    只砍数据    10    64/32   relu   6570    1.0000  0.4425  0.44

    只砍宽度    80    16/8    relu   1266    1.0000  0.8500  1.08

    只砍激活    80    64/32   lin    6570    0.9200  0.6863  2.21

    只砍步数    80    64/32   relu   6570    0.3337  0.2900  0.24

    全砍        10    16/8    lin    1266    0.5400  0.2550  0.02



  基准 0.9275,6570 个参数,训练 3.58 秒。



  每一刀砍下去,掉的点数:

    只砍数据    -0.4850    (6570 个参数,100 个样本)

    只砍宽度    -0.0775    (1266 个参数,800 个样本)

    只砍激活    -0.2412    (6570 个参数,800 个样本)

    只砍步数    -0.6375    (6570 个参数,800 个样本)

    全砍        -0.6725    (1266 个参数,100 个样本)



  四刀都砍在肉上,但它们砍掉的东西完全不一样:

  砍数据砍的是「见过多少种情况」,砍宽度砍的是「能表示多复杂的东西」,

  砍激活砍的是「边界能不能弯」。



[3] 训练准确率这一列,什么也证明不了



    配置        训练    测试    训练减测试

    全开        1.0000  0.9275  0.0725

    只砍数据    1.0000  0.4425  0.5575

    只砍宽度    1.0000  0.8500  0.1500

    只砍激活    0.9200  0.6863  0.2338

    只砍步数    0.3337  0.2900  0.0438

    全砍        0.5400  0.2550  0.2850



  六行里「训练」有 3 行正好 1.0000,其余是 0.9200、0.3337、0.5400。

  可「测试」从 0.2550 一路排到 0.9275,跨度 67.25 个点。

  训练满分是这几行的共同点,不是它们的分水岭。



  原因不复杂:100 个样本碰上 1266 个参数,模型完全可以把每张图当特例记住。

  记住 100 张图不需要理解任何规律——它只要一张够大的表。

  「训练减测试」这一列才是真话:

  0.0438 那行是真学到了规律,0.5575 那行(只砍数据)学的是这 100 张的编号。



[4] 把三个「时代」放进同一张表



    配置            每类  隐层     激活   参数    训练    测试    秒

    1958 感知器     10    无       lin    650     1.0000  0.3713  0.02

    1986 反向传播   20    16       tanh   1210    1.0000  0.5275  0.34

    2012 深度网络   80    64/32   relu   6570    1.0000  0.9275  2.46



  从第一行到第三行:

    样本 100 → 800,8.0 倍

    参数 650 → 6570,10.1 倍

    测试 0.3713 → 0.9275

  这三行不是三个算法在比高低,是同一条路的三段路况:

  1958 年那一段,路是对的,可车(算力)和数据都不够;

  1986 年补上了「怎么把误差分回隐层」,还是不够跑长途;

  2012 年三样一起到位,同一套思路才开到了终点。



[5] 三样为什么不能互相替代



  数据换不了算法:

    把每类张数从 80 加到 800(样本涨十倍),线性模型从 0.6863 变成 0.7462。

    涨了 6.0 个点,还是比不过一个只用八分之一样本训出来的

    1266 参数 ReLU 小网络(0.8500)。

    直线画不出弯的边界,样本再多也只是让这条直线放得更准。



  算法换不了数据:

    6570 个参数的网络,每类只给 10 张,训练准确率冲到 1.0000,

    测试掉到 0.4425——它把 100 张图的每个像素都背下来了。

    参数越多,能背的东西越多,缺数据时反而更危险。



  算力换不了前两样:

    1500 步和 100 步差 0.6375,这是三刀里最狠的一刀;

    再加十倍步数也只是把那个顶磨得更光。



  一句话:三样是相乘的关系,不是相加。任何一样接近零,结果就接近零。

第一段先立任务。±2 像素平移是个不轻的扰动:七段图的笔画本来只有一格宽, 移两格就会有一部分滑出边界。这逼着模型学「哪些像素同时亮」这种相对关系,而不是「第几列亮」。 第二段是主干。基准 0.9275,四刀砍下去分别是 −0.4850、−0.0775、−0.2412、−0.6375。 四个数字的量级差得很开,这本身就是信息:在这份数据上,数据量和步数是最贵的,宽度反而最便宜。 为什么砍宽度只掉 7.75 个点?因为这份任务的判别边界不算复杂:十个数字靠十几个笔画位置就能分开, 1266 个参数已经够画。宽度不是瓶颈的时候,加宽就只是加账单。 第三段那张表才是这一节真正想说的。六行里训练准确率有三行正好 1.0000, 可测试从 0.2550 排到 0.9275,跨度 67.25 个点。训练满分是这几行的共同点,不是它们的分水岭。 「训练减测试」这一列给出了另一条线索: 0.0725 那行是真学到了规律,0.5575 那行(只砍数据)学的是这 100 张图的编号。 100 个样本碰上 6570 个参数, 模型完全可以把每张图当特例记住——记住 100 张图不需要理解任何规律,它只要一张够大的表。 第四段换三套配置。1958 感知器是线性加 100 个样本,测试 0.3713; 1986 配置是 tanh 加 16 个隐单元加 200 个样本,0.5275; 2012 配置是 ReLU 加两层加 800 个样本,0.9275。 注意这三行的训练准确率全是 1.0000。三个时代在训练集上打成平手,差别全在测试集上。 这正是过拟合最容易被忽略的地方:如果你只看训练指标,1958 年和 2012 年没有区别。 第五段回答「能不能互相替代」。把样本从 80 加到 800(涨十倍), 线性模型从 0.6863 爬到 0.7462——涨了 6 个点, 还是比不过一个只用八分之一样本训出来的 1266 参数 ReLU 小网络(0.8500)。 数据能缓解算法不足,但不能替代。 反过来,把样本砍到每类 10 张,6570 个参数的网络训练 1.0000、 测试 0.4425——算法再好也救不回没见过的信息。这就是「相乘不是相加」的字面意思。

30 秒实验

  1. 把 JIT 从 2 改成 0,重跑第二段。 所有行都会涨,而且线性那一行涨得最多——不平移时,七段图几乎是线性可分的, 算法这一维的优势就消失了。这是「结论依赖数据」最直接的例子。
  2. 给 CFG 再加一行 (u'只砍样本数但保持参数', 80, (8,), 'relu', 1500), 看参数掉到 600 左右时测试掉多少。 这一步是用来区分「宽度」和「参数量」的——它们不是同一个旋钮。
  3. 把 run 里的 steps 从 1500 改成 5000,其余不动,重跑基准。 如果测试不再涨,说明基准这一行已经收敛,不是被步数卡住的。下结论之前先排除这个可能。

常见误区

  1. 以为 2012 年是「深度学习被发明」的年份。卷积、池化、ReLU、Dropout、反向传播,零件全都更早。 2012 年是三样外部条件同时过门槛的年份,不是算法从零到一的年份。把功劳全记在结构上, 会漏掉真正的因果。
  2. 以为数据越多越好,不看边际。样本从 80 涨到 800 只带回 6 个点, 而参数从 650 涨到 6570 就带回几十个点。「多」这件事要分维度看,笼统地说「数据为王」是偷懒。
  3. 以为训练准确率 1.0000 是好事。六行配置里三行都做到了 1.0000,测试却从 0.2550 到 0.9275。 训练集上的满分只说明「容量够装下这些样本」,不说明「学到了规律」。
  4. 以为深度学习不需要人干活了。它把「设计特征」的活换成了「收集标注、设计损失、调超参」的活。 活没少,只是换了一种,而且换成了更依赖工程和数据基建的那种。
自测三问(点开对答案) 1. 为什么说 2012 年那次突破不能只归功于算法? —— 因为卷积、池化、ReLU、Dropout 这些零件在 1980 到 1990 年代就都有了。 **2012 年真正变的是数据(ImageNet 1400 万张)、算力(GPU)、** **和能让深网络端到端训起来的技巧,三样同时到位。**任何一样缺席,同样的结构都训不出那个结果。 2. 规则系统、机器学习、深度学习,三者的「人给什么」有什么不同? —— 规则系统:人给 IF-THEN 规则,机器只负责执行和串接。 机器学习:人给特征和标签,机器自己找判别边界。 **深度学习:人只给原始数据和标签,连「该看哪些量」都由机器自己学。**沿着这条线, 人做的事在减少,机器做的事在增加——两次寒冬都发生在「人还得亲自干活」的那两格。 3. 「三样相乘不是相加」在实验里怎么体现? —— 看两行就够:**只砍数据**那一行,参数 6570、训练满分、 测试 0.4425——再好的算法也救不回没见过的情况; **只砍激活**那一行,样本 800、参数 6570、测试 0.6863——再多的数据也只是让那条直线放得更准。 **每一样单独拉满都不够,必须同时在线。**

名字、历史、现状都铺完了。 下一章从最朴素的那条路重新开始:如果不用学,只把专家的判断写成 IF-THEN,能走多远。

相关笔记