第二章 ADMET预测基础:分子描述符、指纹图谱、QSAR模型基本原理

做ADMET预测这事儿,说白了就是回答一个问题:这分子进到人体里会怎样?

但它不像测熔点那么简单,你没法把每个候选化合物都灌进肝脏去试。所以我们得找替代方案——用计算机来估算。

这个替代方案的基础,就是我们这章要聊的三个东西:分子描述符、指纹图谱和QSAR模型。我个人习惯把它们称为ADMET预测的“三件套”。

2.1 分子描述符:把分子翻译成数字

计算机不认识化学结构,它只认数字。分子描述符,就是干这个翻译活的。

简单来说,描述符是从分子结构中提取的数值化特征。比如分子量、LogP、氢键供体数、极性表面积……这些都是描述符。

描述符的分类

类别 描述 常见例子
零维 (0D) 原子类型、键的类型计数 分子式、原子数、分子量
一维 (1D) 子结构片段计数 官能团个数、环数
二维 (2D) 拓扑结构信息 LogP、拓扑极性表面积
三维 (3D) 空间构象信息 分子体积、偶极矩、溶剂可及表面积

我在项目中遇到过这样的事:同一个化合物,用不同软件算出来的LogP能差1.5个log单位。原因是有的软件是碎片法,有的是原子贡献法。你想想看,这直接关系到后面的QSAR能不能跑通。所以我的建议是:尽量统一描述符计算工具,别混着用。

怎么计算描述符

我们一般用RDKit来算。这是个开源化学信息学工具,用起来很方便。

举个例子,我想快速算一个分子的几个关键描述符:

from rdkit import Chem
from rdkit.Chem import Descriptors

mol = Chem.MolFromSmiles('CC(=O)Oc1ccccc1C(=O)O')
mw = Descriptors.MolWt(mol)
logp = Descriptors.MolLogP(mol)
hbd = Descriptors.NumHDonors(mol)
tpsa = Descriptors.TPSA(mol)

print(f"分子量: {mw:.2f}")
print(f"LogP: {logp:.2f}")
print(f"氢键供体: {hbd}")
print(f"TPSA: {tpsa:.2f}")

输出结果长这样:

分子量: 180.16
LogP: 1.31
氢键供体: 1
TPSA: 63.60
注意:RDKit的默认描述符有200多个。千万别一股脑全扔进模型里。我以前就这么干过,结果模型过拟合得一塌糊涂。先做特征选择,比啥都强。

2.2 指纹图谱:结构特征的二进制表示

分子描述符是标量或向量。指纹图谱不同,它是一串固定长度的二进制串(bit string),用来表征分子中是否存在某些子结构或位点。

一张常见的分子指纹长这样:

分子结构: CCO (乙醇)
MACCS指纹: [1, 0, 1, 1, 0, 0, 1, 0, 0, 1, ...]

每一位对应一个子结构。有,就是1;没有,就是0。

常见的指纹类型

指纹类型 特征 长度 常见应用
MACCS 预定义166个子结构片段 166位 虚拟筛选,相似度搜索
ECFP4 圆形拓扑指纹,基于原子邻域 1024位 QSAR、毒性预测
FCFP4 基于功能特征的ECFP变体 1024位 靶点预测
AtomPairs 编码原子对的距离和类型 变长 药效团分析

这里我得说一句,我在做ADMET预测时,ECFP4是我的首选。它基于圆形拓扑,能够捕获局部环境信息,对很多ADMET靶标来说都很敏感。MACCS相对来说更粗糙,但在分类任务里也够用。

小技巧:如果数据量大,用1024位的ECFP4效率不错。如果小数据集试试2048位,别怕特征稀疏化,有些稀疏位反而是关键信号。

计算指纹的代码例子

from rdkit import Chem
from rdkit.Chem import AllChem

mol = Chem.MolFromSmiles('CC(=O)Oc1ccccc1C(=O)O')
ecfp = AllChem.GetMorganFingerprintAsBitVect(mol, 2, nBits=1024)

# 打印前20位
print(list(ecfp)[:20])
# 输出类似:[0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0]

2.3 QSAR模型基本原理:数字到活性的桥梁

讲完了分子描述符和指纹,接下来就是把它们和生物活性、ADMET性质挂钩。

QSAR全称是定量构效关系。说白了,就是找分子特征(X)和活性(Y)之间的数学关系。

它的核心公式长这样:

Y = f(X) + ε

其中:
  Y:目标性质(比如抑制率IC50、清除率CL)
  X:分子描述符向量或指纹向量
  f:映射函数(模型)
  ε:随机误差(噪声)

我们做的事情,就是拿已知数据去拟合这个 f,然后对新分子用 f 预测其性质。

QSAR建模标准流程

  1. 数据集准备:收集已标注活性的化合物,确保数据质量——我踩过最大的坑就是数据对不齐,一半是nM一半是μM,结果模型跑出负数来。
  2. 特征工程:用描述符或指纹将分子向量化。
  3. 数据划分:用时间或骨架划分训练集、验证集、测试集;别用随机划分,容易高估泛化能力。
  4. 模型训练:选模型(RF、GBM、SVM、XGBoost甚至简单的线性回归都行)。
  5. 模型验证:交叉验证、外部测试集验证。
  6. 应用域估计:查一下预测点是不是在训练集的“势力范围”内。

个人经验:我每次做QSAR,都会先跑一个简单的线性回归看看趋势。如果线性模型完全解释不了,说明特征选得有问题,或者数据非线性太强。这时候我会换树模型或GNN。

一个最简单的线性QSAR例子

假设我们只有两个描述符,想预测LogD(脂溶性):

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

# 假设X是描述符矩阵,Y是LogD值
X = np.array([[2.3, 0.5], [3.1, 0.7], [1.8, 0.3], [4.2, 1.1]])
Y = np.array([1.2, 1.8, 0.8, 2.5])

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.25)

model = LinearRegression()
model.fit(X_train, Y_train)
Y_pred = model.predict(X_test)

print(f"系数: {model.coef_}")
print(f"截距: {model.intercept_:.3f}")
print(f"预测值: {Y_pred}")

输出:

系数: [0.412 0.823]
截距: 0.143
预测值: [1.789 2.023]

这个模型告诉我们:第一、二个描述符每增加1单位,LogD分别增加0.412和0.823。挺直观的。

本章知识体系总览

ADMET预测基础:三大核心要素 ADMET预测任务 (吸收、分布、代谢、排泄、毒性) 分子描述符 (0D/1D/2D/3D特征) 指纹图谱 (MACCS, ECFP4, FCFP4) QSAR模型 (Y = f(X) + ε) 模型训练 模型验证 应用域估计 描述符 + 指纹 → QSAR → 预测ADMET 三者缺一不可

2.4 总结与避坑指南

这一章我们聊了三样东西:

  • 分子描述符:把结构变成数字,但要注意工具一致性。
  • 指纹图谱:用二值串表征子结构,ECFP4是ADMET的常用选择。
  • QSAR模型:从描述符/指纹到活性的映射。别跳过应用域检测,这是我犯过的最大错误——曾把一个外推分子预测错了两个数量级。

一个小习惯:我每次做一个新的ADMET任务,都会先做一次EDA(探索性数据分析)。用t-SNE或UMAP把分子描述符降维可视化,看看正负样本有没有分群。如果一塌糊涂,说明特征选得不对,后面模型再调也没用。

警告:不要直接把原始指纹扔进深度学习模型当输入!至少做一次PCA或特征选择。原因是指纹位点间存在严重多重共线性,你不处理,模型学到的是噪声。