@[TOC](DataWhale AI夏令营-催化反应速率预测(机器学习方向思路))
(无特征工程)
个人的机器学习竞赛上分之路分享
因为自己也是机器学习竞赛方面的小白嘛,所以就总结了一下自己一步一步调优的思路吧。
首先是baseline的调优,刚接触催化反应速率预测这个任务的时候没什么思路,就是简单的调参,跟着网上的一些教程,还有AI,接触了网格搜索调参、贝叶斯调参。然后调到一定位置的时候基本想长分就开始困难了。
就开始接触换模型,这部分试了一些感觉更高级的模型,比如之前写到的梯度提升回归模型、XGBoost模型等等,相较于baseline也有不小的提升。
试过很多模型,但上分空间还是有限,看了一些大佬的笔记,了解到了stacking,基本的概念就是通过堆叠一些一级模型的结果,交给一个二级模型学习,能得到更好的效果,刚好自己之前试过很多模型参数调的也都比较好,很适合拿来做一级模型,就用了一个简单的线性回归作为二级模型没想到效果出奇的好。查阅了一些资料,如果增加一级模型也能够使结果得到提升,或者使用更复杂的二级模型学习更多的非线性的知识也能取得更好的效果,就又在不断的尝试。
Stacking
纯分享没干货怎么行。下面是一份我使用随机森林、梯度提升回归模型和XGBoost模型作为一级模型,二级模型选择最简单的线性回归的一份代码,现在我只简单的跑了一次是拿到了0.37+的分数。大家也可以去尝试一下。
在阿里云CPU就可以运行,但是如果你换了更复杂的二级模型,就需要用GPU来跑了,不然带不动
python
# 导入库
import pickle
import pandas as pd
from tqdm import tqdm
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor, StackingRegressor
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from rdkit.Chem import rdMolDescriptors
from rdkit import RDLogger, Chem
import numpy as np
from xgboost import XGBRegressor
RDLogger.DisableLog('rdApp.*')
def mfgen(mol, nBits=2048, radius=2):
# 返回分子的位向量形式的Morgan fingerprint
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol, radius=radius, nBits=nBits)
return np.array(list(map(int, 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)}')
# 从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()
# 将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, 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)
# 划分训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(train_x, train_y, test_size=0.2, random_state=42)
# 定义一级模型
rf_model = RandomForestRegressor(n_estimators=200, max_depth=None, max_features='sqrt', min_samples_leaf=1, min_samples_split=5, n_jobs=-1)
gb_model = GradientBoostingRegressor(n_estimators=200, learning_rate=0.05, max_depth=20, max_features='sqrt', min_samples_leaf=1, min_samples_split=6)
xgb_model = XGBRegressor(n_estimators=300, learning_rate=0.05, max_depth=20, min_child_weight=3, subsample=0.8, colsample_bytree=0.4, n_jobs=-1)
# 定义堆叠模型
stacking_model = StackingRegressor(
estimators=[('rf', rf_model), ('gb', gb_model), ('xgb', xgb_model)],
final_estimator=LinearRegression()
)
# 训练堆叠模型
stacking_model.fit(X_train, y_train)
# 在验证集上进行预测并评估模型
y_val_pred = stacking_model.predict(X_val)
print(f'Validation MSE: {mean_squared_error(y_val, y_val_pred):.4f}')
# 使用整个训练集训练堆叠模型
stacking_model.fit(train_x, train_y)
# 保存模型
with open('./stacking_model.pkl', 'wb') as file:
pickle.dump(stacking_model, file)
# 加载模型
with open('stacking_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))