2. 概率基础与最大似然估计:概率分布、对数似然、MLE损失函数的推导与在分子生成中的应用

好,我们直接进入正题。做分子生成,你绕不开一个核心问题:怎么评价我生成的分子“像不像”真实的分子?

答案就藏在概率里。说白了,我们就是要学一个概率分布,让它能描述真实分子的结构特征。这一章,我带你搞定这个基础——从概率分布到最大似然估计,再落到分子生成的损失函数上。

2.1 概率分布:分子世界的语言

分子生成里,概率分布无处不在。原子的种类是离散的(C、H、O、N...),坐标是连续的(x, y, z),键的类型也是离散的。所以,你得同时处理两类分布。

离散分布:比如原子的类型,我们用分类分布(Categorical Distribution)来描述。这玩意儿就是多项分布的特例,每次选一个类。

连续分布:比如原子的三维坐标,我们用高斯分布(正态分布)来建模。记住,分子结构里的坐标往往是多模态的,一个简单的单峰高斯可能不够,需要用混合高斯。

我个人习惯把分子看成是一个“概率图”。节点是原子,边是化学键,整个分子的概率可以分解成若干条件概率的乘积。这种方式,我们后面讲自回归模型时会反复用到。

核心直觉:生成一个分子,等价于从学习到的概率分布 p(分子) 中采样。损失函数,就是用来衡量这个分布与真实分布之间“距离”的。

2.2 似然函数:凭什么说这个分子“像”真的?

假设你有一堆训练数据,真实分子的结构。你想学一个模型,它参数为θ,能输出一个概率分布 p(x|θ)。

对于给定的一个真实分子 x,p(x|θ) 越大,说明模型认为这个分子出现的可能性越高。这个 p(x|θ) 就是似然函数。

最大似然估计(MLE)的思路很简单:找到一组参数θ,使得所有训练数据的似然乘积最大。

写成公式是:

θ_MLE = argmax_θ ∏_{i=1}^{N} p(x_i | θ)

这里 N 是训练集大小。每一项 p(x_i|θ) 都代表模型对第 i 个分子的“认同度”。

一点经验:直接优化这个乘积容易数值下溢。尤其是分子维度高的时候,概率值很小,乘起来很快就变成0了。所以,我们取个对数,把乘法变加法——这就是对数似然。

2.3 对数似然:把乘法变加法,治好了我的数值恐惧症

对数函数是单调递增的,所以最大化似然等价于最大化对数似然:

θ_MLE = argmax_θ ∑_{i=1}^{N} log p(x_i | θ)

这个求和稳定多了。但我告诉你,这还不是终点。

在深度学习的框架里,我们习惯做最小化。所以,再取个负号:

L(θ) = - ∑_{i=1}^{N} log p(x_i | θ)

这个 L(θ) 就是我的损失函数了。最小化它,等价于最大化对数似然。

我们拿最常用的高斯分布举个例子。假设 p(x|θ) 是单变量高斯:

p(x|μ, σ²) = (1 / √(2πσ²)) * exp(- (x - μ)² / (2σ²))

取负对数:

-log p(x|μ, σ²) = 0.5 * log(2π) + log(σ) + (x - μ)² / (2σ²)

看到没?常数项不用管,核心就是那个 (x - μ)²。这不就是最小二乘法嘛。我在做第一个分子生成模型时,看到这个推导,恍然大悟——原来我一直在用的 L2 损失,默认假设数据是高斯分布的。

避坑指南:我曾经在项目里直接用了 MSE 损失去拟合分子坐标,结果生成了很多“糊”的结构,原子位置全在平均值附近打转。因为 MSE 等价于高斯分布下的 MLE,而分子结构往往是多模态的,单一高斯根本覆盖不了几个不同的构象。后来换成混合高斯损失,效果才上来。

2.4 从 MLE 到分子生成损失函数:一步步落地

