1. 张量(Tensor)的基本概念

人工智能领域,往往需要海量的数据处理,此前在机器学习部分,为了便于理解概念,我们一直将数据表示为标量,而实际应用中,数据可能是向量、矩阵、高阶张量。

接下来的深度学习中,数据愈发丰富,很多数据是高维的,如图像、视频、音频等。同时,深度学习算法本身也愈加和张量的一些数据特性密不可分(很多算法本身基于张量的特性构建),因此,我们有必要系统回顾整理张量.

张量(Tensor)是数学和物理学中常用的一个概念,也是AI领域中的核心数据结构之一。

便于理解,我们可以结合标量等数据格式进行对比:

  • 标量(Scalar):零阶张量,即一个单一的数值。
  • 向量(Vector):一阶张量,即一维数组,如 ​[1, 2, 3]
  • 矩阵(Matrix):二阶张量,即二维数组,如 ​\begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}
  • 高阶张量(Higher-order Tensor):更高维度的数组、矩阵,如三阶张量(立方体数组)和四阶及更高阶张量。

1.1 张量的性质

  • 维度(Dimensions):张量的阶数,即张量所具有的索引数量。例如,一个三阶张量可能是一个形状为 ​(3, 4, 5) 的三维数组。
  • 形状(Shape):从外至内张量每个维度的大小。形状可以用一个元组表示,例如形状为 ​(3, 4, 5) 的张量表示它在第一个维度有3个元素,第二个维度有4个元素,第三个维度有5个元素。
  • 类型(Type):张量中的元素类型,如整数、浮点数等。

1.2 张量的应用与优势

  • 数据表示

    • 多维数据的自然表示:深度学习需要处理各种类型的数据,如图像、音频、文本等。这些数据通常是多维的。
      例如,彩色图像可以表示为三维张量(高度、宽度、颜色通道),视频可以表示为四维张量(时间、高度、宽度、颜色通道)。
    • 批量处理:为了提高训练效率,深度学习通常会对多个样本进行批量处理。批量数据可以用张量表示。
      例如,一个形状为 ​(N, H, W, C) 的四维张量,其中 ​N 是批量大小。
  • 高效运算

    • 矢量化操作:进行张量级别运算,可以避免显式的循环操作,同时可以直接调用cpu、gpu对应的硬件加速,如矩阵乘法、卷积等操作。这种矢量化操作大大提高了计算效率。
    • 并行计算:GPU擅长处理大规模的矩阵和张量操作,现代深度学习框架(如TensorFlow、PyTorch)可以利用GPU进行大规模并行计算。这使得大规模神经网络的训练和推理成为可能。
  • 自动微分

    • 梯度计算:深度学习模型的训练过程依赖于反向传播算法,而反向传播需要计算损失函数相对于模型参数的梯度。张量计算库提供了自动微分功能,可以自动计算复杂张量运算的梯度,从而简化了模型训练。

结合上文,我们可以对张量进行简单概括:AI领域涉及海量数据运算,使用标量格式,无论数据表示、计算效率等,都较为复杂困难,因此,我们因尽量将大量的标量数据处理为向量、矩阵、高阶张量进行运算。
接下来,让我们回忆整理一下张量的数学计算。

2. 张量的逐元素(element-wise)运算

张量的逐元素运算,是张量最基本的运算,它支持加、减、乘、除、求导...等。

它的概念理解起来其实很简单,如果我们将张量理解为一系列标量,那么逐元素运算就是将两组或多组相同形状的数据根据位置一 一对应,逐个进行运算。

它的存在,可以简化大量复杂循环运算的定义,同时因为CPU、GPU等硬件,都是向量化计算,因此,逐元素运算将可以大大提高运算效率。

规则如下

​A​B 是两个形状相同的张量,则其加法和减法定义为:

C = A + B \quad \text{和} \quad D = A - B

其中 ​C​D 的每个元素为:

C_{ijk\ldots} = A_{ijk\ldots} + B_{ijk\ldots}
D_{ijk\ldots} = A_{ijk\ldots} - B_{ijk\ldots}

乘法、除法、求导...类似

示例

​A​B 是两个形状为 ​(2, 2) 的矩阵(即二阶张量):

