当前位置: 首页 > news >正文

杭州余杭做网站公司网页制作好了如果让别人搜到

杭州余杭做网站公司,网页制作好了如果让别人搜到,长沙网站整站优化,同时部署WordPress和django1. 引言 1.1. 分子性质预测概述 分子性质预测是计算机辅助药物发现流程中至关重要的任务之一#xff0c;它在许多下游应用如药物筛选和药物设计中发挥着核心作用#xff1a; 1.1.1. 目的与重要性#xff1a; 分子性质预测旨在通过分子内部信息#xff08;如原子坐标、原…1. 引言 1.1. 分子性质预测概述 分子性质预测是计算机辅助药物发现流程中至关重要的任务之一它在许多下游应用如药物筛选和药物设计中发挥着核心作用 1.1.1. 目的与重要性 分子性质预测旨在通过分子内部信息如原子坐标、原子序数等对分子的物理和化学性质进行预测。这有助于快速从大量候选化合物中筛选出符合特定性质的化合物从而加速药物筛选和设计的流程。 1.1.2. 传统方法 密度泛函理论DFT是传统的分子性质预测方法它精确但计算耗时通常需要数小时才能完成单个分子的性质计算。由于候选化合物数量庞大使用DFT进行预测需要巨大的资源和时间成本。 1.1.3. 深度学习在分子性质预测中的应用 得益于深度学习的快速发展越来越多的研究者开始尝试将深度学习应用于分子性质预测领域。神经网络模型能够从数据中自行学习合适的特征表示不需要依赖人工构建的特征如指纹与描述符。深度学习模型能够处理复杂的分子结构信息并自动提取分子特征从而提高了预测的准确性。 1.1.4. 主流的基于ML的分子性质预测方法 基于描述符的方法对分子特征进行人工提取其质量对预测结果有重要影响。基于图的方法将分子描述为具有节点原子和边键的图结构利用图神经网络GNN进行特征提取。这种方法能够避免人工提取描述符过程中的信息丢失但严重依赖于数据量。基于SMILES的方法将分子性质预测看作自然语言处理NLP问题直接从SMILES字符串中提取分子特征。这种方法对特征提取能力和数据量有更高要求。分子性质预测方法中的消息传递神经网络MPNN是一种重要的技术它特别适用于处理图结构数据如分子结构中的原子和化学键。 1.1.5. 挑战与未来方向 尽管深度学习在分子性质预测中取得了显著进展但仍面临一些挑战如数据稀疏性、模型可解释性等。未来研究将致力于开发更先进的模型架构和算法以提高预测准确性、降低计算成本并探索更多应用场景。 分子性质预测是药物发现和设计中不可或缺的一环。随着深度学习技术的不断发展我们有理由相信未来的分子性质预测将更加高效、准确和智能化。 1.2 消息传递神经网络MPNN 消息传递神经网络MPNN是一种重要的图神经网络GNN框架特别适用于处理具有图结构的数据如社交网络、化学分子等。 1.2.1. MPNN的基本原理 MPNN的核心思想是通过节点之间的消息传递来更新节点的特征表示。在每一步的消息传递过程中节点会接收来自邻居节点的信息并利用这些信息来更新自身的特征表示。通过多轮的消息传递节点之间的信息能够得到充分的交流和融合从而使得整个图的结构和节点特征得到更精细的表示。 1.2.2. MPNN的模型结构 MPNN的模型结构通常包括输入层、消息传递层和输出层。 输入层负责将原始的节点特征和图结构信息转化为可处理的神经网络输入。消息传递层是MPNN的核心部分它通过多轮的消息传递来更新节点的特征表示。每个节点都会接收来自邻居节点的消息并根据这些消息来更新自身的特征表示。具体的更新方式可以通过一种或多种不同的更新函数来实现如加权平均、最大池化等。输出层将最终的节点特征表示转化为具体的任务输出。 1.2.3. MPNN的应用场景 MPNN在分子特性预测方面有着广泛的应用。由于分子可以被视为一种特殊的图结构其中原子是节点化学键是边MPNN能够很好地处理这种结构数据。例如MPNN可以用于预测分子的血脑屏障通透性、溶解度等性质。 1.2.4. MPNN的优势 MPNN能够处理具有不同大小和结构的图数据因此具有高度的灵活性和可扩展性。通过迭代地传递和聚合信息MPNN能够捕获图的全局结构信息从而得到更精细的节点表示。MPNN的模型结构相对简单易于实现和调试。 1.2.5. MPNN的未来发展 随着图神经网络技术的不断发展MPNN的性能和应用场景也将得到进一步的拓展。例如可以探索将MPNN与其他深度学习模型结合使用以进一步提高其性能或者将MPNN应用于更广泛的领域如社交网络分析、推荐系统等。 消息传递神经网络MPNN是一种强大的图神经网络框架它通过节点之间的消息传递来更新节点的特征表示具有高度的灵活性和可扩展性。在分子特性预测等领域有着广泛的应用前景。 1.3. MPNN用于分子预测的优势和局限 1.3.1.MPNN在分子预测方面的优势 自然处理图结构数据MPNN是专为处理图结构数据设计的分子作为图结构数据的一种其中原子作为节点化学键作为边使得MPNN能够自然且高效地处理分子数据。 全局结构信息的捕获MPNN通过迭代地传递和聚合节点间的信息能够捕获分子的全局结构信息。这种能力使得MPNN在预测分子性质时能够综合考虑分子内部各个部分之间的相互作用和整体结构从而提高预测的准确性。 高度灵活性和可扩展性MPNN不受分子大小或结构的限制能够处理从简单有机分子到复杂生物大分子的各种情况。这种灵活性和可扩展性使得MPNN在分子预测领域具有广泛的应用前景。 良好的预测性能MPNN在多个分子预测任务中均取得了良好的性能。例如在预测分子的血脑屏障通透性、溶解度等性质时MPNN能够准确地预测出分子的这些性质为药物设计和筛选提供有力支持。 可解释性MPNN的预测结果具有一定的可解释性。通过分析MPNN在预测过程中不同节点和边的贡献可以了解哪些部分对预测结果的影响最大从而提供对分子性质的深入理解。 1.3.2.MPNN在分子预测方面的局限性 数据依赖性MPNN的性能在很大程度上取决于训练数据的数量和质量。在数据量不足或数据质量不高的情况下MPNN的预测性能可能会受到影响。 计算复杂度 对于大型复杂分子MPNN的计算复杂度可能会较高需要较长的计算时间。这可能会限制MPNN在实时或大规模分子预测任务中的应用。 模型复杂度MPNN的模型结构相对复杂需要较高的技术门槛和计算资源来设计和实现。这可能会增加MPNN在分子预测领域的应用难度和成本。 可解释性的局限性尽管MPNN具有一定的可解释性但其解释能力仍然有限。在某些情况下MPNN可能无法提供足够详细的解释来完全理解其预测结果。 综上来看MPNN在分子预测方面具有显著的优势但也存在一些局限性。为了充分发挥MPNN的优势并克服其局限性需要进一步探索和优化MPNN的模型结构和算法设计并积累更多的训练数据和经验。 2. MPNN实现分子性质预测过程 2.1. 实现过程概述 本文讨论的方法基于消息传递神经网络MPNN实现预测分子血脑屏障通透性BBBP 2.1.1. 文章主要内容 在本文中我们将探索如何使用消息传递神经网络MPNN这一图神经网络GNN的变体来预测分子的一个重要属性——血脑屏障通透性BBBP。BBBP是评估药物能否穿过血脑屏障的关键指标对于药物研发具有重大意义。 2.2.2. 研究的动机 分子可以被自然地建模为无向图G(V, E)其中V代表顶点或节点对应于分子中的原子E代表边对应于原子之间的化学键。这种表示形式为利用图神经网络处理分子数据提供了便利。与基于预计算分子特征的传统方法如随机森林、支持向量机等相比MPNN等GNN模型能够直接在图结构上进行操作从而有可能捕获到更复杂的分子间关系进而提升预测性能。 2.2.3.传统方法与GNN的比较 传统方法通常依赖于一系列预计算的分子特征如分子量、极性、电荷和碳原子数量等。这些特征虽然已被证明是分子属性的有效预测器但它们往往忽略了分子内部的复杂结构和相互作用。相比之下GNN如MPNN能够直接在图的节点和边上进行操作从而更全面地捕捉分子的结构和化学性质。 2.2.4.文章的内容 数据预处理将分子数据集转换为图结构并为每个节点和边提取相关特征。定义MPNN模型 设计消息函数用于在节点之间传递信息。设计更新函数用于根据接收到的消息更新节点状态。设计读出函数用于从节点状态生成整个图的表示。 模型训练使用标记的分子数据集训练MPNN模型通过反向传播算法优化模型参数。 模型评估在测试集上评估模型的性能并使用适当的评估指标如准确率、F1分数、AUC-PR等来衡量预测效果。 模型调优根据评估结果调整模型参数和架构以优化预测性能。 2.2.软件准备 软件安装说明 为了开始我们的教程首先需要安装RDKit和其他必要的依赖项。 RDKit是一个集化学信息学和机器学习软件于一体的工具集使用C和Python编写。在这个教程中我们将利用RDKit将SMILES简化分子输入行表示法转换为分子对象并从中提取原子和键的集合。 SMILES是一种将分子结构表达为ASCII字符串的方式。对于较小的分子SMILES字符串是相对容易理解的。将分子编码为字符串既有助于减轻数据库和/或网络搜索的负担又便于进行这些搜索。RDKit运用算法将给定的SMILES准确转换为分子对象这些对象随后可用于计算大量的分子属性/特征。 通常情况下RDKit通过Conda进行安装。但感谢rdkit_platform_wheels项目我们现在可以更方便地通过pip来安装RDKit。以下是安装RDKit的步骤注意这里只是一个示例命令具体命令可能因您的环境和需求而有所不同 pip install rdkit-pypi 请确保您的Python环境已正确设置并且您已经安装了所有必要的依赖项。此外根据您的项目需求您可能还需要安装其他科学计算库如NumPy、SciPy、Pandas等这些库可以通过类似的pip命令进行安装。 pip -q install pandas pip -q install Pillow pip -q install matplotlib pip -q install pydot sudo apt-get -qq install graphviz在继续教程之前请检查您的安装是否正确并确认所有依赖项都已满足。如有需要您可以查阅RDKit的官方文档或相关社区资源以获取更详细的安装指南和支持。 导入需要的安装包 import os# Temporary suppress tf logs os.environ[TF_CPP_MIN_LOG_LEVEL] 3import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import numpy as np import pandas as pd import matplotlib.pyplot as plt import warnings from rdkit import Chem from rdkit import RDLogger from rdkit.Chem.Draw import IPythonConsole from rdkit.Chem.Draw import MolsToGridImage# Temporary suppress warnings and RDKit logs warnings.filterwarnings(ignore) RDLogger.DisableLog(rdApp.*)np.random.seed(42) tf.random.set_seed(42)2.3. 数据预处理 2.3.1. 下载数据集 段代码首先会尝试从指定的URL下载数据集如果它尚未被下载到默认的Keras缓存目录。然后它会使用Pandas读取CSV文件并选择第2、3和4列因为usecols的索引是从0开始的。最后它会打印出索引为96到103的行。 import keras.utils import pandas as pd # 使用keras.utils.get_file下载数据集如果尚未下载 csv_path keras.utils.get_file( BBBP.csv, https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/BBBP.csv ) # 使用pandas读取csv文件并选择特定的列 df pd.read_csv(csv_path, usecols[1, 2, 3]) # 显示索引为96到103的行注意Python中的索引是从0开始的 print(df.iloc[96:104])序号分子名称p_npSMILES字符串96头孢西丁1CO[C]1(NC(O)Cc2sccc2)[CH]3SCC(C(N3C1O)C(O...97Org341671NC(CCC)c1ccccc1c2noc3c2cccc3989-羟基利培酮1OC1C(N2CCC1)NC(C)C(CCN3CCC(CC3)c4c5ccc(F)cc5...99醋氨酚对乙酰氨基酚1CC(O)Nc1ccc(O)cc1100乙酰水杨酸阿司匹林0CC(O)Oc1ccccc1C(O)O101别嘌醇0OC1NCNC2NNCC12102前列地尔0CCCCC[CH](O)/CC/[CH]1[CH](O)CC(O)[CH]1C...103氨茶碱0CN1C(O)N(C)c2nc[nH]c2C1O.CN3C(O)N(C)c4nc[nH]... 注释p_np渗透性标签 (1能渗透, 0不能渗透) 通过上述代码我们下载了BBBP数据集 数据集名称血脑屏障渗透性Blood-Brain Barrier Penetration, BBBP数据集 来源该数据集可以从MoleculeNet.org下载相关信息可在《一种基于贝叶斯方法的计算机模拟血脑屏障渗透性建模》和《MoleculeNet分子机器学习的基准》等文献中找到。 数据集概述 数据量包含2,050个分子样本。特征每个分子样本都包含以下信息 名称分子的唯一标识符或名称。SMILES字符串表示分子结构的简化分子输入行表示法Simplified Molecular Input Line Entry System。 标签每个分子样本都有一个二进制标签1或0表示该分子是否能够通过血脑屏障BBB。 研究背景 血脑屏障BBB是分隔血液与大脑细胞外液的一层特殊膜它的主要功能是保护大脑免受有害物质和病原体的侵害。然而这也使得许多潜在的治疗中枢神经系统疾病的药物无法进入大脑。因此对血脑屏障渗透性的研究对于开发能够针对中枢神经系统的新药物至关重要。 数据集用途 该数据集可用于训练机器学习模型以预测新分子的血脑屏障渗透性。通过准确的预测研究人员可以更有效地筛选出具有潜在疗效的药物候选物从而加速药物开发的进程。 2.3.2.特征定义 为了对分子中的原子和键进行特征编码我们定义了两个类AtomFeaturizer 和 BondFeaturizer。 为了保持本教程的简洁和明了我们仅选择了几个关键的原子和键特征进行介绍对于原子我们考虑其元素符号、价电子数、氢键数以及轨道杂化情况对于键我们关注其共价键的类型以及共轭状态。这些特征将有助于我们更好地描述分子结构并在后续的机器学习模型中进行有效的特征表示。 import numpy as np from rdkit import Chem class Featurizer: def __init__(self, allowable_sets): self.dim 0 self.features_mapping {} for k, s in allowable_sets.items(): s sorted(list(s)) self.features_mapping[k] dict(zip(s, range(self.dim, len(s) self.dim))) self.dim len(s) def encode(self, inputs): output np.zeros((self.dim,)) for name_feature, feature_mapping in self.features_mapping.items(): feature getattr(self, name_feature)(inputs) if feature in feature_mapping: output[feature_mapping[feature]] 1.0 return output class AtomFeaturizer(Featurizer): def __init__(self, allowable_sets): super().__init__(allowable_sets) # allowable_sets 中至少包含 symbol, n_valence, n_hydrogens, hybridization 这几个键 # 对应的值应该是这些特征的可能取值集合 def symbol(self, atom): # atom 是 RDKit 的 Atom 对象 return atom.GetSymbol() def n_valence(self, atom): # RDKit 的 Atom 对象有 GetTotalValence 方法 return atom.GetTotalValence() def n_hydrogens(self, atom): # RDKit 的 Atom 对象有 GetTotalNumHs 方法 return atom.GetTotalNumHs() def hybridization(self, atom): # GetHybridization 返回的是一个枚举类型并且有 name 属性 hybridization_name atom.GetHybridization().name # 注意 name 是一个字符串并且可能需要转换为小写 # 但有时枚举的 name 属性已经是大写或小写或者可能不是完全小写因此这个转换可能不是必要的 # 根据实际 RDKit 版本和枚举实现来确定是否需要转换 return hybridization_name.lower() # 示例用法 # RDKit 的 Atom 对象 atom以及一个定义允许特征集的字典 allowable_sets # allowable_sets {symbol: set([C, O, N, H]), n_valence: set([1, 2, 3, 4]), ...} # atom_featurizer AtomFeaturizer(allowable_sets) # encoded_features atom_featurizer.encode(atom) class BondFeaturizer(Featurizer): def __init__(self, allowable_sets): super().__init__(allowable_sets) # 增加一个维度来处理没有键的情况例如在一个原子的末端 self.dim_no_bond self.dim self.dim 1 # 额外的维度用于标识没有键 def encode(self, bond): if bond is None: # 如果键是 None则返回一个特殊的编码 return np.zeros(self.dim) [1.0] output np.zeros(self.dim) # 先将没有键的维度设为0 output[-1] 0.0 # 调用基类的 encode 方法传入 bond 的一个属性或方法返回的信息 # 注意这里可能需要一些调整因为 bond 对象可能没有直接的方法可以调用 # 我们假设 bond 是一个有 bond_type 和 conjugated 属性的对象 bond_info {bond_type: bond.GetBondType().name.lower(), conjugated: bond.GetIsConjugated()} for name_feature, feature_value in bond_info.items(): if feature_value in self.features_mapping[name_feature]: output[self.features_mapping[name_feature][feature_value]] 1.0 return output def bond_type(self, bond): return bond.GetBondType().name.lower() def conjugated(self, bond): return bond.GetIsConjugated() atom_featurizer AtomFeaturizer( allowable_sets{ symbol: {B, Br, C, Ca, Cl, F, H, I, N, Na, O, P, S}, n_valence: {0, 1, 2, 3, 4, 5, 6}, n_hydrogens: {0, 1, 2, 3, 4}, hybridization: {s, sp, sp2, sp3}, } ) bond_featurizer BondFeaturizer( allowable_sets{ bond_type: {single, double, triple, aromatic}, conjugated: {True, False}, } ) 代码定义了两个类Featurizer 和它的两个子类 AtomFeaturizer 和 BondFeaturizer。这些类用于将化学原子和键的信息编码成固定维度的特征向量这在机器学习和化学信息学中很常见。 Featurizer 类 __init__(self, allowable_sets): 初始化函数它接受一个字典 allowable_sets其中包含了要编码的特征名和它们各自的可能值的集合。它计算了特征的总维度 self.dim并为每个特征名创建了一个映射到维度的字典 self.features_mapping。encode(self, inputs): 这是一个用于编码输入的函数。它接受一个输入在这种情况下可能是原子或键然后调用适当的方法例如 symbol, n_valence 等来获取输入的特征值。然后它将这些特征值映射到输出向量 output 的相应位置并返回这个向量。 AtomFeaturizer 类 __init__(self, allowable_sets): 初始化函数它调用 Featurizer 的初始化函数来设置原子特征的映射。它定义了四个方法symbol, n_valence, n_hydrogens, 和 hybridization这些方法分别用于从 rdkit.Chem.Atom 对象中提取原子的特定属性。 BondFeaturizer 类 __init__(self, allowable_sets): 初始化函数它首先调用 Featurizer 的初始化函数来设置键特征的映射然后增加了一个额外的维度 self.dim 来处理 None即不存在的键的情况。但这里有一个问题在 Featurizer 的初始化之后增加 self.dim 是不正确的因为这会影响之前创建的 features_mapping 的有效性。encode(self, bond): 这个函数首先检查 bond 是否为 None。如果是它返回一个特殊的编码在最后一个维度上有一个 1。否则它调用 Featurizer 的 encode 方法来编码键的特征。但这里也有一个问题由于 BondFeaturizer 的 encode 方法覆盖了 Featurizer 的 encode 方法它实际上不会使用 bond_type 和 conjugated 方法来编码键的特征。bond_type 和 conjugated 方法分别用于从 rdkit.Chem.Bond 对象中提取键的类型和共轭状态。 2.3.3.生成图 为了从SMILES字符串生成完整的图我们需要先实现以下两个函数 molecule_from_smiles这个函数将SMILES字符串作为输入并借助RDKit库将其转换为分子对象。 graph_from_molecule这个函数将分子对象作为输入并返回一个图。这个图以三元组的形式表示包括原子特征atom_features、键特征bond_features和原子对索引pair_indices。我们将利用之前定义的类来实现这个功能。 最后我们可以编写graphs_from_smiles函数该函数将上述两个函数组合起来应用于训练集、验证集和测试集中的所有SMILES字符串。 # atom_featurizer 和 bond_featurizer 已经被定义并初始化 def molecule_from_smiles(smiles): # 尝试从SMILES字符串创建分子并首先进行基本的清理 molecule Chem.MolFromSmiles(smiles, sanitizeFalse) if molecule is None: raise ValueError(fUnable to parse SMILES string: {smiles}) # 尝试对分子进行清理sanitization flag Chem.SanitizeMol(molecule, catchErrorsTrue) if flag ! Chem.SanitizeFlags.SANITIZE_NONE: # 如果清理失败则尝试移除引起错误的操作 Chem.SanitizeMol(molecule, sanitizeOpsChem.SanitizeFlags.SANITIZE_ALL ^ flag) # 分配立体化学信息 Chem.AssignStereochemistry(molecule, cleanItTrue, forceTrue) return molecule def graph_from_molecule(molecule, atom_featurizer, bond_featurizer): # 初始化图 atom_features [] bond_features [] pair_indices [] for atom_idx, atom in enumerate(molecule.GetAtoms()): # 编码原子特征 atom_features.append(atom_featurizer.encode(atom)) # 添加自环self-loops pair_indices.append([atom_idx, atom_idx]) bond_features.append(bond_featurizer.encode(None)) # 假设对None的编码表示自环 # 遍历邻居原子 for neighbor in atom.GetNeighbors(): bond_idx molecule.GetBondBetweenAtoms(atom_idx, neighbor.GetIdx()).GetIdx() pair_indices.append([atom_idx, neighbor.GetIdx()]) # 编码键特征 bond_features.append(bond_featurizer.encode(molecule.GetBondWithIdx(bond_idx))) # 将列表转换为NumPy数组 return np.array(atom_features), np.array(bond_features), np.array(pair_indices) def graphs_from_smiles(smiles_list): 将SMILES字符串列表转换为图数据 参数: smiles_list (list): SMILES字符串列表 返回: (tuple): 包含原子特征、键特征和配对索引的ragged tensors元组 # 初始化列表以存储图数据 atom_features_list [] # 原子特征列表 bond_features_list [] # 键特征列表 pair_indices_list [] # 配对索引列表 # 将SMILES字符串转换为图 for smiles in smiles_list: molecule molecule_from_smiles(smiles) # 从SMILES字符串创建分子对象 # 从分子对象中提取图数据 atom_features, bond_features, pair_indices graph_from_molecule(molecule) atom_features_list.append(atom_features) bond_features_list.append(bond_features) pair_indices_list.append(pair_indices) # 将列表转换为ragged tensors atom_features_tensor tf.ragged.constant(atom_features_list, dtypetf.float32) # 原子特征张量 bond_features_tensor tf.ragged.constant(bond_features_list, dtypetf.float32) # 键特征张量 pair_indices_tensor tf.ragged.constant(pair_indices_list, dtypetf.int64) # 配对索引张量 # 返回一个包含ragged tensors的元组 return (atom_features_tensor, bond_features_tensor, pair_indices_tensor) # 假设df是一个包含smiles列和p_np列的pandas DataFrame # 打乱DataFrame的索引 permuted_indices np.random.permutation(np.arange(df.shape[0])) # 划分为训练集、验证集和测试集 train_size int(df.shape[0] * 0.8) # 训练集大小 valid_size int(df.shape[0] * 0.99) # 验证集结束位置 # 训练集 train_index permuted_indices[:train_size] x_train graphs_from_smiles(df.iloc[train_index].smiles) # 训练集的图数据 y_train df.iloc[train_index].p_np.values # 训练集的标签确保是NumPy数组 # 验证集 valid_index permuted_indices[train_size:valid_size] x_valid graphs_from_smiles(df.iloc[valid_index].smiles) # 验证集的图数据 y_valid df.iloc[valid_index].p_np.values # 验证集的标签 # 测试集 test_index permuted_indices[valid_size:] x_test graphs_from_smiles(df.iloc[test_index].smiles) # 测试集的图数据 y_test df.iloc[test_index].p_np.values # 测试集的标签2.3.4.测试功能 从pandas DataFrame df 中获取特定索引这里是第100行的数据并使用 molecule_from_smiles 函数将SMILES字符串转换为分子对象。 # df是一个包含name、smiles和p_np列的pandas DataFrame # 打印第100行的数据 print(fName:\t{df.name[100]}\nSMILES:\t{df.smiles[100]}\nBBBP:\t{df.p_np[100]}) # 从SMILES字符串创建分子对象 molecule molecule_from_smiles(df.iloc[100].smiles) # 打印分子对象这取决于molecule_from_smiles函数返回的对象类型 print(Molecule:) # 假设molecule对象有一个可以打印的方法或属性例如.__str__(), .to_smiles()等 # 这里使用了一个假设的.to_string()方法作为例子 try: print(molecule.to_string()) # 或者其他适当的方法来展示分子 except AttributeError: print(The molecule object does not have a direct string representation.) # 在这里您可能需要根据molecule对象的实际类型来实现特定的展示逻辑 # 如果molecule_from_smiles返回的是一个自定义对象并且您想要展示该对象的更多详细信息 # 您可能需要访问该对象的属性或方法。例如 # print(fNumber of atoms: {molecule.num_atoms}) # print(fNumber of bonds: {molecule.num_bonds}) # 这里的num_atoms和num_bonds是假设的分子对象属性您需要根据实际情况来替换Name: acetylsalicylate SMILES: CC(O)Oc1ccccc1C(O)O BBBP: 0 Molecule:从一个分子对象molecule生成一个图graph并且尝试打印出图的原子特征、键特征和配对索引的形状。 # 假设您已经通过molecule_from_smiles函数得到了molecule对象 molecule molecule_from_smiles(df.iloc[100].smiles) # 从分子对象生成图 graph graph_from_molecule(molecule) # 假设graph是一个包含三个元素的元组(atom_features, bond_features, pair_indices) atom_features, bond_features, pair_indices graph print(Graph (including self-loops):) # 对于ragged tensors我们使用不同的方法来获取其形状 print(\tatom features shape\t, atom_features.bounding_shape().numpy()) # ragged tensors的边界形状 print(\tbond features shape\t, bond_features.bounding_shape().numpy()) print(\tpair indices shape\t, pair_indices.bounding_shape().numpy()) # 如果您想查看ragged tensors的实际形状即非边界形状您可能需要遍历它们 # 但请注意这将取决于ragged tensors的具体内容 # 例如对于atom_features您可以这样做 print(\tActual atom features shapes:) for rt in atom_features: print(\t\t, rt.shape.numpy()) # 类似地对于bond_features和pair_indices如果它们也是ragged tensorsGraph (including self-loops):atom features (13, 29)bond features (39, 7)pair indices (39, 2)2.3.5. 建立tf数据集 在本文中MPNN消息传递神经网络的实现将每次迭代接收一个单独的图作为输入。因此给定一批子图分子我们需要将它们合并成一个单一的图我们将这个图称为全局图。这个全局图是一个不连通的图其中每个子图与其他子图完全分开。 使用 TensorFlow 的 tf.data.Dataset 来处理这样的数据你需要确保你的数据可以被正确地加载、预处理并且能够以迭代的方式提供给模型。 import tensorflow as tf def prepare_batch(x_batch, y_batch): 将一批子图合并为一个全局的不连通图 参数: x_batch: 包含三个元素的元组分别为 - atom_features: ragged tensor表示每个图分子中每个原子的特征 - bond_features: ragged tensor表示每个图分子中每个键化学键的特征 - pair_indices: ragged tensor表示每个键连接的原子对的索引 y_batch: 与x_batch对应的标签 返回: 合并后的全局图特征包括原子特征、键特征和原子对索引以及对应的标签 atom_features, bond_features, pair_indices x_batch # 获取每个图分子的原子数和键数 num_atoms tf.ragged.row_lengths(atom_features) num_bonds tf.ragged.row_lengths(bond_features) # 获取分区索引molecule_indicator稍后在模型中用于从全局图中收集子图 molecule_indices tf.range(len(num_atoms), dtypetf.int64) molecule_indicator tf.repeat(molecule_indices, num_atoms) # 收集键特征的索引以创建偏移量 gather_indices tf.concat([tf.zeros(1, dtypetf.int64), tf.cumsum(num_atoms[:-1])], axis0) gather_indices tf.repeat(gather_indices[:-1], num_bonds[1:]) # 增加pair_indices中的索引以创建全局索引 increment tf.gather(gather_indices, tf.range(len(gather_indices), dtypetf.int64)) pair_indices pair_indices.merge_dims(outer_axis0, inner_axis1).to_tensor() pair_indices pair_indices increment[:, tf.newaxis] # 将ragged tensors转换为dense tensors以进行批处理 atom_features atom_features.merge_dims(outer_axis0, inner_axis1).to_tensor() bond_features bond_features.merge_dims(outer_axis0, inner_axis1).to_tensor() return (atom_features, bond_features, pair_indices, molecule_indicator), y_batch def MPNNDataset(X, y, batch_size32, shuffleFalse): 创建一个用于MPNN消息传递神经网络的TensorFlow数据集 参数: X: 包含三个ragged tensors的元组分别代表原子特征、键特征和原子对索引 y: 对应的标签 batch_size: 批处理大小 shuffle: 是否打乱数据集 返回: 一个可用于训练的TensorFlow数据集 # 从tensor slices创建数据集 dataset tf.data.Dataset.from_tensor_slices((X, y)) if shuffle: # 打乱数据集使用buffer_size指定缓冲区大小 dataset dataset.shuffle(buffer_size1024) # 使用map函数对数据集进行批处理和预处理并使用AUTOTUNE自动调整并行度 dataset dataset.batch(batch_size).map(prepare_batch, num_parallel_callstf.data.AUTOTUNE) # 使用prefetch预取数据以重叠预处理和训练 dataset dataset.prefetch(tf.data.AUTOTUNE) return dataset # 示例用法: # X (atom_features_ragged, bond_features_ragged, pair_indices_ragged) # 您的ragged tensors # y ... # 您的标签 # dataset MPNNDataset(X, y, batch_size32, shuffleTrue)2.4. 训练模型 MPNN消息传递神经网络模型可以根据不同的应用需求进行定制。在这个教程中我们将构建一个基于原始论文《Neural Message Passing for Quantum Chemistry》和DeepChem框架中的MPNNModel的MPNN。这个MPNN模型由三个主要部分组成消息传递、读出和分类。 2.4.1.消息传递阶段 消息传递阶段包含两个核心步骤 边网络Edge Network它基于节点v与其1-hop邻居w_{i}之间的边特征e_{vw_{i}}来传递消息。这些消息随后用于更新节点v的状态得到更新后的节点状态v’。这里w_{i}表示v的第i个邻居。 门控循环单元GRU这个单元以最新的节点状态作为输入并基于之前的节点状态来更新它。换句话说GRU使用当前节点状态作为输入同时利用先前的节点状态来更新其内部记忆状态。通过这种方式信息能够在不同的节点状态之间流动比如从v传播到v’。 重要的是这两个步骤边网络和GRU会重复执行k次其中k是预设的迭代次数。在每次迭代中从v节点聚合的信息的半径或跳数都会增加1从而允许模型捕获更广泛的图结构信息。 class EdgeNetwork(Layer): def build(self, input_shape): # 解析输入形状得到原子特征和键特征的维度 self.atom_dim input_shape[0][-1] # 原子特征的维度 self.bond_dim input_shape[1][-1] # 键特征的维度 # 初始化权重和偏置 self.kernel self.add_weight( shape(self.bond_dim, self.atom_dim * self.atom_dim), initializerglorot_uniform, namekernel, trainableTrue # 明确指定为可训练 ) self.bias self.add_weight( shape(self.atom_dim * self.atom_dim), initializerzeros, namebias, trainableTrue # 明确指定为可训练 ) # 标记Layer已经被构建 super(EdgeNetwork, self).build(input_shape) # 调用父类的build方法 def call(self, inputs): # 解包输入得到原子特征、键特征和原子对索引 atom_features, bond_features, pair_indices inputs # 对键特征应用线性变换 bond_features_transformed tf.matmul(bond_features, self.kernel) self.bias # 重塑变换后的键特征以便进行后续的邻域聚合 # 每个键特征现在是一个二维张量对应于两个原子之间的交互 bond_features_reshaped tf.reshape(bond_features_transformed, (-1, self.atom_dim, self.atom_dim)) # 获取邻居的原子特征 # 使用pair_indices[:, 1]作为索引从atom_features中收集邻居的原子特征 atom_features_neighbors tf.gather(atom_features, pair_indices[:, 1]) # 增加一个维度以匹配bond_features_reshaped的维度 atom_features_neighbors_expanded tf.expand_dims(atom_features_neighbors, axis-1) # 应用邻域聚合 # 将键特征和邻居原子特征相乘得到每个原子对的交互特征 transformed_features tf.matmul(bond_features_reshaped, atom_features_neighbors_expanded) # 压缩最后一个维度即删除最后一个维度得到一维的特征向量 transformed_features_squeezed tf.squeeze(transformed_features, axis-1) # 使用tf.math.unsorted_segment_sum进行无序段求和 # 将转换后的特征按照pair_indices[:, 0]指定的索引进行聚合 # pair_indices[:, 0]是每个原子对的起始原子的索引 # num_segments指定了输出的段数即原子的数量 aggregated_features tf.math.unsorted_segment_sum( transformed_features_squeezed, pair_indices[:, 0], num_segmentstf.shape(atom_features)[0] ) return aggregated_features class MessagePassing(Layer): def __init__(self, units, steps4, **kwargs): super().__init__(**kwargs) self.units units # 输出的单元数 self.steps steps # 消息传递的步骤数 def build(self, input_shape): self.atom_dim input_shape[0][-1] # 原子特征的维度 # 初始化EdgeNetwork它将用于聚合邻居信息 self.message_step EdgeNetwork() # 如果所需的units超过atom_features的维度则需要填充 self.pad_length max(0, self.units - self.atom_dim) # 初始化GRUCell用于更新节点状态 # 注意这里可能需要填充atom_features_updated以匹配self.units但考虑到GRUCell内部实现 # 我们可能不需要显式填充因为内部状态可以处理不同维度的输入和状态。 # 但为了保持与原始代码的一致性这里仍然添加填充逻辑。 self.update_step GRUCell(self.units) # 使用self.units作为GRUCell的单元数 super().build(input_shape) # 调用父类的build方法 def call(self, inputs): atom_features, bond_features, pair_indices inputs # 如果所需的units超过atom_features的维度则填充atom_features atom_features_updated tf.pad(atom_features, [[0, 0], [0, self.pad_length]], constant_values0) # 执行指定步数的消息传递 for i in range(self.steps): # 聚合来自邻居的信息 atom_features_aggregated self.message_step([atom_features_updated, bond_features, pair_indices]) # 通过GRU的一个步骤来更新节点状态 # 注意在GRUCell中我们通常将上一个状态或初始状态和输入作为参数传递 # 这里我们使用atom_features_aggregated作为输入并假设atom_features_updated作为初始状态 # 尽管在第一次迭代中它可能不是真正的“前一个”状态 _, atom_features_updated self.update_step(atom_features_aggregated, [atom_features_updated]) # 注意GRUCell的返回值通常是一个元组包含新的状态和输出 # 在这里我们只关心状态因此使用_来忽略输出 return atom_features_updated 当然以下是对改写后的MessagePassing类代码的解读 初始化方法 (__init__) 在类的初始化方法中我们定义了两个关键参数units和steps。 units表示输出的单元数或特征维度大小。这通常决定了消息传递后每个节点特征的维度。steps表示消息传递的步骤数即我们要进行多少次邻居信息的聚合和节点状态的更新。 构建方法 (build) 在build方法中我们根据输入的形状来确定原子特征的维度atom_dim并初始化一些必要的层和变量。 self.atom_dim保存原子特征的维度这是从输入形状中推导出来的。self.message_step初始化EdgeNetwork实例这个实例将用于在消息传递过程中聚合来自邻居的信息。self.pad_length计算填充长度以确保原子特征维度与所需的units相匹配。如果units大于atom_dim则需要进行填充。self.update_step初始化GRUCell实例用于更新节点的状态。注意这里我们直接使用self.units作为GRU单元的维度而不是atom_dim pad_length因为GRU单元内部会处理维度匹配的问题。 调用方法 (call) 在call方法中我们定义了消息传递的具体步骤。 输入解包首先我们将输入解包为atom_features原子特征、bond_features键特征和pair_indices成对索引。 特征填充如果units大于atom_dim则需要对atom_features进行填充以确保其维度与units一致。这里使用tf.pad函数进行填充填充值为0。 消息传递接下来我们执行多次由steps参数定义消息传递步骤。在每个步骤中 使用self.message_step即EdgeNetwork实例来聚合来自邻居的信息。这个步骤会将当前节点的特征、键特征和成对索引作为输入并输出聚合后的特征。使用self.update_step即GRUCell实例来更新节点的状态。这里我们将聚合后的特征作为输入并将当前的节点状态初始时为填充后的atom_features传递给GRU单元。GRU单元会输出新的状态和输出但在这里我们只关心状态因此使用_来忽略输出。 返回更新后的特征最后返回经过多次消息传递和状态更新后的节点特征。 这里定义的MessagePassing类实现了一个通用的消息传递框架其中包括邻居信息的聚合和节点状态的更新。这种框架在图神经网络GNNs中非常常见特别是在处理分子结构、社交网络等图结构数据时。通过多次迭代更新节点能够逐渐捕获到更远距离的邻居信息从而提高模型的表达能力。 2.4.2.消息读出 当消息传递过程完成后为了将k步聚合的节点状态转化为图级别的嵌入我们需要先将它们按照子图对应于批次中的每个分子进行划分。接下来我们会采用以下步骤来处理这些子图以获取图级别的表示 首先将k步聚合后的节点状态按照子图即每个分子进行划分。对于每个子图如果节点数量少于最大子图的节点数则对子图进行填充padding以确保所有子图具有相同的节点数。使用tf.stack(...)将填充后的子图堆叠成一个张量其中每个子图包含一组节点状态。对堆叠后的张量应用掩码mask以确保在后续处理中忽略填充部分避免它们对训练过程产生干扰。最后将掩码后的张量输入到变压器编码器transformer encoder中通过编码器进一步处理子图信息。在变压器编码器之后对编码器输出应用平均池化average pooling操作以聚合所有节点的信息从而得到图级别的嵌入表示。 这个图级别的嵌入表示可以用于后续的预测任务如分类、回归等以捕捉整个分子或图结构的信息。 class PartitionPadding(layers.Layer): def __init__(self, batch_size, **kwargs): super().__init__(**kwargs) self.batch_size batch_size # 初始化时传入批次大小 def call(self, inputs): atom_features, molecule_indicator inputs # 输入为原子特征和分子指示符 # 将原子特征按照分子指示符进行划分得到每个分子的子图 # tf.dynamic_partition 根据第二个参数分子指示符对第一个参数原子特征进行划分 atom_features_partitioned tf.dynamic_partition( atom_features, molecule_indicator, self.batch_size ) # 获取每个子图的原子数量 num_atoms [tf.shape(f)[0] for f in atom_features_partitioned] # 计算最大原子数量用于填充子图以确保所有子图有相同的长度 max_num_atoms tf.reduce_max(num_atoms) # 对每个子图进行填充然后堆叠起来 atom_features_stacked tf.stack( [ # 对每个子图在第一个维度原子数量进行填充 tf.pad(f, [(0, max_num_atoms - n), (0, 0)]) # f 为子图特征n 为当前子图的原子数量 for f, n in zip(atom_features_partitioned, num_atoms) ], axis0 # 堆叠的维度 ) # 移除空的子图通常是在数据集的最后一个批次中可能出现 # 检查堆叠后的子图特征是否全为0即空子图并获取非零子图的索引 gather_indices tf.where(tf.reduce_sum(atom_features_stacked, (1, 2)) ! 0) # 压缩索引的最后一个维度如果有的话 gather_indices tf.squeeze(gather_indices, axis-1) # 根据非零子图的索引从堆叠后的子图特征中收集它们 return tf.gather(atom_features_stacked, gather_indices, axis0) class TransformerEncoderReadout(layers.Layer): def __init__( self, num_heads8, embed_dim64, dense_dim512, batch_size32, **kwargs ): super().__init__(**kwargs) # 子图划分与填充层 self.partition_padding PartitionPadding(batch_size) # 多头注意力层 self.attention layers.MultiHeadAttention(num_heads, embed_dim) # 密集投影层序列包含激活函数 self.dense_proj keras.Sequential( [layers.Dense(dense_dim, activationrelu), layers.Dense(embed_dim),] ) # 两层层归一化 self.layernorm_1 layers.LayerNormalization() self.layernorm_2 layers.LayerNormalization() # 全局平均池化层 self.average_pooling layers.GlobalAveragePooling1D() def call(self, inputs): # 对输入进行子图划分与填充 x self.partition_padding(inputs) # 创建填充掩码用于屏蔽填充部分的影响 padding_mask tf.reduce_any(tf.not_equal(x, 0.0), axis-1) padding_mask padding_mask[:, tf.newaxis, tf.newaxis, :] # 多头注意力处理应用掩码 attention_output self.attention(x, x, attention_maskpadding_mask) # 残差连接与层归一化 proj_input self.layernorm_1(x attention_output) # 通过密集投影层并再次进行残差连接与层归一化 proj_output self.layernorm_2(proj_input self.dense_proj(proj_input)) # 应用全局平均池化得到图级别的嵌入 return self.average_pooling(proj_output) PartitionPadding类 定义了一个名为PartitionPadding的Keras自定义层该层用于处理与分子图相关的数据特别是当数据中包含多个分子且每个分子具有不同数量的原子时。以下是对这段代码的详细总结解读 初始化方法__init__ 在初始化时需要传入batch_size参数这通常代表一批数据中分子的最大数量。然而需要注意的是由于后续处理中可能存在填充并且可能要去除空子图因此这个batch_size并不直接决定最终输出的分子数量。 调用方法call 输入数据inputs是一个元组包含两个元素atom_features原子特征和molecule_indicator分子指示符。使用tf.dynamic_partition根据molecule_indicator将atom_features划分为多个子图每个子图代表一个分子的原子特征。计算每个子图中原子的数量并找到这些数量中的最大值max_num_atoms这将用于后续的填充操作。对每个子图使用tf.pad进行填充确保所有子图都有相同的长度即max_num_atoms。这是为了使得所有分子在后续的Transformer或其他处理步骤中可以被平等对待。将填充后的子图堆叠起来形成一个三维张量其中第一维是分子索引第二维是原子索引第三维是原子特征。检查堆叠后的子图特征去除那些全为0的子图即空子图。这是通过计算每个子图特征的非零元素和并找出非零和的索引来实现的。使用tf.gather根据非零索引从堆叠的子图特征中收集非空子图并返回这些子图作为输出。 TransformerEncoderReadout类 定义了名为TransformerEncoderReadout的Keras自定义层该层是基于Transformer编码器结构的一个变种旨在从图数据或具有填充序列的数据中提取出全局表示。下面是该代码的主要组成部分及功能的总结解读 初始化方法__init__ num_heads定义了多头注意力机制中“头”的数量。embed_dim嵌入维度即每个输入单元或“头”的维度。dense_dim前馈神经网络FFN中隐藏层的维度。batch_size批量大小这个值用于PartitionPadding层尽管在标准Transformer中通常不需要直接指定批量大小。定义了多个子层包括PartitionPadding可能是用于处理图数据或具有不同长度序列的填充、MultiHeadAttention多头注意力机制、Dense层组成的前馈神经网络FFN、两个LayerNormalization层用于层归一化以及GlobalAveragePooling1D用于将序列嵌入转换为全局嵌入。 调用方法call 首先输入数据inputs通过PartitionPadding层进行处理可能是为了处理不同长度的序列或图数据。接着根据处理后的数据x创建一个填充掩码padding_mask该掩码用于在多头注意力机制中屏蔽填充值的影响。使用MultiHeadAttention层计算自注意力并应用填充掩码。通过第一个LayerNormalization层执行残差连接将原始输入x与注意力输出相加并进行层归一化。将归一化后的输出传递给前馈神经网络FFN并通过第二个LayerNormalization层再次执行残差连接和层归一化。最后使用GlobalAveragePooling1D层将序列嵌入转换为全局嵌入这通常用于图数据或需要全局表示的任务。 2.4.3.构建MPNN模型 现在我们将完善消息传递神经网络MPNN模型。除了消息传递和读出机制外我们还将构建一个两层的分类网络以实现对BBBP可能是一个与生物学或化学相关的数据库或任务的预测功能。 def MPNNModel( atom_dim, # 原子特征的维度 bond_dim, # 键特征的维度 batch_size32, # 批处理大小 message_units64, # 消息传递层的单元数 message_steps4, # 消息传递的步数 num_attention_heads8, # 注意力头的数量 dense_units512, # 密集层的单元数 ): 定义消息传递神经网络MPNN模型 Args: atom_dim (int): 原子特征的维度 bond_dim (int): 键特征的维度 batch_size (int, optional): 批处理大小. Defaults to 32. message_units (int, optional): 消息传递层的单元数. Defaults to 64. message_steps (int, optional): 消息传递的步数. Defaults to 4. num_attention_heads (int, optional): 注意力头的数量. Defaults to 8. dense_units (int, optional): 密集层的单元数. Defaults to 512. Returns: keras.Model: MPNN模型 # 输入层定义 atom_features layers.Input((atom_dim,), dtypefloat32, nameatom_features) bond_features layers.Input((bond_dim,), dtypefloat32, namebond_features) pair_indices layers.Input((2,), dtypeint32, namepair_indices) molecule_indicator layers.Input((), dtypeint32, namemolecule_indicator) # 消息传递层 x MessagePassing(message_units, message_steps)([atom_features, bond_features, pair_indices]) # TransformerEncoderReadout层这里假设这个层已经被定义 x TransformerEncoderReadout( num_attention_heads, message_units, dense_units, batch_size )([x, molecule_indicator]) # 密集层 x layers.Dense(dense_units, activationrelu)(x) # 输出层用于二分类问题 x layers.Dense(1, activationsigmoid)(x) # 构造模型 model keras.Model( inputs[atom_features, bond_features, pair_indices, molecule_indicator], outputs[x], ) return model # 实例化MPNN模型这里假设x_train是包含原子特征和键特征的训练数据 mpnn MPNNModel( atom_dimx_train[0][0][0].shape[0], bond_dimx_train[1][0][0].shape[0], ) # 编译模型 mpnn.compile( losskeras.losses.BinaryCrossentropy(), optimizerkeras.optimizers.Adam(learning_rate5e-4), metrics[keras.metrics.AUC(nameAUC)], ) # 可视化模型结构 keras.utils.plot_model(mpnn, show_dtypeTrue, show_shapesTrue)上述代码定义了一个名为MPNNModel的函数该函数用于构建一个基于消息传递神经网络Message Passing Neural Network, MPNN的模型专门用于处理与分子或化合物相关的图结构数据。这里模型被设计为接受原子特征、键特征、原子对索引以及分子指示符作为输入并输出一个二分类预测可能是用于预测某种生物或化学性质的分类问题如药物渗透性。 函数定义:MPNNModel 函数接受多个参数包括原子特征的维度 atom_dim、键特征的维度 bond_dim、批处理大小 batch_size、消息传递层的单元数 message_units、消息传递的步数 message_steps、注意力头的数量 num_attention_heads 和密集层的单元数 dense_units。 输入层定义:定义了四个输入层分别对应原子特征 atom_features、键特征 bond_features、原子对索引 pair_indices 和分子指示符 molecule_indicator。 消息传递层:使用一个名为 MessagePassing 的层假设这是一个已经定义好的自定义层或来自某个库的层进行消息传递。它接受原子特征、键特征和原子对索引作为输入并在多个步骤中传递消息。 TransformerEncoderReadout层:这部分使用了一个名为 TransformerEncoderReadout 的层同样假设这是一个已经定义好的层该层可能用于对经过消息传递处理后的数据进行进一步的处理和整合以便为每个分子生成一个全局的向量表示。这个层还接受分子指示符作为输入可能用于处理批次中不同分子的数据。 密集层与输出层:接下来模型通过两个密集层Dense 层进一步处理数据。第一个密集层使用 ReLU 激活函数第二个密集层则使用 Sigmoid 激活函数输出一个介于 0 和 1 之间的值用于二分类预测。 模型构造与返回:使用 keras.Model 构造一个完整的模型并将所有定义的层组合在一起。模型的输入包括之前定义的四个输入层输出则是最后一个密集层的输出。 模型实例化与编译:在函数外部通过调用 MPNNModel 函数并传入适当的参数从训练数据 x_train 中提取的特征维度来实例化一个模型实例 mpnn。使用 compile 方法编译模型指定损失函数为二元交叉熵BinaryCrossentropy优化器为 Adam学习率设置为 5e-4并添加 AUC 作为评估指标。 模型可视化: 使用 keras.utils.plot_model 函数绘制并显示模型的图形表示以便更直观地理解模型的结构和数据流。 2.4.4.模型训练 # 假设MPNNDataset是一个自定义的数据集类用于加载和预处理MPNN模型所需的数据 # 创建训练、验证和测试数据集 train_dataset MPNNDataset(x_train, y_train) # 使用训练数据x_train和y_train创建训练数据集 valid_dataset MPNNDataset(x_valid, y_valid) # 使用验证数据x_valid和y_valid创建验证数据集 test_dataset MPNNDataset(x_test, y_test) # 使用测试数据x_test和y_test创建测试数据集 # 使用mpnn模型进行训练 # 传入训练数据集验证数据集设置训练轮次、日志输出级别以及类别权重 history mpnn.fit( train_dataset, # 训练数据集 validation_datavalid_dataset, # 验证数据集 epochs40, # 训练轮次 verbose2, # 日志输出级别0-不输出1-进度条2-每个epoch一行日志 class_weight{0: 2.0, 1: 0.5}, # 类别权重用于处理类别不平衡问题 ) # 绘制训练集和验证集的AUC曲线 import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) # 创建一个大小为10x6的绘图窗口 plt.plot(history.history[AUC], labelTrain AUC) # 绘制训练集的AUC曲线 plt.plot(history.history[val_AUC], labelValidation AUC) # 绘制验证集的AUC曲线 plt.xlabel(Epochs, fontsize16) # 设置x轴标签为Epochs字体大小为16 plt.ylabel(AUC, fontsize16) # 设置y轴标签为AUC字体大小为16 plt.legend(fontsize16) # 设置图例字体大小为16 plt.show() # 显示绘制的图形上述代码段主要执行了以下几个关键操作 创建数据集对象使用MPNNDataset类假设这是一个自定义的数据集类分别创建了训练、验证和测试数据集对象。x_train、y_train、x_valid、y_valid、x_test、y_test是已经准备好的数据它们分别代表训练数据、训练标签、验证数据、验证标签、测试数据和测试标签。 模型训练 使用之前定义的mpnn模型一个基于消息传递神经网络MPNN的模型进行训练。调用fit方法并传入训练数据集train_dataset作为训练数据。使用validation_data参数传入验证数据集valid_dataset以便在每个epoch结束后评估模型在未见过的数据上的性能。设置epochs为40表示模型将进行40轮完整的训练。verbose2表示在训练过程中将显示每个epoch的日志信息。使用class_weight参数来指定类别权重以处理类别不平衡问题。这里假设数据集中的类别标签为0和1并且类别0的样本较少因此给予类别0更高的权重2.0而类别1的权重较低0.5。 性能可视化 使用matplotlib库中的pyplot模块绘制训练集和验证集的AUCArea Under the Curve曲线。plt.figure(figsize(10, 6))创建一个新的图形窗口并设置其大小为10x6英寸。plt.plot(history.history[AUC], labelTrain AUC)和plt.plot(history.history[val_AUC], labelValidation AUC)分别绘制了训练集和验证集的AUC曲线。这里假设history对象包含了fit方法返回的每个epoch的训练和验证指标其中AUC和val_AUC分别是训练和验证的AUC值。使用plt.xlabel和plt.ylabel设置x轴和y轴的标签并使用plt.legend添加图例。最后plt.show()函数用于显示绘制的图形。 2.5. 预测分子特性 # df是一个pandas DataFrame其中smiles列包含SMILES字符串p_np列包含真实标签 # test_index是一个包含测试数据索引的列表 # molecule_from_smiles是一个函数用于将SMILES字符串转换为分子对象 # mpnn是已经训练好的MPNN模型 # tf.squeeze是TensorFlow的一个函数用于从张量中删除大小为1的维度 # MolsToGridImage是一个函数用于将分子列表和相应的图例转换为网格图像 # 从DataFrame的smiles列中获取测试分子的SMILES字符串并转换为分子对象 # 使用列表推导式遍历test_index中的每个索引 molecules [molecule_from_smiles(df.smiles.values[index]) for index in test_index] # 从DataFrame的p_np列中获取测试数据的真实标签 y_true [df.p_np.values[index] for index in test_index] # 使用训练好的MPNN模型对测试数据集进行预测 # 假设test_dataset是包含测试数据的MPNNDataset对象 # 预测结果是一个二维张量使用tf.squeeze删除第二个维度通常是batch_size为1的维度 y_pred tf.squeeze(mpnn.predict(test_dataset), axis1) # 生成图例列表其中每个图例都包含真实值和预测值 # 使用列表推导式遍历y_true和y_pred中的每个元素 legends [fy_true/y_pred {y_true[i]}/{y_pred[i]:.2f} for i in range(len(y_true))] # 使用MolsToGridImage函数将分子列表和对应的图例转换为网格图像 # molsPerRow指定每行显示的分子数 MolsToGridImage(molecules, molsPerRow4, legendslegends) # 注意这里假设了molecule_from_smiles, mpnn.predict, tf.squeeze, 和 MolsToGridImage # 都已经正确导入并且是可用的函数/方法/类。以上代码进行分子特性预测并进行可视化展示 1.创建分子对象列表 使用列表推导式遍历test_index中的索引。对于每个索引从df一个假设的pandas DataFrame的’smiles’列中取出对应的SMILES字符串。调用molecule_from_smiles函数假设该函数能够将SMILES字符串转换为分子对象来创建分子对象。将所有分子对象存储在molecules列表中。 获取真实标签 同样使用列表推导式遍历test_index中的索引。从df的’p_np’列中取出对应的真实标签。将所有真实标签存储在y_true列表中。 3.模型预测 调用mpnn.predict方法假设mpnn是一个已经训练好的MPNN模型对test_dataset测试数据集进行预测。predict方法通常返回一个二维张量其中第一个维度是批量大小在这个场景中可能是1因为是对单个样本或批次进行测试第二个维度是预测结果。使用tf.squeeze函数TensorFlow的函数来删除大小为1的维度通常是第一个维度即批量大小维度从而得到一个一维的张量。将预测结果存储在y_pred中。 3.生成图例 使用列表推导式遍历y_true和y_pred中的每个元素由于它们具有相同的长度。对于每一对真实值和预测值生成一个字符串其中包含了真实值和预测值预测值保留两位小数。将所有生成的字符串存储在legends列表中这些字符串将用作可视化时的图例。 4.可视化分子与预测结果 调用MolsToGridImage函数假设这是一个用于将分子对象转换为网格图像并添加图例的函数。传入molecules列表作为要可视化的分子对象。设置molsPerRow4表示每行显示4个分子。传入legends列表作为每个分子对应的图例。函数将生成一个网格图像其中包含分子对象和相应的图例用于展示每个分子的预测结果。 3. 总结和展望 在应用MPNN消息传递神经网络处理分子图数据的实验中我们获得了以下显著的结果 3.1. 模型性能 MPNN模型在分子分类和性质预测等任务上展现出了出色的性能。与传统的机器学习方法相比MPNN模型能够捕获分子中原子之间的复杂相互作用和关系从而更准确地预测分子的性质。在多个基准数据集上的测试结果表明MPNN模型能够实现较高的分类准确率和较低的预测误差。这些结果证明了MPNN在处理分子图数据方面的有效性和可靠性。 3.2. 特征表示 通过MPNN模型中的消息传递和节点更新机制分子中的原子节点能够学习到包含其周围环境的丰富特征表示。这些特征表示不仅包含了原子自身的信息还融合了与其相邻原子之间的相互作用信息。这种基于图的特征表示方式能够更全面地描述分子的结构和性质有助于提高模型在分子分类和性质预测等任务上的性能。 3.3. 参数和架构的影响 在实验过程中我们尝试了不同的模型参数和架构配置。通过调整模型的层数、节点更新函数、消息函数等参数我们发现这些配置对模型的性能有着显著的影响。适当的参数和架构配置能够提升模型的表达能力和泛化能力从而获得更好的实验结果。然而过度的复杂性也可能导致模型过拟合和性能下降。 3.4. 可视化与解释性 通过将MPNN模型预测的结果与分子结构进行可视化对比我们能够更直观地理解模型如何捕获分子的特性。这有助于我们分析模型的预测结果并发现潜在的问题。此外MPNN模型中的节点特征和消息传递机制也具有一定的解释性。通过分析这些特征和机制我们能够更好地理解模型如何学习和推理分子的性质。 3.5. 挑战与未来方向 尽管MPNN在分子图处理方面取得了显著的进展但仍存在一些挑战和限制。例如对于大型复杂的分子图MPNN模型的计算复杂度和资源消耗可能会显著增加。未来的研究可以探索更高效的算法和模型架构来应对这些挑战。此外将MPNN与其他先进技术如注意力机制、图卷积网络等相结合可能会进一步提升模型的性能和泛化能力。 MPNN在处理分子图数据方面展现出了出色的性能和潜力。通过不断地优化模型参数和架构配置以及探索新的技术和方法我们可以期待MPNN在分子科学领域的应用前景将更加广阔。 参考文献 [1] Zonghan Wu, Shirui Pan, Fengwen Chen, Guodong Long, Chengqi Zhang, and Philip S Yu. A Comprehensive Survey on Graph Neural Networks[J]. IEEE Transactions on Neural Networks and Learning Systems, 2020, 32(1): 4-24. [2] Zhou, J., Cui, G., Zhang, Z., Yang, C., Liu, Z., Wang, L. Graph Neural Networks: A Review of Methods and Applications[J]. AI Open, 1: 57-81, 2020. [3] Gilmer, J., Schoenholz, S. S., Riley, P. F., Vinyals, O., Dahl, G. E. (2017). Neural Message Passing for Quantum Chemistry. [https://arxiv.org/abs/1704.01212] [4]DeepChem. (2018). MPNNModel [0407d93] . [https://github.com/deepchem/deepchem/tree/master/contrib/mpnn]
http://www.hkea.cn/news/14397621/

