Series · Linear Algebra · Chapter 13

张量与多线性代数 -- 从标量到高维数据立方体

张量是向量和矩阵到任意维度的推广。本章从标量、向量、矩阵出发,讲解纤维、切片、展开等概念,以及 CP 分解、Tucker 分解和 HOSVD,并探讨张量在神经网络压缩和推荐系统中的应用。

如果你写过深度学习代码,“张量"这个词早已熟到不能再熟 —— PyTorch 里所有数组都叫 torch.Tensor,TensorFlow 干脆把它写进了产品名字。可张量到底是什么?为什么深度学习框架要借这个听起来像物理学的术语?

本章想说的其实只有一句话:

张量就是把"标量、向量、矩阵"这条阶梯继续往上走的结果,是数组到任意维度的自然推广。矩阵能做的事,大多数都能"抬"到张量上去;少数几件事抬不上去,反而最值得记住。

我们会先从最熟悉的对象出发,建立张量的语言(纤维、切片、展开),然后学习两个最常用的分解 —— CP 分解Tucker 分解 —— 看看它们如何压缩神经网络、如何让推荐系统理解上下文。

本章你会学到:

  • 张量的阶、形状、纤维、切片与展开
  • 核心运算:缩并、外积、n-模乘积
  • CP 分解、Tucker 分解、HOSVD
  • 在神经网络压缩与推荐系统中的应用

预备知识: 第 6 章特征值分解、第 9 章 SVD、第 10 章矩阵范数


从标量到张量:维度的自然推广

一个数、一排数、一张表、一个立方体

按维度增加的顺序看,对象一层层堆起来:

  • 标量(0 阶):单个数。今天的气温,你银行卡上的余额。只有大小。
  • 向量(1 阶):一排数。地图上的位置 $(x, y)$,一个 RGB 颜色 $(r, g, b)$。既有大小也有方向。
  • 矩阵(2 阶):一张表。Excel 表格、灰度图像(每格存一个像素亮度)。
  • 3 阶张量:一个"数据立方体”。彩色图像就是这样:高 $\times$ 宽 $\times$ 3 个颜色通道。

每多一阶,就多一根可以"按下标取值"的轴。这条阶梯没有理由止步于 3。

从标量到张量:每一步都增加一根新的轴

把这一规律一直推下去就是张量的一般定义:

张量是向量和矩阵到任意维度的推广。一个 $N$ 阶张量就是一个 $N$ 维数组,记作 $\mathcal{X} \in \mathbb{R}^{I_1 \times I_2 \times \cdots \times I_N}$,其中 $I_1, I_2, \ldots, I_N$ 是各根轴的长度。

生活里到处是张量

你每天都在和张量打交道,只是没有刻意叫出它的名字:

数据形状阶数每根轴的含义
一段话的情感得分$(\,)$0一个数
一座城市一周的气温$(7,)$17 天每天一个温度
灰度照片$(H, W)$2高 $\times$ 宽 的像素表
彩色照片$(H, W, 3)$3每个像素携带 RGB 三元组
一段短视频$(T, H, W, 3)$4$T$ 帧叠在一起
一批训练图像$(N, 3, H, W)$4$N$ 张图打包送进 GPU
用户-电影-时间 评分$(U, M, T)$3用户 $u$ 在时间 $t$ 对电影 $m$ 的评分

一个具体例子。 你在做视频推荐:1000 个用户、10000 部电影、52 周的偏好历史。最自然的表达就是一个 $1000 \times 10000 \times 52$ 的三阶张量。如果硬要把时间这一维"压扁"成传统的用户-电影矩阵,你恰好丢掉了你想建模的那条信号。

阶、形状与秩 —— 三个容易混的词

  • 阶(order,也叫 mode):有几根轴。向量是 1 阶,$480 \times 640 \times 3$ 的图像是 3 阶。
  • 形状(shape):每根轴的长度。同一张图像形状是 $(480, 640, 3)$。
  • 秩(rank):度量"复杂度",和"维度数"完全不是一回事。本章后面会专门讨论 —— 而且你会看到,张量秩的行为和矩阵秩很不一样。

张量的内部结构:纤维与切片

要在张量上做事,得先学会"看穿"它。两个最基本的视角是纤维切片

纤维:穿过张量的牙签

把一个三阶张量想成魔方。用一根牙签从某个方向穿过去,被串起来的那一串小方块,就是一根纤维 —— 张量的一维切口。

定义: 固定除一根索引外的所有索引,得到的向量就是纤维。

对于 $\mathcal{X} \in \mathbb{R}^{I \times J \times K}$:

  • 模-1 纤维 $\mathbf{x}_{:jk}$:固定 $j$ 和 $k$,沿第一根轴变化 —— 长度 $I$ 的向量
  • 模-2 纤维 $\mathbf{x}_{i:k}$:固定 $i$ 和 $k$ —— 长度 $J$
  • 模-3 纤维 $\mathbf{x}_{ij:}$:固定 $i$ 和 $j$ —— 长度 $K$

