为何我的分子Morgan指纹随比特数增加未呈现平台期?
关于Morgan指纹比特向量尺寸与占用比特数振荡问题的解答
问题背景
我是 cheminformatics 及编程领域新手,首个项目采用Morgan指纹作为机器学习模型的特征,但找不到合适的比特向量尺寸。我试图通过分析不同长度下的最大占用比特数来确定合适尺寸,具体步骤如下:
- 编写函数
max_on_bits,输入SMILES列表和比特向量长度,生成所有SMILES的指纹并返回最大占用比特数:
def max_on_bits(smiles_list, nbits): on_bits = [] for smiles in smiles_list: mol = Chem.MolFromSmiles(smiles) fp = AllChem.GetMorganFingerprintAsBitVect(mol, 3, nBits=nbits, useChirality=True, useFeatures=True) num_on_bits = sum(np.array(fp)) on_bits.append(num_on_bits) return max(on_bits)
- 遍历1024到20482、步长256的比特数范围,调用函数获取占用数据:
nbits_range = range(1024, 20483, 256) occupancy = [] for nbits in nbits_range: occupancy.append(max_on_bits(smiles_list, nbits))
- 绘图展示结果:
import matplotlib.pyplot as plt plt.plot(nbits_range, occupancy) plt.xlabel("Bit Vector Size") plt.ylabel("Maximum Number of bits occupied") plt.grid() plt.show()
但得到的曲线未呈现平台期,反而呈振荡状态,想问这是代码问题还是Morgan指纹的固有特性?
解答
振荡原因:Morgan指纹的固有特性,而非代码问题
振荡的核心是Morgan指纹的哈希映射机制导致的随机波动:
- Morgan指纹先提取分子的子结构特征,再通过哈希函数将这些子结构映射到长度为
nbits的比特向量中。哈希函数的输出会随nbits变化而改变——同一个子结构在不同nbits下会被映射到不同的比特位。 - 当
nbits增大时,理论上哈希冲突概率降低,但随机性可能导致某几个分子的子结构刚好在新的nbits下出现更多无重叠的映射,使占用比特数临时上升;反之,偶尔也会出现更多子结构被映射到同一比特位(冲突),导致占用比特数下降。这种随机波动就形成了振荡曲线。
代码优化建议(不影响振荡问题,仅提升效率与稳定性)
你的代码逻辑没问题,但可以做两处优化:
- 直接用
fp.GetNumOnBits()获取占用比特数,无需转成numpy数组求和,更高效; - 过滤无效SMILES(
MolFromSmiles返回None的情况),避免报错。
优化后的函数示例:
def max_on_bits(smiles_list, nbits): max_bits = 0 for smiles in smiles_list: mol = Chem.MolFromSmiles(smiles) if not mol: continue # 跳过无效SMILES fp = AllChem.GetMorganFingerprintAsBitVect( mol, 3, nBits=nbits, useChirality=True, useFeatures=True ) current_bits = fp.GetNumOnBits() if current_bits > max_bits: max_bits = current_bits return max_bits
如何选择合适的比特向量尺寸
不要仅关注最大占用比特数,建议从以下角度判断:
- 看整体数据集的平均占用率(所有分子的平均占用比特数/
nbits),维持在0.1~0.3之间可平衡冲突率和向量稀疏性; - 测试常用尺寸(如1024、2048、4096),结合你的ML模型性能(准确率、AUC等)选择——当性能不再随尺寸增大而提升时,该尺寸即为合适值;
- 若追求更低冲突率,可使用计数型Morgan指纹(
GetMorganFingerprint返回字典形式的计数指纹),再转换为固定长度向量,但计算成本更高。
内容的提问来源于stack exchange,提问作者Arko Mohari
相关产品推荐
相关产品推荐

