掘金 人工智能 07月31日 18:37
用最简单的python语法来利用机器学习算法预测药物分子的xlogp
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文详细介绍了如何利用化学信息学和机器学习技术预测分子的xlogp值。项目首先加载并预处理了包含SMILES和xlogp数据的文件,处理了缺失值和无效的SMILES。接着,使用RDKit工具计算了14个分子描述符,将SMILES转换为机器可理解的数值特征。随后,构建了随机森林回归模型,并将数据集划分为训练集和测试集进行训练。最后,通过R²分数和均方误差评估了模型的预测性能,展示了一个完整的分子性质预测流程。

🗂️ 数据预处理与清洗:项目始于加载包含SMILES和xlogp的数据集,并进行关键的数据清洗。这包括识别并处理xlogp中的缺失值(NaN),利用RDKit计算的logp值进行填充;同时,检测并移除了无效的SMILES字符串,确保后续特征提取的准确性。

🔬 分子特征提取:为了让机器学习模型能够理解化学分子的结构信息,利用RDKit库计算了14个分子描述符,如MolLogP、HeavyAtomCount、NumHAcceptors等,将SMILES字符串转化为一系列数值特征,为模型训练奠定基础。

🌳 模型构建与训练:选择了随机森林回归模型进行训练。在模型训练前,将处理后的特征数据(X)和目标变量(y)分割为训练集和测试集,以评估模型在未见过数据上的泛化能力。模型通过拟合训练数据来学习SMILES特征与xlogp值之间的关系。

📊 模型评估与优化:训练完成后,使用测试集对模型进行预测,并通过R²分数和均方误差(MSE)来评估模型的性能。文中还提到了对数据中可能出现的超大值(如Ipc列)进行处理,以提高模型的鲁棒性和预测精度。

在这个项目中,我们拥有一个含有4000多分子量的文件,我们需要通过这个文件,来预测分子的xlogp值。以下是项目的需求:

第一步先导入需要的包:

# 数据分析所需要的包import pandas as pdimport numpy as np#化学分析需要的包import rdkit as rdfrom rdkit.Chem import Descriptorsfrom rdkit import Chemfrom rdkit.Chem import Crippenfrom rdkit import rdBase #处理rdkit刷屏警告的方法rdBase.DisableLog('rdApp.warning') #处理rdkit刷屏警告的方法#机器学习所需要的包from sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestRegressorfrom sklearn.metrics import r2_score, mean_squared_error

数据的处理

用到的是pandas这个包,这个包经常用于对数据的初步处理:

加载数据集

# 以pubchem中下载的药物分子数据集为例# 读取数据集df = pd.read_csv("data.csv")

可以使用以下代码来了解数据集的结构。

# df.columns # 查看数据集的所有列名# df.head() # 查看数据集的前几行# df.info() # 查看数据集的基本信息,包括数据类型和非空值数量df.shape

选取特定的行,此处将数据集中的列smiles和列xlogp选取了出来。

xlog = df['XLogP']smiles = df['SMILES']data_1 = pd.DataFrame({'SMILES': smiles, 'XLogP': xlog})data_1.head()

数据集的清洗

我们现在已经初步加载了数据集,下一步是对数据的异常有个初步的了解,针对这个数据集,我们通常会遇到以下问题(经验所得):

接下来我们就对症下药:先查看一下目标列中是否存在异常值

xlogp_na = df['XLogP'].isna().sum()smiles_invalid = df['SMILES'].apply(lambda x: Chem.MolFromSmiles(x) is None).sum()print(f"XLogP 中 NaN 的数量:{xlogp_na}")print(f"无效的 SMILES 数量:{smiles_invalid}")

再处理掉异常值:

# 替换无效的 XLogPdf['XLogP'] = df.apply(    lambda x: Crippen.MolLogP(Chem.MolFromSmiles(x['SMILES']))     if pd.isna(x['XLogP']) else x['XLogP'], axis=1)# 删除无效的 SMILESdf = df[df['SMILES'].apply(lambda x: Chem.MolFromSmiles(x) is not None)]

做完这一步,我们可以再次检查一下数据结构

xlogp_na = df['XLogP'].isna().sum()smiles_invalid = df['SMILES'].apply(lambda x: Chem.MolFromSmiles(x) is None).sum()df.shapeprint(f"XLogP 中 NaN 的数量:{xlogp_na}")print(f"无效的 SMILES 数量:{smiles_invalid}")

