Skip to content

第 02 章:表征工程与隐空间连续化实战

“在机器学习中,有一条被无数血泪教训证实的铁律:‘垃圾进,垃圾出’(Garbage In, Garbage Out)。
在材料与分子的逆向设计中,选择什么样的表征,就决定了模型探索能力的物理上限与数学下限。”


1. 引言:离散原子与连续梯度的矛盾

物理微观世界是由离散的微粒构成的:氢、碳、氧、钛等元素以整数个电子与质子离散存在,共价键以离散的拓扑拓扑连接,晶体以离散的空间群对称群元排列。

然而,现代深度学习最强大的优化引擎——反向传播算法(Backpropagation)与梯度下降(Gradient Descent),本质上完全建立在连续微积分、光滑流形(Smooth Manifolds)与偏导数良定义(Well-defined Jacobians)的数学基石之上。

如果我们直接在离散结构空间 X\mathcal{X} 进行搜索,我们面对的是一座由极其割裂的孤岛构成的群岛体系:任意两个结构之间只有离散的“跳跃”,没有可导的“斜坡”。

表征工程与隐空间连续化(Representation Engineering & Latent Continuous Embedding)的核心使命,就是构建一座严谨的可微数学桥梁:将离散破碎的原子微观世界,无损、平滑地铺展在一张低维连续的黎曼流形之上


2. 离散物质的三大连续化参数化途径

为了将分子与材料输入神经网络并使其具备可微性,学界发展了三种主流的几何参数化技术:

2.1 1D 序列文法:从 SMILES 到 SELFIES 的鲁棒性飞跃

  • SMILES 的脆弱性:SMILES 是基于字符规则的序列。在连续潜空间中做优化时,如果生成器微小改变了一个字符(例如将闭环数字 1 变成了字符 C),得到的字符串将无法闭合环,导致 RDKit 解析器抛出语法错误(Syntax Error)。无约束优化中生成的 SMILES 有效性往往暴跌至 20% 以下。
  • SELFIES(Self-Referencing Embedded Strings)的上下文无关文法: Krenn 等人于 2020 年提出的 SELFIES,将化学图生成严格抽象为一个具有后向动态状态机的形式语言系统。
    • 在 SELFIES 中,任何符号(如 [C], [Branch1], [Ring1])在任何上下文环境下都是 100% 语法合法(Syntax-Valid)且满足价键稳定规则(Valence-Valid)的;
    • 即使在连续潜空间中随机采样或者加入巨大的高斯噪声,解码出的 SELFIES 经过确定性自动机还原后,其化学有效性始终保持 100%!这为连续无约束优化扫清了语法断崖的障碍。

2.2 2D 分子图邻接矩阵的连续松弛 (Gumbel-Softmax Continuous Relaxation)

在基于图神经网络的自编码器中,邻接矩阵 A{0,1}N×NA \in \{0, 1\}^{N \times N} 和节点分类 X{0,1}N×CX \in \{0, 1\}^{N \times C} 本身是离散的。为了使离散采样过程能够传导反向传播梯度,我们引入 Gumbel-Softmax(Concrete Distribution)松弛技术

设离散键类型未归一化对数概率为 lRK\mathbf{l} \in \mathbb{R}^K,引入标准 Gumbel 噪声 gk=log(log(uk)),ukUniform(0,1)g_k = -\log(-\log(u_k)), u_k \sim \text{Uniform}(0, 1),连续松弛采样为:

yk=exp(lk+gkτ)j=1Kexp(lj+gjτ)y_k = \frac{\exp\left( \frac{l_k + g_k}{\tau} \right)}{\sum_{j=1}^K \exp\left( \frac{l_j + g_j}{\tau} \right)}

  • 当温度参数 τ\tau \to \infty 时,输出趋向于完全均匀的连续概率分布;
  • τ0\tau \to 0 时,输出平滑收敛至真实的离散单峰 One-Hot 向量;
  • 在反向传播时,导数 yklk\frac{\partial y_k}{\partial l_k} 处处良定义且处处非零,使得分子图生成能够实现端到端误差反向传递。

2.3 3D 周期性晶胞的对称参数化