对一张 RGB 图像 $\mathcal{X} \in \mathbb{R}^{H \times W \times 3}$,模-3 纤维 $\mathbf{x}_{ij:}$ 就是位置 $(i, j)$ 处那个像素的颜色向量 $(R, G, B)$。

切片:穿过张量的薄片

继续用魔方做比喻:换成用刀切。每切一刀得到一个矩阵。

定义: 固定一根索引,得到的矩阵就是切片。

对于 $\mathcal{X} \in \mathbb{R}^{I \times J \times K}$:

  • 水平切片 $\mathbf{X}_{i::}$:固定 $i$,得到 $J \times K$ 矩阵
  • 侧面切片 $\mathbf{X}_{:j:}$:固定 $j$,得到 $I \times K$ 矩阵
  • 正面切片 $\mathbf{X}_{::k}$:固定 $k$,得到 $I \times J$ 矩阵

对视频 $\mathcal{V} \in \mathbb{R}^{H \times W \times T}$,正面切片 $\mathbf{V}_{::t}$ 就是第 $t$ 帧画面,整段视频就是一摞正面切片。

把 3D 张量看成一摞正面切片(矩阵)

这个"一摞矩阵"的图景非常实用:凡是你已经会在矩阵上做的操作,都可以"按帧"扫过第三根轴抬上去。

张量展开(matricization)

很多时候你想把张量摊平成矩阵,让现成的矩阵工具能用。这个操作叫做展开矩阵化

模-$n$ 展开: 把张量"摊"成一个矩阵,让模-$n$ 纤维变成结果矩阵的。对于 $\mathcal{X} \in \mathbb{R}^{I_1 \times I_2 \times \cdots \times I_N}$:

$$\mathbf{X}_{(n)} \in \mathbb{R}^{I_n \times (I_1 \cdots I_{n-1} I_{n+1} \cdots I_N)}$$

举个具体例子,$\mathcal{X} \in \mathbb{R}^{3 \times 4 \times 2}$ 的三种展开形状分别是 $3 \times 8$、$4 \times 6$、$2 \times 12$。

模-1 展开:每根模-1 纤维成为结果矩阵的一列

为什么有用?因为绝大多数张量算法的内层都是"对模-$n$ 展开做一次 SVD"之类的步骤,而我们已经有非常成熟、稳定的矩阵工具可用。


张量的基本运算

加法与数乘

跟向量、矩阵完全一样:同形状张量按位相加,数乘把每个元素一起放大。

$$(\mathcal{X} + \mathcal{Y})_{i_1 \cdots i_N} = x_{i_1 \cdots i_N} + y_{i_1 \cdots i_N}, \qquad (\alpha \mathcal{X})_{i_1 \cdots i_N} = \alpha\, x_{i_1 \cdots i_N}$$

ResNet 的跳跃连接 $\mathbf{y} = F(\mathbf{x}) + \mathbf{x}$,本质就是张量加法。

张量缩并(Einstein 求和)

缩并是张量里最重要的运算,它就是矩阵乘法的自然推广。

核心思路: 从两个张量里各挑一根长度相同的轴,把对应位置乘起来,再沿这根公共轴求和 —— 这根轴就消失了。

矩阵乘法是这个规则最熟悉的样子:

$$C_{ik} = \sum_j A_{ij} B_{jk}$$

公共下标 $j$ 被"缩并"掉了。

一般情形: 设 $\mathcal{A} \in \mathbb{R}^{I_1 \times I_2 \times J}$,$\mathcal{B} \in \mathbb{R}^{J \times K_1 \times K_2}$,沿公共轴 $J$ 缩并:

$$\mathcal{C}_{i_1 i_2 k_1 k_2} = \sum_j \mathcal{A}_{i_1 i_2 j}\, \mathcal{B}_{j k_1 k_2}$$

结果是一个 4 阶张量。

张量缩并:选公共下标、相乘、求和把它消掉

这正是 np.einsum / torch.einsum 背后的规则:写出输入轴、写出输出轴,凡是出现在输入但不出现在输出里的下标都被求和消掉。

外积与秩-1 张量

外积走的是相反方向:用低阶张量拼出高阶张量。

两个向量 $\mathbf{a} \circ \mathbf{b} = \mathbf{a} \mathbf{b}^T$,得到的是一个矩阵,$(i, j)$ 元为 $a_i b_j$。

三个向量 $(\mathbf{a} \circ \mathbf{b} \circ \mathbf{c})_{ijk} = a_i b_j c_k$,得到的是一个三阶张量。

凡是能写成"几个向量外积"形式的张量叫做秩-1 张量。它是最简单的张量:整个内容由几根一维的小东西就能完全决定。

关键事实: 任何张量都可以写成有限个秩-1 张量的和。这一句话就是张量分解的全部理论基础 —— CP 和 Tucker 都是从这里长出来的。

n-模乘积

