人类的视觉系统,是大自然的奇迹之一。
来看看下面一串手写的数字:
大多数人可以毫不费力地认出这些数字是504192。这种轻松是欺骗性的,我们觉得很轻松的一瞬,其实背后过程非常复杂。
在我们大脑的每个半球,人类都有一个初级视觉皮层,也被称为V1,包含1.4亿个神经元,它们之间有几百亿个连接。人类的视觉不仅涉及V1,还涉及整个系列的视觉皮层–V2、V3、V4和V5–一层一层进行逐渐复杂的图像处理。
我们的脑袋里有一台超级计算机,经过数亿年的进化调整,极好地适应了对视觉世界的理解。识别手写的数字并不容易——相反,我们人类在理解眼睛看到的东西上,有着惊人的能力。但几乎所有的工作都是在无意识中完成的。因此,我们通常不了解我们的视觉系统解决了多么艰难的问题。
如果你尝试写一个程序去识别上面的数字,难度是显而易见的。我们自己做的时候很容易,但在编写程序时变得很困难。我们如何识别形状的简单直觉–“9在顶部有一个环,右下方有一个垂直的笔画”–结果发现用算法来表达并不那么简单。
当你试图使这种规则精确化时,你很快就会迷失在例外、异常和特殊情况的泥潭中。让人绝望。
神经网络用另一种方式来解决这个问题。方法是用一堆手写数字来训练,这些数字叫训练例子(training examples)。
然后开发一个能从这些训练例子中学习的系统。学习的意思是,这个系统能利用这些例子自动推断识别手写数字的规则。通过增加训练例子的数量可以提高准确性。虽然刚刚的例子只用了100个训练数字,但我们可以通过上千上万甚至数百亿的数量来建立一个更好的手写识别器。
我们将在本章写一个程序,实现一个学习识别手写数字的神经网络。程序只有74行,而且没有使用任何特殊的神经网络库。这个简短的程序可以识别数字,准确率超过96%,无需人工干预。此外,在后面的章节中,我们继续开发一些新的想法,将这个准确率提高到99%以上。
当前最好的商用神经网络已经非常出色,银行用它来处理支票,邮局用它来识别地址,广泛应用。
我们会专注在手写识别上,因为它是学习神经网络的一个优秀的“原型”问题。作为一个“原型”,它有一个很优秀的地方——识别出手写的数字很有成就感,但同时它没那么难,不需要更复杂的解决方案和巨大的计算资源。此外,它也是开发更多高级技术的好方法,比如深度学习。因此,在这本书中,我们会反复回到手写识别的主题上。在书的后面,我们会讨论如何将其中的想法应用到计算机视觉的其他问题,以及语音、自然语言跟其他领域。
当然,这一章如果只写一个识别手写数字的程序,那么会很短!但在这一过程中,我们会发展出很多关于神经网络的关键思想,包括两种重要的人工神经元(感知器和西格蒙德函数神经元)和标准学习算法(随机梯度下降)。在整个过程中,我着重于解释为什么事情是这样做的,以及建立你的神经网络直觉。这需要比我只介绍基本的机械原理更多的讨论,但这是值得的,因为你会获得更深的理解。
作为回报,在章节末,我们会理解什么是深度学习,以及深度学习的重要性。
感知器
什么是神经网络?我先从解释一种叫 “感知器” 的人工神经元开始。感知器是20世纪50年代和60年代由科学家Frank RosenBlatt 开发的,灵感来自 Warren McCulloch 和 Walter Pitts 的早期工作。现如今,使用其他人工神经元模型更普遍——在本书中,以及在许多现代的神经网络里,主要使用的,是一个叫西格蒙德(Sigmoid)神经元的模型。我们很快就会了解到Sigmoid,但为了更好的理解Sigmoid的定义,值得花时间先了解一下感知器。
那么,感知器是怎么工作的?一个感知器接受多个二进制输入,x1x_1x1,x2x_2x2,… ,产出一个二进制输出:
上图的例子里,感知器有三个输入:x1x_1x1,x2x_2x2,x3x_3x3。通常它的输入可多可少。Rosenblatt 提出一个简单的规则来计算输出。他引入了权重的概念,w1w_1w1,w2w_2w2,w3w_3w3表示各自输入对输出的重要性的实数。神经元的输出0或1,是由加权和∑jwjxj\sum_{j}w_jx_j∑jwjxj是否小于或大于某个阈值决定的。就像权重一样,阈值是一个实数,是神经元的一个参数。用更精确的代数术语来表示:
output={0∑jwjxj⩽threshold1∑jwjxj>threshold(1)output=\begin{cases}0&\sum_{j}w_jx_j\leqslant threshold\\1&\sum_{j}w_jx_j>threshold\end{cases} (1)output={01∑jwjxj⩽threshold∑jwjxj>threshold(1)
这就是感知器原理的全部内容!
这就是基本的数学模型。你可以把感知器想象是一种通过权衡不同因素来做出决定的设备。举个例子。这不是一个很现实的例子,但它很容易理解,我们很快就会有更现实的例子。假设周末快到了,你听说在你的城市会有一个奶酪节。你喜欢奶酪,并试图决定是否要去参加这个节日。你可能会通过权衡三个因素来做决定:
天气好吗?你的男朋友或者女朋友愿意陪你一起去吗?你没有车,奶酪节离公共交通近吗?
我们用三个变量来代表这三个因素:x1x_1x1、x2x_2x2、x3x_3x3。比如,如果天气好,x1=1x_1=1x1=1,如果天气不好 xx1=0x_1=0x1=0。类似的,男女朋友想去 x2=1x_2=1x2=1,不想去 x2=0x_2=0x2=0。再类似的,奶酪节离公共交通近,x3=1x_3=1x3=1,不近 x3=0x_3=0x3=0。
现在,假设你绝对喜欢奶酪,喜欢到你非常乐意去参加节日,即使你的男女朋友对奶酪不感兴趣,而且节日也很难去。但也许你非常厌恶糟糕的天气,天气不好的话你不可能去参加。你就可以用感知器来模拟决策了。一种方法是为天气选择一个权重w1=6w_1=6w1=6,其他条件选择w2=2w_2=2w2=2和w3=2w_3=2w3=2。w1w_1w1的值越大,表明天气对你来说越重要。最后,假设你为感知器选择一个555的阈值。有了这些选择,感知器就实现了所需的决策模型,只要天气好就输出111,天气不好就输出000。你的男女朋友想不想去,或者附近公共交通怎么样,这对输出没有区别。
通过改变权重和阈值,我们可以得到不同的决策模型。例如,假设我们选择了一个为333的阈值,那么感知器就会这么决定:只要天气好,或者节日在公共交通附近同时你的男女朋友愿意一起,你就该去参加节日。也就是说,这是一个不同于刚刚的决策模型。降低阈值意味着你更加愿意去参加节日。
很明显,感知器并不是人类决策的完整模型!但这个例子说明了感知器是如何权衡并决策的。可以想象,一个复杂的感知器模型可以做出很微妙的决定:
在这个网络里,第一列感知器——我们称之为第一层感知器——会通过权衡证据,做出三个简单的决策。那么第二层的感知器呢?第二层的每一个感知器则通过第一层的感知器的权衡结果来做决定。这样,第二层可以做出比第一层更加复杂的决策来,继而第三层则可以更加复杂。以此类推,多层感知器网络就可以进行更加复杂的决策了。
顺带一提,当我定义感知器时,我说一个感知器只有一个输出。而在上面的网络里,感知器看上去像是有多个输出。事实上它们并不违背单一输出的定义。多个箭头只是一种表达方式,表明一个感知器的输出被用作其他几个感知器的输入。这种表示方法,比画一个单一的输出线,然后再分叉要方便一些。
让我们简化一下我们描述感知器的方式。∑jwjxj>threshold\sum_{j}w_jx_j>threshold∑jwjxj>threshold这种表达太笨重了,我们可以修改两个符号来简化它。第一,把∑jwjxj\sum_{j}w_jx_j∑jwjxj写成点积,w⋅x=∑jwjxjw·x=\sum_{j}w_jx_jw⋅x=∑jwjxj,其中www跟xxx是向量,其组成部分分别是权重跟输入。第二,把阈值移到不等式的另一边,用所谓的感知器的偏置来代替它,b≡−thresholdb \equiv-thresholdb≡−threshold(恒等式),使用偏置来代替阈值。感知器的规则公式可以重写为:
output={0∑jw⋅x+b⩽01∑jw⋅x+b>0(2)output=\begin{cases}0&\sum_{j}w·x+b\leqslant 0\\1&\sum_{j}w·x+b>0\end{cases} (2)output={01∑jw⋅x+b⩽0∑jw⋅x+b>0(2)
你可以把偏置看作是衡量让感知器输出111的难易程度的标准。对于一个有很大的偏置值的感知器来说,感知器很容易就会输出111。相应的,如果偏置值是很大的负数,感知器就很难输出111了。显然,目前来看,引入偏差只是描述感知器的一个小变化,但是我们在后面会看到它会导致更大的符号简化。因此,在接下来,我们再不使用阈值,而是使用偏置值。
我们已经把感知器描述为一种权衡证据已做出决定的方法。另一种感知器的用法,是用来进行一些基础计算。这些基础计算方法,可以是我们比较熟悉的底层运算,比如 与(AND)、或(OR)、与非(NAND)等。比如说,假设我们有个感知器,它有两个输入,每个输入的权重是−2-2−2,同时有一个值为333的偏置。感知器如下:
然后我们可以看到,输入000000之后感知器会输出111,因为(−2)∗0+(−2)∗0+3=3(-2)*0+(-2)*0+3=3(−2)∗0+(−2)∗0+3=3结果为正。这里我引入了∗*∗符号,使得乘法明确化。类似的计算有,输入010101和101010会输出111,但是输入111111会输出0,因为(−2)∗1+(−2)∗1+3=−1(-2)*1+(-2)*1+3=-1(−2)∗1+(−2)∗1+3=−1,结果是负数。到这里,我们的感知器实现了一个与非门!
与非门的例子说明我们可以用感知器来进行一些简单的逻辑运算。事实上,我们的确可以用感知器网络来进行任意的逻辑运算。因为,与非门是通用的计算方式,我们可以用与非门来组合成任何计算。例如,我们可以使用与非门来构建一个电路,相加两个比特,x1x_1x1,x2x_2x2。这需要计算位相加,x1⊕x2x_1 \oplus x_2x1⊕x2,以及一个进位,当x1x_1x1和x2x_2x2都为1的时候,进位被设置为111,也就是说,进位只是位相乘x1x_1x1和x2x_2x2:
为了得到一个等效的感知器网络,我们用具有两个输入的感知器取代所有的NAND门,每个感知器的权重为-2,整体偏置为3。*注意,我把对应于右下角NAND门的感知器移了一点,只是为了让图上的箭头更容易画出来:
值得注意的是,这个感知网络最左边的感知器的输出,被两次用作最下面的感知器的输入。当我定义感知器模型的时候,我没有说这种双重输出到同一位置的做法是否被允许。实际上,这不太重要。如果我们不想允许这种用法,那可以简单地将两条权重为−2-2−2的链接合并成权重为-4的一条链接。(如果你不觉得这很显然,最好在此停下来证明这个变化成立。)。有了这个改变,网络看起来如下,所有未标记的权重都等于−2-2−2,所有的偏差都等于333,而单一的权重为−4-4−4,如标记的那样:
到目前为止,我一直在画输入,如x1x_1x1和x2x_2x2,作为变量漂浮在感知器网络的左边。事实上,传统的做法是画一个额外的感知器层–输入层–来编码输入:
这种输入感知器的符号,我们有一个输出,但没有输入:
是一种速记法。它并不表示一个没有输入的感知器。要理解这一点,假设我们确实有一个没有输入的感知器。那么加权和w⋅x=∑jwjxjw·x=\sum_{j}w_jx_jw⋅x=∑jwjxj将总是零,所以如果b>0b>0b>0,感知器将输出111,如果b≤0b≤0b≤0,感知器将输出000。也就是说,感知器只是输出一个固定的值,而不是期望的值(上面的例子中的x1x_1x1)。最好是把输入感知器看作根本不是真正的感知器,而是简单定义为输出期望值的特殊单元,x1x_1x1,x2x_2x2,…
加法器的例子展示了一个感知器网络如何被用来模拟一个包含许多与非门的电路。因为与非门是通用的计算方法,所以感知器也是通用的计算方法。
感知器的计算普遍性既令人欣慰又令人失望。令人欣慰的是,它告诉我们,感知器网络可以像任何其他计算设备一样强大。但它也令人失望,因为它使我们觉得感知器似乎只是一种新型的与否门。这并不是什么新鲜事物。
然而,情况比想象的要好。事实证明,我们可以设计出学习算法,可以自动调整人工神经元网络的权重和偏置。这种调整是对外部刺激的反应,无需程序员的直接干预。这些学习算法使我们能够以一种与传统逻辑门完全不同的方式使用人工神经元。我们的神经网络可以简单地学习解决问题,而不是明确地布置一个由与非门和其他门组成的电路——有时直接设计一个传统电路是非常困难的。