AI 数学课程 · 第 38 课,共 180

用一个微型神经网络理解线性代数

通过 NumPy 示例和交互式探索器,逐步追踪两层网络中的每个形状、偏置、激活和秩变化。

分享这篇文章

神经网络看起来可能只是一串矩阵运算,但每一步都会改变数值的含义。沿着一个小型网络从输入追踪到输出,就能看清整个过程:矩阵形状决定哪些映射可以连接,偏置会平移这些映射,秩限制独立方向,而激活函数会改变组合所能表达的内容。

学完后,你将能够逐步追踪每个形状,手算或用 NumPy 计算一个两层网络,并说明偏置、秩和激活分别会在何处影响结果。

本课综合了三个此前学过的工具:不显式求逆来解线性系统、用核与像描述丢失和可达的方向,以及用偏置平移仿射映射。《Mathematics for Machine Learning》第 2 章提供了这次综合所需的线性代数基础。下面的网络、数字、代码、图示和练习均为原创内容。

逐层追踪两个仿射层的维度

这里的“两层网络”指两个可学习的仿射映射,中间夹着一个隐藏层激活函数。我们将输出保留为两个分数,不再应用概率转换或类别判定。设输入为列向量 x∈R2x\in\mathbb{R}^2。每个仿射映射都由矩阵和偏置向量组合而成:

相邻映射之间的共享维度必须匹配:W1W_1 有两列,对应两个输入特征;它有两行,对应两个隐藏层输出。接着 W2W_2 接收这两个隐藏值,并返回两个输出分数。偏置 b1b_1 分别加到每个隐藏坐标上,b2b_2 则分别加到每个输出上。维度不兼容的矩阵无法执行所述映射;这与 NumPy 矩阵乘积所做的形状检查相同。

追踪一个双输入、双隐藏单元的网络移动任一输入坐标,或切换隐藏层规则。初始状态为 x = (1, 1) 且使用 ReLU;下方所有数值都在 JavaScript 运行前渲染。
  1. 输入 · 2 个特征
  2. 仿射映射 · 2 个隐藏值
  3. 隐藏层激活
  4. 仿射映射 · 2 个分数
  5. 输出 · 2 个分数

z = W₁x + b₁

h = σ(z)

y = W₂h + b₂

更改输入和隐藏层规则

使用方向键或滑块更改每个输入,范围为 −2 到 2,步长为 0.25。

隐藏层规则
激活前 z
(-0.50, 0.50)
隐藏层输出 h
(0.00, 0.50)
分数 y
(1.50, -0.50)
激活的隐藏单元
1 / 2
局部雅可比矩阵的秩
1

初始输入为 1、1;隐藏层输出为 0、0.5;分数为 1.5、−0.5。

图示中的初始值使用以下固定且由人工选定的参数:

W1=[1−111],b1=[−0.5−1.5],W2=[12−11],b2=[0.5−1]。W_1=\begin{bmatrix}1&-1\\1&1\end{bmatrix},\quad b_1=\begin{bmatrix}-0.5\\-1.5\end{bmatrix},\quad W_2=\begin{bmatrix}1&2\\-1&1\end{bmatrix},\quad b_2=\begin{bmatrix}0.5\\-1\end{bmatrix}。

这个例子足够小,可以逐项追踪。所选数值旨在展示 ReLU 如何关闭一个隐藏单元;它们不是训练得到的参数,也不代表典型模型。

计算一次预测,保留每个中间结果

取 x=(1,1)Tx=(1,1)^\mathsf{T}。第一个矩阵用不同方式组合两个输入坐标,再加上偏置,平移每个结果:

NumPy 的 @ 运算符执行矩阵乘法。下面的 + b 运算使用 NumPy 的逐元素广播,将长度为二的偏置加到长度为二的结果上。这段小程序会打印实际的中间向量:

import numpy as np

W1 = np.array([[1.0, -1.0], [1.0, 1.0]])  # (2, 2)
b1 = np.array([-0.5, -1.5])               # (2,)
W2 = np.array([[1.0, 2.0], [-1.0, 1.0]])  # (2, 2)
b2 = np.array([0.5, -1.0])                # (2,)
x = np.array([1.0, 1.0])                  # (2,)

z = W1 @ x + b1                           # (2,) = (2, 2) @ (2,) + (2,)
h = np.maximum(z, 0.0)                    # (2,), elementwise ReLU
y = W2 @ h + b2                           # (2,) = (2, 2) @ (2,) + (2,)

print('z:', z)  # [-0.5  0.5]
print('h:', h)  # [0.  0.5]
print('y:', y)  # [ 1.5 -0.5]

相同的宽度逻辑也适用于一批样本。如果将 BB 个样本按行存储,数组形状为 (B,2)(B,2);右乘 W1TW_1^\mathsf{T} 后得到形状为 (B,2)(B,2) 的隐藏值,再右乘 W2TW_2^\mathsf{T} 得到形状为 (B,2)(B,2) 的分数。PyTorch 的 Linear 层采用这种按行排列的约定:其权重形状为 (out features,in features)(\text{out features},\text{in features}),并执行 y=xAT+by=xA^\mathsf{T}+b。因此,Linear(2, 2) 保存一个形状为 (2,2)(2,2) 的权重和一个长度为二的偏置。PyTorch 的 ReLU 逐元素应用并保持输入形状,与上面的步骤一致(Linear 文档、ReLU 文档、NumPy matmul 文档)。