n-模乘积把一个矩阵作用到张量的某一根轴上。

定义: 对 $\mathcal{X} \in \mathbb{R}^{I_1 \times \cdots \times I_N}$ 和 $\mathbf{U} \in \mathbb{R}^{J \times I_n}$:

$$(\mathcal{X} \times_n \mathbf{U})_{i_1 \cdots i_{n-1} j i_{n+1} \cdots i_N} = \sum_{i_n} x_{i_1 \cdots i_N}\, u_{j i_n}$$

结果跟 $\mathcal{X}$ 同形状,只是模 $n$ 的长度从 $I_n$ 变成 $J$。

矩阵视角: 用展开来看就清楚得多 —— 它就是一次普通矩阵乘法:$\mathbf{Y}_{(n)} = \mathbf{U}\, \mathbf{X}_{(n)}$。

直观图景: “只在第 $n$ 根轴上做一次线性变换”。比如对 RGB 图像 $\mathcal{I} \in \mathbb{R}^{H \times W \times 3}$,用一个 $3 \times 3$ 颜色变换矩阵 $\mathbf{M}$ 做 $\mathcal{I} \times_3 \mathbf{M}$,就是逐像素地把 $(R, G, B)$ 三元组线性混合 —— 白平衡、色调映射用的就是这个。

两个常用性质:

  • 不同模的乘积可以交换:$\mathcal{X} \times_m \mathbf{A} \times_n \mathbf{B} = \mathcal{X} \times_n \mathbf{B} \times_m \mathbf{A}$(当 $m \neq n$)。
  • 同模相继乘积合并为矩阵乘积:$\mathcal{X} \times_n \mathbf{A} \times_n \mathbf{B} = \mathcal{X} \times_n (\mathbf{B}\mathbf{A})$。

Kronecker 积与 Khatri-Rao 积

这两个积在张量分解中反复出现,需要认得。

Kronecker 积 $\mathbf{A} \otimes \mathbf{B}$:设 $\mathbf{A} \in \mathbb{R}^{I \times J}$、$\mathbf{B} \in \mathbb{R}^{K \times L}$,结果 $\in \mathbb{R}^{IK \times JL}$:

$$\mathbf{A} \otimes \mathbf{B} = \begin{bmatrix} a_{11}\mathbf{B} & \cdots & a_{1J}\mathbf{B} \\ \vdots & \ddots & \vdots \\ a_{I1}\mathbf{B} & \cdots & a_{IJ}\mathbf{B} \end{bmatrix}$$

直觉就是"把 $\mathbf{A}$ 的每个元素换成那个元素 $\times$ 整个 $\mathbf{B}$"。

Khatri-Rao 积 $\mathbf{A} \odot \mathbf{B}$(按列做 Kronecker):设 $\mathbf{A} \in \mathbb{R}^{I \times R}$、$\mathbf{B} \in \mathbb{R}^{K \times R}$(列数相同),结果 $\in \mathbb{R}^{IK \times R}$:

$$\mathbf{A} \odot \mathbf{B} = [\mathbf{a}_1 \otimes \mathbf{b}_1, \; \mathbf{a}_2 \otimes \mathbf{b}_2, \; \ldots, \; \mathbf{a}_R \otimes \mathbf{b}_R]$$

CP-ALS 算法的更新公式就是把这两个积粘起来的。


张量范数

Frobenius 范数

跟矩阵一模一样:所有元素平方求和再开方。

$$\|\mathcal{X}\|_F = \sqrt{\sum_{i_1, \ldots, i_N} x_{i_1 \cdots i_N}^2}$$

有用的性质: Frobenius 范数对展开不变 —— 对任意模 $n$ 都有 $\|\mathcal{X}\|_F = \|\mathbf{X}_{(n)}\|_F$。所以你可以挑最方便的形状去算。

内积

同形状的两个张量:

$$\langle \mathcal{X}, \mathcal{Y} \rangle = \sum_{i_1, \ldots, i_N} x_{i_1 \cdots i_N}\, y_{i_1 \cdots i_N}, \qquad \|\mathcal{X}\|_F = \sqrt{\langle \mathcal{X}, \mathcal{X} \rangle}$$

张量积:多重线性映射的视角

换一个抽象视角看张量很值得:张量本质上是多重线性映射

线性映射 大家熟:$f(\mathbf{x}) = \mathbf{A}\mathbf{x}$,满足 $f(\alpha \mathbf{x} + \beta \mathbf{y}) = \alpha f(\mathbf{x}) + \beta f(\mathbf{y})$。

双线性映射 接收两个向量,对每个变量分别线性:$f(\mathbf{x}, \mathbf{y}) = \mathbf{x}^T \mathbf{A} \mathbf{y}$ 就是双线性的,因为它对 $\mathbf{x}$、$\mathbf{y}$ 都满足线性叠加。

多重线性映射 把这条规律推广到 $N$ 个向量输入。

