4、网格搜索(Grid Search):原理、优缺点、在药物模型中的应用

网格搜索,说白了就是「暴力枚举」。你给它一个参数列表,它就把所有组合挨个试一遍。我刚开始做超参数调优那会儿,觉得这方法太笨了,直到有一次在分子活性预测模型上,随机搜索怎么都找不到最优解,网格搜索一把就给我干到了 0.89 的 AUC,嗯,从那以后我再也不敢小看它了。

4.1 网格搜索的核心原理

它的逻辑特别简单:你预先设定好每个超参数的候选值,然后生成这些值的笛卡尔积。对每一组参数组合,训练一次模型,评估性能,最后选出表现最好的那一组。

举个例子,假设你要调两个参数:learning_rate 和 n_estimators。

  • learning_rate 候选值:[0.01, 0.1, 0.5]
  • n_estimators 候选值:[50, 100, 200]

那么网格搜索会生成 3 × 3 = 9 组组合。每组都跑一遍,最后告诉你哪组最好。你想想看,如果参数多了,候选值又细,这个组合数会膨胀得非常快。

from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestRegressor

param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [10, 20, None],
    'min_samples_split': [2, 5, 10]
}

# 我在项目中习惯用 5 折交叉验证,这样评估更稳
grid_search = GridSearchCV(
    estimator=RandomForestRegressor(),
    param_grid=param_grid,
    cv=5,
    scoring='r2',
    n_jobs=-1,
    verbose=1
)

grid_search.fit(X_train, y_train)

print("Best params:", grid_search.best_params_)
print("Best score:", grid_search.best_score_)

我个人习惯把 verbose 设为 1,这样能看到进度条。跑大网格的时候,心里有底。

4.2 参数空间的定义技巧

参数空间怎么定义,直接影响搜索效果。我早期踩过一个坑:把所有参数都塞进网格,结果组合数爆炸,跑了三天三夜还没跑完。

后来我总结出几条经验:

  • 先粗后细:第一轮用大步长扫一遍,锁定好区域,第二轮再精细搜索。
  • 区分参数类型:离散型参数(如树的数量)适合网格,连续型参数(如学习率)更适合用对数尺度。
  • 控制候选值数量:每个参数别超过 5-6 个候选值,否则组合数会失控。
参数类型 示例 候选值设计建议
离散型 n_estimators, max_depth 均匀间隔,如 [50, 100, 150, 200]
连续型 learning_rate, gamma 对数尺度,如 [0.001, 0.01, 0.1]
布尔型 bootstrap, use_gpu 直接枚举 True/False
类别型 kernel, solver 列出所有可能取值
小技巧:在药物模型中,我经常遇到样本量小但特征多的情况。这时候网格搜索的交叉验证轮数不要设太高,否则每个组合都跑 10 折,时间全浪费在验证上了。5 折通常是个不错的折中。

4.3 网格搜索的优缺点分析

咱们不绕弯子,直接说优缺点。我做决策的时候喜欢列一张表,一目了然。

优点 缺点
实现简单,几乎零门槛 维度灾难:参数每多一个,组合数翻几倍
结果可解释,知道每组的性能 计算成本高,不适合大规模调优
适合参数间有交互的场景 需要人工预设候选值,依赖先验知识
并行化容易(n_jobs=-1 搞定) 连续型参数只能用离散近似,可能漏掉最优

为什么会这样?说白了就是「穷举」的代价。你想想看,如果我要调 5 个参数,每个给 10 个候选值,那就是 10 万种组合。跑一次模型要 1 分钟,10 万分钟就是 69 天。这个账一算,你就知道网格搜索不是万能的了。

核心判断标准:当参数总数 ≤ 4,且每个参数的候选值 ≤ 5 时,网格搜索是最稳妥的选择。超过这个范围,建议改用随机搜索或贝叶斯优化。

4.4 在药物模型中的应用场景

网格搜索在药物研发中特别常用在以下几个地方:

4.4.1 分子性质预测(QSAR/QSPR)

这类模型通常用随机森林或 XGBoost,参数不多。我在做 CYP450 抑制活性预测时,就用网格搜索调了三个参数:

param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [5, 10, 15],
    'learning_rate': [0.05, 0.1, 0.2]
}

# 3 × 3 × 3 = 27 组,跑起来很快
# 我在自己机器上只花了 40 分钟就出结果了

4.4.2 药物-靶标相互作用预测

这类任务常用图神经网络或矩阵分解模型。有一次我帮团队调一个 DeepDTA 模型的超参数,网格搜索帮我找到了最优的嵌入维度和卷积核大小组合。

param_grid = {
    'embedding_dim': [64, 128, 256],
    'kernel_size': [3, 5, 7],
    'dropout': [0.2, 0.3, 0.5]
}

# 我建议先把 dropout 固定到 0.3,调完前两个参数再细搜 dropout

4.4.3 药物 ADMET 预测

ADMET(吸收、分布、代谢、排泄、毒性)预测是药物研发的关键环节。我遇到过一个小样本数据集,只有 200 个分子,但网格搜索仍然有效——因为参数空间控制得小。

from sklearn.svm import SVR

param_grid = {
    'C': [0.1, 1, 10],
    'gamma': ['scale', 'auto'],
    'kernel': ['rbf', 'linear']
}

# 2 × 2 × 3 = 12 组,小样本下几分钟跑完

4.5 避坑指南

我曾经犯过的错:有一次做溶解度预测,我把所有参数都加了网格,包括 subsample, colsample_bytree, min_child_weight 等,组合数直接飙到 648 组。跑了 8 个小时,结果发现最优参数就在一个很窄的区间里。从那以后,我学会了先做一次随机搜索,缩小范围,再用网格精调。

几点具体建议:

  • 别一次性全调:先调最重要的 2-3 个参数,其他用默认值。
  • 注意资源规划:先算一下总组合数 × 单次训练时间 × 交叉验证折数,如果超过 8 小时,果断换方法。
  • 别忽略早停:如果模型本身支持早停(如 XGBoost 的 early_stopping_rounds),一定要开,可以节省大量时间。

4.6 网格搜索的完整工作流程

下面这张图展示了我做网格搜索的完整流程,你可以把它当作自己的操作手册:

网格搜索完整工作流程 步骤1: 定义参数空间 步骤2: 生成所有组合(笛卡尔积) 步骤3: 对每组进行交叉验证评估 遍历所有组合 步骤4: 比较所有组合的性能 步骤5: 输出最优参数组合 步骤6: 用最优参数在全量数据上训练 注意:训练/验证集 不要混用

4.7 什么时候该用网格搜索

我个人的决策流程是这样的:

  1. 先估算一下组合数。如果 ≤ 50 组,直接上网格搜索。
  2. 如果组合数在 50-200 之间,考虑减少候选值或者先做一次随机搜索定位。
  3. 如果组合数 > 200,果断换随机搜索或贝叶斯优化。

嗯,这里要注意:在药物模型中,很多场景的数据量不大(几百到几千个分子),模型本身也不复杂(随机森林、SVM、浅层神经网络)。这种情况下,网格搜索反而是最省心的选择。你想想看,花一个小时跑完,拿到最优参数,比花半小时折腾随机搜索的候选方案更靠谱。