1. 这不是数学课,是工程师手边的“求导速查手册”
你打开一份机器学习模型的推导文档,第一页就卡在了 $\frac{\partial}{\partial \mathbf{W}} \left( \mathbf{x}^\top \mathbf{W} \mathbf{y} \right)$ —— 符号密密麻麻,下标横飞,连括号都分不清是矩阵乘还是转置。你翻遍教材,发现线性代数讲的是行列式和特征值,微积分讲的是单变量函数,中间那块“矩阵对矩阵求导”的空白,像一道没焊上的电路板:逻辑断开,信号不通,整个反向传播流程直接死机。
这就是绝大多数人在接触深度学习底层、优化算法推导、或者自定义损失函数时的真实困境。它不考智商,只考工具——而矩阵向量求导,就是那个被长期低估、却高频使用的工程工具。刘建平Pinard老师的笔记之所以被反复搬运、截图、收藏,根本原因不是它多“学术”,而是它把一套混乱的符号体系,整理成了可检索、可套用、可验证的操作型知识库。它不教你如何证明链式法则,而是告诉你:当你的损失是 $\mathcal{L} = |\mathbf{A}\mathbf{x} - \mathbf{b}|^2_2$,想算 $\frac{\partial \mathcal{L}}{\partial \mathbf{x}}$,三步就能写出答案,且每一步都有对应规则编号、适用条件和常见变形。
我带过不少刚从学校出来的算法实习生,他们能手推SVM的拉格朗日对偶,但一碰到PyTorch里.backward()报出的梯度形状不对,就陷入无解循环。后来我发现,问题不在框架,而在他们脑中缺少一张“求导映射表”:不知道一个标量对列向量求导,结果该是行向量还是列向量;不清楚 $\frac{\partial \mathbf{y}}{\partial \mathbf{x}}$ 的雅可比矩阵到底是按“输出维度×输入维度”排,还是反过来;更别说 $\frac{\partial}{\partial \mathbf{X}} \text{tr}(\mathbf{A}\mathbf{X}\mathbf{B})$ 这种带迹运算的复合结构了。这些不是理论缺陷,是工程接口缺失——就像你有台好车,但说明书丢了,油门刹车标反了,再强的驾驶技术也白搭。
这篇笔记的核心价值,恰恰在于它彻底放弃了“从定义出发”的教学路径,转而采用“场景驱动+模式匹配”的工程师思维。它默认你已经知道偏导数是什么,现在要解决的是:在真实代码推导中,如何不犯错、不查维基、不重推三遍,直接写出正确梯度表达式。它把抽象的张量运算,压缩成一组带编号的“求导公式卡片”,每张卡片背面印着使用前提(比如“$\mathbf{a}$为常向量,$\mathbf{x}$为变量向量”)、左侧是原始表达式、右侧是化简结果、右下角还标注了结果的维度形状。这不是数学笔记,这是写在IDE注释里的“梯度速查API文档”。
所以,如果你正面临以下任一场景,这篇内容就是为你准备的:
- 正在手推一个新损失函数的梯度,但卡在某一步矩阵转置上,不确定该不该加$^\top$;
- 在调试自定义Layer时,
torch.autograd.gradcheck报错,怀疑是自己写的backward函数维度不匹配; - 阅读某篇论文的附录推导,看到 $\nabla_{\mathbf{W}} \text{tr}(\mathbf{W}^\top \mathbf{A} \mathbf{W} \mathbf{B})$ 就头皮发麻;
- 想真正理解为什么全连接层权重更新是 $\Delta \mathbf{W} = -\eta \cdot \frac{\partial \mathcal{L}}{\partial \mathbf{y}} \mathbf{x}^\top$,而不是 $\mathbf{x} \frac{\partial \mathcal{L}}{\partial \mathbf{y}}^\top$。
它不承诺让你成为矩阵微积分专家,但它能确保你在下次遇到类似问题时,打开这份笔记,30秒内定位到对应规则,5分钟内完成推导,10分钟内把结果写进代码并跑通。这才是工程实践中最稀缺的能力:把模糊的数学直觉,转化为确定的、可执行的、零歧义的操作步骤。
2. 为什么不能照搬标量求导?矩阵求导的三大“反直觉陷阱”
很多人试图用标量微积分的经验去硬套矩阵求导,结果屡试屡败。这不是你不够聪明,而是矩阵求导本身存在三类根本性差异,它们像三道隐形墙,把习惯于单变量思维的人挡在外面。我见过太多人花一整天推导,最后发现错在其中一个基础约定上——而这个约定,教材里往往一笔带过,笔记里却必须前置强调。
2.1 陷阱一:求导结果的“方向性”不再天然唯一
标量对单变量求导,结果永远是一个数,没有方向问题。但标量对向量求导,结果是一个向量;而这个向量是行向量还是列向量,取决于你采用的“分子布局”(numerator layout)还是“分母布局”(denominator layout)。这绝非学术争论,而是直接影响你代码实现的生死线。
举个最典型的例子:设 $y = \mathbf{a}^\top \mathbf{x}$,其中 $\mathbf{a}, \mathbf{x} \in \mathbb{R}^n$。
- 若按分子布局(以分子为基准),$\frac{\partial y}{\partial \mathbf{x}}$ 应与 $\mathbf{x}$ 同构,即列向量,结果为 $\mathbf{a}$;
- 若按分母布局(以分母为基准),则结果应与 $\mathbf{x}^\top$ 同构,即行向量,结果为 $\mathbf{a}^\top$。
初看只是转置差别,但后果极其严重。假设你正在写一个线性层的backward函数,输入梯度grad_output是形状为(batch, out_features)的矩阵,权重W是(in_features, out_features)。根据链式法则,权重梯度应为input.T @ grad_output。这个.T从哪来?正是源于我们统一采用分子布局:将损失 $\mathcal{L}$ 视为分子(标量),将 $\mathbf{W}$ 视为分母(矩阵),那么 $\frac{\partial \mathcal{L}}{\partial \mathbf{W}}$ 必须与 $\mathbf{W}$ 同构,即(in_features, out_features)。而推导过程必然出现 $\frac{\partial \mathcal{L}}{\partial \mathbf{z}} \mathbf{x}^\top$ 这一项,其中 $\mathbf{x}^\top$ 的转置,正是为了匹配维度——没有这个转置,矩阵乘法根本无法进行。
提示:刘建平笔记全文采用分子布局(Numerator Layout),这是PyTorch、TensorFlow等主流框架的默认约定。所有公式右侧结果的形状,均与求导变量(分母)一致。务必在开始前确认这一点,否则后续所有推导都会“系统性错误”。
2.2 陷阱二:乘法不可交换,导致链式法则必须严格保序
标量乘法满足交换律:$ab = ba$。但矩阵乘法不满足:$\mathbf{A}\mathbf{B} \neq \mathbf{B}\mathbf{A}$(除非特殊情形)。这意味着,在对含矩阵乘积的表达式求导时,你不能随意调换因子顺序,链式法则的应用必须严格遵循运算顺序。
考虑 $f(\mathbf{X}) = \mathbf{a}^\top \mathbf{X} \mathbf{b}$,其中 $\mathbf{a} \in \mathbb{R}^m$, $\mathbf{X} \in \mathbb{R}^{m \times n}$, $\mathbf{b} \in \mathbb{R}^n$。这是一个标量函数。若错误地认为“$\mathbf{a}^\top \mathbf{X}$ 是行向量,再乘 $\mathbf{b}$ 是点积”,进而尝试先对 $\mathbf{X}$ 求导再处理,极易出错。正确做法是:将整个表达式视为 $\text{tr}(\mathbf{a}^\top \mathbf{X} \mathbf{b})$(因为标量的迹等于自身),再利用迹的循环置换性质:$\text{tr}(\mathbf{a}^\top \mathbf{X} \mathbf{b}) = \text{tr}(\mathbf{b} \mathbf{a}^\top \mathbf{X})$。此时,$\mathbf{b} \mathbf{a}^\top$ 是一个 $n \times m$ 矩阵,与 $\mathbf{X}$ 同构,因此 $\frac{\partial f}{\partial \mathbf{X}} = \mathbf{b} \mathbf{a}^\top$。
这个推导的关键,在于把标量表达式主动改写为迹的形式,从而将求导变量 $\mathbf{X}$ “挪”到迹运算的最右侧,使其系数矩阵自然成为梯度结果。这背后是一整套基于迹运算的求导技巧,而非简单套用乘积法则。笔记中大量公式(如 $\frac{\partial}{\partial \mathbf{X}} \text{tr}(\mathbf{A}\mathbf{X}\mathbf{B}) = \mathbf{A}^\top \mathbf{B}^\top$)都是这一思想的结晶。它规避了直接对矩阵乘积求导的复杂性,转而利用迹的优良代数性质,将问题降维到“标量对矩阵求导”的标准形式。
2.3 陷阱三:高阶导数与张量,现实工程中必须主动降维
理论上,矩阵对矩阵求导的结果是一个四阶张量($\frac{\partial \mathbf{Y}}{\partial \mathbf{X}}$ 的每个元素 $\frac{\partial y_{ij}}{\partial x_{kl}}$ 构成一个 $p \times q \times m \times n$ 张量)。但在实际工程中,我们几乎从不显式构造或操作这个四阶张量。原因很简单:它太大,且绝大多数应用场景(如梯度下降)只需要其在特定方向上的“收缩”结果。
例如,在神经网络反向传播中,我们真正需要的,从来不是完整的 $\frac{\partial \mathbf{Y}}{\partial \mathbf{X}}$,而是给定上游梯度 $\frac{\partial \mathcal{L}}{\partial \mathbf{Y}}$ 后,计算 $\frac{\partial \mathcal{L}}{\partial \mathbf{X}} = \frac{\partial \mathcal{L}}{\partial \mathbf{Y}} \cdot \frac{\partial \mathbf{Y}}{\partial \mathbf{X}}$。这里的“$\cdot$”并非普通乘法,而是张量收缩(tensor contraction),其具体实现就是矩阵乘法、转置、逐元素乘等基本运算的组合。刘建平笔记中所有“矩阵对矩阵求导”的公式,本质上都是这种收缩后的梯度表达式,而非原始的四阶张量。
这就引出了一个关键实操原则:永远以“最终需要的梯度形状”为起点,反向设计推导路径。比如,已知 $\mathbf{Y} = \mathbf{X}\mathbf{W}$,上游梯度 $\frac{\partial \mathcal{L}}{\partial \mathbf{Y}}$ 形状为(batch, out),$\mathbf{W}$ 形状为(in, out),那么 $\frac{\partial \mathcal{L}}{\partial \mathbf{X}}$ 必须是(batch, in)。由此倒推,$\frac{\partial \mathcal{L}}{\partial \mathbf{X}}$ 必然等于 $\frac{\partial \mathcal{L}}{\partial \mathbf{Y}} \mathbf{W}^\top$。这个结论不需要你推导四阶张量,只需维度分析+矩阵乘法规则即可锁定。笔记的价值,正在于它把大量这类“维度驱动”的推导结果,固化为可直接调用的公式,省去了每次都要重新做线性代数“拼图”的时间。
3. 核心公式拆解:从“抄作业”到“懂原理”的四层穿透
刘建平笔记的主体,是一系列编号公式。但单纯记忆编号毫无意义。真正的掌握,是理解每个公式背后的生成逻辑、适用边界、维度约束和典型变体。下面我以四个最具代表性的公式为例,进行逐层穿透式解析,展示如何把“死记硬背”变成“活学活用”。
3.1 公式 (1): $\frac{\partial \mathbf{a}^\top \mathbf{x}}{\partial \mathbf{x}} = \mathbf{a}$ —— 最简起点,也是最大误区源头
表面看:这似乎是最简单的公式,一个常向量 $\mathbf{a}$ 和变量向量 $\mathbf{x}$ 的内积,对 $\mathbf{x}$ 求导,结果就是 $\mathbf{a}$。很多初学者看到这里就跳过了。
第一层穿透(维度验证):
- 左侧:$\mathbf{a}^\top \mathbf{x}$ 是标量($1 \times 1$),$\mathbf{x}$ 是 $n \times 1$ 列向量,按分子布局,求导结果应为 $n \times 1$ 列向量。
- 右侧:$\mathbf{a}$ 必须是 $n \times 1$ 列向量,才能匹配。如果 $\mathbf{a}$ 是行向量,此公式不成立。
→实操心得:在代码中,永远检查a.shape == x.shape。若a是(1, n),必须先a.squeeze()或a.T。
第二层穿透(推导本质):
展开内积:$\mathbf{a}^\top \mathbf{x} = \sum_{i=1}^n a_i x_i$。对 $x_j$ 求偏导,只有 $i=j$ 项非零,结果为 $a_j$。因此,整个梯度向量就是 $(a_1, a_2, ..., a_n)^\top = \mathbf{a}$。
→关键洞察:这个公式是“线性函数的梯度恒为其线性系数”的向量版。它揭示了求导的本质——找变化率,而线性函数的变化率处处相同。
第三层穿透(常见变体):
- 若表达式为 $\mathbf{x}^\top \mathbf{a}$,结果仍是 $\mathbf{a}$(因标量转置不变)。
- 若为 $\mathbf{a}^\top \mathbf{X} \mathbf{b}$,则需升维:先固定 $\mathbf{X}$,视作 $\mathbf{a}^\top (\mathbf{X}\mathbf{b})$,再套用本公式,得 $\frac{\partial}{\partial (\mathbf{X}\mathbf{b})} = \mathbf{a}$,再结合链式法则处理 $\mathbf{X}\mathbf{b}$ 对 $\mathbf{X}$ 的导数,最终导向公式 (17)。
第四层穿透(避坑现场):
我在调试一个自定义注意力层时,曾将query @ key.T的梯度误写为key,导致训练崩溃。根源在于混淆了求导对象:query是变量,key是常量,正确梯度应为key(公式1),但我的代码里key是(seq_len, dim),而query是(batch, seq_len, dim),维度不匹配。修正方案是:key需广播或unsqueeze(0)以匹配query的 batch 维度。公式是骨架,维度是血肉,缺一不可。
3.2 公式 (6): $\frac{\partial \mathbf{x}^\top \mathbf{A} \mathbf{x}}{\partial \mathbf{x}} = \mathbf{x}^\top (\mathbf{A} + \mathbf{A}^\top)$ —— 二次型的灵魂
表面看:这是二次型求导的标准结果,常用于推导岭回归、PCA等目标函数的梯度。
第一层穿透(对称性破局):
若 $\mathbf{A}$ 是对称矩阵($\mathbf{A} = \mathbf{A}^\top$),公式简化为 $2\mathbf{x}^\top \mathbf{A}$。但现实中,$\mathbf{A}$ 很少天然对称。公式中明确写出 $(\mathbf{A} + \mathbf{A}^\top)$,其物理意义是:任何矩阵的二次型,其梯度只依赖于该矩阵的对称部分。因为 $\mathbf{x}^\top \mathbf{A} \mathbf{x} = \mathbf{x}^\top \left( \frac{\mathbf{A} + \mathbf{A}^\top}{2} \right) \mathbf{x}$,反对称部分贡献为零。
第二层穿透(推导复现):
设 $f(\mathbf{x}) = \mathbf{x}^\top \mathbf{A} \mathbf{x} = \sum_{i,j} x_i a_{ij} x_j$。对 $x_k$ 求偏导:
$$ \frac{\partial f}{\partial x_k} = \sum_j a_{kj} x_j + \sum_i x_i a_{ik} = (\mathbf{A}\mathbf{x})_k + (\mathbf{x}^\top \mathbf{A})_k $$
注意,$(\mathbf{x}^\top \mathbf{A})_k$ 是标量,等于其转置 $(\mathbf{A}^\top \mathbf{x})_k$。因此,整个梯度向量为 $\mathbf{A}\mathbf{x} + \mathbf{A}^\top \mathbf{x} = (\mathbf{A} + \mathbf{A}^\top)\mathbf{x}$。由于我们采用分子布局,结果应为行向量,故写作 $\mathbf{x}^\top (\mathbf{A} + \mathbf{A}^\top)$。
第三层穿透(工程映射):
在PyTorch中,torch.nn.functional.linear的权重梯度计算,本质就是此公式的应用。设输出 $\mathbf{y} = \mathbf{W}\mathbf{x} + \mathbf{b}$,损失 $\mathcal{L} = \frac{1}{2} |\mathbf{y} - \mathbf{y}{\text{true}}|^2$。则 $\frac{\partial \mathcal{L}}{\partial \mathbf{W}}$ 的推导,会经过 $\frac{\partial}{\partial \mathbf{W}} \text{tr}((\mathbf{W}\mathbf{x} + \mathbf{b} - \mathbf{y}{\text{true}})^\top (\mathbf{W}\mathbf{x} + \mathbf{b} - \mathbf{y}_{\text{true}}))$,最终化简为 $\mathbf{x} \mathbf{e}^\top$,其中 $\mathbf{e}$ 是误差向量。这正是公式 (6) 在更高维度的投影。
第四层穿透(参数敏感性):
我曾在一个推荐系统项目中,用此公式推导用户嵌入向量的梯度。初始 $\mathbf{A}$ 是稀疏的交互矩阵,直接套用公式导致梯度爆炸。后来发现,问题出在 $\mathbf{A} + \mathbf{A}^\top$ 放大了稀疏矩阵的数值范围。解决方案是:对 $\mathbf{A}$ 进行归一化预处理,或在梯度更新时加入更强的 L2 正则项。这说明,公式是普适的,但其数值稳定性高度依赖于输入矩阵的性质。
3.3 公式 (17): $\frac{\partial}{\partial \mathbf{X}} \text{tr}(\mathbf{A}\mathbf{X}\mathbf{B}) = \mathbf{A}^\top \mathbf{B}^\top$ —— 迹运算的“万能钥匙”
表面看:这是迹运算求导的基石公式,看似简单,却是解锁绝大多数复杂矩阵求导的钥匙。
第一层穿透(迹的魔法):
迹(trace)是方阵对角线元素之和,具有三个关键性质:
- $\text{tr}(\mathbf{C}) = \text{tr}(\mathbf{C}^\top)$;
- $\text{tr}(\mathbf{C}\mathbf{D}) = \text{tr}(\mathbf{D}\mathbf{C})$(循环置换);
- $\text{tr}(\mathbf{C} + \mathbf{D}) = \text{tr}(\mathbf{C}) + \text{tr}(\mathbf{D})$。
性质2是核心——它允许我们将求导变量 $\mathbf{X}$ “挪”到迹的最右侧,从而将问题转化为标准形式。
第二层穿透(推导还原):
设 $f(\mathbf{X}) = \text{tr}(\mathbf{A}\mathbf{X}\mathbf{B})$。利用循环置换:
$$ f(\mathbf{X}) = \text{tr}(\mathbf{B}\mathbf{A}\mathbf{X}) = \text{tr}((\mathbf{A}^\top \mathbf{B}^\top)^\top \mathbf{X}) $$
现在,表达式变为 $\text{tr}(\mathbf{C}^\top \mathbf{X})$,其中 $\mathbf{C} = \mathbf{A}^\top \mathbf{B}^\top$。而 $\text{tr}(\mathbf{C}^\top \mathbf{X}) = \sum_{i,j} c_{ij} x_{ij}$,即所有元素的点积。因此,$\frac{\partial f}{\partial x_{ij}} = c_{ij}$,故 $\frac{\partial f}{\partial \mathbf{X}} = \mathbf{C} = \mathbf{A}^\top \mathbf{B}^\top$。
第三层穿透(组合应用):
几乎所有复杂公式都由它派生。例如,公式 (18) $\frac{\partial}{\partial \mathbf{X}} \text{tr}(\mathbf{X}^\top \mathbf{A} \mathbf{X} \mathbf{B})$,可先令 $\mathbf{Y} = \mathbf{X}^\top \mathbf{A} \mathbf{X}$,则 $f = \text{tr}(\mathbf{Y} \mathbf{B})$,再对 $\mathbf{Y}$ 求导得 $\mathbf{B}^\top$,最后用链式法则处理 $\mathbf{Y}$ 对 $\mathbf{X}$ 的导数。整个过程,就是将复杂表达式不断“剥洋葱”,直至露出最内层的 $\text{tr}(\cdot \mathbf{X} \cdot)$ 结构。
第四层穿透(框架验证):
我曾用此公式手推一个自定义归一化层的梯度,并用 PyTorch 的torch.autograd.gradcheck进行数值验证。设置 $\mathbf{A} = \begin{bmatrix}1 & 2\3 & 4\end{bmatrix}, \mathbf{B} = \begin{bmatrix}0.5 & 0\0 & 2\end{bmatrix}, \mathbf{X} = \begin{bmatrix}1 & 0\0 & 1\end{bmatrix}$,计算理论梯度 $\mathbf{A}^\top \mathbf{B}^\top = \begin{bmatrix}0.5 & 6\1 & 8\end{bmatrix}$。数值梯度(h=1e-5)结果为 $\begin{bmatrix}0.5000 & 5.9999\1.0000 & 7.9999\end{bmatrix}$,误差在 $10^{-4}$ 量级,完全吻合。亲手验证一次,胜过背诵十遍。
3.4 公式 (25): $\frac{\partial}{\partial \mathbf{X}} \log |\mathbf{X}| = \mathbf{X}^{-\top}$ —— 行列式对数的优雅解
表面看:这是涉及矩阵行列式的高级公式,常用于高斯过程、信息论或流形学习。
第一层穿透(可逆性前提):
此公式成立的前提是 $\mathbf{X}$ 可逆。若 $\mathbf{X}$ 奇异,$\log|\mathbf{X}|$ 无定义,梯度亦无意义。在实操中,这意味着你必须确保 $\mathbf{X}$ 始终远离奇异点,通常通过添加小扰动(如 $\mathbf{X} + \epsilon \mathbf{I}$)实现。
第二层穿透(微分视角):
更本质的理解来自矩阵微分:$d\log|\mathbf{X}| = \text{tr}(\mathbf{X}^{-1} d\mathbf{X})$。对比标准形式 $df = \text{tr}(\mathbf{G}^\top d\mathbf{X})$,立即得到 $\mathbf{G} = \mathbf{X}^{-\top}$。这个微分形式,是所有矩阵求导公式的统一语言,它绕开了繁琐的索引展开,直击本质。
第三层穿透(计算代价):
$\mathbf{X}^{-\top}$ 的计算成本很高($O(n^3)$)。在大规模应用中(如 $n=1000$),直接求逆不可行。此时,应转向迭代法或低秩近似。例如,在变分自编码器(VAE)中,若 $\mathbf{X}$ 是协方差矩阵,常用 Cholesky 分解 $\mathbf{X} = \mathbf{L}\mathbf{L}^\top$,则 $\log|\mathbf{X}| = 2\sum_i \log l_{ii}$,梯度可直接对 $\mathbf{L}$ 计算,避免求逆。
第四层穿透(数值陷阱):
在一次金融风控模型中,我用此公式计算协方差矩阵的对数行列式梯度。初始数据未中心化,导致 $\mathbf{X}$ 条件数极大,torch.inverse返回 NaN。排查三天后发现,只需在计算前对数据做x = x - x.mean(0),问题迎刃而解。数学公式是完美的,但数据是粗糙的。工程能力,就是在这两者之间架桥。
4. 实操全流程:从一个真实损失函数推导到 PyTorch 代码落地
理论终须落地。下面我以一个在工业界真实使用的损失函数为例,完整演示如何运用刘建平笔记中的公式,从零开始推导梯度,并最终转化为可运行的 PyTorch 代码。这个过程,就是笔记从“纸面知识”变成“肌肉记忆”的关键跃迁。
4.1 场景设定:带结构先验的矩阵分解损失
假设我们正在构建一个推荐系统,用户-物品交互矩阵 $\mathbf{R} \in \mathbb{R}^{m \times n}$ 稀疏且噪声大。我们采用矩阵分解模型:$\hat{\mathbf{R}} = \mathbf{U} \mathbf{V}^\top$,其中 $\mathbf{U} \in \mathbb{R}^{m \times k}, \mathbf{V} \in \mathbb{R}^{n \times k}$ 是用户和物品的隐因子矩阵。为提升泛化性,我们引入一个结构先验:用户隐因子应服从一个已知的图拉普拉斯矩阵 $\mathbf{L}_u \in \mathbb{R}^{m \times m}$ 所定义的平滑性约束。具体损失函数为:
$$ \mathcal{L}(\mathbf{U}, \mathbf{V}) = \underbrace{\frac{1}{2} \sum_{(i,j) \in \Omega} (r_{ij} - \mathbf{u}_i^\top \mathbf{v}j)^2}{\text{重建误差}} + \underbrace{\frac{\lambda}{2} \text{tr}(\mathbf{U}^\top \mathbf{L}u \mathbf{U})}{\text{图正则项}} $$
其中 $\Omega$ 是观测到的交互集合,$\mathbf{u}_i$ 是 $\mathbf{U}$ 的第 $i$ 行,$\mathbf{v}_j$ 是 $\mathbf{V}$ 的第 $j$ 列,$\lambda > 0$ 是正则化系数。我们的目标是:推导 $\frac{\partial \mathcal{L}}{\partial \mathbf{U}}$ 和 $\frac{\partial \mathcal{L}}{\partial \mathbf{V}}$,并用 PyTorch 实现。
4.2 推导 $\frac{\partial \mathcal{L}}{\partial \mathbf{U}}$:分步拆解与公式调用
步骤 1:处理重建误差项
重建误差项可写为矩阵形式:$\mathcal{L}{\text{rec}} = \frac{1}{2} |\mathbf{R} - \mathbf{U}\mathbf{V}^\top|{\mathcal{F}}^2$,其中 $|\cdot|{\mathcal{F}}$ 是 Frobenius 范数。回忆 Frobenius 范数的平方等于矩阵的迹:$|\mathbf{A}|{\mathcal{F}}^2 = \text{tr}(\mathbf{A}^\top \mathbf{A})$。因此,
$$ \mathcal{L}_{\text{rec}} = \frac{1}{2} \text{tr}\left( (\mathbf{R} - \mathbf{U}\mathbf{V}^\top)^\top (\mathbf{R} - \mathbf{U}\mathbf{V}^\top) \right) $$
展开: $$ \mathcal{L}_{\text{rec}} = \frac{1}{2} \text{tr}(\mathbf{R}^\top \mathbf{R}) - \text{tr}(\mathbf{R}^\top \mathbf{U}\mathbf{V}^\top) + \frac{1}{2} \text{tr}(\mathbf{V}\mathbf{U}^\top \mathbf{U}\mathbf{V}^\top) $$
现在,对 $\mathbf{U}$ 求导。第一项不含 $\mathbf{U}$,导数为 0。第二项:$-\text{tr}(\mathbf{R}^\top \mathbf{U}\mathbf{V}^\top)$,符合公式 (17) 形式 $\text{tr}(\mathbf{A}\mathbf{U}\mathbf{B})$,其中 $\mathbf{A} = \mathbf{R}^\top, \mathbf{B} = \mathbf{V}^\top$。应用公式 (17):$\frac{\partial}{\partial \mathbf{U}} \text{tr}(\mathbf{R}^\top \mathbf{U}\mathbf{V}^\top) = \mathbf{R} \mathbf{V} + \mathbf{V} \mathbf{R}^\top$?等等,这里需要小心!公式 (17) 要求 $\mathbf{A}\mathbf{U}\mathbf{B}$ 中 $\mathbf{U}$ 是中间变量,且 $\mathbf{A}, \mathbf{B}$ 维度需匹配。$\mathbf{R}^\top$ 是 $n \times m$,$\mathbf{U}$ 是 $m \times k$,$\mathbf{V}^\top$ 是 $k \times n$,乘积为 $n \times n$,迹合法。应用公式:$\frac{\partial}{\partial \mathbf{U}} \text{tr}(\mathbf{R}^\top \mathbf{U}\mathbf{V}^\top) = (\mathbf{R}^\top)^\top (\mathbf{V}^\top)^\top = \mathbf{R} \mathbf{V}$。注意,这是分子布局下的结果,形状为 $m \times k$,与 $\mathbf{U}$ 一致。
第三项:$\frac{1}{2} \text{tr}(\mathbf{V}\mathbf{U}^\top \mathbf{U}\mathbf{V}^\top) = \frac{1}{2} \text{tr}(\mathbf{U}^\top \mathbf{U}\mathbf{V}^\top \mathbf{V})$(利用循环置换)。现在,它变成了 $\text{tr}(\mathbf{U}^\top \mathbf{A} \mathbf{U})$ 的形式,其中 $\mathbf{A} = \mathbf{V}^\top \mathbf{V}$。这正是公式 (6) 的变体。公式 (6) 给出 $\frac{\partial}{\partial \mathbf{U}} \text{tr}(\mathbf{U}^\top \mathbf{A} \mathbf{U}) = \mathbf{U}^\top (\mathbf{A} + \mathbf{A}^\top) = 2\mathbf