AminoHydro Classifier:用 SMILES 和 MLP 判断氨基酸亲疏水性

基于 RDKit 提取 Morgan 指纹,使用 MLP 神经网络对氨基酸进行亲疏水性二分类的入门项目完整笔记

2026/5/5
0 分钟阅读

最近我做了一个很小但很完整的 AI 制药入门项目:输入一个氨基酸的 SMILES 字符串,程序会判断它更偏向亲水还是疏水。

这个项目的名字叫 AminoHydro Classifier。它不追求复杂模型,也不假装自己已经能替代真正的药物性质预测系统。它更像一个“最小可运行样例”:把计算化学里的分子表示、RDKit 特征提取、机器学习分类器训练和模型预测串成一条完整链路。

核心流程可以概括为:

SMILES 字符串 -> RDKit 解析 -> Mol 对象 -> Morgan 指纹 -> MLP 神经网络 -> 亲水 / 疏水

这条链路虽然短,但已经包含了很多 AI 制药项目里反复出现的基础概念。

为什么从氨基酸开始

氨基酸是蛋白质的基本组成单元。标准蛋白质编码氨基酸有 20 种,它们可以通过不同排列组合形成大量蛋白质。每个氨基酸都有共同的主链结构:

  • 氨基:-NH2
  • 羧基:-COOH
  • 一个氢原子:H
  • 一个侧链基团:R group

真正决定不同氨基酸性质的,主要是侧链 R。有的侧链偏非极性,更容易和脂溶性环境相处,通常被认为更疏水;有的侧链带有羟基、羧基、氨基等极性或可电离基团,更容易和水形成相互作用,通常被认为更亲水。

亲疏水性在药物设计里非常重要。比如,一个分子如果过于亲水,可能难以穿过脂质膜;如果过于疏水,又可能带来溶解度差、非特异性结合强等问题。血脑屏障 BBB 也是一个典型例子:一般来说,更合适的脂溶性有助于分子跨越屏障,但真实情况还会受到分子量、极性表面积、氢键供受体、转运蛋白等多种因素影响。

所以,氨基酸亲疏水性是一个很适合作为入门任务的性质:概念直观,数据量小,容易验证,也能自然引出分子机器学习的基本流程。

SMILES:把分子写成字符串

计算机不能直接理解“亮氨酸”“赖氨酸”这样的中文名称,也不能直接理解课本上的二维结构图。我们需要一种机器可读的表示方法。

SMILES 就是其中一种常见格式。它可以用一串文本描述分子的原子、键、环、支链等结构信息。例如:

亮氨酸 Leu: CC(C)CC(N)C(=O)O
丝氨酸 Ser: NC(CO)C(=O)O
赖氨酸 Lys: NCCCCC(N)C(=O)O

这些字符串看起来像化学版的“密码”,但对 RDKit 这样的化学信息学工具来说,它们是可以被解析的结构描述。

项目的数据集在 data/amino_acids.csv,一共包含 20 个蛋白质编码氨基酸,每条数据包括名称、缩写、三字母代码、SMILES 字符串和亲疏水标签。

标签约定是:

1 = 疏水
0 = 亲水

当前数据集中有 9 个疏水氨基酸、11 个亲水氨基酸。

RDKit 负责什么

一个很容易混淆的问题是:RDKit 是不是分类器?

答案是:不是。

RDKit 更像是一个“分子工具箱”。它不会自动告诉我们一个分子是亲水还是疏水,但它能做很多机器学习前必需的事情,比如:

  • 读取 SMILES 字符串
  • 检查分子是否合法
  • 构建内部的分子对象 Mol
  • 计算分子指纹、描述符、子结构等特征

在这个项目里,RDKit 的第一步工作是把 SMILES 转成 Mol 对象:

mol = Chem.MolFromSmiles(smiles)

可以把 Mol 理解成 RDKit 在程序内部使用的分子结构。它比字符串更结构化,知道哪些原子连在一起,哪些是支链,哪些是环结构。

但是机器学习模型仍然不能直接吃 Mol 对象。下一步,我们还要把 Mol 转成数值向量。

Morgan 指纹:把分子变成 2048 位向量

这个项目使用 Morgan fingerprint,也就是常说的 Morgan 指纹。它和 ECFP 类指纹思想接近,会围绕每个原子观察一定半径内的局部化学环境,然后把这些结构信息编码到一个固定长度的二进制向量里。