相关文章:

  • 手机app界面设计论文网站的链接优化
  • 给网站挂黑链做引流推广的平台600
  • 请简要描述如何进行网站设计规划网站开发过程分为哪几个阶段
  • 学做网站书籍免费外贸网站有哪些
  • 网站品牌高端定制上鼎工程建设有限公司网站
  • 网站的管理与维护wordpress 内置 函数大全
  • 如何做网站教学大型门户网站建设哪家好
  • 大型网站建设视频php网站开发视频教程
  • 知名网站建设托管个人网站设计师
  • 水果网站建设的策划书化妆品销售网站开发与设计
  • 手机网站跳转代码长沙培训网站制作
  • 中国住房和城乡建设部网站6网站建设一龙条
  • 免费建外贸网站wordpress api post
  • 衡水网站制作与推广最干净在线代理
  • 企业自助建站源码16素材网
  • 如何自己做框架开发网站投教网站建设
  • 黑河网站建设网站建设中管理员登录的代码怎么写
  • 寿光 网站建设页面设计升级访问紧急通知
  • 温州论坛招聘百度seo软件
  • 河北seo网站设计做感恩网站的图片
  • wordpress转移整站梧州网站建设哪家好
  • 做网站灵宝商业网站运营成本
  • 无锡建设建设局网站大学做机器人比赛的网站论坛
  • 广东两学一做考学网站大连做网站的公司有哪些
  • 学做蛋糕什么网站html5支持最好的浏览器
  • 英文网站seo如何做wordpress循环评论
  • 网站建设和邮箱的关联seo网站推广下载
  • 写作网站云网站系统平台建设
  • 云南省建设培训中心网站招聘网站做销售
  • 中小企业建站是什么威海市建设局官方网站