分子性质预测挑战赛之特征工程与集成学习(Datawhale AI 夏令营)

        在baseline阶段,我们使用CatBoost完成了解决机器学习问题的全部流程,得到了基础的分数。在进阶实践部分,我们将在原有Baseline基础上进行更多优化,通常从特征工程和模型选择两个方面着手。

优化方法建议
  1. 提取更多特征: 特征工程是数据挖掘比赛中制胜的关键。我们需要挖掘更多有价值的信息并将其转化为模型输入特征。对于本次赛题,可以从专业角度构建特征,除了Smiles特征外,还可以从InChI字符串中提取详细的分子结构信息。例如:

    • 分子式:从InChI字符串提取分子式部分,例如/C47H61N7O6S表示分子由47个碳原子、61个氢原子、7个氮原子、6个氧原子和1个硫原子组成。
    • 分子量:通过计算各原子数量乘以其原子质量来获得。例如:碳(C)原子质量约为12.01 g/mol,氢(H)原子质量约为1.008 g/mol,氮(N)原子质量约为14.01 g/mol,氧(O)原子质量约为16.00 g/mol,硫(S)原子质量约为32.07 g/mol。公式如下:
       
    分子量 = (47 × 12.01) + (61 × 1.008) + (7 × 14.01) + (6 × 16.00) + (1 × 32.07)
    

    尝试不同的模型: 不同模型的预测结果会有差异,通过不断实验和试错,找到最佳模型并增强对模型的理解能力。我们可以从以下模型进行尝试:

    • LightGBM
    • XGBoost
    • CatBoost
特征优化

针对InChI字符串,我们可以进行以下特征提取:

  1. 提取分子式: 从InChI字符串中提取分子式信息。

    import re
    from rdkit import Chem
    
    def parse_inchi(row):
        inchi_str = row['InChI']
        formula = ''
        molecular_weight = 0
        element_counts = {}
        
        formula_match = re.search(r"InChI=1S/([^/]+)/c", inchi_str)
        if formula_match:
            formula = formula_match.group(1)
        
        for element, count in re.findall(r"([A-Z][a-z]*)([0-9]*)", formula):
            count = int(count) if count else 1
            element_mass = atomic_masses.get(element.upper(), 0)
            molecular_weight += element_mass * count
            element_counts[element.upper()] = count
        
        return pd.Series({'Formula': formula, 'MolecularWeight': molecular_weight, 'ElementCounts': element_counts})
    

  2. 计算分子量和原子计数: 计算各分子的分子量并展开原子计数。

    train[['Formula', 'MolecularWeight', 'ElementCounts']] = train.apply(parse_inchi, axis=1)
    
  3. 生成原子计数特征: 将原子计数展开为独立的特征列。

    keys = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn']
    df_expanded = pd.DataFrame({key: pd.Series() for key in keys})
    
    for index, item in enumerate(train['ElementCounts'].values):
        for key in keys:
            df_expanded.at[index, key] = item.get(key, 0)
            
    df_expanded = pd.DataFrame(df_expanded)
    
模型融合