比较前后数据结构的变化。到这一步,xlogp中的na值与smiles中的异常值都被我们去掉了,数据的清洗完美结束了!下一步就是对数据进行特征化处理了。

特征化数据

由于smiles所含的信息太少,并且机器只能读懂数字,我们需要用到rdkit中提供的分子指纹办法,将smiles进行特征化处理。

# 把 SMILES 转成分子对象molecules = df["SMILES"].apply(Chem.MolFromSmiles)# 分别计算 14 个描述符,并添加到新的列中df["MolLogP"] = molecules.apply(Descriptors.MolLogP)df["HeavyAtomCount"] = molecules.apply(Descriptors.HeavyAtomCount)df["NumHAcceptors"] = molecules.apply(Descriptors.NumHAcceptors)df["NumHeteroatoms"] = molecules.apply(Descriptors.NumHeteroatoms)df["NumHDonors"] = molecules.apply(Descriptors.NumHDonors)df["MolWt"] = molecules.apply(Descriptors.MolWt)df["NumRotatableBonds"] = molecules.apply(Descriptors.NumRotatableBonds)df["RingCount"] = molecules.apply(Descriptors.RingCount)df["Ipc"] = molecules.apply(Descriptors.Ipc)df["HallKierAlpha"] = molecules.apply(Descriptors.HallKierAlpha)df["NumValenceElectrons"] = molecules.apply(Descriptors.NumValenceElectrons)df["NumSaturatedRings"] = molecules.apply(Descriptors.NumSaturatedRings)df["NumAliphaticRings"] = molecules.apply(Descriptors.NumAliphaticRings)df["NumAromaticRings"] = molecules.apply(Descriptors.NumAromaticRings)print(df.head())

做完这一步,可以查看一下现在表格的结构,是否维度发生了变化,
简单的特征化处理就结束了。

模型的训练与评估

我们现在计算完分子描述符,可以尝试使用这些分子描述符进行训练了。

数据的切割

我们需要将描述符作为输入,并将xlogp值作为输出,因此我们需要定义一个包含14个描述符的X(输入),和一个包含xlogp的y(输出)。

# X:描述符X = df[['MolLogP', 'HeavyAtomCount', 'NumHAcceptors', 'NumHeteroatoms',          'NumHDonors', 'MolWt', 'NumRotatableBonds', 'RingCount', 'Ipc',          'HallKierAlpha', 'NumValenceElectrons', 'NumSaturatedRings',          'NumAliphaticRings', 'NumAromaticRings']]# y:目标性质y = df['XLogP'] 

rdkit计算出来的某些描述符可能会很大,也有可能产生新的na值。我们可以检查一下X中数据是否能够训练

# 1. 检查 NaN 个数nan_counts = X.isna().sum()# 2. 检查 +inf 和 -inf 个数posinf_counts = np.isposinf(X).sum()neginf_counts = np.isneginf(X).sum()# 3. 检查超大值(>1e5,可调整阈值)threshold = 1e5large_counts = (X > threshold).sum()print("=== NaN 个数 ===")print(nan_counts[nan_counts > 0])print("\n=== 正无穷个数 ===")print(posinf_counts[posinf_counts > 0])print("\n=== 负无穷个数 ===")print(neginf_counts[neginf_counts > 0])print("\n=== 大于阈值的个数 ===")print(large_counts[large_counts > 0])

以[Ipc]超大值为例,如果出现超大值,使用以下方式去掉:

# 看到Ipc有超大值,进行处理# 将 Ipc 列中的超大值替换为阈值X["Ipc"] = X["Ipc"].clip(upper=1e5)

接下来就可以对数据进行分割了:

# 分割数据集X_train, X_test, y_train, y_test = train_test_split(    X, y, test_size=0.2, random_state=42)

使用随机森林模型

分割完数据集,我们就可以开始训练模型了,我们直接选用简单的随机森林模型。

model = RandomForestRegressor(n_estimators=100, random_state=42)model.fit(X_train, y_train)

运行完后,进行模型的评估

y_pred = model.predict(X_test)print("R²:", r2_score(y_test, y_pred))print("MSE:", mean_squared_error(y_test, y_pred))

一个简单的预测xlogp的训练模型就完成了。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

xlogp预测 分子描述符 随机森林 RDKit 机器学习
相关文章