A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, \quad B = \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix}

则其加法为:

C = A + B = \begin{pmatrix} 1+5 & 2+6 \\ 3+7 & 4+8 \end{pmatrix} = \begin{pmatrix} 6 & 8 \\ 10 & 12 \end{pmatrix}

2.1 逐元素运算的注意事项

  • 零除问题:在逐元素除法中,需要注意除数张量中的元素不能为零,否则会导致除零错误或产生无穷大(Infinity)的结果。在进行逐元素除法前,应该对除数张量进行检查或替换零值。
  • 形状匹配:逐元素运算要求两个张量的形状相同。如果形状不同,将尝试通过广播机制使其形状相同后再进行运算。如果无法广播,则无法进行计算。
  • 数据类型:进行逐元素运算时,两个张量的数据类型应该兼容。NumPy会自动处理不同数据类型之间的运算,但在某些情况下,可能需要手动转换数据类型。

代码相关
在 python中,基于pytorch创建张量后,使用 +-*/** 等运算符时默认进行逐元素运算。
同时,使用 torch.add()torch.sub()...等也可以进行操作。

以上操作都是产生新的张量,而不是改变原张量的值。如果需要接受原张量的值,则需要使用 torch.add_()torch.div_()...等。

2.2 张量的广播(broadcasting)机制

张量的广播机制(broadcasting)是一种用于处理不同形状张量之间运算的技术,使得张量操作更加灵活和高效。
广播机制可以自动扩展较小的张量,使其形状与较大的张量兼容,从而能够进行逐元素运算。
广播机制广泛应用于数值计算库,如NumPy、TensorFlow和PyTorch。

广播机制的规则

  • 维度对齐:如果两个张量的维度数不同,则将维度较少的张量的形状前面补1,直到维度数与另一个张量相同。
  • 从尾部维度开始对齐:检查两个张量的每一个维度,按以下规则处理:
    • 如果两个维度相等,继续处理下一个维度。
    • 如果一个维度是1,则将该维度扩展到与另一个张量的维度相同。
    • 如果维度不相等且都不为1,则两个张量不兼容,无法进行广播。

如何理解尺度为1的维度可以拓展?

想象一下,将多为张量理解为多维数组,维度为1,则代表该维度仅有一个元素,所以拓展N次即是将该元素复制N次。

例如:
假设有两个张量 ​A​B,它们的形状分别是 ​(4, 2, 3)​(2, 1)

  • 匹配维度数:

    ​B 的形状通过在前面添加尺寸1来扩展,以匹配 ​A 的维度数。

    • ​A 的形状: ​(4, 2, 3)
    • ​B 的形状扩展为: ​(1, 2, 1)
A = \begin{pmatrix} \begin{pmatrix} a_{111} & a_{112} & a_{113} \\ a_{121} & a_{122} & a_{123} \end{pmatrix} \\ \begin{pmatrix} a_{211} & a_{212} & a_{213} \\ a_{221} & a_{222} & a_{223} \end{pmatrix} \\ \begin{pmatrix} a_{311} & a_{312} & a_{313} \\ a_{321} & a_{322} & a_{323} \end{pmatrix} \\ \begin{pmatrix} a_{411} & a_{412} & a_{413} \\ a_{421} & a_{422} & a_{423} \end{pmatrix} \end{pmatrix}
B = \begin{pmatrix} b_{11} \\ b_{21} \end{pmatrix}
  • 扩展尺寸为1的维度:

    比较两个张量的每个维度:

    • 第一个维度: ​A 的尺寸为4,​B 的尺寸为1,所以将 ​B 沿第一个维度复制4次,得到形状为 ​(4, 2, 1)
    • 第二个维度: ​A​B 的尺寸均为2,无需扩展。
    • 第三个维度: ​A 的尺寸为3,​B 的尺寸为1,所以将 ​B 沿第三个维度复制3次,得到形状为 ​(4, 2, 3)
B' = \begin{pmatrix} \begin{pmatrix} b_{11} & b_{11} & b_{11} \\ b_{21} & b_{21} & b_{21} \end{pmatrix} \\ \begin{pmatrix} b_{11} & b_{11} & b_{11} \\ b_{21} & b_{21} & b_{21} \end{pmatrix} \\ \begin{pmatrix} b_{11} & b_{11} & b_{11} \\ b_{21} & b_{21} & b_{21} \end{pmatrix} \\ \begin{pmatrix} b_{11} & b_{11} & b_{11} \\ b_{21} & b_{21} & b_{21} \end{pmatrix} \end{pmatrix}

最终,两个张量的形状都是 ​(4, 2, 3),可以进行按元素操作。

代码相关
在 python 常见框架中,当我们定义了张量,计算时如果可以广播的话,会自动进行广播。

3 张量的乘法

张量乘法(tensor multiplication),不同于标量,涉及到宽泛且复杂多样的操作,有逐元素乘法,点积,外积...等等。将其完全列出,篇幅过大,如果没有实际场景,概念比较抽象难以理解。

因此,我们将只列举深度学习以及机器学习中涉及的乘法,逐步结合实例进行演示,并关注于为什么要使用。

3.1 矩阵点乘(Matrix Dot Product)

矩阵乘法(matrix multiplication)也称为Hadamard Product或Element-wise Multiplication。是两个矩阵(二阶张量)的计算,也称为矩阵的内积(inner product)。

本质其实就是第2节中 Element-wise 的乘法,不改变结构,仅改变值,是默认的张量乘法。

3.2 点乘(dot product)

点乘(dot product)是两个向量(一般指一阶张量)的计算,也称为内积(inner product)。
定义如下:

\mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n} a_i b_i

其中,​\mathbf{a} = [a_1, a_2, \ldots, a_n]​\mathbf{b} = [b_1, b_2, \ldots, b_n] 是两个 n 维向量。
例如
假设有两个三维向量 ​\mathbf{a} = [1, 2, 3]​\mathbf{b} = [4, 5, 6],它们的点乘计算如下:

\mathbf{a} \cdot \mathbf{b} = (1 \times 4) + (2 \times 5) + (3 \times 6) = 4 + 10 + 18 = 32

代码相关
在 python 常见的框架中,通常使用 .dot()进行计算。

应用

在数学中,我们通常将点乘用于夹角(几何定义)、投影、垂直等概念的计算。

而在机器学习中,点乘通常用于计算向量之间的相似度或距离,例如在后续的NLP中,点乘用于计算词向量之间的相似度。

这里我们仅将其作为矩阵乘法的引入,不再展开说明。

3.4 矩阵乘法(Matrix Multiplication)

给定两个矩阵 ​\mathbf{A}​\mathbf{B},其中 ​\mathbf{A} 的形状为 ​(m, n)​\mathbf{B} 的形状为 ​(n, p),矩阵乘法的结果是一个形状为 ​(m, p) 的矩阵 ​\mathbf{C}

计算方法

矩阵乘法的计算方法是将矩阵 ​\mathbf{A} 的行向量(Row Vector)与矩阵 ​\mathbf{B} 的列向量(Column Vector)进行点乘(Dot Product),然后将结果放在结果矩阵 ​\mathbf{C} 的对应位置。

具体地,对于矩阵 ​\mathbf{A}​\mathbf{B},结果矩阵 ​\mathbf{C} 的元素 ​\mathbf{C}_{ij} 通过以下点乘计算得到:

\mathbf{C}_{ij} = \sum_{k=1}^{n} A_{ik} B_{kj}

其中,​\mathbf{A}_{ik} 表示矩阵 ​\mathbf{A} 的第 ​i 行、第 ​k 列的元素,​\mathbf{B}_{kj} 表示矩阵 ​\mathbf{B} 的第 ​k 行、第 ​j 列的元素。

示例

假设有两个矩阵 ​\mathbf{A}​\mathbf{B},它们的形状分别为 ​(2, 3)​(3, 2)

\mathbf{A} = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{pmatrix}
\mathbf{B} = \begin{pmatrix} 7 & 8 \\ 9 & 10 \\ 11 & 12 \end{pmatrix}

矩阵乘法 ​\mathbf{C} = \mathbf{A} \times \mathbf{B} 的结果是一个 ​(2, 2) 的矩阵 ​\mathbf{C},其元素通过以下点乘计算得出:

\mathbf{C}_{11} = (1 \times 7) + (2 \times 9) + (3 \times 11) = 7 + 18 + 33 = 58
\mathbf{C}_{12} = (1 \times 8) + (2 \times 10) + (3 \times 12) = 8 + 20 + 36 = 64
\mathbf{C}_{21} = (4 \times 7) + (5 \times 9) + (6 \times 11) = 28 + 45 + 66 = 139
\mathbf{C}_{22} = (4 \times 8) + (5 \times 10) + (6 \times 12) = 32 + 50 + 72 = 154

因此,结果矩阵 ​\mathbf{C} 为:

\mathbf{C} = \begin{pmatrix} 58 & 64 \\ 139 & 154 \end{pmatrix}

应用

  • 线性变换(Linear Transformations):
    线性变换是矩阵乘法的几何定义,我们学习过的线性回归的预测计算,就是一个线性变化过程。除此之外,二维空间中的旋转、缩放和变换都可以用矩阵乘法表示。
  • 深度学习(Deep Learning):
    在机器学习中,矩阵乘法用于计算神经网络(Neural Networks)的加权和(Weighted Sum)、梯度下降(Gradient Descent)等。
  • 图像处理(Image Processing):
    在图像处理领域,矩阵乘法用于图像的卷积操作(Convolution Operations)、滤波(Filtering)、变换(Transformations)等。

为什么使用矩阵乘法?
在矩阵计算中,逐元素乘法解决了矩阵中元素一 一对应乘的问题,而矩阵乘法则解决A矩阵中每一个元素要与矩阵中对应行或列中每一个元素相乘的问题。

假设我们有一个由两个线性方程w1和w2组成的集成模型,省略偏置b,我们可以将其归纳为矩阵表示:

  • 输入向量​\mathbf{x} = [x_1, x_2, x_3]
  • 权重矩阵​\mathbf{W},其形状为 ​2 \times 3,表示有2个神经元,每个神经元有3个权重。
\mathbf{W} = \begin{pmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \end{pmatrix}

其中w11,w12,w13为w1的权重。

  • 计算 ​\mathbf{z} = \mathbf{W} \mathbf{x}
\mathbf{z} = \begin{pmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ x_3 \end{pmatrix}
  • 具体计算每一个元素:
z_1 = w_{11}x_1 + w_{12}x_2 + w_{13}x_3
z_2 = w_{21}x_1 + w_{22}x_2 + w_{23}x_3

上例其实就是后续神经网络的神经元加权计算简化版,可见,使用矩阵乘法简化了表示,同时通过硬件的支持也可以大大提高运算效率。

使用注意

  • 维度匹配(Dimension Matching)

    • 矩阵乘法要求:矩阵 ​\mathbf{A} 的列数必须等于矩阵 ​\mathbf{B} 的行数。即如果 ​\mathbf{A} 的形状为 ​(m, n)​\mathbf{B} 的形状必须为 ​(n, p),乘积矩阵 ​\mathbf{C} = \mathbf{A} \mathbf{B} 的形状将是 ​(m, p)
    • 常见错误:尝试对维度不匹配的矩阵进行乘法操作将导致错误。因此,确保在进行矩阵乘法之前检查矩阵的维度。
  • 矩阵的稀疏性(Sparsity)

    • 稀疏矩阵:如果矩阵中有大量的零元素,称为稀疏矩阵。在这种情况下,使用专门的稀疏矩阵存储和计算方法可以显著提高计算效率和减少存储空间。
    • 优化:许多线性代数库提供了稀疏矩阵运算的支持。
  • 计算效率(Computational Efficiency)

    • 算法选择:对于大规模矩阵乘法,选择高效的算法和实现非常重要。大多数线性代数库已经高度优化了矩阵乘法的实现。
    • 并行计算:利用并行计算(Parallel Computing)和硬件加速(如GPU、TPU)可以显著提高矩阵乘法的计算效率。
  • 内存管理(Memory Management)

    • 内存使用:大规模矩阵乘法可能会消耗大量内存,尤其是在高维数据处理中。确保在进行大规模计算时有足够的内存,并注意内存管理。
    • 分块计算:对于超大矩阵,可以考虑使用分块计算(Block Matrix Multiplication)的方法,分批次进行计算以减少内存占用。