赞
踩
目录
step2 读取数据并使用Morgan分子指纹建模SMILES
本次夏令营以题带练,题目为第二届世界科学智能大赛物质科学赛道:催化反应产率预测。利用历史催化反应数据,并结合AI技术,可以预测新催化反应的产率,从而有效地帮助科研人员和产业界加快高活性反应条件的筛选速度,减少资源与人力的消耗,促进新物质的创造与合成。
反应底物和反应条件是决定其产率的关键因素。因此,我们可以利用AI模型来捕捉底物、条件与产率之间的内在联系。借助产率预测AI模型,仅需输入底物和条件的信息,我们就能够预测该反应组合下的产率,从而有效提升催化反应的条件筛选效率。
本次比赛提供在药物合成中常见的多种催化反应实验数据,其中包括反应的底物、包括催化剂在内的反应添加剂、反应溶剂以及反应产物,期待选手通过分析反应数据,利用机器学习、深度学习算法或者大语言模型,建立产率预测模型,从而辅助未知新反应的反应条件筛选。
任务:构建一个能够准确预测碳氮成键反应产率的预测模型
通过对反应中所包含的反应底物、添加剂、溶剂以及产物进行合理的特征化,运用机器学习模型或者深度学习模型拟合预测反应的产率。或者利用训练集数据对开源大语言模型进行微调以预测反应的产率。
初赛数据集仅包含碳氮成键类型反应数据,其中训练集中包含23538条反应数据,测试集中包含2616条反应数据。训练集与测试集的比例接近9:1。每条训练数据包含 rxnid, Reactant1, Reactant2 , Product , Additive , Solvent , Yield字段。其中 Reactant1 , Reactant2 , Product , Additive , Solvent 字段中为对应物质的SMILES字符串,Yield字段为目标字段,是经过归一化的浮点数。
我们提供一个基础的baseline,利用分子指纹对化合物进行编码,并且使用随机森林模型来预测反应的产率。
实验真实结果与预测结果决定系数来进行评测:
本次学习活动的赛题是一个典型的回归问题,机器学习一般分为两种类型的问题:回归问题和分类问题。
回归问题:即预测的结果是连续的值。
分类问题:预测的结果是离散的值。
赛题要求就是建模SMILES式子,然后模型输出到0-1之间的连续值。
分子指纹将分子结构转化为一系列数字或位向量,以此表示分子的结构信息。这些特征通常包括原子和分子的电荷、大小、极性等,旨在刻画分子所具备的一些重要化学特征。
较为常见的输入格式是SMILES化学式表示,使用一些化学信息学软件/库(如RDKit、ChemAxon等)计算分子指纹。
在本项目,采用Morgan指纹表示。关于不同的化学指纹表示方法及其特点可以看Rdkit|化学指纹(fingerprint)
决策树作为机器学习算法中较为简单和常见的算法之一,可以用于解决分类和回归两类问题。
决策树用于分类问题:对于一个数据(数值、图片、视频),对于该数据节点做是非题,判断该数据节点应当向树的哪个方向走,直至叶子节点,得到该数据对应的标号。
决策树用于回归问题:叶节点不再是上述的分类,而是一个具体的实数值。
决策树的优点:①可解释性,可看到数据处理的过程【常用于银行业保险业】;②处理数值型和类别型的特征。
决策树的缺点:①不稳定性(对于数据的噪音不同,树的构建将会不同),可以属于集成学习解决该问题;②数据过于复杂会生成复杂的树,导致过拟合,针对过拟合问题可以把决策树的枝剪掉一些;③大量的判断语句(可能是if else)不容易并行,性能差。
随机森林是让决策树稳定的方法。
针对决策树的不稳定性缺点,随机森林模型将训练多个决策树综合得到结果,提升稳定性。
随机森林的随机性(randomness)来自于:①bagging,在训练集随机采样一些样本出来(放回导致可重复性);②在bagging出来的数值中,随机采样一些特征,而并不是全部特征。
继续使用魔搭GPU环境,在对应的文件夹添加dataset压缩包。
新建终端并执行下面操作,以解压数据集。
unzip dataset.zip
- !pip install pandas
- !pip install -U scikit-learn
- !pip install rdkit
需要安装三个库:pandas
(用于数据处理和分析),scikit-learn
(机器学习库),rdkit
(化学信息工具)。
- import pickle
- import pandas as pd
- from tqdm import tqdm
- from sklearn.ensemble import RandomForestRegressor
- from rdkit.Chem import rdMolDescriptors
- from rdkit import RDLogger,Chem
- import numpy as np
- RDLogger.DisableLog('rdApp.*') #表示禁用 RDKit 生成的日志消息
pickle 是 Python 库中的一个模块,用于将 Python对象序列化和反序列化。pickle 可以将对象序列化为字符串或字节序列,以便在网络上传输或保存到文件中。
在Saving model to file和Load model from file步骤时,使用了pickle模块的dump方法保存为文件,load方法从文件中加载出来。
Pandas 库是一个免费、开源的第三方 Python 库,是数据分析常见工具。Datawhale开源教程项目叫 Joyful Pandas 可以学习pandas
监测程序运行的进度,估计运行的时长,协助debug。
详细用法可见这篇博客【python第三方库】tqdm——超详细解读
化学信息学中主要的工具,开源。网址:http://www.rdkit.org
详细用法可见博客RDkit入门
官方文档:numpy.org ,这里结合baseline中出现的语句进行介绍。
numpy的核心:ndarray ,数组对象表示固定大小项的多维、同构数组。
生成ndarray的方法有二:numpy.array() 和 numpy.zeros() ,baseline中均有体现
- # 返回给定形状和类型的新数组。
- numpy.array ( object , dtype = None , * , copy = True , order = 'K', subok = False , ndmin = 0 , like = None )
-
- # 返回给定形状和类型的新数组,并用零填充。
- numpy.zeros ( shape, dtype = float, order = 'C', *, like =None )
在向量化时用到concatenate方法,具体参数及解释如下
numpy.concatenate ( (a1, a2, ...), axis=0 ,out=None,dtype =None, casting="same_kind" )
表示沿现有轴连接一系列数组。
- def mfgen(mol,nBits=2048, radius=2):
- '''
- Parameters
- ----------
- mol : mol
- RDKit mol object.
- nBits : int
- Number of bits for the fingerprint.
- radius : int
- Radius of the Morgan fingerprint.
- Returns
- -------
- mf_desc_map : ndarray
- ndarray of molecular fingerprint descriptors.
- '''
- fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol,radius=radius,nBits=nBits)
- return np.array(list(map(eval,list(fp.ToBitString()))))
由三个参数(摩尔值、指纹的位数、指纹的半径)-->返回的是分子指纹描述符阵列
这里使用的是
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol,radius=radius,nBits=nBits)
生成为Morgan指纹,可用于描述分子的结构和相似性,它基于分子的拓扑结构和半径参数生成。
指纹的长度和半径参数可以根据需要进行调整,以平衡指纹的信息量和计算效率。
- def vec_cpd_lst(smi_lst):
- smi_set = list(set(smi_lst)) #为什么这么操作?
- smi_vec_map = {}
- # 得到了mol值,调用上面的函数,得到对应分子的分子指纹描述符阵列
- #用字典保存{分子smiles表示方式:分子指纹描述符阵列}
- for smi in tqdm(smi_set):
- # tqdm是进度条模块,tqdm可以让我们在训练过程中看到每个epoch的进度
- mol = Chem.MolFromSmiles(smi)
- smi_vec_map[smi] = mfgen(mol)
- smi_vec_map[''] = np.zeros(2048)
-
- #保存对应的分子指纹阵列的列表推导式
- vec_lst = [smi_vec_map[smi] for smi in smi_lst]
- return np.array(vec_lst)
- dataset_dir = '../dataset' # Change this to your dataset directory
- # 读训练集和测试集
- train_df = pd.read_csv(f'{dataset_dir}/round1_train_data.csv')
- test_df = pd.read_csv(f'{dataset_dir}/round1_test_data.csv')
- # 打印出训练集和测试集的size
- print(f'Training set size: {len(train_df)}, test set size: {len(test_df)}')
- # 提取训练集的反应物1/2、催化剂、溶剂的smiles表示方法
- train_rct1_smi = train_df['Reactant1'].to_list()
- train_rct2_smi = train_df['Reactant2'].to_list()
- train_add_smi = train_df['Additive'].to_list()
- train_sol_smi = train_df['Solvent'].to_list()
-
- # 得到分子指纹描述符阵列 fingerprint=fp
- train_rct1_fp = vec_cpd_lst(train_rct1_smi)
- train_rct2_fp = vec_cpd_lst(train_rct2_smi)
- train_add_fp = vec_cpd_lst(train_add_smi)
- train_sol_fp = vec_cpd_lst(train_sol_smi)
- train_x = np.concatenate([train_rct1_fp,train_rct2_fp,train_add_fp,train_sol_fp],axis=1)
- train_y = train_df['Yield'].to_numpy() #将数据转换为一个numpy数组
-
- #将测试集的内容提取出来
- test_rct1_smi = test_df['Reactant1'].to_list()
- test_rct2_smi = test_df['Reactant2'].to_list()
- test_add_smi = test_df['Additive'].to_list()
- test_sol_smi = test_df['Solvent'].to_list()
-
- test_rct1_fp = vec_cpd_lst(test_rct1_smi)
- test_rct2_fp = vec_cpd_lst(test_rct2_smi)
- test_add_fp = vec_cpd_lst(test_add_smi)
- test_sol_fp = vec_cpd_lst(test_sol_smi)
- test_x = np.concatenate([test_rct1_fp,test_rct2_fp,test_add_fp,test_sol_fp],axis=1)
model fitting and saving,使用随机森林模型【这里可以调参】
- # Model fitting模型拟合
- model = RandomForestRegressor(n_estimators=100,max_depth=10,min_samples_split=2,min_samples_leaf=1,n_jobs=-1)
- model.fit(train_x,train_y)
参数解释:
n_estimators=10: 决策树的个数,越多越好;但是越多意味着计算开销越大;
max_depth: (default=None)设置树的最大深度,默认为None;
min_samples_split: 根据属性划分节点时,最少的样本数;
min_samples_leaf: 叶子节点最少的样本数;
n_jobs=1: 并行job个数,-1表示使用所有cpu进行并行计算。
- # Saving model to file模型保存为文件
- with open('./random_forest_model.pkl', 'wb') as file:
- pickle.dump(model, file)
- # Load model from file从文件中加载出来模型
- with open('random_forest_model.pkl', 'rb') as file:
- loaded_model = pickle.load(file)
- # Inference预测/推理
- test_pred = loaded_model.predict(test_x)
- ans_str_lst = ['rxnid,Yield']
- for idx,y in enumerate(test_pred):
- ans_str_lst.append(f'test{idx+1},{y:.4f}')
- with open('./submit.txt','w') as fw:
- fw.writelines('\n'.join(ans_str_lst))
格式符合要求。
mfgen()函数:指纹的长度和半径参数可以根据需要进行调整,以平衡指纹的信息量和计算效率。
随机森林模型:决策树的个数和深度可以调参。
本周还会发布更加深入的分析文章,期待您的点赞、收藏,欢迎讨论交流!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。