让我们思考一个典型的神经网络 (neural network),即使是相对简单的。它可能包含一个输入层、一个或多个隐藏层以及一个输出层。数据流经这个网络,在每一步都进行变换。我们如何从数学上描述这一点呢?
让我们首先设想一个单一的神经元。它接收一些输入,计算加权和(加上一个偏置 (bias)),然后将该和通过一个激活函数 (activation function)。这看起来像:
- 线性组合: z=w1x1+w2x2+...+wnxn+b=w⋅x+b
- 激活: a=g(z)
神经网络中,数据通过层层变换。激活函数 g (例如Sigmoid、ReLU等) 对数据进行非线性处理。最终,输出 a 是输入 x 的一个函数。
现在,我们考虑一个多层网络。一个层的输出激活值(a(l))成为下一层(x(l+1))的输入。我们来追踪一个带有一个隐藏层的简单网络的路径:
- 输入: x
- 隐藏层计算:
- 线性组合: z(1)=W(1)x+b(1)
- 激活: a(1)=g(1)(z(1))
- 输出层计算:
- 线性组合(使用隐藏层的输出 a(1) 作为输入): z(2)=W(2)a(1)+b(2)
- 激活(最终输出): y^=a(2)=g(2)(z(2))
仔细观察最终输出 y^。我们可以通过代入中间步骤来写出它:
y^=g(2)(z(2))
y^=g(2)(W(2)a(1)+b(2))
y^=g(2)(W(2)g(1)(z(1))+b(2))
y^=g(2)(W(2)g(1)(W(1)x+b(1))+b(2))
这个方程表明得很清楚:神经网络是复合函数的数学表示。它是一个函数(g(2))应用于另一个计算结果(W(2)⋅+b(2)),而这个结果本身又涉及到另一个函数(g(1))的输出,以此类推,一直追溯到原始输入 x。
把它想象成俄罗斯套娃。最终输出取决于输出层的计算,这又取决于隐藏层的输出,这再取决于隐藏层的计算,这最终取决于输入 x 以及参数 (parameter) W(1),b(1)。
我们可以将这个流程可视化:
一个简单的前馈神经网络被视为一系列函数变换。每一层都应用一个线性变换,随后是一个非线性激活。一个层的输出作为下一个层的输入,从而形成一个复合函数。
为什么这个视角很重要?因为当我们训练神经网络时,我们通常会有一个损失函数 (loss function)(或成本函数),我们称之为 L,它衡量网络输出 y^ 与真实目标值 y 之间的偏差。损失 L 取决于 y^。正如我们刚刚看到的,y^ 是一个复杂的函数,它涉及到网络中的所有权重 (weight)(W(1),W(2),...)和偏置(b(1),b(2),...)。
为了使用梯度下降 (gradient descent)法训练网络(我们在前一章讨论过),我们需要计算损失 L 对网络中每一个权重和偏置的梯度。例如,我们需要 ∂W(1)∂L。
由于 L 取决于 y^,y^ 取决于 a(1),a(1) 取决于 z(1),而 z(1) 最终取决于 W(1),因此我们有一个依赖链。计算这个导数需要穿过这种嵌套结构。这正是链式法则不可或缺的地方。它提供了计算这些梯度的数学方法,通过将复杂的导数分解为网络计算每一步中较简单导数的乘积,从而高效地完成计算。链式法则在神经网络中的这种系统性应用,是反向传播 (backpropagation)算法的根本思想,我们将在下文查看。