想象你走进剧院,看到两位舞者摆出定格造型:左边的人站得笔直,如雕塑一般纹丝不动;右边的人则围绕他伸展、侧移、张开双臂,摆出复杂姿态。真正精妙的是:因为左边的人始终没动,无论右边摆出多夸张的动作,摄影师都能凭借左边人的姿势,用一条固定规则把右边的动作“倒带”还原,分毫不差。这正是 RealNVP 耦合层的灵魂。它把输入数据像双人雕塑一样拆成两半:一半原样保留(锚点),另一半根据锚点做“放大缩小+前后挪动”(响应),而且响应参数完全由锚点那一半产生。锚点不动,整个变换严格可逆;响应只依赖锚点,衡量“空间被拉伸多少”的账本变得极其简单。于是,模型既能精确算出每个数据出现的概率,又能高效地训练和采样——这正是标准化流区别于其他生成模型的独门绝技。
理解耦合层“锚点+响应”的设计,你就拿到了流模型精确似然与高效采样的核心钥匙,在生成建模、密度估计与异常检测工作中都能直接转化为工程能力。
可视化演示
这篇文章深入讲RealNVP的耦合层设计细节。你会学到它怎么把输入维度分成两半,一半保持不变另一半做仿射变换,... 切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
耦合层的输入:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现
标准化流概览
走进剧院的导演有个终极目标:把一张“简单画布”(标准高斯噪声)通过一连串可逆的舞台调度,变成“复杂画面”(真实数据)。RealNVP 属于标准化流(Normalizing Flows),而“耦合层”正是这场调度中最关键的动作指令。在生成模型大家族里,GAN 像“临摹高手”——没有相机,只凭记忆作画;VAE 像“模糊速写师”——能画个大概,细节却总妥协;而流模型像“摄影师”——每次拍摄都带着精确的曝光参数,能记录并放大画面上的每一处细节。
摄影师之所以能精确,是因为手中握着“变量替换公式”。这个公式告诉他:当我用可逆变换 f 把简单分布 p_Z 的随机变量搬到数据空间时,新分布 p_X 在任意点 x 的密度,等于隐空间对应点 z=f(x) 的密度,再乘以“变换在这点把空间拉伸或压缩了多少倍”。这个拉伸倍数就是雅可比矩阵的行列式的绝对值——你可以把它理解为“空间被拉扯后的体积变化率”。取对数后,乘法变加法,写进目标函数:
所以,整场舞台调度要想“记账”精确,就必须知道每一步动作把空间拉扯了多少。而耦合层的天才之处在于:它让这个“伸缩账本”变得几乎免费——这套账本,就是后面要讲的三角雅可比。
- \log p_X(x)想要计算的量——真实数据 x 在模型下的对数似然,它是训练时唯一要最大化的目标。
- \log p_Z(f(x))x 先被可逆函数 f 送到隐变量 z,然后查表得标准高斯在 z 处的对数密度。
- \det \frac{\partial f}{\partial x}f 在 x 邻域的线性化——雅可比矩阵的行列式,衡量局部体积的伸缩倍数。
- \log |\cdot|取绝对值保证倍数为正,取对数把乘法伸缩变成加法项,便于梯度优化。
可逆变换原理
摄影师想“倒带”,前提是录像功能严格可逆。雕塑游戏的规则是双人组合:正向拍摄时锚点站定、跟舞者摆出动作;倒放时,只要锚点没动,凭公式就能把跟舞者带回原位。这个“正向拍照、逆向复原”来回不丢信息的能力,数学上叫双射(bijection)。
在 RealNVP 里,每个变换层都必须是双射:不仅要存在前向 f_θ: x→z,还要存在解析的逆向 f_θ^{-1}: z→x。这与普通神经网络截然不同——随便一个全连接层的逆通常不存在,更写不出公式。两个方向由同一套参数 θ 定义,就像同一份编舞手册既记录“正放”也记录“倒放”:
有了双射,流模型才能同时做到两件事:训练时对任意数据点精确计算似然,采样时严格从学习到的分布中抽取新样本。GAN 只能“画”不能“算密度”,VAE 只能算变分下界(一种近似),而流模型这种“一拍即合”的可逆结构,让密度精确可算。代价是每个变换层都必须精心设计——耦合层就是为此诞生的标准答案。回到雕塑游戏:导演之所以敢拍复杂动作,是因为他给每个动作都配了“倒带规则”。接下来我们看看,这条“倒带规则”具体长什么样。
- z = f_θ(x)正向映射——把数据 x(复杂分布)拉到隐变量 z(简单分布),训练时使用。
- x = f_θ^{-1}(z)逆向映射——从简单分布采样 z,经过逆变换生成新数据 x,采样时使用。
- 同一参数 θ两个方向的“编舞规则”完全来自同一套网络参数,不额外训练任何逆向网络。
耦合层核心设计
把双人雕塑的规则具体化:把一组姿态拆成两半——锚点舞者(x1)和跟舞者(x2)。导演的指令只有两条:锚点不许动;跟舞者参照锚点做“缩放+平移”。这个“一个人定住、另一个人因他而动”的配合,就是“耦合(coupling)”一词的由来。
数学上,输入向量 x 先按维度切成两段:x = (x_{1:d}, x_{d+1:D})。输出 y 也切成同样的两段。第一段原封不动复制(identity),第二段做仿射变换:先用锚点段 x1 通过神经网络算出“缩放量的对数” s 和“平移量” t,然后 y2 = exp(s) ⊙ x2 + t:
这个设计的妙处在“倒带”极简单。因为锚点段没动,y1 就是 x1;把仿射公式反解,得到逆向:
你看,整段舞蹈的关键就是“锚点不动”。只要锚点没变,跟舞者无论被怎么缩放、平移,倒带都只需要同一个公式:先退平移,再除缩放。可逆性不是靠复杂的近似,而是靠“有人站桩”这个朴素规则实现的。
- 锚点段直接曝光由于 y_{1:d}=x_{1:d},逆向第一步就是原样取回锚点。
- 减去平移把 y2 中由 t 造成的偏移去掉。
- 除以缩放乘上 exp(-s),恰好抵消正向的 exp(s),实现完全还原。
仿射参数网络
跟舞者凭什么知道“该放大多少、该往哪儿挪”?因为他时刻看着锚点。在耦合层里,s 和 t 不是自由变量,而是“由锚点段 x1 实时计算出来”的两个数值列。
具体做法是用神经网络:把 x1 喂进一个特征网络(通常是几个全连接层加激活函数),然后分出两个头——一个头输出 s(x1),另一个头输出 t(x1)。两个头可以共享同一个特征提取主干,也可以各自独立,总之它们只依赖 x1,绝不看 x2:
一个关键细节:s 网络的输出是任意实数(可正可负),但放进公式前要套一层 exp。为什么?因为 exp 的值域恒为正,保证 exp(s)>0,于是“除以缩放”永不会遇到除零。这个小小的指数函数,是整条可逆链的保险丝——只要缩放系数不为零,逆向就一定存在:
回到雕塑游戏:跟舞者的每个动作幅度,都由锚点的姿态经“老导演的直觉”(神经网络)现场生成。锚点不变,直觉就能复现;直觉能复现,倒带就有保障。s 与 t 就是这对“直觉”的两个输出旋钮。
- ∀ s ∈ ℝs 网络可以输出任意实数,训练时没有任何取值范围约束。
- exp(s) > 0指数函数的值域是 (0, +∞),正数乘法永远可逆。
- 可逆保障任何正缩放系数都能用 exp(-s) 还原,避免除零崩溃。
雅可比三角结构
前面说摄影师要记“空间被拉伸多少”的账。如果变换是普通的稠密网络,这笔账要算一个 D 维矩阵的行列式,复杂度 O(D^3),贵得吓人。耦合层的天才在于:它把账本直接设计成了“三角账本”——大多数格子永远是 0,不用记。
求雅可比 J = ∂y/∂x。由于 y1 = x1,左上块是单位阵 I,右上块是零;由于 y2 对 x2 求导时只经过逐元素乘 exp(s),右下块是一个对角阵;只有左下块(y2 对 x1 的导数)是任意稠密矩阵——但行列式根本不关心它:
分块三角矩阵的行列式 = 对角块行列式之积 = ∏ exp(s_j),取对数后指数与对数相消:
账本真的便宜到几乎不用记账:每层的 log|det J| 只是该层 s 输出之和。这让“精确似然”从理论口号变成了工程现实——导演只需要随口报出几个数字,就知道整段舞蹈拉伸了多少空间。
- det(分块三角) = 对角块之积行列式只保留对角块的乘积。
- log 消去 exp对数与指数互为逆运算,乘积变成累加。
- 最终求和整个耦合层的对数行列式 = s 向量各元素之和,O(D) 时间算完。
正反传播效率
排练时正向走一遍,演出倒放时按公式逆向走一遍——耦合层把训练和采样两件事都变成了“单程票”。
训练阶段:给定数据 x,只需一次前向传播得到 z = f(x) 和 logdet,立刻拼出对数似然并反向传播梯度。采样阶段:从标准高斯抽一个 z,一次逆向传播得到新样本 x。两个方向各自只过一遍特征网络,不像某些方法需要迭代求解或额外训练逆向网络。整个训练目标写成:
另一个效率红利:反传时只需对“共享特征网络 + s/t 头”求梯度,雅可比的行列式已被压缩成求和,梯度流干净利落;采样时不涉及任何行列式计算,速度几乎和正向对称。需要说明的是,训练时中间激活的存储取决于工程实现,但“两个方向各自一次网络前向、无需矩阵求逆”这一点是结构性的。回到双人雕塑的名场面:排练(训练)一遍成型,倒带(采样)一遍复原,导演手里只有一份简单的动作手册,没有复杂的不可逆道具。这正是标准化流能“既要精确又要高效”的底气。
- 负对数似然整个括号内就是单个样本的 log p_X(x),取负号变成损失。
- 隐空间对数密度f_θ(x) 把数据映到标准高斯处读出的密度对数。
- 伸缩补偿项log|det J| 校正空间被扭曲的程度,保证密度处处归一。
- 批量平均对 N 个样本求平均,得到可做梯度下降的最终损失。
多层堆叠应用
一场完整的舞蹈绝不止一个定格造型——是很多“一人定、一人动”的小节连缀而成。耦合层也是一样:单层只变换了一半维度(另一半被“定住”),如果只用一层,模型表达能力极弱。解决方法是多堆几层,并在每层之间交换维度身份(permutation),轮流让不同的维度当“锚点”。
数学上,多层变换就是复合函数 f = f_K ∘ …… ∘ f_1;每层的逆都存在,复合的逆就是“从最后一层开始逐层倒带”。更妙的是,复合变换的雅可比行列式遵循乘法法则,取对数后各层的 logdet 直接相加:
RealNVP 的经典实践是:先切分维度,做几个耦合层,然后做一次维度置换(把两半交错打乱),再继续堆叠。这样经过足够多轮,每个维度都轮流做过“锚点”也做过“跟舞者”,整个数据分布就能被精细地雕刻出来。层数越多表达力越强,但每层都要保持可逆结构,这也是流模型通常比 GAN/VAE 更深的原因之一。如果说一层耦合是一个“双人定格”,那么 RealNVP 就是把几十个这样的定格连成一支完整舞蹈,每换一个镜头就换一个人当锚点。正是这套“轮流定锚”的编舞哲学,让流模型既能表达复杂世界,又永远有一盘精确的倒带录像带。
- 复合双射每一层 f_k 都是可逆耦合层,逐层作用把简单分布塑造成复杂分布。
- 行列式乘法法则复合函数雅可比 = 各层雅可比相乘,行列式因此相乘。
- 对数相加取对数后乘法变加法,训练时逐层累加 s 的和即可。
AI 应用场景
图像生成与密度建模
阶段:模型训练与生成推理
解决的问题:传统 GAN 训练不稳定、易模式坍塌;VAE 生成图像偏模糊。RealNVP 用耦合层堆叠出可逆变换,把标准高斯噪声映射为图像分布,训练目标是精确的负对数似然,无对抗博弈,收敛稳定,并能直接计算一张图“有多正常”。
具体案例:在 MNIST/CIFAR 数据集上,用几十个耦合层 + 维度置换构建图像流模型。训练时把 28×28 像素展平成向量,逐层做“锚点-仿射”变换;采样时从标准高斯抽样,逆向过一遍网络即得到清晰的手写数字图像。
异常检测与欺诈识别
阶段:推理部署阶段的评分服务
解决的问题:欺诈样本稀少、标注困难。耦合层让模型能算出每个样本的精确对数似然——正常样本密度高,异常样本密度低,无需标签即可打分,就像摄影师一眼看出“这个队形不合常理”。
具体案例:在金融交易信用卡特征上训练 RealNVP。每条交易提取若干实值特征,模型学到正常交易的流形;上线时对每笔交易算 log p(x),分数低于某阈值就触发人工风控,捕获伪造交易。
科学计算与分子构象生成
阶段:数据增强与样本生成
解决的问题:物理模拟成本高,分子构象空间复杂。可逆变换保证生成的构象携带精确的分布权重,且每层都有解析逆,便于在构象与隐变量之间反复穿梭。
具体案例:训练 RealNVP 拟合分子动力学模拟得到的构象样本,之后用它快速生成大量新构象,替代部分昂贵模拟,加速药物分子筛选。每个构象都像一段“可倒带的舞蹈”,随时能从隐空间跳回数据空间。
Python 演示
import numpy as np def make_target(n, seed=1): """生成目标数据: x0 服从标准正态, x1 的均值方差随 x0 变化""" rng = np.random.default_rng(seed) x0 = rng.standard_normal((n, 1)) x1 = 0.8 * x0 + rng.standard_normal((n, 1)) * np.exp(0.5 * x0) return np.hstack([x0, x1]) class AffineCoupling: """RealNVP 仿射耦合层: 前半恒等, 后半仿射, 严格可逆""" def __init__(self, d=1, hidden=16, seed=0): rng = np.random.default_rng(seed) self.d = d self.W1 = rng.standard_normal((d, hidden)) * 0.5 self.b1 = np.zeros((1, hidden)) self.W2s = rng.standard_normal((hidden, d)) * 0.05 self.b2s = np.zeros((1, d)) self.W2t = rng.standard_normal((hidden, d)) * 0.05 self.b2t = np.zeros((1, d)) def _st(self, x1): """用锚点段 x1 生成缩放与平移参数""" h = np.tanh(x1 @ self.W1 + self.b1) return h @ self.W2s + self.b2s, h @ self.W2t + self.b2t def forward(self, x): x1, x2 = x[:, :self.d], x[:, self.d:] s, t = self._st(x1) y = np.hstack([x1, x2 * np.exp(s) + t]) return y, s.sum(axis=1) def inverse(self, y): y1, y2 = y[:, :self.d], y[:, self.d:] s, t = self._st(y1) return np.hstack([y1, (y2 - t) * np.exp(-s)]) def loss(model, x): """负对数似然: 0.5*||f(x)||^2 - sum(s)""" y, logdet = model.forward(x) return 0.5 * np.sum(y ** 2, axis=1).mean() - logdet.mean() def train(model, x, steps=120, lr=0.03, eps=1e-4): params = [model.W1, model.b1, model.W2s, model.b2s, model.W2t, model.b2t] for step in range(steps): grads = [] for p in params: g = np.zeros_like(p) it = np.nditer(p, flags=['multi_index']) while not it.finished: idx = it.multi_index old = p[idx] p[idx] = old + eps lp = loss(model, x) p[idx] = old - eps lm = loss(model, x) p[idx] = old g[idx] = (lp - lm) / (2 * eps) it.iternext() grads.append(g) for p, g in zip(params, grads): p -= lr * g if step % 30 == 0: print('step', step, 'loss', round(loss(model, x), 4)) def main(): x = make_target(1000) model = AffineCoupling(d=1) y, _ = model.forward(x) xr = model.inverse(y) print('正向后再逆向, 重建误差:', np.abs(x - xr).max()) print('初始负对数似然:', round(loss(model, x), 4)) train(model, x) print('训练后负对数似然:', round(loss(model, x), 4)) z = np.random.standard_normal((5, 2)) print('从隐空间采样得到新样本:', model.inverse(z).shape) main()
def _st(self, x1):仿射参数 s 和 t 都由锚点段 x1 生成,实现“一个人看着另一个人做动作”y = np.hstack([x1, x2 * np.exp(s) + t])正向仿射核心:前半恒等,后半缩放再平移return y, s.sum(axis=1)顺带算出对数雅可比行列式,它只是 s 的各元素求和(y2 - t) * np.exp(-s)逆向还原公式:先退平移、再除缩放0.5 * np.sum(y ** 2, axis=1).mean() - logdet.mean()负对数似然:隐空间密度 + 伸缩补偿g[idx] = (lp - lm) / (2 * eps)用数值梯度验证“可逆变换也可以端到端训练”p -= lr * g参数更新,等价于让“老导演的直觉”越来越准
常见误区
误区“模型只变换了另一半,那保持的这一半是不是什么都没学到?模型表达能力岂不是直接砍半?”
正解每一层确实只变换一半,但 RealNVP 是几十层堆叠的,层与层之间有维度置换(permutation),让每个维度轮流当“锚点”和“跟舞者”。第 k 层没动的维度,第 k+1 层可能就被重度改造。就像双人雕塑,每小节都换一个舞者当锚点,整场舞蹈每个人都被充分使用。“保持”只是单层的局部策略,不是全局懒惰。
为何常见:只盯着单层看,忘了流模型是多层复合结构,局部不动不代表全局不动。
误区“exp(s) 是为了得到正数,那直接让 s 网络输出正数不就行了?还能省掉指数运算。”
正解s 网络输出任意实数(可负可正),exp 自动把它映射到正数。如果强行约束 s 输出为正,反而限制了网络的表达能力,还容易让输出饱和。exp 这个“保险丝”负责正性,网络负责表达,两个角色分离,训练才稳定。可逆性只要求缩放系数非零,正数只是充分条件里最省心的一个。
为何常见:把“缩放系数为正”这个数学要求,错误地转移成了“网络输出范围”的约束。
误区“要算 log|det J|,肯定得对整个雅可比矩阵做数值行列式,O(D^3) 逃不掉吧?”
正解耦合层的雅可比是分块三角矩阵,行列式等于对角块(diag(exp(s)))之积,取对数后就是 s 元素求和,O(D) 完成。左下角那块稠密矩阵完全不用碰——这正是耦合层对比普通可逆网络的最大红利。
为何常见:把通用矩阵行列式的复杂度套路,硬套到了有特殊稀疏结构的矩阵上。
误区“训练要正向,采样要逆向,两个方向都要过网络,不是双倍开销吗?”
正解训练只用正向(一次前向拿到 z 和 logdet),采样只用逆向(一次反向生成样本)。生命周期中的两个阶段各走一个方向,不存在“同一任务两个方向都算”。s/t 网络在正逆向是同一套权重,采样时连 logdet 都不用算,甚至更便宜。可逆不等于双程并行,而是“按需选方向”。
为何常见:把“可逆”理解成“每步都必须双向计算”,实际是两阶段各自单程。
误区“AE/VAE 也是 encoder-decoder,把图片压到隐空间再还原,跟 RealNVP 不是一回事吗?”
正解自编码器的解码是另一套网络学出来的近似,encoder 之后信息会丢失,不能保证 x = decode(encode(x)) 精确成立;RealNVP 的逆向是解析逆,由数学保证逐位还原(浮点误差级)。这也是为什么流模型能做精确似然而自编码器只能做近似重建。
为何常见:只看到“压缩-还原”的形似,忽略了“精确可逆”这个本质差异。
面试题
Q1. RealNVP 耦合层让输入维度“一分为二”的根本目的是什么?
- A.减少网络参数量以加快训练速度
- B.一半恒等保证整体变换严格可逆← 正确答案
- C.避免过拟合让模型泛化能力更强
- D.让两半维度各自学习不同特征
答案:B。一半保持恒等是“锚点”,另一半仿射依赖锚点,正是这个不对称设计让变换可逆且雅可比呈三角状。减少参数量、防过拟合都不是切分维度的目的,D 也只是附带效果而非根本原因。
Q2. 耦合层对另一半做仿射变换的公式是哪一个?
- A.y2 = exp(s(x1)) ⊙ x2 + t(x1)← 正确答案
- B.y2 = s(x1) ⊙ x2 + t(x2)
- C.y2 = x1 ⊙ exp(s) + t(x2)
- D.y2 = (x2 - t(x1)) / s(x1)
答案:A。标准形式是“以 x1 为条件生成缩放与平移,对 x2 逐元素缩放再平移”。D 是逆向公式的形状;B 错在 t 依赖 x2;C 错在把锚点 x1 也拿去缩放,破坏了“锚点不动”原则。
Q3. 为什么 exp(s(x1)) 能保证耦合层整体可逆?
- A.因为指数函数值域恒为正数永不为零← 正确答案
- B.因为指数函数具有周期性便于循环还原
- C.因为 s 网络被限定为只能输出正数
- D.因为指数函数在原点处导数恰好等于一
答案:A。exp 把任意实数映射到正数,缩放系数永不取零,逆变换时除以它就绝对安全。可逆性不需要周期性,也不要求 s 网络输出正数,导数信息与可逆性无关。
Q4. 耦合层的雅可比矩阵具有什么结构?
- A.完全对角矩阵每一行都独立互不干扰
- B.分块三角矩阵且右下块是对角矩阵← 正确答案
- C.上三角矩阵且对角线上全部是零
- D.任意稠密矩阵没有可利用的结构
答案:B。左上单位阵、右上零、左下任意、右下 diag(exp(s)),是分块三角结构。正因如此,行列式只取对角块之积,计算极快。完全对角或上三角都不符合耦合层的真实导数形态。
Q5. 单个耦合层的对数行列式为何计算极快?
- A.因为需要先对雅可比矩阵做数值求逆
- B.因为三角结构让行列式塌缩成 s 求和← 正确答案
- C.因为 exp(s) 连乘后恰好等价于乘以零
- D.因为行列式结果被常数无条件固定住
答案:B。分块三角矩阵的行列式 = 对角块乘积 = ∏exp(s_j),取对数后成为 ∑s_j,O(D) 完成。求逆完全不需要;exp(s) 连乘不等于零;结果随 s 变化,不是固定常数。
Q6. 训练 RealNVP 时计算目标函数需要什么?
- A.一次正向传播得到隐变量与对数行列式← 正确答案
- B.先数值求逆矩阵再计算矩阵的逆
- C.必须保存所有中间层的雅可比矩阵
- D.用蒙特卡洛采样近似估计行列式
答案:A。训练只需一次正向拿到 z 和 logdet,两项直接拼出对数似然。无需求逆、无需保存雅可比、也用不着采样估计——三角结构保证 logdet 是解析求和。
Q7. 使用 RealNVP 生成新样本时正确的操作是?
- A.对数据做正向传播并记录对数行列式
- B.从标准高斯采样再过逆向变换得到数据← 正确答案
- C.对每个样本训练一个单独的编码网络
- D.用对抗判别器判断生成样本的真假
答案:B。生成即采样:z ~ N(0,I),然后 x = f^{-1}(z),只需一次逆向。正向和 logdet 只在训练计算似然时使用;流模型不需要额外的编码网络,也不需要对抗判别器。
Q8. 关于耦合层中 s 与 t 网络的描述正确的是?
- A.它们只以维度的前半 x1 作为输入← 正确答案
- B.它们可以共享同一个特征提取主干← 正确答案
- C.s 的输出为任意实数由 exp 保证正← 正确答案
- D.t 必须输出正数否则平移不可逆
答案:ABC。s 和 t 都只依赖锚点段 x1,且可共享骨干网络;s 的输出可以正负任意,exp 负责正性保障。t 可以是任意实数,平移可逆性不受符号影响,所以 D 错误。
Q9. RealNVP 堆叠多层耦合层时哪些说法正确?
- A.层间需要维度置换让所有维度轮流变换← 正确答案
- B.每层雅可比行列式相乘取对数后相加← 正确答案
- C.每一层都必须单独训练互不联系
- D.整体逆向是各层逆向按照反序执行← 正确答案
答案:ABD。置换把“锚点角色”轮流分配给各维度;复合变换的行列式相乘、取对数后逐层相加;整体逆变换按“后进先出”反序对各层求逆。所有层端到端联合训练,C 错误。
Q10. 下列关于耦合层雅可比行列式的说法哪个最准确?
- A.堆叠多层的总 logdet 需要重新计算雅可比
- B.每一层左下块任意但行列式不受其影响← 正确答案
- C.单层行列式取决于 x2 的数值大小
- D.逆向传播时行列式数值需要重新估计
答案:B。分块三角矩阵的行列式只由对角块决定,左下块虽稠密但被“忽略”;多层总 logdet 就是逐层求和,无需重算。行列式只通过 s 依赖 x1,与 x2 无关,且是解析精确值,无需估计。