#Datawhale AI 夏令营
一、环境依赖
1.安装所需库
需要先分别安装任务所需的pandas、scikit-learn和rdkit,这三个库在数据科学和化学信息学领域中都有广泛的应用。其中,rdkit是一个用于化学信息学的开源python工具包,用来处理化学分子。
!pip install pandas
!pip install -U scikit-learn
!pip install rdkit
2.导入所需库
然后导入包括刚刚安装的库在内的需要使用的库。
# 首先,导入库
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.*')
其中,RDLogger.DisableLog('rdApp.*')表示禁用 RDKit 生成的日志消息。在数据处理和建模过程中,RDKit 可能会生成大量的警告和信息消息,导致输出变得杂乱。
二、数据预处理
1.加载数据
设置数据集路径,使用pd.read_csv函数读取训练集和测试集的csv文件,并打印它们的大小。
# 设置数据集目录路径
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)}')
2.数据处理
1)定义了一个mfgen函数,生成分子指纹并返回NumPy 数组
def mfgen(mol,nBits=2048, radius=2):
# 返回分子的位向量形式的Morgan fingerprint
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol,radius=radius,nBits=nBits)
return np.array(list(map(eval,list(fp.ToBitString()))))
该函数使用rdkit库的“GetMorganFingerprintAsBitVect”
函数生成 Morgan 指纹。其中,radius
参数指定了指纹计算中包含的原子邻域的半径。
最后return中,np.array(list(map(eval,list(fp.ToBitString())))):
“fp.ToBitString()
”将 Morgan 指纹转换为一个二进制字符串(如 '101000...010'
)。
“list(fp.ToBitString())
”将这个二进制字符串转换为字符列表(如 ['1', '0', '1', ...]
)。
“map(eval,
list(fp.ToBitString()
)
”将每个字符从字符串 '0'
或 '1'
转换为整数 0
或 1
。 eval
在这里是用来将字符 '1'
转换为整数 1
, '0'
转换为 0
。
“np.array(
map(eval,
list(fp.ToBitString()
)
)
”将处理后的列表转换为 NumPy 数组。
2)定义了一个vec_cpd_lst函数,将一组 SMILES 字符串转换为对应的分子指纹,并返回这些指纹的一个 NumPy 数组。
具体解释看注释。之所以返回NumPy 数组,是因为其可以将数据转换成向量的形式,方便机器运算。
def vec_cpd_lst(smi_lst):
smi_set = list(set(smi_lst)) # 1. 去重SMILES字符串
smi_vec_map = {} # 2. 创建一个字典用于存储SMILES字符串与其对应的分子指纹
for smi in tqdm(smi_set): # 3. 对每个唯一的SMILES字符串进行处理
mol = Chem.MolFromSmiles(smi) # 4. 将SMILES字符串转换为RDKit分子对象
smi_vec_map[smi] = mfgen(mol) # 5. 生成分子指纹并存储在字典中
smi_vec_map[''] = np.zeros(2048) # 6. 处理无效的SMILES字符串,使用全零向量代替
vec_lst = [smi_vec_map[smi] for smi in smi_lst] # 7. 将原始SMILES列表中的
#每个SMILES转换为指纹
return np.array(vec_lst) # 8. 返回分子指纹的NumPy数组
3)处理训练集与测试集数据
1.读取训练和测试数据:
从 train_df
和test_df
中提取 “Reactant1”
、“Reactant2”
、“Additive
”和 “Solvent
”列的 SMILES 字符串,并转换为列表。
# 从csv中读取数据
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()
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()
2.转换特征:
使用 “vec_cpd_lst
”函数将训练集和测试集 SMILES 字符串转换为分子指纹,并对这些指纹进行拼接,形成特征矩阵。
# 将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)
# 在dim=1维度进行拼接。即:将一条数据的Reactant1,Reactant2,Product,Additive,Solvent字段的morgan fingerprint拼接为一个向量。
train_x = np.concatenate([train_rct1_fp,train_rct2_fp,train_add_fp,train_sol_fp],axis=1)
# 测试集也进行同样的操作
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)
3.提取目标变量:
获取训练集中的目标变量,以便进行模型训练。
train_y = train_df['Yield'].to_numpy()
3.训练模型
1)随机森林模型
使用fit函数来训练模型。
# Model fitting
model = RandomForestRegressor(n_estimators=10,max_depth=10,min_samples_split=2,min_samples_leaf=1,n_jobs=-1) # 实例化模型,并指定重要参数
model.fit(train_x,train_y) # 训练模型
本次训练模型使用随机森林模型,使用方法及参数可参考如下:RandomForestRegressor 参数_randomforestregressor参数-CSDN博客
参数如下:
- n_estimators=10: 决策树的个数,越多越好;但是越多意味着计算开销越大;
- max_depth: (default=None)设置树的最大深度,默认为None;
- min_samples_split: 根据属性划分节点时,最少的样本数;
- min_samples_leaf: 叶子节点最少的样本数;
- n_jobs=1: 并行job个数,-1表示使用所有cpu进行并行计算。
2)保存模型
将训练好的随机森林模型保存到目录下的一个文件中,以便将来加载和使用。
# 保存模型
with open('./random_forest_model.pkl', 'wb') as file:
pickle.dump(model, file)
4.模型预测
1)从磁盘目录中加载之前训练好的模型。
# 加载模型
with open('random_forest_model.pkl', 'rb') as file:
loaded_model = pickle.load(file)
2) 使用模型进行预测。
# 预测\推理
test_pred = loaded_model.predict(test_x)
5.生成提交的submit文件
1)创建列表
ans_str_lst = ['rxnid,Yield']
2)格式化预测结果
for idx,y in enumerate(test_pred):
ans_str_lst.append(f'test{idx+1},{y:.4f}')
其中,f‘test{idx+1}
’是样本的 ID,{y:.4f}
是预测值,格式化为四位小数。f‘test{idx+1},{y:.4f}'
:使用格式化字符串生成每一行的结果。append()函数将每一行格式化后的字符串添加到“ ans_str_lst”
列表中
3)写入并生成submit.txt文件
with open('./submit.txt','w') as fw:
fw.writelines('\n'.join(ans_str_lst))