-
Datawhale 夏令营第三期 机器学习 笔记1
-
比赛题目
-
构建一个能够准确预测碳氮成键反应产率的预测模型。
-
-
数据
-
初赛数据集仅包含碳氮成键类型反应数据,其中训练集中包含23538条反应数据,测试集中包含2616条反应数据。训练集与测试集的比例接近9:1。每条训练数据包含 rxnid, Reactant1, Reactant2 , Product , Additive , Solvent , Yield字段。其中 Reactant1 , Reactant2 , Product , Additive , Solvent 字段中为对应物质的SMILES字符串,Yield字段为目标字段,是经过归一化的浮点数。
-
-
赛题类型
-
回归问题
-
赛题要求就是建模SMILES式子,然后模型输出到0-1之间的连续值。
-
ps:
-
机器学习一般分为两种类型的问题:回归问题和分类问题。
-
回归问题:即预测的结果是连续的值。
-
分类问题:预测的结果是离散的值。
-
-
-
-
评审规则
-
实验真实结果与预测结果$R^2$决定系数来进行评测:
-
-
baseline代码
-
-
!pip install pandas !pip install -U scikit-learn !pip install 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 from sklearn.model_selection import cross_val_score RDLogger.DisableLog('rdApp.*') #将数据转为向量形式 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())))) # 加载数据 def vec_cpd_lst(smi_lst): smi_set = list(set(smi_lst)) smi_vec_map = {} for smi in tqdm(smi_set): # tqdm:显示进度条 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' # # 注:如果是在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)}') # 从df中读取数据 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转化为分子指纹 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) 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) #训练模型 # Model fitting model = RandomForestRegressor(n_estimators=100,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) # 加载模型 with open('random_forest_model.pkl', 'rb') as file: loaded_model = pickle.load(file) # 预测\推理 test_pred = loaded_model.predict(test_x) # 交叉验证 accuracy_score = cross_val_score(model, train_x, train_y) print(accuracy_score) #生成赛题要求的结果文件 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)第一次直接提交baseline的运行结果,什么也没改,得分如下:
日期:2024-07-26 09:57:39
分数:0.1985
-
结果分析:树个数为10,深度为10,由于树是按照特征不断进行划分的,深度为10的划分对分类不够全面。另外随机森林是群体决策,树个数过少影响精度。
-
(2)第二次将随机森林的个数改为100个,深度改为默认none,得分如下:
日期:2024-07-27 23:14:34
分数:0.3367
-
结果分析:增加树的个数和深度后,分数显著提升。经查找资料,一般通过交叉验证来确定最佳参数组合,是后续的优化方向。
-
(3)第三次通过多次交叉验证比较
-