簡介
這是 Datawhale 2024 年 AI 夏令營第三期的學(xué)習(xí)活動《從零入門AI for Science》中的一個示例愤诱,基于天池平臺第二屆世界科學(xué)智能大賽 物質(zhì)科學(xué)賽道:催化反應(yīng)產(chǎn)率預(yù)測開展的實踐學(xué)習(xí)。
這是一個簡單的上手baseline城瞎,數(shù)據(jù)集包括測試集和訓(xùn)練集:
-
訓(xùn)練集包含23538條反應(yīng)數(shù)據(jù):
-
樣本序號:
- rxnid:樣本序號
-
樣本的特征:
Reactant1:反應(yīng)物1绞呈,用SMILES表示。SMILES將化學(xué)分子用ASCII字符表示的方法唾琼,是化學(xué)信息學(xué)領(lǐng)域非常重要的工具
Reactant2:反應(yīng)物2渴邦,用SMILES表示
Product:產(chǎn)物疯趟,用SMILES表示
dditive:催化劑,用SMILES表示
Solvent:溶劑谋梭,用SMILES表示
-
樣本標(biāo)簽:
- Yield:反應(yīng)產(chǎn)率
-
測試集包含2616條反應(yīng)數(shù)據(jù)信峻,和訓(xùn)練集相比,沒有樣本標(biāo)簽瓮床。
分解步驟
1. 導(dǎo)入必要的庫
#首先盹舞,導(dǎo)入庫
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.*')
pickle主要是用來保存訓(xùn)練的模型到本地,這樣只需訓(xùn)練一次得到模型隘庄,后面可以直接使用踢步,不需要重新訓(xùn)練。
rdkit是一個開源的化學(xué)信息python軟件包丑掺。rdMolDescriptors是RDKit 庫中的一個模塊获印,用于量化分子的結(jié)構(gòu)特征。
2. 從數(shù)據(jù)集提取數(shù)據(jù)
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.
'''
# 返回分子的位向量形式的Morgan fingerprint
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol,radius=radius,nBits=nBits)
return np.array(list(map(eval,list(fp.ToBitString()))))
# 加載數(shù)據(jù)
def vec_cpd_lst(smi_lst):
smi_set = list(set(smi_lst))
smi_vec_map = {}
for smi in tqdm(smi_set): # tqdm:顯示進(jìn)度條
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)
mfgen函數(shù)用于生成 Morgan 指紋街州。
vec_cpd_lst函數(shù)將數(shù)據(jù)集中的各物質(zhì)SMILES字符串轉(zhuǎn)化為分子指紋向量構(gòu)成的數(shù)組兼丰。
使用這兩個函數(shù)玻孟,可以將原始的分子字符串?dāng)?shù)據(jù)集轉(zhuǎn)化為可用于模型訓(xùn)練數(shù)據(jù)集:
# 使用Pandas讀取數(shù)據(jù)集
dataset_dir = '../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)}')
# 使用Pandas讀取數(shù)據(jù)集
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()
# 將SMILES轉(zhuǎn)化為分子指紋
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維度進(jìn)行拼接。即:將一條數(shù)據(jù)的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)
train_y = train_df['Yield'].to_numpy()
# 測試集也進(jìn)行同樣的操作
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)
3. 訓(xùn)練模型
實例化并訓(xùn)練一個隨機(jī)森林回歸模型:
# 模型訓(xùn)練
model = RandomForestRegressor(n_estimators=10 # 決策樹的個數(shù)黍翎,越多通常效果越好,但也容易過擬合
,max_depth=10 # 設(shè)置樹的最大深度蟆技,與剪枝相關(guān)的參數(shù)玩敏,默認(rèn)值None
,min_samples_split=2 # 根據(jù)屬性劃分節(jié)點時,最少的樣本數(shù)质礼,默認(rèn)是2,調(diào)整模型是否過擬合或欠擬合
,min_samples_leaf=1 # 葉子節(jié)點最少的樣本數(shù)织阳,默認(rèn)是1眶蕉,也是調(diào)整模型是否過擬合或欠擬合
,n_jobs=-1) # 并行job個數(shù),-1表示使用所有cpu進(jìn)行并行計算
model.fit(train_x,train_y)
保存模型唧躲,避免后面使用時重新訓(xùn)練模型:
with open('./random_forest_model.pkl', 'wb') as file:
pickle.dump(model, file)</pre>
加載模型并預(yù)測:
with open('random_forest_model.pkl', 'rb') as file:
loaded_model = pickle.load(file)
# 測試集預(yù)測
test_pred = loaded_model.predict(test_x)</pre>
4. 生產(chǎn)結(jié)果文本
生成賽題要求的submit文件:
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))