好,现在我们把 MLE 的框架装到分子生成上。下面这个表格,把几个常见分子生成场景的损失函数做了一一对应:

分子生成任务 建模的分布 对应的损失函数 MLE 等价形式
预测原子类型 分类分布 交叉熵损失 -∑ log p(类别 | θ)
预测 3D 坐标 各向同性高斯 MSE 损失 (x - μ)² / (2σ²) + C
预测化学键存在性 伯努利分布 二元交叉熵损失 -∑ [y log p + (1-y) log(1-p)]
预测键角/二面角 冯·米塞斯分布 负对数冯·米塞斯 -κ cos(x - μ) + C

你会发现,几乎所有的分子生成损失,都能纳入 MLE 的统一框架。为什么?因为我们在做的是密度估计。

现在的生成模型,无论是 VAE、GAN 还是扩散模型,核心目标都是在拟合数据的真实分布 p_data(x)。负对数似然(NLL)就是那个最通用的评价指标。

关键结论:在分子生成领域,设计损失函数就是在为你的模型选择一个(显式或隐式的)概率分布假设。选对了分布,损失函数自然就推导出来了。

2.5 一张图总结:从概率分布到损失函数

下面这张图,是我画的知识结构图。它串联了这一章的核心逻辑:


[概率分布] (描述分子)
    |
    ├── 离散型:分类分布、伯努利分布
    │          (原子类型、键类型)
    └── 连续型:高斯分布、混合高斯、冯·米塞斯分布
               (坐标、键角、二面角)
    |
    v
[最大似然估计 MLE] (学习分布参数)
    |
    ├── 核心思想:让观测数据出现的概率最大
    └── 数学形式:最大化 ∏ p(x_i | θ)
    |
    v
[对数似然变换] (解决数值下溢)
    |
    ├── 乘法 → 加法
    └── 形式:最大化 ∑ log p(x_i | θ)
    |
    v
[损失函数构建] (取负号,适配梯度下降)
    |
    ├── 高斯分布 → MSE 损失
    ├── 分类分布 → 交叉熵损失
    └── 伯努利分布 → 二元交叉熵损失
    |
    v
[分子生成应用]
    |
    ├── 训练模型拟合真实分子分布
    └── 损失函数引导模型学出合理结构

嗯,这张图的逻辑线很清楚了。从概率分布出发,到 MLE,再到具体的损失函数,最后一头扎进分子生成的应用里。

我个人非常喜欢用这张图做教学开场。它把抽象的统计概念,直接映射到了你每天要调的损失函数上。下次你写代码选损失函数时,脑子里过一遍这张图,就知道为什么选它了。

2.6 实践:一个简单的 MLE 损失计算

我们来点实际的。假设你有一个 atom_type 预测任务,输出是 5 类原子的 logits。用 PyTorch 的 CrossEntropyLoss 就是 MLE:

import torch
import torch.nn as nn

# 假设 batch_size=4, num_classes=5
logits = torch.randn(4, 5)  # 模型输出的 logits
targets = torch.tensor([1, 3, 0, 2])  # 真实原子类型索引

# 内置的 CrossEntropyLoss = LogSoftmax + NLLLoss
criterion = nn.CrossEntropyLoss()
loss = criterion(logits, targets)

print(loss.item())
# 输出类似: 1.8765

这段代码背后,做的就是我刚才推导的负对数似然。你传入 logits(未归一化的分数),CrossEntropyLoss 内部做了 softmax 变成概率,再取负对数。

我的建议:理解了这个对应关系,你就能自定义损失函数了。比如你想对稀有原子类型加权重,只需要修改 CrossEntropyLoss 的 weight 参数,本质上是改了先验分布。

内容到这就够了。MLE 是你构建分子生成损失函数的基石,后面所有复杂的损失,比如对比学习损失、KL 散度、ELBO,全都是从 MLE 这个根上长出来的。你把它吃透了,后面就容易了。