张量积空间。 给定向量空间 $V$ 和 $W$,它们的张量积 $V \otimes W$ 是一个新空间,使得任意双线性映射 $f: V \times W \to Z$ 都能"穿过它":存在唯一线性映射 $\tilde{f}: V \otimes W \to Z$ 满足 $f(\mathbf{v}, \mathbf{w}) = \tilde{f}(\mathbf{v} \otimes \mathbf{w})$。

回报很直接:张量把多重线性问题转化成线性问题,于是你可以把整个线性代数工具箱搬过来用。


CP 分解:把张量拆成简单成分之和

CP 分解:任意张量都可以写成秩-1 外积之和

什么是 CP 分解

CP 分解(CANDECOMP/PARAFAC)把张量写成秩-1 张量的加权和:

$$\mathcal{X} \approx \sum_{r=1}^{R} \lambda_r \, \mathbf{a}_r \circ \mathbf{b}_r \circ \mathbf{c}_r$$

把每路向量整理成因子矩阵 $\mathbf{A} = [\mathbf{a}_1, \ldots, \mathbf{a}_R]$、$\mathbf{B} = [\mathbf{b}_1, \ldots, \mathbf{b}_R]$、$\mathbf{C} = [\mathbf{c}_1, \ldots, \mathbf{c}_R]$,常用速记 $\mathcal{X} \approx [\![\boldsymbol{\lambda}; \mathbf{A}, \mathbf{B}, \mathbf{C}]\!]$。

直觉:把简单模式叠加起来

拿用户-电影-时间评分张量来想,CP 分解告诉你:

$$\text{评分}(u, m, t) \approx \sum_{r=1}^{R} \lambda_r \cdot \text{用户}_r(u) \cdot \text{电影}_r(m) \cdot \text{时间}_r(t)$$

每个成分 $r$ 都是一种几乎可以叫出名字的"简单模式":

  • 成分 1:年轻用户、动作片、周末晚上看
  • 成分 2:中年用户、文艺片、工作日深夜看
  • ……

把若干个这样的成分叠起来,就能近似还原用户实际的复杂行为。其实 CP 同时在三根轴上做了一次"软聚类"。

张量的秩:跟矩阵秩很不一样

张量的是精确表示它所需的最少秩-1 成分数:

$$\operatorname{rank}(\mathcal{X}) = \min\bigl\{ R : \mathcal{X} = \sum_{r=1}^{R} \mathbf{a}_r \circ \mathbf{b}_r \circ \mathbf{c}_r \bigr\}$$

三件看似该和矩阵一样、其实不一样的事:

  • 计算张量秩是 NP-hard 的。 SVD 顺手就能给出矩阵秩;张量没有这种好事。
  • 最佳低秩近似可能根本不存在。 矩阵的最佳低秩近似 SVD 就给了;张量的最优可以无限逼近但永远达不到 —— 这种现象叫"边界秩"(border rank)。
  • 张量秩可以超过任何一根轴的长度。 矩阵秩有 $\min(m, n)$ 这个天花板;张量秩没有。

CP 的独门优势:本质唯一性

CP 相对于矩阵分解有一个非常重要的优势:在温和条件下,CP 分解是本质唯一的 —— 因子只能差一个成分顺序的置换和列的伸缩,再没有其他自由度。

Kruskal 条件: 若 $k_{\mathbf{A}} + k_{\mathbf{B}} + k_{\mathbf{C}} \geq 2R + 2$(其中 $k_{\mathbf{A}}$ 是 $\mathbf{A}$ 的 Kruskal 秩,即任意 $k$ 列都线性无关的最大 $k$),则 CP 分解本质唯一。

对比 SVD:$\mathbf{U}$ 和 $\mathbf{V}$ 可以乘上任意正交矩阵互相抵消,得到的 $\mathbf{A} = \mathbf{U}\boldsymbol{\Sigma}\mathbf{V}^T$ 不变。CP 没有这种自由度,所以它的因子直接可以当作"用户画像 / 物品画像 / 时间模式"来解释 —— 这正是它在推荐系统里特别受欢迎的原因。

交替最小二乘(ALS)算法

求解 CP 最常用的方法是交替最小二乘:固定其他因子矩阵不动,剩下的就是普通最小二乘问题。

  1. 随机初始化 $\mathbf{A}, \mathbf{B}, \mathbf{C}$。
  2. 固定 $\mathbf{B}, \mathbf{C}$,更新 $\mathbf{A}$: $$\mathbf{A} \leftarrow \mathbf{X}_{(1)}\, (\mathbf{C} \odot \mathbf{B})\, \bigl[(\mathbf{C}^T \mathbf{C}) * (\mathbf{B}^T \mathbf{B})\bigr]^{\dagger}$$
  3. 同样地依次更新 $\mathbf{B}$ 和 $\mathbf{C}$。
  4. 重构误差不再下降即收敛。

其中 $*$ 是 Hadamard(按元素)积,$\dagger$ 是 Moore–Penrose 伪逆。