通过模型融合可以提高预测的稳定性和准确性。我们可以采用以下方式:

  1. 定义交叉验证模型训练和预测函数

    def cv_model(clf, train_x, train_y, test_x, clf_name, seed=2023):
        folds = 5
        kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
        oof = np.zeros(train_x.shape[0])
        test_predict = np.zeros(test_x.shape[0])
        cv_scores = []
    
        for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
            print(f'************************************ {i+1} ************************************')
            trn_x, trn_y = train_x.iloc[train_index], train_y[train_index]
            val_x, val_y = train_x.iloc[valid_index], train_y[valid_index]
    
            if clf_name == "lgb":
                train_matrix = clf.Dataset(trn_x, label=trn_y)
                valid_matrix = clf.Dataset(val_x, label=val_y)
                params = {
                    'boosting_type': 'gbdt',
                    'objective': 'binary',
                    'min_child_weight': 6,
                    'num_leaves': 2 ** 6,
                    'lambda_l2': 10,
                    'feature_fraction': 0.8,
                    'bagging_fraction': 0.8,
                    'bagging_freq': 4,
                    'learning_rate': 0.35,
                    'seed': 2024,
                    'nthread' : 16,
                    'verbose' : -1,
                }
                model = clf.train(params, train_matrix, 2000, valid_sets=[train_matrix, valid_matrix],
                                  categorical_feature=[], verbose_eval=1000, early_stopping_rounds=100)
                val_pred = model.predict(val_x, num_iteration=model.best_iteration)
                test_pred = model.predict(test_x, num_iteration=model.best_iteration)
    
            if clf_name == "xgb":
                xgb_params = {
                  'booster': 'gbtree', 
                  'objective': 'binary:logistic',
                  'num_class':3,
                  'max_depth': 5,
                  'lambda': 10,
                  'subsample': 0.7,
                  'colsample_bytree': 0.7,
                  'colsample_bylevel': 0.7,
                  'eta': 0.35,
                  'tree_method': 'hist',
                  'seed': 520,
                  'nthread': 16
                  }
                train_matrix = clf.DMatrix(trn_x , label=trn_y)
                valid_matrix = clf.DMatrix(val_x , label=val_y)
                test_matrix = clf.DMatrix(test_x)
                
                watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]
                
                model = clf.train(xgb_params, train_matrix, num_boost_round=2000, evals=watchlist, verbose_eval=1000, early_stopping_rounds=100)
                val_pred  = model.predict(valid_matrix)
                test_pred = model.predict(test_matrix)
                
            if clf_name == "cat":
                params = {'learning_rate': 0.35, 'depth': 5, 'bootstrap_type':'Bernoulli','random_seed':2024,
                          'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False}
                
                model = clf(iterations=2000, **params)
                model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
                          metric_period=1000,
                          use_best_model=True, 
                          cat_features=[],
                          verbose=1)
                
                val_pred  = model.predict_proba(val_x)
                test_pred = model.predict_proba(test_x)
    
            oof[valid_index] = val_pred
            test_predict += test_pred / kf.n_splits
            F1_score = f1_score(val_y, np.where(val_pred>0.5, 1, 0))
            cv_scores.append(F1_score)
            print(cv_scores)
            
        return oof, test_predict
    
  2. 模型融合: 通过将多个模型的预测结果进行平均融合,或者采用stacking方法。

    lgb_oof, lgb_test = cv_model(lgb, x_train, y_train, x_test, 'lgb')
    xgb_oof, xgb_test = cv_model(xgb, x_train, y_train, x_test, 'xgb')
    cat_oof, cat_test = cv_model(CatBoostClassifier, x_train, y_train, x_test, 'cat')
    
    final_test = (lgb_test + xgb_test + cat_test) / 3
    

        将结果取平均进行融合是比较基础的融合的方式,另外一种经典融合方式为stacking,stacking是一种分层模型集成框架。以两层为例,第一层由多个基学习器组成,其输入为原始训练集,第二层的模型则是以第一层基学习器的输出作为特征加入训练集进行再训练,从而得到完整的stacking模型。

第一层:(类比cv_model函数)

  1. 划分训练数据为K折(5折为例,每次选择其中四份作为训练集,一份作为验证集);

  2. 针对各个模型RF、ET、GBDT、XGB,分别进行5次训练,每次训练保留一份样本用作训练时的验证,训练完成后分别对Validation set,Test set进行预测,对于Test set一个模型会对应5个预测结果,将这5个结果取平均;对于Validation set一个模型经过5次交叉验证后,所有验证集数据都含有一个标签。此步骤结束后:5个验证集(总数相当于训练集全部)在每个模型下分别有一个预测标签,每行数据共有4个标签(4个算法模型),测试集每行数据也拥有四个标签(4个模型分别预测得到的)

第二层:(类比stack_model函数)

  1. 将训练集中的四个标签外加真实标签当作五列新的特征作为新的训练集,选取一个训练模型,根据新的训练集进行训练,然后应用测试集的四个标签组成的测试集进行预测作为最终的result。

Stacking参考代码

def stack_model(oof_1, oof_2, oof_3, predictions_1, predictions_2, predictions_3, y):
    train_stack = pd.concat([oof_1, oof_2, oof_3], axis=1)
    test_stack = pd.concat([predictions_1, predictions_2, predictions_3], axis=1)
    
    oof = np.zeros((train_stack.shape[0],))
    predictions = np.zeros((test_stack.shape[0],))
    scores = []

    from sklearn.linear_model import Ridge
    from sklearn.metrics import roc_auc_score
    from sklearn.model_selection import RepeatedKFold

    folds = RepeatedKFold(n_splits=5, n_repeats=2, random_state=2021)
    
    for fold_, (trn_idx, val_idx) in enumerate(folds.split(train_stack, train_stack)): 
        print(f"fold n°{fold_+1}")
        trn_data, trn_y = train_stack.loc[trn_idx], y[trn_idx]
        val_data, val_y = train_stack.loc[val_idx], y[val_idx]
        
        clf = Ridge(random_state=2024)
        clf.fit(trn_data, trn_y)

        oof[val_idx] = clf.predict(val_data)
        predictions += clf.predict(test_stack) / (5 * 2)
        
        score_single = roc_auc_score(val_y, oof[val_idx])
        scores.append(score_single)
        print(f'{fold_+1}/{5}', score_single)
    print('mean: ', np.mean(scores))
   
    return oof, predictions

结语

        通过本次分子性质预测挑战赛的探讨,我们从基础的CatBoost模型出发,逐步引入了特征工程和模型融合等优化方法。在特征工程方面,我们深入挖掘了InChI字符串中的信息,提取了丰富的分子结构特征;在模型优化方面,我们尝试了不同的机器学习模型并进行了融合,显著提升了预测性能。特别是通过Stacking方法,我们有效地整合了多种模型的优势,进一步提升了整体表现。希望这些实践经验能为大家在数据科学和机器学习领域的研究和应用提供有价值的参考。持续尝试和创新,将是我们不断进步的动力。

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的Anthony

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值