项目里默认使用:

radius = 2
n_bits = 2048

也就是说,每个氨基酸最终都会变成一个长度为 2048 的 0/1 向量。

可以这样理解:

SMILES: CC(C)CC(N)C(=O)O

Morgan 指纹:
[0, 1, 0, 0, 1, 0, ..., 1, 0]

这个向量不是给人看的,而是给模型看的。它把“分子结构”转换成了“机器学习特征”。

项目中的对应代码在 src/features.py

generator = rdFingerprintGenerator.GetMorganGenerator(radius=radius, fpSize=n_bits)
fp = generator.GetFingerprint(mol)
return np.array(fp, dtype=int)

这里有一个关键点:Morgan 指纹不是神经网络,也不是预测模型。它只是特征工程的一部分。真正做分类的是后面的 MLP。

MLP:最经典的前馈神经网络

MLP,全称 Multi-Layer Perceptron,多层感知机。它是最经典的前馈神经网络之一。

它的基本结构可以简单理解为:

输入层 -> 隐藏层 -> 激活函数 -> 隐藏层 -> 输出层

每一层都在做类似这样的事情:

加权求和 -> 加上偏置 -> 经过激活函数 -> 传给下一层

如果没有激活函数,多层线性变换叠在一起本质上仍然只是线性模型。加入 ReLU、Sigmoid、Tanh 这样的非线性激活函数后,模型才有能力学习更复杂的非线性关系。

在这个项目里,MLP 的输入是 2048 维 Morgan 指纹,输出是一个二分类结果:

0 = 亲水
1 = 疏水

模型定义在 src/model.py

model = MLPClassifier(
    hidden_layer_sizes=(128, 64),
    activation="relu",
    solver="adam",
    alpha=0.001,
    max_iter=1000,
    random_state=42,
    early_stopping=True,
    validation_fraction=0.2,
    n_iter_no_change=20,
)

这里用了两层隐藏层,分别有 128 和 64 个神经元。early_stopping=True 表示如果验证集效果长时间没有提升,训练会提前停止,避免在小数据集上过度训练。

训练流程

训练入口在 main.py,运行:

python main.py train

完整训练流程如下:

  1. data/amino_acids.csv 读取 20 个氨基酸。
  2. 每个 SMILES 先通过 RDKit 转成 Mol
  3. 每个 Mol 再转成 2048 维 Morgan 指纹。
  4. 得到特征矩阵 X 和标签数组 y
  5. 使用 train_test_split(..., stratify=y) 切分训练集和测试集。
  6. 训练 MLPClassifier
  7. 打印准确率、分类报告和混淆矩阵。
  8. 使用 joblib 保存模型到 models/mlp_classifier.pkl

其中 stratify=y 是一个小但重要的细节。因为数据集很小,只有 20 条。如果随机切分时某一类样本在测试集里太少,评估结果会非常不稳定。stratify=y 可以尽量保证训练集和测试集中的亲水/疏水比例接近原始数据。

训练时,项目还做了 5 折交叉验证。交叉验证会重复多次切分训练集和验证集,比单次切分更能反映模型在小数据集上的波动。

实验结果

在当前数据和默认参数下,我运行了一次训练,得到结果如下:

数据集大小: 20 个氨基酸
疏水性氨基酸: 9 个
亲水性氨基酸: 11 个

5 折交叉验证准确率: 0.5000 (+/- 0.1581)
测试集准确率: 0.6000

混淆矩阵:
[[2 1]
 [1 1]]

这个结果并不惊艳,甚至可以说很朴素。但这正是这个项目值得写出来的地方:数据只有 20 条,模型还使用了 2048 维稀疏指纹,这在机器学习里是典型的“小样本、高维特征”场景。

在这种情况下,模型很容易受到训练/测试划分影响。一次测试集准确率 0.60,并不能说明模型已经学到了稳定、可泛化的化学规律。它更多说明:这条技术链路可以跑通,模型能基于分子指纹做出一个可解释的二分类输出。

如果要做成更严肃的性质预测模型,需要更多数据、更稳健的评估方式,以及更仔细的特征和模型选择。

预测流程

训练完成后,模型会保存为:

models/mlp_classifier.pkl

预测时的流程和训练时保持一致:

  1. 读取已经保存的模型。
  2. 输入一个新的 SMILES 字符串。
  3. 用 RDKit 解析成 Mol
  4. 转成同样参数的 Morgan 指纹。
  5. 输入 MLP,得到类别和概率。

运行演示:

python main.py demo

也可以进入交互模式:

python main.py interactive

预测函数在 src/predict.py,返回结果包括:

{
    "smiles": smiles,
    "prediction": 0 or 1,
    "label": "亲水" or "疏水",
    "confidence": confidence,
    "hydrophobic_prob": hydrophobic_prob,
    "hydrophilic_prob": hydrophilic_prob,
}

这里的 confidence 本质上是模型对预测类别的概率估计。它可以作为参考,但在这么小的数据集上,不能把它理解成严格可靠的化学置信度。

这个项目真正学到什么

我觉得这个项目的价值不在于“用 MLP 精准预测氨基酸亲疏水性”,而在于把 AI 制药中一个常见问题拆成了几个清楚的层次:

化学对象 -> 机器可读表示 -> 数值特征 -> 机器学习模型 -> 性质预测

更具体地说,它帮助我理解了:

  • SMILES 是分子的文本表示,不是模型特征本身。
  • RDKit 是化学信息学工具箱,不是神经网络分类器。
  • Mol 是 RDKit 内部的结构化分子对象。
  • Morgan 指纹把分子局部结构编码成固定长度向量。
  • MLP 接收的是数值向量,而不是原始化学名称或 SMILES。
  • 亲疏水标签是人为定义的数据监督信号,模型是在学习这个标签规则。
  • 小数据集上的准确率很容易波动,不能过度解读。

如果把这个项目看成一个“分子机器学习 Hello World”,它已经足够完整。

局限性

这个项目也有明显局限。

第一,数据量太小。20 个样本无法支撑复杂神经网络稳定学习,尤其输入还是 2048 维指纹。

第二,亲疏水标签本身是简化的。真实分子的亲疏水性通常会用 logP、logD、溶解度、极性表面积等更连续、更细致的指标描述,而不是简单二分类。

第三,氨基酸在不同 pH 条件下会有不同电荷状态。当前 SMILES 和标签没有显式考虑环境 pH、电离状态、两性离子形式等因素。

第四,Morgan 指纹虽然经典,但它会压缩分子信息,也可能发生哈希碰撞。对于更复杂任务,可以尝试更多分子描述符、图神经网络或预训练分子模型。

第五,模型评估还很初级。当前的交叉验证和测试集评估适合教学演示,但不适合宣称真实性能。

可以怎么继续优化

如果继续扩展这个项目,我会优先考虑这些方向:

  1. 增加数据量,引入更多天然或人工氨基酸及其性质数据。
  2. 把二分类标签换成连续性质,例如 logP 或实验溶解度。
  3. 加入更多 RDKit 分子描述符,例如分子量、TPSA、氢键供体/受体数量、可旋转键数量。
  4. 对比不同模型,例如 Logistic Regression、Random Forest、SVM、XGBoost。
  5. 使用留一法交叉验证,适配 20 条样本这种极小数据集。
  6. 做一个简单 Web 页面,让用户输入 SMILES 后实时看到预测结果。
  7. 增加非法 SMILES 检查和可视化分子结构展示。

这些扩展会让项目从“跑通流程”逐渐接近一个可展示、可解释、可交互的小型 AI 制药工具。

总结

AminoHydro Classifier 是一个很小的项目,但它覆盖了分子机器学习中最基础也最重要的一条链路:

分子字符串 -> 化学结构 -> 分子指纹 -> 神经网络 -> 性质预测

通过它,我更清楚地理解了 RDKit 和机器学习模型之间的分工:RDKit 负责把化学结构变成可计算特征,MLP 负责从特征中学习分类边界。

这个项目目前不适合作为严肃的药物性质预测模型,但非常适合作为 AI 制药入门练习。它足够小,小到每一步都能看懂;又足够完整,完整到可以从数据、训练、评估一直走到预测。

对我来说,这正是学习 AI 制药很好的起点:不是一上来追逐巨大的模型,而是先把一条最小的分子机器学习流水线亲手跑通。