固态晶体结构无法简单套用孤立分子图的参数化,因为它在空间三维方向具有无限平移周期性。一个规范良定义的晶体表征必须满足:

  1. 晶胞基矢矩阵 LL 的正定性

    L=[a,b,c]=[axbxcx0bycy00cz]R3×3,ax>0,by>0,cz>0L = [\mathbf{a}, \mathbf{b}, \mathbf{c}] = \begin{bmatrix} a_x & b_x & c_x \\ 0 & b_y & c_y \\ 0 & 0 & c_z \end{bmatrix} \in \mathbb{R}^{3 \times 3}, \quad a_x > 0, b_y > 0, c_z > 0

    通常采用 Niggli 约化胞(Reduced Cell)或上三角矩阵消除刚体旋转不确定度,确保体积 det(L)=axbycz>0\det(L) = a_x b_y c_z > 0
  2. 分数坐标在三维环面(3-Torus)上的周期性: 原子的相对位置用无量纲分数坐标表示:

    si=(si,x,si,y,si,z)T[0,1)3\mathbf{s}_i = (s_{i,x}, s_{i,y}, s_{i,z})^T \in [0, 1)^3

    其度量空间不是平坦的 R3\mathbb{R}^3,而是拓扑环面 T3=R3/Z3\mathbb{T}^3 = \mathbb{R}^3 / \mathbb{Z}^3。两原子间距离在周期性边界条件(PBC)下由最小镜像法则(Minimum Image Convention)计算:

    rij=L((sisj)sisj+12)\mathbf{r}_{ij} = L \left( (\mathbf{s}_i - \mathbf{s}_j) - \left\lfloor \mathbf{s}_i - \mathbf{s}_j + \frac{1}{2} \right\rfloor \right)


3. 潜在流形(Latent Manifold)的数理拓扑与测地线几何

当我们使用变分自编码器(VAE)将高维离散结构映射至低维连续潜变量 zZRdz \in \mathcal{Z} \subset \mathbb{R}^d 时,潜空间绝不是一个平坦平庸的欧几里得几何体,而是一个由解码器非线性映射 pθ(xz)p_\theta(x \mid z) 所感生的黎曼流形(Riemannian Manifold)

3.1 黎曼度量张量与内在距离

潜空间中两点 zzz+dzz + \mathrm{d}z 之间的真实物理结构差异,不能用欧氏距离 dz2\| \mathrm{d}z \|^2 衡量,而应当由**费希尔信息度量张量(Fisher Information Metric Tensor gij(z)g_{ij}(z))**决定:

ds2=i,jgij(z)dzidzj,其中 gij(z)=Epθ(xz)[logpθ(xz)zilogpθ(xz)zj]\mathrm{d}s^2 = \sum_{i,j} g_{ij}(z) \mathrm{d}z_i \mathrm{d}z_j, \quad \text{其中 } g_{ij}(z) = \mathbb{E}_{p_\theta(x \mid z)}\left[ \frac{\partial \log p_\theta(x \mid z)}{\partial z_i} \frac{\partial \log p_\theta(x \mid z)}{\partial z_j} \right]

  • gij(z)g_{ij}(z) 极大时,意味着在潜空间移动微小距离,解码出的分子性质或结构会剧烈突变;
  • 一个理想平滑的生成潜空间,应当具备接近各向同性的度量张量场,使得化学性质能够随潜在坐标均匀变化。

3.2 球面线性插值(Slerp)vs 欧几里得线性插值(Lerp)

在低维直觉中,人们习惯用欧氏线性插值连接两个分子潜编码:z(α)=(1α)zA+αzBz(\alpha) = (1-\alpha) z_A + \alpha z_B然而,在高维高斯先验空间(例如 d=64512d = 64 \sim 512)中,这是一种极其严重的拓扑错误!

根据高维概率论的高斯薄壳定理(Gaussian Annulus Theorem / Poincaré 集中现象): 在高维空间中,标准多元正态分布 N(0,Id)\mathcal{N}(0, I_d) 的概率测度几乎 100% 集中在一个半径为 R=dR = \sqrt{d} 的极薄球壳(Hyperspherical Shell)表面,原点附近反而是几乎无样本分布的概率沙漠!

图 2-1:化学隐空间拓扑结构与测地线球面连续插值机制示意图

图 2-2:高维隐空间中欧氏线性插值(Lerp)与球面线性插值(Slerp)的径向范数与概率密度对比验证

如果采用欧氏线性插值,中间过渡点 z(0.5)z(0.5) 会不可避免地跌入半径远小于 d\sqrt{d} 的中心低概率荒原,导致解码出的分子出现大量几何畸变或物理未定义构型。

因此,在隐空间进行任何结构过渡与连续探索时,**必须严格采用球面线性插值(Spherical Linear Interpolation, Slerp)**沿着高维球壳的测地线大圆弧前进:

zSlerp(α)=sin((1α)Ω)sinΩzA+sin(αΩ)sinΩzB,其中 cosΩ=zAzBzAzBz_{\text{Slerp}}(\alpha) = \frac{\sin((1-\alpha)\Omega)}{\sin\Omega} z_A + \frac{\sin(\alpha\Omega)}{\sin\Omega} z_B, \quad \text{其中 } \cos\Omega = \frac{z_A \cdot z_B}{\|z_A\| \|z_B\|}