ALS 不保证收敛到全局最优,但实战中通常表现良好 —— 多跑几次随机初始化、留下最好的那个就行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import numpy as np
import tensorly as tl
from tensorly.decomposition import parafac

# 模拟一个用户-电影-时间评分张量
# 100 个用户,50 部电影,12 个月,评分 [0, 5]
np.random.seed(42)
n_users, n_movies, n_months = 100, 50, 12
X = np.random.rand(n_users, n_movies, n_months) * 5

# 取 5 个潜在成分做 CP 分解
weights, factors = parafac(tl.tensor(X), rank=5, n_iter_max=100)
user_factors, movie_factors, time_factors = factors

print(user_factors.shape)   # (100, 5)
print(movie_factors.shape)  # (50, 5)
print(time_factors.shape)   # (12, 5)

user_factors 的每一行是一个用户的 5 维嵌入;movie_factorstime_factors 的每一行同理。CP 一次性把三根轴都嵌进了同一个 5 维的潜在空间。


Tucker 分解:更灵活的张量表示

Tucker 分解:一个小核张量加上每根轴一个因子矩阵

定义

Tucker 分解是更一般的形式:

$$\mathcal{X} \approx \mathcal{G} \times_1 \mathbf{A} \times_2 \mathbf{B} \times_3 \mathbf{C}$$

按元素写:

$$x_{ijk} \approx \sum_{p=1}^{P} \sum_{q=1}^{Q} \sum_{r=1}^{R} g_{pqr}\, a_{ip}\, b_{jq}\, c_{kr}$$

这里 $\mathcal{G} \in \mathbb{R}^{P \times Q \times R}$ 叫核张量,是一个小张量,承载了所有的"交互结构";$\mathbf{A}, \mathbf{B}, \mathbf{C}$ 是因子矩阵,把核张量"放大"回原始尺寸。

Tucker 与 CP 的关系

  • CP 是 Tucker 的特例。 当核张量 $\mathcal{G}$ 是"超对角"的(只有 $g_{rrr}$ 非零)时,Tucker 退化成 CP。
  • Tucker 严格更灵活。 CP 强制三个因子矩阵的列数都等于 $R$;Tucker 允许每根轴有自己的截断秩 $(P, Q, R)$,对于"三根轴本质维度差很多"的数据更合适(比如 $1000 \times 1000 \times 12$ 的张量,前两根轴可能想要几十维,最后那根只要 4 就够)。
  • CP 唯一,Tucker 不唯一。 Tucker 跟 SVD 一样有旋转自由度:把 $\mathbf{A}$ 换成 $\mathbf{A}\mathbf{Q}$,再把 $\mathbf{Q}^{-1}$ 吸进核张量里,结果不变。所以 Tucker 压缩效果好,但因子不像 CP 那样可直接解释。

HOSVD:高阶奇异值分解

HOSVD 是规范的正交 Tucker 分解,做法非常直接:

  1. 对每根模 $n$,计算其展开 $\mathbf{X}_{(n)}$ 的 SVD。
  2. 取前 $R_n$ 个左奇异向量,组成因子矩阵 $\mathbf{U}^{(n)}$。
  3. 反推出核张量 $\mathcal{G} = \mathcal{X} \times_1 \mathbf{U}^{(1)T} \times_2 \mathbf{U}^{(2)T} \times_3 \mathbf{U}^{(3)T}$。

跟矩阵 SVD 的对应关系一目了然:

矩阵 SVDHOSVD
$\mathbf{A} = \mathbf{U} \boldsymbol{\Sigma} \mathbf{V}^T$$\mathcal{X} = \mathcal{G} \times_1 \mathbf{U}^{(1)} \times_2 \mathbf{U}^{(2)} \times_3 \mathbf{U}^{(3)}$
$\boldsymbol{\Sigma}$ 对角$\mathcal{G}$ “全正交"但不必对角
$\mathbf{U}, \mathbf{V}$ 正交每个 $\mathbf{U}^{(n)}$ 都正交

HOSVD 是数据压缩、降噪的主力工具,也常用作非线性 Tucker 求解(如 HOOI 高阶正交迭代)的初值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from tensorly.decomposition import tucker

# 对同一个评分张量做 Tucker 分解
# 100 用户压到 10 维,50 电影压到 8 维,12 个月压到 4 维
core, factors = tucker(tl.tensor(X), rank=[10, 8, 4])

print(core.shape)                       # (10, 8, 4)
print([f.shape for f in factors])       # [(100, 10), (50, 8), (12, 4)]

original_size   = 100 * 50 * 12
compressed_size = 10 * 8 * 4 + 100 * 10 + 50 * 8 + 12 * 4
print(f"压缩比: {original_size / compressed_size:.2f}x")

多线性秩

Tucker 自带一个秩的概念,叫多线性秩

