让我们思考一个典型的神经网络,即使是相对简单的。它可能包含一个输入层、一个或多个隐藏层以及一个输出层。数据流经这个网络,在每一步都进行变换。我们如何从数学上描述这一点呢?让我们首先设想一个单一的神经元。它接收一些输入,计算加权和(加上一个偏置),然后将该和通过一个激活函数。这看起来像:线性组合: $z = w_1 x_1 + w_2 x_2 + ... + w_n x_n + b = \mathbf{w} \cdot \mathbf{x} + b$激活: $a = g(z)$神经网络中,数据通过层层变换。激活函数 $g$ (例如Sigmoid、ReLU等) 对数据进行非线性处理。最终,输出 $a$ 是输入 $\mathbf{x}$ 的一个函数。现在,我们考虑一个多层网络。一个层的输出激活值($a^{(l)}$)成为下一层($x^{(l+1)}$)的输入。我们来追踪一个带有一个隐藏层的简单网络的路径:输入: $\mathbf{x}$隐藏层计算:线性组合: $\mathbf{z}^{(1)} = \mathbf{W}^{(1)}\mathbf{x} + \mathbf{b}^{(1)}$激活: $\mathbf{a}^{(1)} = g^{(1)}(\mathbf{z}^{(1)})$输出层计算:线性组合(使用隐藏层的输出 $\mathbf{a}^{(1)}$ 作为输入): $\mathbf{z}^{(2)} = \mathbf{W}^{(2)}\mathbf{a}^{(1)} + \mathbf{b}^{(2)}$激活(最终输出): $\hat{y} = a^{(2)} = g^{(2)}(\mathbf{z}^{(2)})$仔细观察最终输出 $\hat{y}$。我们可以通过代入中间步骤来写出它:$$ \hat{y} = g^{(2)}(\mathbf{z}^{(2)}) $$ $$ \hat{y} = g^{(2)}(\mathbf{W}^{(2)}\mathbf{a}^{(1)} + \mathbf{b}^{(2)}) $$ $$ \hat{y} = g^{(2)}(\mathbf{W}^{(2)} g^{(1)}(\mathbf{z}^{(1)}) + \mathbf{b}^{(2)}) $$ $$ \hat{y} = g^{(2)}(\mathbf{W}^{(2)} g^{(1)}(\mathbf{W}^{(1)}\mathbf{x} + \mathbf{b}^{(1)}) + \mathbf{b}^{(2)}) $$这个方程表明得很清楚:神经网络是复合函数的数学表示。它是一个函数($g^{(2)}$)应用于另一个计算结果($\mathbf{W}^{(2)} \cdot + \mathbf{b}^{(2)}$),而这个结果本身又涉及到另一个函数($g^{(1)}$)的输出,以此类推,一直追溯到原始输入 $\mathbf{x}$。把它想象成俄罗斯套娃。最终输出取决于输出层的计算,这又取决于隐藏层的输出,这再取决于隐藏层的计算,这最终取决于输入 $\mathbf{x}$ 以及参数 $\mathbf{W}^{(1)}, \mathbf{b}^{(1)}$。我们可以将这个流程可视化:digraph G { rankdir=LR; node [shape=record, style=filled, fillcolor="#e9ecef", color="#adb5bd"]; edge [color="#495057"]; input [label="{<in> 输入 | x }", shape= Mrecord, fillcolor="#a5d8ff"]; subgraph cluster_1 { label = "隐藏层 (L1)"; style=filled; color="#dee2e6"; node [shape=record]; lin1 [label="{ 线性 | <in> z^(1) = W^(1)x + b^(1) }"]; act1 [label="{ 激活 | <out> a^(1) = g^(1)(z^(1)) }"]; lin1:out -> act1:in [style=invis]; // 强制子图内垂直布局 } subgraph cluster_2 { label = "输出层 (L2)"; style=filled; color="#dee2e6"; node [shape=record]; lin2 [label="{ 线性 | <in> z^(2) = W^(2)a^(1) + b^(2) }"]; act2 [label="{ 激活 | <out> ŷ = a^(2) = g^(2)(z^(2)) }"]; lin2:out -> act2:in [style=invis]; // 强制子图内垂直布局 } output [label="{<in> 输出 | ŷ }", shape=Mrecord, fillcolor="#a5d8ff"]; input:out -> lin1:in; act1:out -> lin2:in; act2:out -> output:in; }一个简单的前馈神经网络被视为一系列函数变换。每一层都应用一个线性变换,随后是一个非线性激活。一个层的输出作为下一个层的输入,从而形成一个复合函数。为什么这个视角很重要?因为当我们训练神经网络时,我们通常会有一个损失函数(或成本函数),我们称之为 $L$,它衡量网络输出 $\hat{y}$ 与真实目标值 $y$ 之间的偏差。损失 $L$ 取决于 $\hat{y}$。正如我们刚刚看到的,$\hat{y}$ 是一个复杂的函数,它涉及到网络中的所有权重($\mathbf{W}^{(1)}, \mathbf{W}^{(2)}, ...$)和偏置($\mathbf{b}^{(1)}, \mathbf{b}^{(2)}, ...$)。为了使用梯度下降法训练网络(我们在前一章讨论过),我们需要计算损失 $L$ 对网络中每一个权重和偏置的梯度。例如,我们需要 $\frac{\partial L}{\partial \mathbf{W}^{(1)}}$。由于 $L$ 取决于 $\hat{y}$,$\hat{y}$ 取决于 $\mathbf{a}^{(1)}$,$\mathbf{a}^{(1)}$ 取决于 $\mathbf{z}^{(1)}$,而 $\mathbf{z}^{(1)}$ 最终取决于 $\mathbf{W}^{(1)}$,因此我们有一个依赖链。计算这个导数需要穿过这种嵌套结构。这正是链式法则不可或缺的地方。它提供了计算这些梯度的数学方法,通过将复杂的导数分解为网络计算每一步中较简单导数的乘积,从而高效地完成计算。链式法则在神经网络中的这种系统性应用,是反向传播算法的根本思想,我们将在下文查看。