二、机器究竟学到了什么
还是从手写数字开始。
一张28×28像素的灰度图片进入计算机以后,可以表示成784个数,每个数记录一个位置的明暗程度。为了说明方便,我们暂时只看其中几个,把它们叫作a、b、c……
下一层会产生另一批数,例如d、e、f……其中每一个数,都由上一层的许多数按照同一种简单办法算出来。例如:
d = 0.8a − 0.3b + 0.2c + … + 0.1
算完以后再作一个很简单的处理:如果结果小于0,就把它记成0;如果大于0,就保留原来的结果。
e也是这样算,只不过前面的系数换成另一组数;f也是如此。这一层的其他数字,都采用同一种基本计算方法。
到了下一层,还是一样。假如这一层已经得到d、e、f……,下一层的某个数p可以是:
p = 0.4d + 0.7e − 0.2f + … + 0.3
然后同样判断一次正负。
所以,如果某一层是一批数a、b、c……,下一层是另一批数d、e、f……,每一个下一层的数,都是把上一层的许多数分别乘上不同系数,再加起来。基本规则并不会随着层数增加而突然变得神秘,变化的是每个连接使用什么系数。
每层的数字个数也不必相同。一张图片开始时有784个输入,下一层可以变成128个数,再下一层变成64个,最后只留下10个数,分别表示这幅图像更像0、1、2……还是9。哪个数最大,模型就作出相应判断。
单独拿出任何一个节点来看,它所做的事情都只是乘法、加减法,再加一次简单判断。复杂性来自另一件事:这样的节点很多,一层接一层,而且每一条连接前面的系数都可以不同。
这些系数,以及算式最后额外加上的数,就是模型能够通过训练改变的参数。
一个很小的“784—128—64—10”网络,已经有大约11万个这样的参数。怎样理解这个数字所产生的复杂程度?
不妨作一个极端简化:假定每个参数只有10种可能取值。那么整个网络就有10的11万次方种可能的参数组合。这个数字写出来并没有多少帮助,可以换一种想法。
宇宙从大爆炸到今天大约经过了4×10^17秒。可观测宇宙中的恒星数量大约在10^22到10^24这个量级。假定每一颗恒星都变成一台计算机,而且每秒能够试十亿种参数组合,从宇宙诞生一直试到今天,所有恒星加在一起也只能尝试大约10^51种组合。和前面的参数空间相比,这个数量仍然小得几乎没有意义。
甚至可以继续作一个荒唐的想象:把每一颗恒星都换成一整个同样大小的可观测宇宙,再让里面每颗恒星都这样计算;然后再把里面的每颗恒星继续换成一个宇宙,一层一层套下去。要把这个数量级追上,需要套上数千层。
真实训练当然绝不会把所有可能组合逐个尝试。它利用的是另一件事:模型每次犯错以后,可以算出许多参数分别朝哪个方向稍微改变,通常能使误差减小一点。于是它不是在一个不可想象的组合空间里随机试错,而是沿着能够让结果逐渐改善的方向,一点点改变参数。
经过大量训练以后,一批局部看起来极其简单的加减乘法,最后却可以共同完成数字识别这样的任务。神经网络的复杂,并不意味着每一步计算复杂。恰恰相反,每一步可以简单得近乎乏味;复杂能力来自海量简单关系经过训练以后形成的组合。
经过这些层以后,模型内部形成了一批新的数值关系。同一个“7”可以写得很大、很小、很斜,原始像素可能相差很多;经过多层计算以后,它们却可能变成较为接近的内部状态。相反,有些局部相似的“1”和“7”,又可以逐渐被分开。机器学习中所谓“表示”,大体说的就是这种事情:原始数据仍然是那些数据,但模型学会了一种更适合当前任务的组织方式。