如果你接触过机器学习,或许已经无数次见过 Tensor(张量)这个词。
PyTorch 中的数据以 Tensor 的形式存在,神经网络中的参数和计算过程也都建立在 Tensor 之上。
但 Tensor 究竟是什么?
它只是一个高维数组的别称吗?为什么普通的向量和矩阵无法满足机器学习的需求?又为什么神经网络训练过程中产生的梯度,同样需要用 Tensor 来表示?
要回答这些问题,我们需要回到最基础的数学对象:标量、向量和矩阵。
因为 Tensor 并不是一个突然出现的新概念,而是数学表示能力自然发展的结果。
在机器学习中,我们经常处理彩色图像(高 × 宽 × 通道)和视频(时间 × 高 × 宽 × 通道)这类多维数据。数学上,这些多维数组统称为张量(tensor)。
- 标量(一个数)是零阶张量。
- 向量(一维数组)是一阶张量。
- 矩阵(二维数组)是二阶张量。
- 三维及更高维的数组统称为高阶张量。
和矩阵一样,张量可以逐元素相加、逐元素相乘(Hadamard 乘法),也可以与标量相乘。而更复杂的张量积则不在本文讨论范围内,当然如果你真的很感兴趣,可以看这篇内容:张量积
Table of contents
Open Table of contents
张量求导
训练模型时,我们需要最小化损失函数 L。若 L 是参数 W 的函数,便要计算 ∇WL 来更新 W。这引出一个问题:输入是矩阵或更高阶张量时,导数的形状是什么?
下面从最简单的情形开始,再逐步推广。
标量函数的梯度
设 f(x):Rn→R 是标量函数,输入为 n 维向量 x。它的梯度 ∇xf 仍是 n 维向量,每个分量对应 f 对一个输入分量的偏导数:
∇xf=(∂x1∂f,∂x2∂f,⋯,∂xn∂f)⊤
这就是熟悉的梯度向量。
矩阵梯度
现在令输入为 m×n 矩阵 X,输出仍为标量 f(X)。对 X 中的每个元素 xij 求偏导,并按 X 的形状排成矩阵,就得到矩阵梯度:
设 f:Rm×n→R 为标量函数,则 f 对矩阵
X 的梯度定义为一个 m×n 的矩阵:
∇Xf=∂x11∂f∂x21∂f⋮∂xm1∂f∂x12∂f∂x22∂f⋮∂xm2∂f⋯⋯⋱⋯∂x1n∂f∂x2n∂f⋮∂xmn∂f
因此,标量对矩阵求导,得到的矩阵与输入矩阵形状相同。 这正是反向传播中反复使用的结论。
雅可比矩阵
再进一步,输入为 n 维向量 x,输出为 p 维向量 f(x)=(f1(x),…,fp(x))⊤。每个输出分量 fi 都可对每个输入分量 xj 求偏导,共有 p×n 个偏导数。按「第 i 行第 j 列为 ∂fi/∂xj」排列,便得到雅可比矩阵(Jacobian matrix):
设 f:Rn→Rp,其雅可比矩阵
Jf 为 p×n 矩阵:
Jf=∂x1∂f1∂x1∂f2⋮∂x1∂fp∂x2∂f1∂x2∂f2⋮∂x2∂fp⋯⋯⋱⋯∂xn∂f1∂xn∂f2⋮∂xn∂fp,(Jf)ij=∂xj∂fi
在大多数优化与机器学习文献中,雅可比矩阵都采用「输出维度为行、输入维度为列」的 p×n 约定。统一这一约定后,就不会因行、列方向不同而混淆。
一般情形
如果输入、输出都是矩阵,例如 F:Rm×n→Rp×q,每个输出元素 Fij 都要对每个输入元素 Xkl 求偏导。这些偏导数构成一个四维数组,也就是四阶张量:
(∇XF)ijkl=∂Xkl∂Fij
把导数记为 D,它的形状是 p×q×m×n:
- 前两轴 (i,j) 是输出轴,它们选择输出矩阵中的 Fij。
- 后两轴 (k,l) 是输入轴,它们选择输入矩阵中的 Xkl。
因此,Dijkl 的完整含义是「输出 Fij 对输入 Xkl 的敏感程度」。
对矩阵求导的含义
令
X=(X1,1X2,1X1,2X2,2).
取一个具体的矩阵函数:
A=(1324),F(X)=AX.
例如,代入
X=(5768),
输出左上角元素为
F1,1=1×5+2×7=19.
求导时不代入具体数值,而是保留变量。因此,F1,1 的表达式为
F1,1=X1,1+2X2,1.
固定一个输出元素 Fi,j 后,它是 X 的四个元素组成的标量函数。因此,
∂X∂Fi,j=∂X1,1∂Fi,j∂X2,1∂Fi,j∂X1,2∂Fi,j∂X2,2∂Fi,j.
这里的 ∂Fi,j/∂X 不是普通的「相除」,而是把 Fi,j 对 X 中每个元素的偏导数按原来的位置排成一个矩阵。
对这个具体的 F1,1 逐项求导:
∂X1,1∂F1,1∂X2,1∂F1,1=1,=2,∂X1,2∂F1,1∂X2,2∂F1,1=0,=0.
所以
∂X∂F1,1=(1200).
这正是四阶导数中的一个切片,也就是 D1,1,:,:。
更一般地,若输入是 k 阶张量、输出是 l 阶张量,导数就有 k+l 个轴。实际编程时,自动求导框架会直接计算所需的缩并1结果,我们通常无需手动展开这些高维结构。
设 x∈Rn 为输入向量,W∈Rn×m 为权重矩阵,b∈Rm 为偏置向量,定义
f(W)=W⊤x+b.这里 f 是 m 维向量,可以拆成 m 个标量函数:
f(W)=f1(W)f2(W)⋮fm(W).先固定一个输出分量 fi。此时 fi 是标量,而 W 是由 n×m 个元素组成的矩阵。因此,∂fi/∂W 的含义是 fi 对 W 每个元素的偏导数,并按 W 的形状排成矩阵:
∂W∂fi=∂W1,1∂fi⋮∂Wn,1∂fi⋯⋱⋯∂W1,m∂fi⋮∂Wn,m∂fi.这个矩阵的形状为 n×m。对 f1,f2,…,fm 分别做同样的计算,再沿着新的输出轴叠起来,就得到形状为 m×n×m 的三阶张量:
(∂W∂f)i,j,k=∂Wj,k∂fi.对当前函数,逐元素写出第 i 个输出:
fi=r=1∑nWr,ixr+bi.因此,
∂Wj,k∂fi={xj,0,k=i,k=i.也就是说,fi 只依赖 W 的第 i 列。固定 i 后,∂fi/∂W 只有第 i 列非零,这一列正是输入向量 x。
实际训练中,我们关心的是标量损失 L 对 W 的梯度。利用链式法则,可直接计算 ∇WL,结果是与 W 同形的矩阵。中间的高阶导数在链式法则的缩并中被消去,最终只留下参数对应的梯度。
因此,我们常说「梯度与参数同形」。
求导法则
梯度运算与标量求导遵循相同的基本规则:
- 线性法则:∇X(f+g)=∇Xf+∇Xg,∇X(αf)=α∇Xf(α 为常数)。
- 乘积法则:∇X(f⋅g)=f⋅∇Xg+g⋅∇Xf。
- 链式法则:若 y=g(X),z=f(y),则 ∇Xz=∇yz⋅∇Xg(这里的乘法需要按照张量缩并来理解,但形式上与以前学的链式法则一致)。
到这里,我们已经从标量、向量和矩阵,一步步走到了张量。
回顾整个过程,张量并不是一个突然出现的新概念。它只是描述数据的一种更自然的方式:当我们需要表示图片、声音、文本,甚至一个神经网络中的参数时,低维的向量和矩阵已经不足以进行表达,而张量提供了一种统一的语言。
同样,对于张量求导,我们不要望而生畏,这只需要用到一些基础的微积分和线性代数而已。它本质上仍然遵循微积分中熟悉的思想:研究变化,并利用这些变化寻找更好的方向。
如今,深度学习框架中的自动求导、梯度下降和神经网络训练,都建立在这些看似抽象的数学结构之上。理解张量,不只是理解一个数据结构,而是在理解现代机器学习背后的计算语言。