$$\operatorname{rank}_{\text{ml}}(\mathcal{X}) = \bigl(\operatorname{rank}(\mathbf{X}_{(1)}),\, \operatorname{rank}(\mathbf{X}_{(2)}),\, \operatorname{rank}(\mathbf{X}_{(3)})\bigr)$$

每一项就是一根模展开的秩,告诉你数据沿那根轴本质上有几个独立方向。它和 CP 秩完全相反 —— 计算很容易,每根轴一次 SVD 就够了。


张量与深度学习

为什么框架叫 TensorFlow?因为神经网络里几乎所有运算其实都是张量运算。

用张量眼光看卷积

一个标准的 2D 卷积层涉及四个张量:

  • 输入 $\mathcal{X} \in \mathbb{R}^{B \times C_{\text{in}} \times H \times W}$ —— batch、输入通道、高、宽。
  • 卷积核 $\mathcal{W} \in \mathbb{R}^{C_{\text{out}} \times C_{\text{in}} \times K_H \times K_W}$。
  • 输出 $\mathcal{Y} \in \mathbb{R}^{B \times C_{\text{out}} \times H' \times W'}$。

卷积本身就是一次大缩并:在 $C_{\text{in}}$、$K_H$、$K_W$ 三根轴上同时求和,再加上空间的滑窗 —— 一个有结构的 4 路缩并。

用 CP 压缩卷积层

卷积层的权重就是个 4 阶张量,“秩"在这里直接决定参数量。把 $\mathcal{W}$ CP 分解成 $R$ 个秩-1 张量之和,原本一层胖卷积就被改写成四层瘦卷积串联:

  1. $1 \times 1$ 卷积:把 $C_{\text{in}}$ 通道压到 $R$;
  2. $K_H \times 1$ 深度卷积:在 $R$ 路上分别做;
  3. $1 \times K_W$ 深度卷积:同上;
  4. $1 \times 1$ 卷积:把 $R$ 路扩回 $C_{\text{out}}$。

参数量对比:

原始CP-rank-$R$ 之后
参数量$C_{\text{out}} \cdot C_{\text{in}} \cdot K_H \cdot K_W$$R \cdot (C_{\text{out}} + C_{\text{in}} + K_H + K_W)$

$R$ 不大时压缩比非常可观。VGG-16 的某个 $512 \times 512 \times 3 \times 3$ 卷积层有约 236 万参数;用 rank-64 CP 分解能压到约 6.6 万 —— 35 倍压缩,微调一下精度损失也能控制住。

用 Tucker 压缩卷积层

实际工程里更常用 Tucker,因为它能分别压缩输入、输出通道,更贴近 CNN 的结构。配方是:

  1. $1 \times 1$ 卷积:把 $C_{\text{in}}$ 压到 $P$;
  2. 小 $K_H \times K_W$ 卷积:在 $P \times Q$ 的压缩空间里做;
  3. $1 \times 1$ 卷积:把 $Q$ 扩回 $C_{\text{out}}$。

这种"瓶颈"形状是不是跟 MobileNet、SqueezeNet 的基本块很像?这不是巧合 —— 那些手工设计的轻量模块,本质上就是把 Tucker 分解写进了网络结构。


张量分解在推荐系统里

从矩阵分解到张量分解

经典协同过滤用的是用户-物品矩阵 $\mathbf{R} \in \mathbb{R}^{U \times M}$,$r_{um}$ 表示用户 $u$ 对物品 $m$ 的评分。矩阵分解(SVD、NMF、ALS)找到嵌入:

$$\mathbf{R} \approx \mathbf{P} \mathbf{Q}^T,\quad \mathbf{P} \in \mathbb{R}^{U \times K},\; \mathbf{Q} \in \mathbb{R}^{M \times K}$$

预测值 $\hat{r}_{um} = \mathbf{p}_u^T \mathbf{q}_m$。

可这丢掉了一件重要的事:人的偏好是带上下文的。周二早上通勤想看的电影,跟周六晚上想看的不是一回事。

张量化的思路: 加一根上下文轴(时间、地点、设备、心情……),变成三阶张量 $\mathcal{R} \in \mathbb{R}^{U \times M \times T}$,再 CP 分解:

$$r_{umt} \approx \sum_{r=1}^{R} \lambda_r\, p_{ur}\, q_{mr}\, t_{tr}$$

新冒出来的 $\mathbf{t}_r$ 描述了第 $r$ 个成分在时间维度上的调制。又因为 CP 本质唯一,每个成分的实际含义可以直接解释出来。

稀疏数据:只在观测位置算损失

真实评分数据非常稀疏 —— 通常远低于 0.1% 的位置有观测。所以你不会去拟合整个张量,而是只拟合观测过的位置:

$$\min_{\mathbf{A}, \mathbf{B}, \mathbf{C}} \sum_{(i,j,k) \in \Omega} \left( x_{ijk} - \sum_{r=1}^{R} a_{ir} b_{jr} c_{kr} \right)^2 + \lambda \bigl(\|\mathbf{A}\|_F^2 + \|\mathbf{B}\|_F^2 + \|\mathbf{C}\|_F^2\bigr)$$

