恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
抗乳腺癌候选药物优化建模:分子描述符、活性预测与特征筛选全流程解析
首页
资讯中心
/
抗乳腺癌候选药物优化建模:分子描述符、活性预测与特征筛选全流程解析
抗乳腺癌候选药物优化建模:分子描述符、活性预测与特征筛选全流程解析
发布时间:2026/10/9 11:53:42
简介2021年华为杯数学建模D题抗乳腺癌候选药物的优化建模解答2是一份面向参赛学生和生物信息、药物研发方向学习者的完整参考实现覆盖从数据清洗、特征筛选到模型评估与结果可视化的全流程。压缩包共83个文件包含32个Python脚本、22个CSV数据集、14张PNG结果图、5个Excel表格及4个XML配置等整体大小31.82MB目录结构清晰便于按模块阅读与复用。代码覆盖特征选择、回归预测、二分类建模与最优化求解等关键环节例如卡方检验、RFE、Lasso回归与网格搜索均有对应实现配套CSV数据与xlsx表格便于复现14张结果图直接展示ROC曲线与特征重要性等分析结果3份MD文档对算法原理和调参思路做了梳理可帮助快速理解乳腺癌候选药物优化建模的核心方法。目前已有3479人学习下载适合需要对照参考实现、开展赛题复盘或延伸学习的建模爱好者。1. 这道抗乳腺癌候选药物优化建模题真正的分水岭不在模型2021 年那道华为杯数学建模 D 题——抗乳腺癌候选药物的优化建模——很多参赛队拿到数据的第一反应是赶紧训练模型。但真正把差距拉开的是拿到几百维分子描述符之后最早的那几步缺失值怎么处理、IC50 要不要先取负对数、特征筛选有没有让验证集信息提前泄进去。这些步骤决定了预测分数的上限模型只是在验证流程逼近这个上限之后替你走最后一步。这篇笔记按一线拆解这类赛题的思路把数据体检、特征筛选、活性预测、参数调试和常见翻车点串成一条可以直接照着操作的路径适合手里有这类赛题解答包但不想只抄结果、想真正复现整套流程的人。2. 先把题拆开药物活性数据里到底藏着哪些看得见的建模点2.1 分子描述符、IC50 与 pIC50三个决定后面所有操作的名词这类药物优化建模题的核心数据就两块。一块是分子信息表一行是一个候选药物分子列是从分子结构算出来的各种数值属性统称分子描述符比如分子量、氢键供体数量、脂水分配系数、拓扑极性表面积等。另一块是活性标签文件给出部分分子的实验活性结论。描述符的列数决定了这道题的工作量几十列时线性模型也能打几百列时不筛选任何模型都会被噪音描述符拖累。IC50 是判断药物活性的常用指标表示抑制剂把某种酶的活性压到一半所需浓度。IC50 越小药效越强。麻烦的是数值跨度一组候选分子里最强的和最弱的可能差出好几个数量级。直接用 IC50 做回归目标平方误差会被几个大数值样本垄断模型把大量容量花在拟合大数值样本上小活性样本几乎不被照顾。把 IC50 取负对数得到 pIC50标签从偏态分布变成接近正态分布模型收敛和误差分布都会健康很多。我一般会把 pIC50 作为回归目标所有交叉验证评估都基于 pIC50最后提交时反算回 IC50 与题目口径对齐。如果题目明确要求预测 IC50也不要直接在原尺度上回归仍然按 pIC50 建模再还原数值稳定且便于诊断。这一步在药物活性预测题里几乎是通用预处理无论目标癌种是什么只要你看到浓度值跨数量级先取对数总是稳妥的。很多赛题还会附带分子的 SMILES 字符串本质是一种把分子结构编码成文本的方式。看到这个字段后不少队伍直接往图神经网络上引。这里要泼一盆冷水表格样本量如果只有几百条图神经网络很难学到可靠的结构-活性关系反而是表格建模更稳。SMILES 正确的用法是生成摩根指纹或做分子相似度特征补充不要一上来就换模型栈。读入数据还要确认两件事。第一标签是连续数值还是 0/1 类别。有的题给的是 IC50有的给的是有没有活性的二元标签前者走回归评价是 RMSE 和 R²后者走分类评价是 ROC-AUC 甚至 F1。在数据体检阶段就把标签语义确认清楚。第二那个看似没用的分子编号列不要急着删。药物实验里同一批次测得的活性值经常共享系统误差如果编号是按批次排的随机切分会让同批次分子同时出现在训练和验证两边分数虚高。遇到这种情况应该保留编号列专门用来按批次分组交叉验证走 GroupKFold而不是普通 KFold 一把梭。2.2 这类题目的小问结构先回归预测再做机理解释最后给候选清单这类药物建模题常拆成三个小问。第一问是活性预测在训练集上构建预测模型对预测集分子活性打分第二问是特征归因要求给出影响力最大的若干分子描述符并解释其与抗乳腺癌作用机理的关系第三问是候选药物筛选在给定分子池里找活性达标且具备成药性的候选物。三问共用同一个特征工程底座但产出物与评价方式差异很大。第一问是基本盘评价指标以 RMSE 和 R² 为主。这里的提升空间不在于换模型而在于验证协议是否严格。我的习惯是先固定一个随机种子做五折交叉验证把 RMSE 的波动范围摸清楚再去调参。没有基准线后面做的所有比较都是在一堆随机噪声里找规律。另一个容易被忽略的细节是训练集规模只有几百条时交叉验证折数不要贪多五折就够留出足够的训练量。第二问容易被误解为贴一张特征重要性图。实际上评委更愿意看到哪些描述符排名前列、它们对应的分子结构含义是什么、这个结论是否在多折上都稳定。树模型的特征重要性、SHAP 的全局摘要、LASSO 系数的正负号三份材料并排解释才有支撑。如果连续几折里某几个描述符都稳定排名靠前那才是真正值得写进论文的关键变量。第三问要克制预测分数高就往前推的冲动。如果题目只要求活性排序直接降序即可但常见的是还要考察类药性筛选。我的做法是先把预测活性整体输出再逐条过滤类药性条件最后在剩余集合里按预测 pIC50 排序每一步保留中间表。中间表既方便自查也是论文里筛选流程的素材。时间分配上可以按这个比例参考。环节时间占比原因数据理解与特征工程40%决定上限交叉验证与防泄漏20%决定可信度模型与调参30%逼近上限论文与图表10%决定呈现特征工程决定分数上限防泄漏决定结果可信度。多数翻车都发生在交叉验证与防泄漏这一块偏偏这一块又最容易被低估。拿到这类赛题解答包时我先看的不是最终 RMSE而是三件事特征选择发生在交叉验证内部还是外部、标签有没有取对数、标准化是整套数据 fit 还是每折单独 fit。这三件事只要有一件处理得不对最终分数再漂亮流程也无法迁移到下一批药物数据上。而流程可迁移才是这类题真正要训练的能力。3. 特征筛选这条路决定预测分数的上限3.1 先做数据体检缺失率、方差、相关性一轮代码筛掉一半特征拿到表格后不要急着进模型。先用一段很短但固定的代码把数据过一遍缺失率、常数列、高度相关列三个检查下来通常能筛掉一半特征。这是我做药物描述符数据的第一道工序也是后面所有操作的地基。import pandas as pd import numpy as np df pd.read_csv(train.csv) id_col molecule_id if id_col in df.columns: df df.drop(columns[id_col]) # 1. 缺失率超过 30% 的特征直接丢弃 miss_rate df.isna().mean() drop_cols miss_rate[miss_rate 0.3].index.tolist() df df.drop(columnsdrop_cols) # 2. 方差接近 0 的数值特征丢弃 num_cols [c for c in df.columns if df[c].dtype in (np.float64, np.int64)] var_series df[num_cols].var() const_cols var_series[var_series 1e-6].index.tolist() df df.drop(columnsconst_cols) # 3. 相关性超过 0.98 的特征对只留一个 corr df[num_cols].corr().abs() upper corr.where(np.triu(np.ones(corr.shape), k1).astype(bool)) high_corr [col for col in upper.columns if any(upper[col] 0.98)] df df.drop(columnshigh_corr) print(保留特征数:, df.shape[1]) print(被剔除的特征:, len(drop_cols) len(const_cols) len(high_corr))这段代码的逻辑是按信息量倒着筛。缺失率高的特征意味着大量分子的该项性质没有算出来后续用均值填充也只是编数据不如直接不要。方差过滤针对接近常数的列这些列没有区分度。相关性去重针对同一信息源的不同表达方式两个描述符相关系数超过 0.98保留哪一个对模型的影响都不大但少一列就少一分冗余。参数说明缺失率阈值 0.3 是我常用的保守线特征数量紧张的赛题可以放宽到 0.5但要人工确认被丢的列里没有核心性质。方差阈值 1e-6 只删几乎恒定的列不要设成 1e-3 这种级别否则把离散的 0/1 指示变量误删。相关性阈值 0.98 比 0.95 更严格保留的信息更完整代价是冗余列会多一点。实际比赛中我在这三步之后一般能保留原来五到六成的特征剩下的交给第二轮筛选。提示方差过滤和相关性过滤建议分开写别合并成一条后面排查哪列被删时能少走弯路。3.2 用 LASSO 和树模型做第二轮筛选固定一套候选特征集数据体检之后剩下的特征仍然偏多而且线性相关性和非线性交互混在一起。第二轮我习惯用两把刀同时上LASSO 做线性视角的稀疏选择随机森林做非线性重要性的粗筛最后取它们的并集作为候选特征集。from sklearn.linear_model import LassoCV from sklearn.ensemble import RandomForestRegressor from sklearn.preprocessing import StandardScaler X df.drop(columns[pIC50]) X X.select_dtypes(include[np.number]).fillna(X.median()) y df[pIC50] # LASSO线性稀疏选择 scaler StandardScaler() Xs scaler.fit_transform(X) lasso LassoCV(cv5, random_state42, max_iter20000) lasso.fit(Xs, y) lasso_selected X.columns[(lasso.coef_ ! 0)].tolist() # 随机森林非线性重要性粗筛 rf RandomForestRegressor(n_estimators300, random_state42, n_jobs-1) rf.fit(X, y) imp pd.Series(rf.feature_importances_, indexX.columns) rf_selected imp.sort_values(ascendingFalse).head(30).index.tolist() selected sorted(set(lasso_selected) | set(rf_selected)) print(LASSO 选中:, len(lasso_selected)) print(RF 选中:, len(rf_selected)) print(并集大小:, len(selected))逻辑说明LASSO 对线性相关的强特征倾向于只保留其中一个代表随机森林却会把相关性高的一簇特征分散给权重所以两边选出来的列表经常不完全重合。这种差异不是坏事它说明信息是冗余的取并集能提高容错率后面模型自己会再权衡。随机森林这里只取 top 30是因为它的重要性容易偏向取值基数大的连续特征排名靠后的项噪声居多不宜贪多。参数说明LassoCV 的 cv5 用自身内部的交叉验证确定正则强度max_iter20000 是为了保证高维数据下收敛标准量级是 1 万到 5 万。随机森林 n_estimators300 足够稳定再大收益有限反而拖时间。两处 random_state 都固定 42保证每次跑出来的候选集一致便于复现。注意这段代码把特征选择做在了整个数据集上严格来说会引入信息泄漏。它在入门阶段有助于快速看清流程但进入正式比赛或需要参赛提交时必须把特征选择放进每一折交叉验证内部。具体的修正版在第 5 章 5.1 节给出。3.3 给筛选后的特征做分布检查与标准化别让它悄悄泄漏确定候选特征集后检查每个特征的偏度。药物描述符里常见大部分样本集中在某小区间、少数样本拖着长尾的分布这种长尾进入线性模型会拉偏回归系数。对偏度大的列做一次 log1p 挪动把分布收紧。import scipy.stats as stats X_sel X[selected].copy() for col in X_sel.columns: s stats.skew(X_sel[col].dropna()) if abs(s) 1.0: X_sel[col] np.log1p(X_sel[col] - X_sel[col].min() 1e-3)这里的做法是只做分布变换不做标准化 fit。原因在于标准化需要记录均值和方差如果先把 scaler fit 到全量数据上后面折内部再做一次 fit两套均值方差会相互干扰。更干净的做法是分布变换可以一次性完成标准化必须留在每一折内部做。偏度阈值 1.0 是一个经验值超过它就值得做变换。变换时加的 1e-3 是防止出现 log(0)。需要注意的是如果某个特征的最小值离群很远直接用 min 做偏移会把大部分样本挤到很小范围这时可以改成用 5% 分位数做偏移底效果更稳。4. 活性预测建模回归为主、分类打辅助4.1 为什么优先选 LightGBM 和随机森林而不是线性回归药物描述符数据有三个特点维度高、特征之间强相关、活性与特征之间大多是复杂的非线性交互。比如某个描述符只有当另一个描述符落在某个区间时才显著影响活性这种交互用线性模型很难表达。随机森林和 LightGBM 作为树模型天然能捕捉这种分段关系又不需要对特征做太多假设。在树模型内部我更推荐以 LightGBM 作为主力随机森林作为基线。LightGBM 的直方图算法在几百维特征和千级样本上速度很快叶节点生长方式让它在小数据集上也有机会把精度推到更高但它更容易过拟合。随机森林的袋外机制反而稳定缺点是精度上限通常比调好的 boosting 低一些。模型优点主要风险使用建议线性回归/LASSO解释性强、训练快无法处理非线性交互做基线、特征选择随机森林抗过拟合、参数不敏感精度上限偏低卡分数下限、投票融合LightGBM/XGBoost精度高、训练快小样本容易过拟合主力模型需限制深度图神经网络能利用分子结构几百样本学不动不推荐作为主方案逻辑上要清楚一件事模型选择的顺序是先定数据规模再定模型复杂度。训练样本几百条、特征几百维树模型是合理的上限样本上万时再考虑更复杂的结构。不要为了炫技在几百条数据上赌深度学习结果通常是训练指标很好看测试集上完全失灵。4.2 一组直接可以套用的 5 折交叉验证 随机搜索调参代码我常用的调参方式是手写折循环配合随机试参原理和 RandomizedSearchCV 一样但能更好地控制每次跑的时间也方便在每一折里加特征选择或标准化。import lightgbm as lgb import numpy as np from sklearn.model_selection import KFold from sklearn.metrics import mean_squared_error, r2_score def lgb_cv(X, y, params, n_splits5, seed42): kf KFold(n_splitsn_splits, shuffleTrue, random_stateseed) oof np.zeros(len(y)) for tr_idx, va_idx in kf.split(X): X_tr, X_va X.iloc[tr_idx], X.iloc[va_idx] y_tr, y_va y.iloc[tr_idx], y.iloc[va_idx] model lgb.LGBMRegressor(**params) model.fit( X_tr, y_tr, eval_set[(X_va, y_va)], eval_metricrmse, callbacks[lgb.early_stopping(50), lgb.log_evaluation(0)] ) oof[va_idx] model.predict(X_va, num_iterationmodel.best_iteration_) rmse mean_squared_error(y, oof, squaredFalse) r2 r2_score(y, oof) return rmse, r2, oof params { n_estimators: 2000, learning_rate: 0.05, num_leaves: 31, min_child_samples: 20, colsample_bytree: 0.8, subsample: 0.9, random_state: 42, } rmse, r2, oof lgb_cv(X_sel, y, params) print(LightGBM 5折 RMSE:, round(rmse, 4)) print(LightGBM 5折 R²:, round(r2, 4))逻辑说明核心思路是每折内部独立训练、独立早停避免验证信息混入训练。early_stopping(50) 的意思是连续 50 轮验证集 RMSE 没有下降就停最终用 best_iteration_ 对应的迭代次数做预测。这样 n_estimators 不用精确调只要给一个足够大的值。oof 数组保存的是每个样本在没被训练过的那一折上的预测全量收集后就可以算真实的泛化误差。参数说明n_estimators2000 只是上限early stopping 会帮我们截断。learning_rate0.05 是精度和训练时间的折中点追求更高精度可以降到 0.01。num_leaves31 是 LightGBM 默认值在几百维特征上够用特征越多越要小心调小到 15 到 31。min_child_samples20 强制每个叶子至少有 20 个样本防止个别离群点撑起一个叶子。colsample_bytree0.8 控制每棵树随机抽 80% 的特征列这是对抗高维冗余的重要参数。subsample0.9 做行采样进一步降方差。参数建议范围说明learning_rate0.01~0.1越小越精细迭代次数相应增加num_leaves15~63超过 64 容易把噪声学进去min_child_samples5~30样本少时调大防过拟合colsample_bytree0.6~0.9每棵树随机抽特征列subsample0.8~0.95行采样比例调参节奏不要一口吃成胖子。先把 learning_rate 和 num_leaves 拉开跑一轮观察 RMSE 区间再固定它们去扫 min_child_samples、colsample_bytree、subsample最后把 learning_rate 调低、n_estimators 相应调大做一轮精修。每轮只动一两个参数记录五折 RMSE 的均值和方差而不是只看最好那一折。4.3 用 RMSE、R² 和残差图判断模型是否真的能用于预测调参结束时只看 RMSE 是不够的。我会把每折 oof 和真实值之间的残差画一张图横轴是真实 pIC50纵轴是预测减去真实值。健康的残差应该随机分布在 0 轴两侧不随横轴变化出现喇叭形或弯曲。如果残差随真实值变大而明显发散说明模型在低活性区域系统性失灵这时要对标签做进一步变换或者在训练时给样本加权。import matplotlib.pyplot as plt residual oof - y.values plt.figure(figsize(8, 5)) plt.scatter(y, residual, alpha0.5, s20) plt.axhline(y0, colorred, linestyle--) plt.xlabel(True pIC50) plt.ylabel(Residual (pred - true)) plt.title(Residual Distribution) plt.show()另一个判断标准是 oof 的 RMSE 和单折 RMSE 的差距。如果两者相差悬殊说明某几折的运气成分大训练集划分不均匀。此时回到数据层面检查分子编号是否按某种实验批次排序如有KFold 的 shuffle 可能不够要改成 GroupKFold 按批次切分。这个细节在药物活性数据里很常见因为同一批次的分子测得的活性往往带有一致的实验误差。提示R² 高并不代表模型好要看它是不是建立在对验证集的偷看之上。所有评估都绑定在 oof 上不要单独挑某一折的分数写进报告。5. 建模避坑抗乳腺癌药物预测的 5 个高频翻车点5.1 特征选择跑全量数据验证集信息提前抄答案现象五折交叉验证的 RMSE 异常低R² 接近 0.9但一上测试集分数崩掉一大截。原因在跑交叉验证之前先用 LASSO 或随机森林在全量数据上筛过特征。筛选过程本身看过全部样本的标签等于把验证集的信息写进了特征集后面无论怎么折验证集都已经被污染。这是这类题最隐蔽也是最高频的翻车点。解决把特征选择放进每一折内部只在训练部分做筛选然后对验证部分应用同样的列选择。代码可以封装成一个 Pipelinefrom sklearn.pipeline import Pipeline from sklearn.feature_selection import SelectFromModel from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LassoCV def select_in_fold(X_tr, y_tr): pipe Pipeline([ (scale, StandardScaler()), (select, SelectFromModel( LassoCV(cv3, random_state0, max_iter20000), threshold1e-6 )) ]) pipe.fit(X_tr, y_tr) mask pipe.named_steps[select].get_support() return X_tr.columns[mask]注意这里 LassoCV 内部还有一层 cv3叠加外层 5 折后计算量会上升但对于几百维特征完全可以接受。threshold1e-6 让系数绝对值小于 1e-6 的特征被剔除比直接用 coef_ ! 0 更稳健。缺失填充同样要放进折内用训练部分的统计量填充验证部分不能在整套数据上先 fillna 再切分。5.2 IC50 不取对数直接回归模型全在为少数大数值打工现象RMSE 大得离谱预测值几乎全部落在样本均值附近高活性小数值样本的误差被完全忽视。原因IC50 的数值跨度可能是 0.001 到 1000平方误差下个别 1000 级别的样本贡献了绝大部分损失模型把所有能力用来压低这个大数值样本的误差。小数值样本虽然活性强、更有生物学价值但在损失函数里完全没有话语权。解决回归目标统一使用 pIC50并且把极小值做 clip防止取对数时出现无穷大df[IC50_clipped] df[IC50].clip(lower1e-6) df[pIC50] -np.log10(df[IC50_clipped])clip 保底 1e-6对应 pIC50 上限 6符合这批实验数据的量级。如果实际数据里有小于 1e-6 的 IC50说明测量已经接近仪器极限clip 掉也不会造成信息损失。预测完成后再把 pIC50 反算回 IC50 提交口径就对齐了。5.3 标准化写在整套数据上交叉验证形同虚设现象每一折的验证分数都异常好而且折与折之间波动极小换随机种子结果也没变化。原因StandardScaler 在进入交叉验证前就对全量数据 fit 了均值和方差里包含了验证集的信息等价于验证集仍被偷看。这个问题比特征选择泄漏更隐蔽很多人从头到尾都没意识到。解决把 StandardScaler 放进折内训练部分 fit_transform验证部分只 transformfor tr_idx, va_idx in kf.split(X_sel): scaler StandardScaler().fit(X_sel.iloc[tr_idx]) X_tr scaler.transform(X_sel.iloc[tr_idx]) X_va scaler.transform(X_sel.iloc[va_idx]) # 后续训练照常这是三处泄漏里最简单的一处也是很多人调参调到最后才意识到的一处。代码里只要把 scaler 挪进循环问题立解。它和 5.1 的本质一样所有需要看数据的步骤都必须只看训练折。5.4 高维数据开默认参数要么过拟合要么训练时间爆表现象LightGBM 默认参数下训练日志几百轮看不到收敛验证 RMSE 不降反升训练时间比预期翻了好几倍。原因特征维度高且样本有限时默认 num_leaves31 给了树模型过大的表达空间叶子越深越容易记住噪音。max_iter 又设得很大时间全耗在无效迭代上。解决先限制复杂度再谈精度。把 num_leaves 降到 15 到 20min_child_samples 提到 20 以上colsample_bytree 降到 0.7learning_rate 用 0.05 先跑通。跑通后再按第 4 章的顺序逐参数放开。一个可以直接试的保守组合learning_rate0.05num_leaves15min_child_samples30colsample_bytree0.7subsample0.85。5.5 只盯 RMSE预测结果出现没有生物学意义的数值现象预测 pIC50 出现负值或者反算回 IC50 后出现 10 的几百次方这种离谱值。原因回归模型输出无界训练样本的标签范围只覆盖了一个有限区间模型在特征组合外推时可能跑到区间外。解决输出阶段做边界压缩对 pIC50 做 clip再反算 IC50pred_pic50 np.clip(model.predict(X_test), -1.0, 10.0) pred_ic50 10 ** (-pred_pic50)负的 pIC50 对应 IC50 大于 10基本等于没有活性clip 到 -1 不影响排序上限 10 对应 IC50 小于 0.1 纳摩级超出这批数据观测范围截断是合理的。这样处理既保住排序逻辑也让最终答案在生物学上可解释。这五条翻车点里前三条本质都是数据泄漏只是泄漏的位置不同后两条是数值处理和模型边界问题。把前三条修干净模型的泛化能力才能真正经得起测试集检验。6. 把建模结果变成候选药物推荐融合、可解释与多目标打分前五章把活性预测做到可信之后剩下的是让结果往回答题目靠拢。我自己常用的三件套是模型融合、SHAP 可解释性、多目标筛选。这三步分别对应第三问的准确率、第二问的解释材料和最后的候选名单。模型融合最简单的可靠方案是加权平均不折腾 Stackingpred_final 0.5 * pred_lgb 0.3 * pred_rf 0.2 * pred_xgb权重分配到主力模型上两个辅助模型负责兜底。如果非要用 Stacking一定要记住 meta 模型的训练数据必须是 OOF 预测而且 meta 训练本身也要再走一遍交叉验证否则叠加后的信息又会被折进去一次。加权平均没有这个隐患性价比高。可解释性用 SHAP 修正树模型特征重要性对高基数特征的偏置。正式算 SHAP 前建议在全量训练集上重新拟合一次最终模型让树结构用上全部数据import shap explainer shap.TreeExplainer(final_lgb) shap_values explainer.shap_values(X_test) shap.summary_plot(shap_values, X_test)summary_plot 呈现每个特征对预测值的正负贡献方向。写机理分析时把 SHAP 排第一位的特征和 LASSO 系数中符号明确的特征对应起来解释就既有方向也有量级。候选物推荐流程上我习惯留四步中间表预测所有候选分子的 pIC50保留排序按类药性规则过滤分子量小于 500、氢键供体不超过 5、氢键受体不超过 10、脂水分配系数在 -2 到 5 之间如题目给了安全性标签再叠加一个二分类模型的预测结果过滤对剩余分子按 pIC50 降序取前 20 个作为推荐名单。第 2 步和第 1 步的顺序不要反。先把活性排序再过滤成药性能避免过滤后可选分子太少这种尴尬反过来先做硬性过滤可能把评分最高的一批分子误伤。我自己的习惯是每完成一道这类药物优化赛题就把数据体检→折内特征选择→模型评估→候选筛选这条链固化成一批标准函数。下一次再遇到类似的题哪怕癌种变了列名变了整个流程也只是换数据文件的事。这套流程比任何一次比赛的最终 RMSE 都值钱。希望帮到你。本文还有配套的精品资源点击获取