Datawhale AI夏令营 task1学习笔记

题目概述

第一次任务比较简单,仅仅运行给出的代码也可以获得分数。当然,如果想要提高成绩,就要调整模型超参数,或者使用其他模型。

大致要求:使用相关模型训练,对催化效率 yield 进行预测。

  1. 首先是数据的读取和处理。从给出的 csv 文件中 read_csv 依次提取出反应物一、反应物二、添加剂、溶剂并使用 to_list 转换成列表。这里我发现似乎并没有提取产物,不知道是题目不需要提取,还是让我们自己添加产物,提高模型精度(可惜当时没发现这里)。
  2. 在提取到原始数据后,还不能直接使用,因为我们的模型无法识别这一串奇怪的东西,现在需要将数据按照某种规则转化为特征向量。“幸运”地是,题目已经给出了相应的解决方案。原始数据采用的是化学分子的SMILES表达式是一种将化学分子用ASCII字符表示的方法。我们只要使用rdkit工具直接提取SMILES的分子指纹(向量)Morgan fingerprint作为特征即可。
    这里又涉及到两个比较陌生的知识:rdkit 工具,化学领域重要的开源工具(github地址),用于处理分子结构、化学反应、化学属性等信息,同时提供了丰富的化学信息处理功能,包括分子表示、相似性比较、药物设计、化学数据分析等等;Morgan fingerprint,摩根指纹通过考虑分子的局部环境来生成位向量表示,即由0,1组成的向量,根据资料,选择摩根指纹的原因正是摩根指纹能捕捉分子的局部化学环境,在许多化学信息学任务中很有价值。
  3. 最后在 dim = 1 维度使用 np.concatenate 进行拼接。即将一条数据的 Reactant1,Reactant2,Product,Additive,Solvent 字段的摩根指纹拼接为一个特征向量。

回到源码中,关键在于理解两个工具函数。

mfgen 函数用于生成分子的位向量形式的Morgan指纹。

def mfgen(mol,nBits=2048, radius=2):
    '''
    Parameters
    ----------
    mol : mol
        RDKit mol object. 一个 RDKit 的分子对象
    nBits : int
        Number of bits for the fingerprint. 指纹的位数。
    radius : int
        Radius of the Morgan fingerprint. Morgan 指纹的半径。
    Returns
    -------
    mf_desc_map : ndarray
        ndarray of molecular fingerprint descriptors.
    '''
    # 返回分子的位向量形式的Morgan fingerprint,调用GetMorganFingerprintAsBitVect即可
    fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol,radius=radius,nBits=nBits)
    return np.array(list(map(eval,list(fp.ToBitString()))))

vec_cpd_lst 函数用于处理一组 SMILES 字符串,生成它们对应的摩根指纹向量。

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) # 调用Chem.MolFromSmiles将 SMILES 字符串转化为分子对象
        smi_vec_map[smi] = mfgen(mol) # 调用工具函数mfgen生成分子的 Morgan 指纹,并存储在字典中
    smi_vec_map[''] = np.zeros(2048)
    
    vec_lst = [smi_vec_map[smi] for smi in smi_lst]
    return np.array(vec_lst) # 最终返回形式为NumPy数组

主要思路

虽然我在上个学期刚刚学习过学校开设人工智能课程,但是很遗憾,没有学过随机森林……只好从头开始。

一、调参

主要的思路就是调参了。代码中对于模型的参数进行了必要的介绍,我在网络上也进行了相应查找:

  • n_estimators(决策树的数量):

    增加 n_extimators 通常会提高模型的性能,但会增加训练时间和内存使用。
  • max_depth(树的最大深度):

    增加 max_depth 可以捕捉更多的细节,但也可能导致过拟合。
  • min_samples_split(内部节点再划分所需的最小样本数):

    增加 min_samples_split 可以减少过拟合,但可能也会增加偏差。
  • min_samples_leaf(叶节点最少样本数):

    增加 min_samples_leaf 可以减少过拟合,特别是对于噪声较大的数据集。
  • n_jobs(并行job个数)

    -1表示使用所有 cpu 进行并行计算。

初始代码如下

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) # 训练模型

可以看到其实参数是比较小的。结合资料,我将四个参数逐步调高,最终选择500,50,10,4的组合。当然,模型训练时间也相应变长。成绩提高到0.3以上。(后来了解到决策树数量似乎影响不大,突然发现自己的方向好像搞错了……)

二、模型优化

此外考虑对模型优化,我主要使用了标准化特征和重要特征选择提高模型稳定性,防止过拟合。主要代码如下:

# 注意导入
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectFromModel

# 标准化特征
scaler = StandardScaler()
train_x_scaled = scaler.fit_transform(train_x)
test_x_scaled = scaler.transform(test_x)

# 使用随机森林进行特征选择
feature_selector = RandomForestRegressor(n_estimators=500, max_depth=50, min_samples_split=10, min_samples_leaf=4, n_jobs=-1)
feature_selector.fit(train_x_scaled, train_y)

# 使用 SelectFromModel 选择重要特征
selector = SelectFromModel(feature_selector, prefit=True)
train_x_selected = selector.transform(train_x_scaled)
test_x_selected = selector.transform(test_x_scaled)

# 训练最终模型
model = RandomForestRegressor(n_estimators=500, max_depth=50, min_samples_split=10, min_samples_leaf=4, n_jobs=-1)
model.fit(train_x_selected, train_y)

成绩微升0.01左右。

三、模型替换

我还尝试了另一个思路:替换模型。我了解到了两种类似的模型:GradientBoostingRegressorAdaBoostRegressor。而且GradientBoostingRegressor在复杂数据集上的表现有时要优于随机森林模型。于是我将模型替换为GradientBoostingRegressor

不过我又发现了严重的问题:GradientBoostingRegressor的训练时间更长!我只好将相应的参数适当调小,最终效果如下:

# 注意导入
from sklearn.ensemble import GradientBoostingRegressor, AdaBoostRegressor

scaler = StandardScaler()
train_x_scaled = scaler.fit_transform(train_x)
test_x_scaled = scaler.transform(test_x)

# 替换为GradientBoostingRegressor,大致逻辑不变
feature_selector = GradientBoostingRegressor(n_estimators=50, max_depth=50, min_samples_split=10, min_samples_leaf=4)
feature_selector.fit(train_x_scaled, train_y)

selector = SelectFromModel(feature_selector, prefit=True)
train_x_selected = selector.transform(train_x_scaled)
test_x_selected = selector.transform(test_x_scaled)

model = GradientBoostingRegressor(n_estimators=50, max_depth=50, min_samples_split=10, min_samples_leaf=4)
model.fit(train_x_selected, train_y)

成绩不升反降……如果以后有机会,再进行调参尝试。

总结

贴上最终成绩

这次的任务总体上不需要自己书写或修改太多东西,只需要多进行调参尝试就有不错的成绩(指排行榜位次)。还是要注意不同参数对结果的影响程度,避免出现牺牲过多效率,得分只有微量提升的情况。

大家的成绩似乎都集中在0.3~0.4,说明大多数同学也只是进行了调参优化。我对自己task1的成绩还算满意。下一个任务加油!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值