其中 $\Omega$ 是观测位置集合,$\lambda$ 是防止嵌入向量爆炸的正则项。

这就是工业级"上下文感知推荐"的骨架,神经网络的扩展只是在它之上再加一层而已。


一个你每天都在用的应用:图像

你打开的每一张 JPEG 都是一个三阶张量。下面这张图把它说得很直接:一个 $H \times W \times 3$ 的数组拆成三个单色"通道"矩阵,沿深度轴叠在一起。

彩色图像就是 3 阶张量:H × W × 3,每个颜色通道一个矩阵

把图像看成张量之后,你就明白为什么计算机视觉里到处都是张量代数:颜色空间转换是对 3 路做一次 n-模乘积;图像压缩就是低秩近似;卷积特征是张量缩并;甚至 CNN 中间层"特征图"的所谓通道,本质上跟 R、G、B 没有区别 —— 只是学出来的通道,而不是物理给定的而已。


其他张量分解方法

Tensor Train (TT) 分解

对非常高阶的张量 —— 比如量子多体波函数,或者那种 50 阶但每根轴只长 2 的张量 —— CP 和 Tucker 的复杂度都会爆炸。Tensor Train 把张量分解成一串小"车厢”:

$$\mathcal{X}(i_1, i_2, \ldots, i_N) = \mathbf{G}_1(i_1)\, \mathbf{G}_2(i_2)\, \cdots\, \mathbf{G}_N(i_N)$$

每个 $\mathbf{G}_k(i_k)$ 都是一个 $r_{k-1} \times r_k$ 的小矩阵。它的好处:

  • 参数量是 $O(N\, d\, r^2)$ —— 关于阶数 $N$ 线性增长,不是指数增长。
  • 有稳定的构造算法(TT-SVD)。
  • 现代多体量子模拟器(DMRG / MPS)用的就是这种表示。

非负张量分解(NTF)

当张量元素本身有"非负"的物理含义 —— 像素亮度、词频、化学浓度 —— 你通常希望分解出来的因子也是非负的:

$$\mathcal{X} \approx \sum_{r=1}^{R} \mathbf{a}_r \circ \mathbf{b}_r \circ \mathbf{c}_r,\quad \mathbf{a}_r, \mathbf{b}_r, \mathbf{c}_r \geq 0$$

非负约束逼着各个成分长得像"零件”(彼此叠加构成整体),而不是正负相互抵消。这就是为什么 NMF / NTF 能给出可解释的"主题 / 部件"分解,而 SVD 一般给不出。


练习题

概念题

练习 1。 判断以下数据结构的张量阶数:

  • 一首单声道 MP3(44.1 kHz)
  • 一首立体声歌曲
  • 一段 5 分钟、30 fps 的 1080p RGB 视频
  • ImageNet 数据集(100 万张 $224 \times 224$ RGB 图像)
  • Transformer 注意力权重张量 $\in \mathbb{R}^{B \times H \times L \times L}$

练习 2。 对于 $\mathcal{X} \in \mathbb{R}^{3 \times 4 \times 2}$:

  • 有多少根模-1 纤维?每根多长?
  • 有多少个正面切片?每个的形状是什么?
  • 模-2 展开 $\mathbf{X}_{(2)}$ 的形状是什么?

计算题

练习 3。 设 $\mathbf{a} = [1, 2]^T$、$\mathbf{b} = [3, 4, 5]^T$、$\mathbf{c} = [6, 7]^T$。

  • 计算外积 $\mathbf{a} \circ \mathbf{b}$。
  • 构造三阶张量 $\mathcal{T} = \mathbf{a} \circ \mathbf{b} \circ \mathbf{c}$,写出 $t_{121}$ 的值。
  • 计算 $\|\mathcal{T}\|_F$。

练习 4。 设 $\mathbf{A} = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}$、$\mathbf{B} = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}$。

  • 计算 Kronecker 积 $\mathbf{A} \otimes \mathbf{B}$。
  • 验证恒等式 $(\mathbf{A} \otimes \mathbf{B})\, \operatorname{vec}(\mathbf{X}) = \operatorname{vec}(\mathbf{B} \mathbf{X} \mathbf{A}^T)$(自选合适的 $\mathbf{X}$)。

分解题

练习 5。 考虑秩-2 张量 $\mathcal{X} = \mathbf{a}_1 \circ \mathbf{b}_1 \circ \mathbf{c}_1 + \mathbf{a}_2 \circ \mathbf{b}_2 \circ \mathbf{c}_2$,其中:

$$\mathbf{a}_1 = [1, 0]^T,\; \mathbf{b}_1 = [1, 1, 0]^T,\; \mathbf{c}_1 = [1, 0]^T$$$$\mathbf{a}_2 = [0, 1]^T,\; \mathbf{b}_2 = [0, 1, 1]^T,\; \mathbf{c}_2 = [0, 1]^T$$