没有激活函数时,两层仍可合并为一个仿射映射

仿射层中的偏置起平移作用。如果隐藏层步骤是恒等映射,可以通过代入将两层合成为:

y=W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2)。y=W_2(W_1x+b_1)+b_2 =(W_2W_1)x+(W_2b_1+b_2)。

因此,堆叠更多没有非线性激活的仿射映射,并不能把整体输入输出规则变成非线性规则;矩阵会相乘并合并为一个矩阵,偏置也会合并为一个偏移量。使用本例所选数值,W2W1=[3102]W_2W_1=\begin{bmatrix}3&1\\0&2\end{bmatrix},且 W2b1+b2=(−3,−2)TW_2b_1+b_2=(-3,-2)^\mathsf{T}。因此,采用恒等映射时,(1,1)T(1,1)^\mathsf{T} 会映射到 (1,0)T(1,0)^\mathsf{T}。

矩阵的秩表示其独立输出方向的数量。在这个恒等映射示例中,W1W_1 和 W2W_2 的秩都是二,它们的乘积秩也为二。如果网络的隐藏层宽度为 rr,仿射组合的线性部分的秩不可能大于 rr,因为它只通过 rr 个坐标进行分解。如果任一矩阵的秩更低,组合后的秩还可能更低。这些都是上界:窄层可能形成瓶颈,但仅凭宽度无法保证具体的秩。

ReLU 使映射成为分段仿射函数

当隐藏单元的激活前值为负数时,ReLU 会将其变为零。在所有激活前值符号保持不变的任一区域内,激活单元的集合也固定不变。令 DD 为对角矩阵,其中每个激活的 ReLU 单元对应一个 11,每个未激活单元对应一个 00。在该区域内,两层网络的局部线性部分为

J=W2DW1。J=W_2DW_1。

对于上面的输入,z=(−0.5,0.5)Tz=(-0.5,0.5)^\mathsf{T},所以 D=[0001]D=\begin{bmatrix}0&0\\0&1\end{bmatrix}。于是

J=[12−11][0001][1−111]=[2211],rank⁡(J)=1。J= \begin{bmatrix}1&2\\-1&1\end{bmatrix} \begin{bmatrix}0&0\\0&1\end{bmatrix} \begin{bmatrix}1&-1\\1&1\end{bmatrix} =\begin{bmatrix}2&2\\1&1\end{bmatrix}, \qquad \operatorname{rank}(J)=1。

在该输入附近,只要第一个隐藏单元仍未激活,两个输出分数都会随相同的输入组合 x1+x2x_1+x_2 改变;差异方向 (1,−1)(1,-1) 不会产生影响。矩阵 JJ 描述的是该激活区域内的局部变化,而不是整个网络共用的一个全局矩阵。在 ReLU 的边界上,如果激活前值等于零,函数会出现折点,因此没有任何一个相邻区域的雅可比矩阵能够描述边界两侧。

隐藏层宽度和权重也会限制网络可以表达的内容。在这个特定示例中,两条激活前值为零的直线将输入平面划分为若干区域,每个区域具有不同的激活单元模式;每个区域都有一条仿射规则,跨越一条直线时,这条规则可能改变。更一般地说,加入非线性激活函数可以使非线性函数成为可能,但并不意味着权重的任意取值都能表达复杂函数。隐藏单元可能对关注的输入始终不激活,矩阵也可能压缩方向。矩阵形状、秩、偏置偏移量和激活模式共同决定网络的行为。

这就是为什么仅看输出值只能提供有限的调试信息。追踪形状可以发现不兼容的连接;比较 zz 和 hh 可以看到哪些特征被门控;检查层的秩或局部雅可比矩阵,可以识别丢失的方向。如果单独的拟合步骤要求你求解 Ax≈bAx\approx b,第 025 课解释了数值求解器为何可以避免构造 A−1A^{-1} 的风险。网络的前向传播本身会使用矩阵乘积,不需要对学得的权重求逆。

看清这些映射和方向后,接下来要问的是向量有多大。第 039 课介绍范数,用于衡量向量大小、比较嵌入尺度并描述参数惩罚。

检查理解情况

Question 1

对于本课中的网络,请列出 x、W₁、b₁、z、h、W₂、b₂ 和 y 的形状。为什么 W₂ 可以接收 h?

Show the step-by-step solution

xx 的形状为 (2,)(2,),W1W_1 为 (2,2)(2,2),b1b_1 为 (2,)(2,),而 zz 和 hh 都是 (2,)(2,)。第二个矩阵 W2W_2 为 (2,2)(2,2),其偏置 b2b_2 的形状为 (2,)(2,),输出 yy 的形状为 (2,)(2,)。W2W_2 的内维度为二,与 hh 的两个分量匹配;因此该矩阵可将隐藏向量映射为两个输出分数。

