5.2 聚类、骨架分析与库多样性 本节摘要:把分子库组织成可导航的结构,有三件工具:Butina 聚类按相似度切分区,Murcko 骨架分解盘点骨架家底,MaxMin 挑选在预算内铺满化学空间。本节讲清三者机制与参数脾气,并用"五千分子挑五百采购"的完整案例串起全部流程。 上一节解决"点对点",本节解决"全局":不设查询中心,把整个库组织成主题区、盘点家底、按预算铺设覆盖。这三件工具是库设计(买什么、合成什么)与数据集划分(第6章建模前怎么分组)的共同基础。 一群分子怎么分成几堆 聚类在分子库上的主流做法与前述相似性底座严丝合缝。
本节摘要:把分子库组织成可导航的结构,有三件工具:Butina 聚类按相似度切分区,Murcko 骨架分解盘点骨架家底,MaxMin 挑选在预算内铺满化学空间。本节讲清三者机制与参数脾气,并用"五千分子挑五百采购"的完整案例串起全部流程。
上一节解决"点对点",本节解决"全局":不设查询中心,把整个库组织成主题区、盘点家底、按预算铺设覆盖。这三件工具是库设计(买什么、合成什么)与数据集划分(第6章建模前怎么分组)的共同基础。
聚类在分子库上的主流做法与前述相似性底座严丝合缝。Butina 聚类(文献里也称 Diamond-Broker 风格的中心聚类)是化学信息学的本地特产:算好全库两两距离后,先给每个分子数一数"近亲数量"(距离小于阈值的邻居数),从社交最广的分子开始,把它和它的近亲圈成一个簇、整体撤出,再选下一个社交最广的,直到无人圈养。它的好处是不需要预设簇数、天然产出簇心(每个簇的分子就是"代表"),代价是要一次算全距离矩阵——几千到几万个分子可行,十万级就要换近似方法。
阈值是 Butina 的灵魂旋钮:距离阈值取小,簇碎而多(每个簇很小,代表性强但分区琐碎);取大,簇大而松(区少但簇内化学异质)。没有万能值,惯用做法是扫一组阈值看簇数曲线,结合业务口径("我们想要大约二十个主题区"还是"要三百个微簇")拍板。对比一下k-均值:它要求预设簇数、天然偏向球形簇,在描述符空间里用得多,在指纹空间里因质心无意义而水土不服——指纹是比特串,没有"平均分子"这种东西。
骨架分析回答另一个维度的问题:"这个库在骨架层面重复建设了多少?"Murcko 分解把每个分子剥去侧链,留下环系与连接链构成的核心骨架(乙酰水杨酸的骨架是联苯环系骨架)。对全库分解后统计骨架频率,常常能暴露惊人的集中度——不少历史库的前十个骨架占据三成以上分子,都是当年某个成功系列留下的"恐龙化石层"。
骨架统计的判读口径:骨架集中度高不必然是坏事——项目聚焦某个系列时集中是常态;坏事的是"无意识的集中",即采购与合成从未审视过骨架分布。修正手段也直接:骨架分桶,每桶限额取样,或在下轮采购的多样性约束里加入骨架新颖度。

背景。采购预算只够买五百个样品,候选池五千个分子(已过类药过滤)。目标有两个:覆盖尽量多的化学空间(多样性),且每个区域选出的分子在区内有代表性(代表性)——只顾前者会挑出一堆孤僻的怪分子,只顾后者会买一堆近亲。
操作。Butina 聚类定基调,MaxMin 在簇内与全局补多样性:
from rdkit import Chem from rdkit.Chem import AllChem, DataStructs # 1) 全库指纹与距离矩阵 fps = [AllChem.GetMorganGenerator().GetFingerprint(m) for m in mols] # 五千个分子 n = len(fps) dism = [[1 - DataStructs.TanimotoSimilarity(fps[i], fps[j]) for j in range(i + 1, n)] for i in range(n - 1)] # 2) Butina 聚类:阈值 0.4(相似度 0.6 以上算近亲) from rdkit.ML.Cluster import Butina clusters = Butina.ClusterData(dism, n, 0.4, isDistData=True) print("簇数:", len(clusters)) # 典型输出:六百上下 print("最大簇规模:", len(clusters[0])) # 3) MaxMin:贪心挑五百个彼此远离的分子 from rdkit.SimDivFilters import rdSimDivPickers picker = rdSimDivPickers.MaxMinPicker() picked = picker.LazyBitVectorPick(fps, n, 500) print("已选:", len(picked))
结果。阈值零点四下五千分子聚出六百个左右的簇,最大簇数百上下(通常是某类常见骨架家族);MaxMin 的五百个选中分子在指纹距离上彼此远离,逐簇落点均匀,骨架终检显示选中集的骨架数接近簇数——没有哪个系列垄断名额。
解读。流程里藏着三次取舍。聚类阈值零点四不是算出来的,是业务口径翻译出来的("六百个主题区"恰好与预算五百同量级,保证多数簇能分到一个名额);MaxMin 挑选对簇内代表性是间接保障(起点用簇心可强化),严格的做法是"每簇先 MaxMin 配额、再跨簇平衡"的两级挑选;骨架终检是防呆——指纹多样性不保证骨架多样性,两个指纹远距离的分子可能共享同一骨架,最后的骨架表兜住这种遗漏。
变式。预算改变策略不变但参数变:预算提高到两千,阈值应放宽(零点五)让簇变大,或直接跳过聚类用 MaxMin 全局挑选。任务换成建模数据集划分时(第6章),聚类还有个隐藏用途——按簇划分训练集与测试集,逼模型面对"没见过的骨架"外推,测出来的性能比随机划分诚实得多。
聚类的技能清单里还有一项常被漏记:给第6章的建模提供按簇划分的数据集切分。
随机划分训练集与测试集的漏洞(近亲泄漏)在 6.2 节已经审过;Butina 聚类给出了修补的标准工序:先按指纹把全库聚簇,再按簇为单位分到训练集与测试集——同一簇的分子同进同出,测试集因此由"模型没见过的新化学区"组成。参数上,簇数越多划分越细、测试集越接近随机划分;想要最严格的外推评估,就粗聚成几十个大簇,把整片化学空间留给测试。这套划分还有个诊断价值:若模型在簇划分下成绩骤降,降幅集中在哪些簇,答案直接告诉你模型的外推弱点在哪个化学区——比一个孤零零的 RMSE 数字有营养得多。
同样的逻辑沿用到第8章依然成立:深度模型时代,簇划分升级为"骨架划分加批次划分"的双保险(同一实验批次的分子也同进同出),因为批次效应是深度模型最爱钻的又一条近亲通道。