Morgan 指纹(RDKit 中 ECFP 的实现)是化学信息学中使用最广的指纹。它的算法思想简单而有效——理解这个算法,就能理解为什么它与图神经网络的信息范围本质上等价。
算法:迭代的邻域扩展
# 【核心思想】:
# 每个原子的「标识」由它自己和周围的环境决定
# 通过迭代,逐步扩大考虑的环境范围
#
# 【步骤】:
#
# 第 0 轮(radius=0):
# 每个原子分配一个初始标识
# 基于原子的固有性质:
# - 元素类型
# - 连接的重原子数
# - 连接的氢数
# - 形式电荷
# - 是否在环中
# - 同位素质量
# → 【这 6 个属性构成 Daylight 的原子不变量】
#
# 第 1 轮(radius=1):
# 每个原子的新标识 =
# 哈希(自己的旧标识, 所有邻居的旧标识与键类型)
# → 现在标识包含了 1 跳邻域的信息
#
# 第 2 轮(radius=2):
# 同样的操作再做一次
# → 标识包含 2 跳邻域的信息
#
# ...
#
# 【收集】:
# 把所有轮次中出现过的所有标识收集起来
# → 这就是分子的「子结构集合」
#
# 【哈希到固定长度】:
# 每个标识对固定位数取模,置位
# → 得到定长的位向量
#
# 【关键观察】:
# 这个迭代过程与 GNN 的消息传递【结构完全一致】
# → radius=K 的 ECFP ≈ K 层 GNN 的信息范围
# → 【这解释了为什么两者性能常常接近】(见 159)
# → 差别在于:ECFP 是「枚举+哈希」,GNN 是「学习聚合」
命名约定:ECFP4 = radius 2
# 【最容易搞混的一点】
#
# ECFP 后面的数字是【直径】,不是半径
# RDKit 的 radius 参数是【半径】
#
# ECFP2 ←→ radius=1
# ECFP4 ←→ radius=2 【最常用】
# ECFP6 ←→ radius=3
#
# 【读论文时要注意】:
# 看到「ECFP4」,对应 RDKit 的 radius=2
from rdkit import Chem
from rdkit.Chem import rdFingerprintGenerator
mol = Chem.MolFromSmiles("CC(=O)Nc1ccc(O)cc1")
# ECFP4 = radius 2(标准选择)
gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
fp = gen.GetFingerprint(mol)
print(f"置位数: {fp.GetNumOnBits()} / {fp.GetNumBits()}")
# 【注意】:旧的 API 已废弃
# AllChem.GetMorganFingerprintAsBitVect(mol, 2, nBits=2048)
# → 新代码应该用 rdFingerprintGenerator
查看每一位对应什么子结构
# 【这是 ECFP 相对深度学习模型的一个优势】:
# 每一位可以追溯到具体的化学子结构
from rdkit import Chem
from rdkit.Chem import rdFingerprintGenerator, Draw
mol = Chem.MolFromSmiles("CC(=O)Nc1ccc(O)cc1")
gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
ao = rdFingerprintGenerator.AdditionalOutput()
ao.AllocateBitInfoMap()
fp = gen.GetFingerprint(mol, additionalOutput=ao)
bit_info = ao.GetBitInfoMap()
# bit_info: {位号: ((中心原子索引, 半径), ...)}
for bit, envs in list(bit_info.items())[:5]:
print(f"位 {bit}: {envs}")
# 可视化某一位对应的子结构
bits = list(bit_info.keys())[:12]
img = Draw.DrawMorganBits(
[(mol, b, bit_info) for b in bits],
molsPerRow=4,
legends=[f"bit {b}" for b in bits],
)
# 【用途】:
# 1) 【模型可解释性】:哪些子结构驱动了预测(见 168)
# 2) 调试:检查指纹是否捕捉到了预期的特征
# 3) 教学:直观理解指纹在编码什么
变体:FCFP 与计数版
# ---- FCFP:用药效团特征替代原子类型 ----
inv_gen = rdFingerprintGenerator.GetMorganFeatureAtomInvGen()
fcfp_gen = rdFingerprintGenerator.GetMorganGenerator(
radius=2, fpSize=2048, atomInvariantsGenerator=inv_gen)
fp_fcfp = fcfp_gen.GetFingerprint(mol)
# 【差异】:
# ECFP 的原子不变量:元素类型、连接数等【具体属性】
# FCFP 的原子不变量:【功能类别】
# - 氢键供体 / 受体
# - 芳香性
# - 卤素
# - 正电 / 负电
#
# 【含义】:
# ECFP:苯环上的碳 ≠ 吡啶环上的碳
# FCFP:都是「芳香原子」→ 可能相同
#
# → 【FCFP 更抽象,更容易发现骨架不同但功能相似的分子】
# → 骨架跃迁场景下值得试(见 352)
# ---- 计数指纹 ----
fp_count = gen.GetCountFingerprint(mol)
counts = fp_count.ToList()
# 【保留了「某个子结构出现几次」的信息】
# → 机器学习中常常略优于位向量
# ---- 稀疏版本(不哈希,保留完整标识)----
sparse = gen.GetSparseCountFingerprint(mol)
# 【无哈希碰撞】,但长度不固定
# → 适合需要精确子结构匹配的场景
实践中的要点
| 要点 | 说明 |
|---|---|
| 复用 generator 对象 | 不要在循环内创建——初始化有开销 |
用 BulkTanimotoSimilarity |
批量相似性比循环快很多 |
| 先标准化分子 | 去盐、规范化,否则同一化合物的指纹可能不同 |
| 手性的处理 | includeChirality=True 才区分对映体 |
| 缓存指纹 | 大库只算一次存起来 |
| 位数的选择 | 2048 通常够;化学空间大时用 4096 |
# 大规模计算的模板
from rdkit import Chem, DataStructs
from rdkit.Chem import rdFingerprintGenerator
from multiprocessing import Pool
import numpy as np
import pickle
GEN = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
def compute_fp(smiles):
mol = Chem.MolFromSmiles(smiles)
if mol is None:
return None
return GEN.GetFingerprint(mol)
def batch_fingerprints(smiles_list, n_workers=8):
with Pool(n_workers) as pool:
fps = pool.map(compute_fp, smiles_list)
valid = [(s, f) for s, f in zip(smiles_list, fps) if f is not None]
print(f"【{len(smiles_list) - len(valid)} 个失败】")
return valid
# 【缓存】:大库的指纹算一次就存起来
# with open("fps.pkl", "wb") as f:
# pickle.dump(valid, f)
# 批量相似性搜索
def search(query_smiles, library_fps, top_k=100):
q = compute_fp(query_smiles)
if q is None:
return []
sims = DataStructs.BulkTanimotoSimilarity(
q, [f for _, f in library_fps])
order = np.argsort(sims)[::-1][:top_k]
return [(library_fps[i][0], sims[i]) for i in order]
关键要点
- ECFP 后的数字是直径——ECFP4 对应 RDKit 的 radius=2;
- 迭代邻域扩展的过程与 GNN 消息传递结构完全一致,这解释了两者性能相近;
- 每一位可追溯到具体子结构,这是相对深度模型的可解释性优势;
- FCFP 用功能类别替代具体元素,更适合骨架跃迁场景。
延伸资源
- 分子指纹总览:035《分子指纹是什么》;ECFP 详解:037《ECFP 指纹详解》;Tanimoto:039《Tanimoto 相似性》;
- GNN:159《图神经网络 GNN》;骨架跃迁:352《Scaffold Hopping 实战》;可解释性:168《可解释性 AI》。