Question 2

当 x = (1, 1)ᵀ 时,使用本课的矩阵和 ReLU 计算 z、h 和 y。请展示偏置与激活步骤。

Show the step-by-step solution

首先 W1x=(0,2)TW_1x=(0,2)^\mathsf{T},加上 b1=(−0.5,−1.5)Tb_1=(-0.5,-1.5)^\mathsf{T} 后得到 z=(−0.5,0.5)Tz=(-0.5,0.5)^\mathsf{T}。逐坐标应用 ReLU,得到 h=(0,0.5)Th=(0,0.5)^\mathsf{T}。接着 W2h=(1,0.5)TW_2h=(1,0.5)^\mathsf{T};再加上 b2=(0.5,−1)Tb_2=(0.5,-1)^\mathsf{T},得到 y=(1.5,−0.5)Ty=(1.5,-0.5)^\mathsf{T}。

Question 3

为什么移除隐藏层激活函数后仍然是一个仿射映射?请计算本例中合并后的矩阵和偏置。

Show the step-by-step solution

将第一个仿射规则代入第二个:W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2)W_2(W_1x+b_1)+b_2=(W_2W_1)x+(W_2b_1+b_2)。这里矩阵乘积为 W2W1=[3102]W_2W_1=\begin{bmatrix}3&1\\0&2\end{bmatrix}。此外,W2b1=(−3.5,−1)TW_2b_1=(-3.5,-1)^\mathsf{T},因此加上 b2=(0.5,−1)Tb_2=(0.5,-1)^\mathsf{T} 后,合并偏置为 (−3,−2)T(-3,-2)^\mathsf{T}。结果仍具有 Ax+cAx+c 的形式,所以它是一个仿射映射。

Question 4

当 x = (1, 1)ᵀ 时,采用恒等隐藏步骤的版本会产生什么输出?与 ReLU 结果相比有何不同?

Show the step-by-step solution

采用恒等映射时,使用上一题得到的合并映射:[3102](1,1)T+(−3,−2)T=(1,0)T\begin{bmatrix}3&1\\0&2\end{bmatrix}(1,1)^\mathsf{T}+(-3,-2)^\mathsf{T}=(1,0)^\mathsf{T}。使用 ReLU 时,输出为 (1.5,−0.5)T(1.5,-0.5)^\mathsf{T},因为第一个隐藏激活前值为负数并变为零,而第二个仍为 0.50.5。差异来自逐坐标应用的激活函数。

Question 5

一个网络把 5 个输入映射到宽度为 3 的隐藏层,再映射到 4 个输出,中间没有激活函数。其线性部分的最大可能秩是多少?若第一个权重矩阵的秩为 2,第二个为 3 呢?

Show the step-by-step solution

组合矩阵经过三个隐藏坐标分解,因此其秩最多为三。它也不能超过输入维度五或输出维度四。如果第一个矩阵的秩为二,第二个矩阵的秩为三,则乘积的秩至多为这些上界中最小的值,即二。这些都是上界;如果方向彼此错位,实际乘积的秩还可能更低。

Question 6

在示例输入处,第一个 ReLU 单元未激活,第二个已激活。请计算局部雅可比矩阵的秩,并解释其含义。

Show the step-by-step solution

激活掩码为 D=diag⁡(0,1)D=\operatorname{diag}(0,1)。相乘得到 J=W2DW1=[2211]J=W_2DW_1=\begin{bmatrix}2&2\\1&1\end{bmatrix}。它的第二列与第一列相同,因此只有一个独立列,秩为一。在这个激活区域内,沿一个方向的小幅输入变化不会改变输出;两个分数都依赖相同的输入组合。这是局部结论,因为 ReLU 状态改变时矩阵 DD 也会改变。

Question 7

当 ReLU 激活前值恰好为零时,会发生什么变化?能否把任一相邻区域的雅可比矩阵当作该点的单一导数?

Show the step-by-step solution

ReLU 在零处有一个拐角:零的左侧斜率为零,右侧斜率为一。当激活前值恰好为零时,该坐标处的函数不可微,因此不存在一个能同时描述两侧的普通雅可比矩阵。网络输出仍然有定义;无法成立的是跨越边界的单一局部线性近似。

Question 8

一批数据有 8 个样本,每个样本有 4 个特征。对于 PyTorch Linear(4, 3),输入、输出形状和权重形状分别是什么?

Show the step-by-step solution

批次输入形状为 (8,4)(8,4),即八行、每行四个输入特征。PyTorch 保存的层权重形状为 (out features,in features)(\text{out features},\text{in features}),因此权重为 (3,4)(3,4),偏置形状为 (3,)(3,)。该层返回 (8,3)(8,3),保留批次维度,并将最后一个特征维度替换为三个输出。

资料来源

  1. Mathematics for Machine Learning book PDF, Chapter 2
  2. NumPy documentation: numpy.matmul
  3. PyTorch documentation: torch.nn.Linear
  4. PyTorch documentation: torch.nn.ReLU