(a) 计算 $\mathcal{X}$ 的若干元素。 (b) 写出三个因子矩阵。 (c) 计算模-1 展开 $\mathbf{X}_{(1)}$。

练习 6。 对 $3 \times 3 \times 3$ 单位张量($\delta_{ijk} = 1$ 当且仅当 $i = j = k$):

  • 计算 Frobenius 范数。
  • CP 秩是多少?(提示:能否写成少于 3 个秩-1 张量之和?)
  • 多线性秩是多少?

应用题

练习 7(推荐系统)。 某视频平台有 3 个用户、4 部电影、2 个时间段,观测到的评分如下:

用户电影时间段评分
1115
1214
2123
2315
3222
3414
  • 构造评分张量 $\mathcal{R} \in \mathbb{R}^{3 \times 4 \times 2}$(未观测位置设为 0)。
  • 用秩-2 CP 分解需要估计多少个参数?
  • 比较秩-1 和秩-2 分解各自的取舍。

练习 8(图像压缩)。 一张 $1024 \times 768$ 的 RGB 图像看作 $1024 \times 768 \times 3$ 张量。

  • 原始存储需要多少字节(每个像素 1 字节)?
  • 用 Tucker 分解 $(100, 75, 3)$ 后需要多少字节?
  • 计算压缩比。
  • 改用秩 100 的 CP 分解需要多少字节?

编程题

练习 9。 用 Python 实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# (a) 三阶张量的模-n 展开
def unfold(X, mode):
    """X: ndarray (I, J, K);mode 取 0/1/2;返回展开后的矩阵。"""
    pass

# (b) n-模乘积
def mode_n_product(X, A, mode):
    """X: (I, J, K);A: (P, I_mode);返回乘积张量。"""
    pass

# (c) CP-ALS
def simple_cp_als(X, rank, n_iter=100):
    """返回 (A, B, C) 三个因子矩阵。"""
    pass

练习 10。tensorly 库完成:

  • 构造一个秩-3 的随机 $20 \times 15 \times 10$ 张量(先生成因子,再用外积拼出来)。
  • 分别用秩 2、3、4、5 做 CP 分解,比较重构误差。
  • 画出"重构误差 vs. 秩"曲线。
  • 用 HOSVD 对同一张量分解,与 CP 结果比较。

证明题

练习 11。 证明对任意模 $n$,$\|\mathcal{X}\|_F = \|\mathbf{X}_{(n)}\|_F$。

练习 12。 证明 n-模乘积的两条性质:

(a) 当 $m \neq n$ 时,$\mathcal{X} \times_m \mathbf{A} \times_n \mathbf{B} = \mathcal{X} \times_n \mathbf{B} \times_m \mathbf{A}$。 (b) $\mathcal{X} \times_n \mathbf{A} \times_n \mathbf{B} = \mathcal{X} \times_n (\mathbf{B}\mathbf{A})$。

练习 13。 设 $\mathcal{X} = \mathbf{a} \circ \mathbf{b} \circ \mathbf{c}$ 是秩-1 张量,证明:

(a) $\|\mathcal{X}\|_F = \|\mathbf{a}\|\, \|\mathbf{b}\|\, \|\mathbf{c}\|$。 (b) $\mathbf{X}_{(1)} = \mathbf{a}\, (\mathbf{c} \otimes \mathbf{b})^T$。


本章小结

概念。 张量是标量、向量、矩阵到任意阶的推广。纤维、切片、展开是分析张量结构的基本工具。核心运算 —— 加法、数乘、缩并、外积、n-模乘积 —— 都是矩阵运算的自然抬升。

分解。 CP 把张量写成秩-1 外积之和,在温和条件下本质唯一,因子可直接解释。Tucker 更灵活(每根轴的秩可不同),但有旋转自由度;HOSVD 是它的规范正交版本。

当心"秩"。 张量秩是 NP-hard、最佳低秩近似可能不存在、张量秩可能超过任意一根轴的长度 —— 这三点都是矩阵世界没有的现象。

应用。 卷积层压缩(CP / Tucker)、上下文感知推荐(用户-物品-时间 CP)、以及图像 / 视频 / 脑电 / 量子态的天然表示。共同主题就一句话:把高维数据的复杂结构分解为简单成分的组合


参考资料

  • Kolda, T. G., & Bader, B. W. (2009). Tensor decompositions and applications. SIAM Review, 51(3), 455–500.
  • Sidiropoulos, N. D., et al. (2017). Tensor decomposition for signal processing and machine learning. IEEE Transactions on Signal Processing, 65(13), 3551–3582.
  • Cichocki, A., et al. (2015). Tensor decompositions for signal processing applications. IEEE Signal Processing Magazine, 32(2), 145–163.
  • TensorLy 文档 —— Python 张量学习库。
  • Strang, G. (2019). Linear Algebra and Learning from Data,第 7 章。

系列导航

Liked this piece?

Follow on GitHub for the next one — usually one a week.

GitHub