矩阵运算


文档摘要

矩阵运算 矩阵运算是深度学习的计算引擎。本文件讲解矩阵加法、标量乘法、矩阵-向量积、矩阵乘法、逐元素运算、Kronecker 积与广播——每一次前向传播和梯度更新背后的运算。 矩阵可以像向量一样相加和数乘。 加法要求两个矩阵维度相同,逐元素相加: 标量乘法就是把每个元素都乘以这个标量: 矩阵能做的最简单的事,就是乘一个向量。矩阵-向量乘法(matrix-vector multiplication) $A\mathbf{x}$ 是以 $\mathbf{x}$ 的分量为权重,把 $A$ 的各列组合起来: 这是 ML 中最核心的运算。每一个神经网络层都在计算 $A\mathbf{x} + \mathbf{b}$:一个矩阵乘以输入向量,再加上一个偏置。

矩阵运算

矩阵运算是深度学习的计算引擎。本文件讲解矩阵加法、标量乘法、矩阵-向量积、矩阵乘法、逐元素运算、Kronecker 积与广播——每一次前向传播和梯度更新背后的运算。

  • 矩阵可以像向量一样相加和数乘。

  • 加法要求两个矩阵维度相同,逐元素相加:

\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} + \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix} = \begin{bmatrix} 6 & 8 \\ 10 & 12 \end{bmatrix}
  • 标量乘法就是把每个元素都乘以这个标量:
3 \times \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} = \begin{bmatrix} 3 & 6 \\ 9 & 12 \end{bmatrix}
  • 矩阵能做的最简单的事,就是乘一个向量。矩阵-向量乘法(matrix-vector multiplication) A\mathbf{x} 是以 \mathbf{x} 的分量为权重,把 A 的各列组合起来:
\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \begin{bmatrix} 5 \\ 6 \end{bmatrix} = 5 \begin{bmatrix} 1 \\ 3 \end{bmatrix} + 6 \begin{bmatrix} 2 \\ 4 \end{bmatrix} = \begin{bmatrix} 17 \\ 39 \end{bmatrix}
  • 这是 ML 中最核心的运算。每一个神经网络层都在计算 A\mathbf{x} + \mathbf{b}:一个矩阵乘以输入向量,再加上一个偏置。

  • 更一般的情况是矩阵乘法(matrix multiplication)。给定 Am \times n)和 Bn \times p),乘积 C = AB 是一个 m \times p 的矩阵,其中每个元素都是一个点积:

C_{ij} = \sum_{k=1}^{n} A_{ik} B_{kj}
  • 结果中的每一项是 A 的一行与 B 的一列的点积。内层维度必须匹配(n),结果取外层维度(m \times p)。

  • 另一种理解方式:结果的每一列是 A 各列的一个加权和,权重来自 B 中对应的那一列。

  • 如果 B 的某一列是 [2, 3]^T,那么结果的那一列就是 2 \times (\text{A 的第 1 列}) + 3 \times (\text{A 的第 2 列})

  • 一个有用的特例:矩阵乘它自己的转置,结果总是方阵。AA^Tm \times mA^TAn \times n

\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix} = \begin{bmatrix} 14 & 32 \\ 32 & 77 \end{bmatrix}
  • 矩阵乘法有几条重要的规则:

    • 不满足交换律:一般情况下 AB \neq BA,顺序很重要。

    • 满足结合律(AB)C = A(BC)。你可以随意地分组。

    • 满足分配律A(B + C) = AB + AC

    • 单位元AI = IA = A

  • Hadamard 积(Hadamard product)(逐元素乘积)把两个同样大小的矩阵逐项相乘,记作 A \odot B

\begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \odot \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix} = \begin{bmatrix} 5 & 12 \\ 21 & 32 \end{bmatrix}
  • 与普通的矩阵乘法不同,Hadamard 积满足交换律(A \odot B = B \odot A),并且要求两个矩阵维度相同。它在 ML 里被大量用于门控(gating):逐元素地乘上一个值在 0 到 1 之间的掩码,就能控制每个分量「放行」多少。

  • 两个向量 \mathbf{u}\mathbf{v} 的**外积(outer product)**产生一个矩阵:\mathbf{u}\mathbf{v}^T。每一项是 \mathbf{u} 的一个元素与 \mathbf{v} 的一个元素的乘积:

\begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix} \begin{bmatrix} 4 & 5 \end{bmatrix} = \begin{bmatrix} 4 & 5 \\ 8 & 10 \\ 12 & 15 \end{bmatrix}
  • 结果的秩总是 1,因为每一行都是 \mathbf{v}^T 的缩放版本。任意矩阵都可以写成若干个秩为 1 的外积之和,这正是 SVD 所做的事(在「分解」一节中讲解)。

  • 矩阵乘法的计算代价很高。两个 n \times n 矩阵相乘需要 O(n^3) 次运算。对于一个 1000 \times 1000 的矩阵,那就是十亿次乘法。

  • 当矩阵是稀疏的(大部分为零)时,朴素的乘法会把大量时间浪费在与零相乘上。**压缩稀疏行(Compressed Sparse Row,CSR)**格式只存储非零元素及其位置:

    • 值(Values):按行顺序排列的非零项
    • 列索引(Column indices):每个值属于哪一列
    • 行偏移(Row offsets):每一行在值列表中的起始位置
  • 例如,下面这个矩阵:

A = \begin{bmatrix} 5 & 0 & 0 & 2 \\ 0 & 0 & 3 & 0 \\ 0 & 0 & 0 & -1 \end{bmatrix}
  • 存储为:values = [5, 2, 3, -1],columns = [0, 3, 2, 3],row offsets = [0, 2, 3, 4]。这样就跳过了所有的零,让稀疏运算快得多。

  • 矩阵的一个核心用途是求解线性方程组(systems of linear equations)。方程组 A\mathbf{x} = \mathbf{b} 问的是:「什么向量 \mathbf{x}A 变换后会得到 \mathbf{b}?」

  • 例如,假设你在买水果。苹果每个 x_1 美元,香蕉每个 x_2 美元。你知道 2 个苹果加 1 个香蕉要 5 美元,1 个苹果加 3 个香蕉要 10 美元。写成矩阵形式:

\begin{bmatrix} 2 & 1 \\ 1 & 3 \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} = \begin{bmatrix} 5 \\ 10 \end{bmatrix}
  • 把矩阵逐行乘以向量(每一行与 [x_1, x_2]^T 做点积),就得到两个方程:
2x_1 + 1x_2 = 5 \qquad \text{(第 1 行)} \qquad \qquad x_1 + 3x_2 = 10 \qquad \text{(第 2 行)}
  • 由第 1 行得 x_2 = 5 - 2x_1。代入第 2 行:x_1 + 3(5 - 2x_1) = 10,解得 x_1 = 1,于是 x_2 = 3。苹果每个 1 美元,香蕉每个 3 美元。

  • 验证一下——结果正确:

\begin{bmatrix} 2 & 1 \\ 1 & 3 \end{bmatrix} \begin{bmatrix} 1 \\ 3 \end{bmatrix} = \begin{bmatrix} 2 + 3 \\ 1 + 9 \end{bmatrix} = \begin{bmatrix} 5 \\ 10 \end{bmatrix}
  • 如果 A 有逆,解就是 \mathbf{x} = A^{-1}\mathbf{b}。但直接求逆既昂贵又在数值上不稳定。实际中我们会改用分解方法。

  • 并非每个矩阵都是方阵,也并非每个方阵都可逆。伪逆(pseudo-inverse) A^+ 把逆推广到了任意矩阵。它永远存在,并提供「尽可能好」的逆:

A^+ = (A^TA)^{-1}A^T
  • A 是下三角矩阵时,求解 L\mathbf{x} = \mathbf{b} 用**前代换(forward substitution)**很容易:先解出 x_1,再用它求 x_2,如此向下进行。

  • A 是上三角矩阵时,求解 U\mathbf{x} = \mathbf{b}回代(back substitution):先解最后一个变量,再向上推进。

  • 这正是把矩阵分解为三角因子(见「分解」一节)如此有用的原因。它把一个难题变成两个简单的题。

编程练习(使用 CoLab 或 notebook)

  1. 把两个矩阵相乘并核对结果的维度。然后交换顺序,观察结果如何变化(或者维度不匹配时如何报错)。
import jax.numpy as jnp A = jnp.array([[1.0, 2.0], [3.0, 4.0]]) B = jnp.array([[5.0, 6.0], [7.0, 8.0]]) print(f"A @ B:\n{A @ B}") print(f"B @ A:\n{B @ A}") print(f"Equal: {jnp.allclose(A @ B, B @ A)}")
  1. 求解线性方程组 A\mathbf{x} = \mathbf{b},并通过乘回去验证解。试试改变 \mathbf{b},看解如何移动。
import jax.numpy as jnp A = jnp.array([[2.0, 1.0], [5.0, 3.0]]) b = jnp.array([4.0, 7.0]) x = jnp.linalg.solve(A, b) print(f"Solution x: {x}") print(f"A @ x: {A @ x}")

发布者: 作者: HenryNdubuaku 转发
评论区 (0)
U