3.3 潜空间的病态行为及其现代消除技术

在训练连续生成潜空间时,算法工程师常常会遭遇两大核心病态现象:

1. 后验坍塌(Posterior Collapse)

  • 现象:当解码器(如强自回归 Transformer)容量过强时,模型学会完全忽略潜编码 zz,使得变分后验坍塌为先验 qϕ(zx)p(z)=N(0,I)q_\phi(z \mid x) \approx p(z) = \mathcal{N}(0, I),KL 散度项 0\to 0。此时潜空间完全退化为无序随机白噪声,丧失任何分子化学表征能力。
  • 根治方案
    • KL 退火(β\beta-Annealing):在前期的训练步数中,给 KL 损失乘以从 0 渐进增加到 1 的系数 β(t)=min(1,tTwarmup)\beta(t) = \min\left(1, \frac{t}{T_{\text{warmup}}}\right),先让解码器充分学习重构;
    • 自由比特机制(Free Bits / KL Clipping):约束每个维度的最小 KL 惩罚,防止部分重要特征维度被先验无情抹杀:

      LKL=j=1dmax(λ,DKL(qϕ(zjx)p(zj)))\mathcal{L}_{\text{KL}} = \sum_{j=1}^d \max\left( \lambda, D_{\text{KL}}\left( q_\phi(z_j \mid x) \parallel p(z_j) \right) \right)

2. 流形空洞与流形越界(Holes & Out-of-Distribution, OOD)

  • 现象:潜空间中某些区域在训练集中从未覆盖,解码器在这些盲区被强行查询时,会输出包含极其荒谬物理缺陷的“空洞结构”。
  • 根治方案:引入联合半监督性质回归头,在潜空间施加全局 Lipschitz 光滑正则化,强行抹平无数据区域的剧烈梯度突起。

4. 工业级代码实战:构建多任务连续可微潜空间

下面,我们将使用纯 PyTorch 搭建一个面向材料/分子表征的多任务连续潜在流形模型(Continuous Multi-Task Latent VAE with Joint Property Predictor),并实现高维高斯测地线插值采样。

python
"""
文件名: continuous_latent_manifold.py
功能: 构建可微潜在连续流形,实现分子特征编码、可微性质预测与球面测地线插值。
"""

import math
import torch
import torch.nn as nn
import torch.nn.functional as F


class LatentContinuousManifold(nn.Module):
    """
    具备联合物理性质预测头的变分连续潜在流形核心模型
    """
    def __init__(self, input_dim: int = 256, latent_dim: int = 32, property_dim: int = 2):
        super().__init__()
        self.input_dim = input_dim
        self.latent_dim = latent_dim
        self.property_dim = property_dim

        # 1. 编码器网络 (Inference Network)
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, 128),
            nn.LayerNorm(128),
            nn.SiLU(),
            nn.Linear(128, 64),
            nn.SiLU(),
        )
        self.fc_mu = nn.Linear(64, latent_dim)
        self.fc_logvar = nn.Linear(64, latent_dim)

        # 2. 解码器网络 (Generative Reconstruction Network)
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 64),
            nn.SiLU(),
            nn.Linear(64, 128),
            nn.SiLU(),
            nn.Linear(128, input_dim),
        )

        # 3. 可微性质预测头 (Surrogate Property Predictor Surface g(z))
        self.property_predictor = nn.Sequential(
            nn.Linear(latent_dim, 32),
            nn.SiLU(),
            nn.Linear(32, 16),
            nn.SiLU(),
            nn.Linear(16, property_dim)
        )

    def reparameterize(self, mu: torch.Tensor, logvar: torch.Tensor) -> torch.Tensor:
        """重参数化技巧: z = mu + sigma * epsilon"""
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + eps * std

    def forward(self, x: torch.Tensor):
        # 编码至潜在均值与对数方差
        hidden = self.encoder(x)
        mu = self.fc_mu(hidden)
        logvar = self.fc_logvar(hidden)

        # 连续潜变量采样
        z = self.reparameterize(mu, logvar)

        # 重构原结构特征与前向预测目标性质
        x_recon = self.decoder(z)
        y_pred = self.property_predictor(z)

        return x_recon, y_pred, mu, logvar, z

    def compute_loss(self, x: torch.Tensor, y_true: torch.Tensor,
                     beta_kl: float = 1.0, alpha_prop: float = 10.0):
        """
        联合多任务损失: 重构损失 + β * KL 散度 + α * 连续性质预测均方误差
        """
        x_recon, y_pred, mu, logvar, _ = self.forward(x)

        # 重构损失 (均方误差)
        recon_loss = F.mse_loss(x_recon, x, reduction='mean')

        # 潜在高斯先验正则化 (KL 散度)
        kl_loss = -0.5 * torch.mean(1 + logvar - mu.pow(2) - logvar.exp())

        # 性质代理表面回归损失
        prop_loss = F.mse_loss(y_pred, y_true, reduction='mean')

        # 总可微损失
        total_loss = recon_loss + beta_kl * kl_loss + alpha_prop * prop_loss

        return {
            "total_loss": total_loss,
            "recon_loss": recon_loss,
            "kl_loss": kl_loss,
            "prop_loss": prop_loss
        }

    @staticmethod
    def slerp(z0: torch.Tensor, z1: torch.Tensor, steps: int = 10) -> torch.Tensor:
        """
        高维高斯潜空间的球面线性插值 (Slerp) 测地线计算
        确保插值路径始终贴合高斯高概率等能超球壳表面
        """
        # 归一化向量并计算夹角 Omega
        z0_norm = z0 / torch.norm(z0, dim=-1, keepdim=True)
        z1_norm = z1 / torch.norm(z1, dim=-1, keepdim=True)
        dot = torch.clamp(torch.sum(z0_norm * z1_norm, dim=-1), -0.9995, 0.9995)
        omega = torch.acos(dot)

        alphas = torch.linspace(0, 1, steps, device=z0.device)
        path = []
        for alpha in alphas:
            s0 = torch.sin((1 - alpha) * omega) / torch.sin(omega)
            s1 = torch.sin(alpha * omega) / torch.sin(omega)
            z_interp = s0 * z0 + s1 * z1
            path.append(z_interp)

        return torch.stack(path, dim=0)


