Datawhale AI 夏令营---催化反应产量预测
一、赛事介绍
1、背景知识
碳氮成键反应、Diels-Alder环加成反应等一系列催化合成反应,被广泛应用于各类药物的生产合成中。研究人员与产业界在针对特定反应类型开发新的催化合成方法时,往往追求以高产率获得目标产物,也即开发高活性的催化反应体系,以提升原子经济性,减少资源的浪费与环境污染。然而,开发具有高活性的催化反应体系通常需要对包括催化剂和溶剂在内的多种反应条件进行详尽的探索,这导致了它成为了一项极为耗时且资源密集的任务。这要求对包括催化剂和溶剂在内的多种反应条件进行详尽的探索。目前,反应条件的筛选在很大程度上依赖于经验判断和偶然发现,导致催化反应条件的优化过程既耗时又费力,并且严重制约了新的高效催化合成策略的开发。
反应底物和反应条件是决定其产率的关键因素。因此,我们可以利用AI模型来捕捉底物、条件与产率之间的内在联系。借助产率预测AI模型,仅需输入底物和条件的信息,我们就能够预测该反应组合下的产率,从而有效提升催化反应的条件筛选效率。
2、比赛任务
本次比赛提供在药物合成中常见的多种催化反应实验数据,其中包括反应的底物、包括催化剂在内的反应添加剂、反应溶剂以及反应产物,期待选手通过分析反应数据,利用机器学习、深度学习算法或者大语言模型,建立产率预测模型,从而辅助未知新反应的反应条件筛选。
3、数据集介绍
初赛数据集仅包含碳氮成键类型反应数据,其中训练集中包含23538条反应数据,测试集中包含2616条反应数据。训练集与测试集的比例接近9:1。每条训练数据包含 , , , , , 字段。其中 , , , 字段中为对应物质的SMILES字符串,字段为目标字段,是经过归一化的浮点数。rxnid
Reactant1,
Reactant2
Product
Additive
Solvent
Yield
Reactant1
Reactant2
Product
Additive
Solvent
Yield
4、评价指标
本次任务采用实验真实结果与预测结果𝑅^2决定系数来进行评测
二、Task1 baseline解析
1、导入相关库
import pickle # 用于序列化和反序列化Python对象
import pandas as pd # 用于数据处理和分析
from tqdm import tqdm # 用于显示进度条
from sklearn.ensemble import RandomForestRegressor # 导入随机森林回归模型
from rdkit.Chem import rdMolDescriptors # 导入rdkit库中的分子描述符模块
from rdkit import RDLogger, Chem # 导入rdkit库中的日志和化学模块
import numpy as np # 用于进行数值计算
RDLogger.DisableLog('rdApp.*') # 禁用rdkit的日志输出
2、特征提取
def mfgen(mol, nBits=2048, radius=2):
'''
生成分子指纹描述符。
Parameters
----------
mol : mol
RDKit mol对象。
nBits : int
指纹的位数。
radius : int
Morgan指纹的半径。
Returns
-------
mf_desc_map : ndarray
分子指纹描述符的ndarray。
'''
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol, radius=radius, nBits=nBits)
return np.array(list(map(eval, list(fp.ToBitString()))))
mfgen
函数生成给定分子的Morgan指纹描述符。指纹描述符是用于量化分子结构特征的位向量。
def vec_cpd_lst(smi_lst):
smi_set = list(set(smi_lst)) # 去除重复的SMILES字符串
smi_vec_map = {} # 创建一个空字典来存储SMILES字符串及其对应的向量表示
for smi in tqdm(smi_set): # 遍历去重后的SMILES列表,并使用tqdm显示进度条
mol = Chem.MolFromSmiles(smi) # 从SMILES字符串创建分子对象
smi_vec_map[smi] = mfgen(mol) # 调用mfgen函数生成分子指纹描述符,并将其添加到字典中
smi_vec_map[''] = np.zeros(2048) # 添加一个空键,其值为全零向量
vec_lst = [smi_vec_map[smi] for smi in smi_lst] # 根据输入的SMILES列表生成对应的向量列表
return np.array(vec_lst) # 将向量列表转换为numpy数组并返回
vec_cpd_lst
函数将SMILES字符串列表转换为分子指纹描述符向量,用于后续模型的输入。
数据集说明
本次的数据集是对化学分子的SMILES表达式,具体来说有rxnid,Reactant1,Reactant2,Product,Additive,Solvent,Yield字段
其中:
- rxnid 对数据的id标识,无实际意义
- Reactant1 反应物1
- Reactant2 反应物2
- Product 产物
- Additive 添加剂(包括催化剂catalyst等辅助反应物合成但是不对产物贡献原子的部分)
- Solvent 溶剂
- Yield 产率 其中Reactant1,Reactant2,Product,Additive,Solvent都是由SMILES表示。
SMILES
SMILES,全称是Simplified Molecular Input Line Entry System,是一种将化学分子用ASCII字符表示的方法,是化学信息学领域非常重要的工具。
表1:一些常见的化学结构用SMILES表示。
表2:化学反应也可以用SMILES表示,用“>>”连接产物即可。
由于Reactant1,Reactant2,Product,Additive,Solvent都是由SMILES表示。所以,可以使用rdkit工具直接提取SMILES的分子指纹(向量),作为特征。
Morgan fingerprint
位向量(bit ector)形式的特征,即由0,1组成的向量。
RDKit
化学信息学中主要的工具,开源。网址:http://www.rdkit.org,支持WIN\MAC\Linux,可以被python、Java、C调用。几乎所有的与化学信息学相关的内容都可以在上面找到。
3、数据预处理
读取数据集:
dataset_dir = '../dataset' # # 注:如果是在AI Studio上,将这里改为'dataset'
train_df = pd.read_csv(f'{dataset_dir}/round1_train_data.csv')
test_df = pd.read_csv(f'{dataset_dir}/round1_test_data.csv')
print(f'Training set size: {len(train_df)}, test set size: {len(test_df)}')
提取训练集和测试集中的SMILES字符串列表,并将其转换为分子指纹描述符向量:
# 提取训练集和测试集中的反应物、添加剂和溶剂的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()
# 使用vec_cpd_lst函数将SMILES字符串列表转换为分子指纹描述符向量
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() # 提取训练集的产量数据作为目标变量
# 对测试集执行相同的操作
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)
4、模型训练和保存
使用随机森林回归模型进行训练
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)
将训练好的模型保存到文件
with open('./random_forest_model.pkl', 'wb') as file:
pickle.dump(model, file)
5、模型加载和预测
从文件中加载模型并进行预测:
with open('random_forest_model.pkl', 'rb') as file:
loaded_model = pickle.load(file)
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))
三、随机森林模型说明介绍
1、为什么选择随机森林模型?
抗过拟合:随机森林通过集成多个决策树模型,有效降低了过拟合风险。
处理高维数据:能够处理高维特征空间(如分子指纹描述符)。
鲁棒性强:对数据中的噪声和缺失值不敏感。
2、模型参数解释
n_estimators=100
:构建100棵决策树,增加模型的稳定性。max_depth=10
:限制每棵树的最大深度,防止过拟合。min_samples_split=2
:每个内部节点至少有2个样本才会继续分裂。min_samples_leaf=1
:每个叶子节点至少包含1个样本。n_jobs=-1
:使用所有可用的CPU核心进行并行计算,提高训练速度。
3、sklearn (scikit-learn)
是一个非常广泛使用的开源机器学习库,基于Python,建立在NumPy、SciPy、Pandas和Matplotlib等数据处理和分析的库之上。
它涵盖了几乎所有主流机器学习算法,包括分类、回归、聚类、降维等。API设计亲民,整个使用简单易上手,非常适合作为机器学习入门的工具。 官网:scikit-learn: machine learning in Python — scikit-learn 1.5.1 documentation
在sklearn中,几乎所有的机器学习的流程是:
- 实例化模型(并指定重要参数);
- model.fit(x, y) 训练模型;
五、提交结果展示
基础版baseline提交结果
六、进阶尝试
1、LightGB模型
在对上述的代码及其手册的学习后,尝试使用LightGB模型,并且在设置了一些参数,分数从0.2044上升到0.2748。
# 使用LightGBM回归模型进行训练
model = LGBMRegressor(
n_estimators=1000,
learning_rate=0.01,
max_depth=-1,
num_leaves=31,
min_child_samples=20,
subsample=0.8,
colsample_bytree=0.8,
n_jobs=-1
)
model.fit(train_x, train_y)
后来对参数进行调参,n_estimators=2000,num_leaves=50。调参之后分数又提升到0.3127。感觉还可以进一步调参,进而提高分数。
# 分割训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(train_x, train_y, test_size=0.2, random_state=42)
model = LGBMRegressor(
n_estimators=2000,
learning_rate=0.01,
max_depth=-1,
num_leaves=50,
min_child_samples=20,
subsample=0.8,
colsample_bytree=0.8,
n_jobs=-1
)
callbacks = [
early_stopping(stopping_rounds=100),
log_evaluation(100)
]
model.fit(
X_train, y_train,
eval_set=[(X_val, y_val)],
eval_metric='rmse',
callbacks=callbacks
)
2、随机森林进阶尝试
在原有的基础上对随机森林的模型参数进行修改尝试,进而使得分数取得一定的提高。
# 使用随机森林回归模型进行训练
model = RandomForestRegressor(n_estimators=1000, max_depth=50, min_samples_split=5, min_samples_leaf=2, n_jobs=-1)
model.fit(train_x, train_y)
通过上述的参数修改,使得分数从 0.2044上升到0.3366(随机森林网络模型)
七、Task1学习总结
Task1的学习还是比较简单上手的,通过手册的指导学习,成功跑通基础班的baseline。之后又对数据集,结果评分准则以及代码进行学习了解。并且在此基础上进行简单的改进优化,之后打算继续尝试优化