# =====================================================================
# 单元验证模块: 验证连续流形可微性、插值测地线与性质平滑演进
# =====================================================================
if __name__ == "__main__":
    torch.manual_seed(42)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    model = LatentContinuousManifold(input_dim=128, latent_dim=16, property_dim=2).to(device)
    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)

    print(">>> [TEST 1] 执行合成数据微观潜空间自适应训练...")
    # 构造假定分子描述符 x 与两项物理性质 y (如带隙与形成能)
    x_dummy = torch.randn(64, 128).to(device)
    y_dummy = torch.randn(64, 2).to(device)

    for epoch in range(5):
        optimizer.zero_grad()
        losses = model.compute_loss(x_dummy, y_dummy, beta_kl=0.1, alpha_prop=5.0)
        losses["total_loss"].backward()
        optimizer.step()
    print(f"    训练完成: 重构损失={losses['recon_loss']:.4f}, 性质拟合损失={losses['prop_loss']:.4f}")

    print(">>> [TEST 2] 验证球面测地线插值 (Slerp) 连续性与性质单调平滑演变...")
    # 选取两个具有不同性质的先导分子潜编码 z_A 与 z_B
    z_A = torch.randn(16).to(device)
    z_B = torch.randn(16).to(device)

    interpolated_path = model.slerp(z_A, z_B, steps=5)
    with torch.no_grad():
        # 解码过渡结构的性质预测
        props = model.property_predictor(interpolated_path)
        print(f"    起点 A 性质: 带隙={props[0, 0]:.3f} eV, 能量={props[0, 1]:.3f} meV")
        print(f"    中点 2 性质: 带隙={props[2, 0]:.3f} eV, 能量={props[2, 1]:.3f} meV")
        print(f"    终点 B 性质: 带隙={props[4, 0]:.3f} eV, 能量={props[4, 1]:.3f} meV")

    assert interpolated_path.shape == (5, 16), "插值路径张量维度异常!"
    print(">>> [SUCCESS] 表征工程与连续潜流形单元验证全部通过!")

5. 本章小结

本章系统解构了连接离散化学结构与连续梯度的表征桥梁:

  1. 文法鲁棒性至关重要:明确了解析脆弱的 SMILES 与 100% 有效的 SELFIES 在逆向设计中的决定性差距;
  2. 高维高斯的测地线哲学:从高维几何薄壳定理解析了为何不能进行欧氏直线插值,必须通过球面线性插值(Slerp)沿着等能流形大圆滑行,彻底杜绝了穿越中心空洞沙漠导致的构型畸变;
  3. 可微联合流形工程化实现:亲手构建了融合重构、KL 散度退火与性质曲面多任务损失的可微 PyTorch 原型,使潜在变量具有明确的连续物理梯度指向。

在潜空间的连续公路被成功修通之后,下一步就是如何在这张光滑的高速公路上驾驶“最强跑车”——进入 第 03 章:潜在空间优化算法(高斯过程贝叶斯优化与梯度上升),精准向着全球极值物理性能全速冲刺!

《原子智能》· 纸质出版预备版 · PolyAI Team 著