今天给大家分享机器学习中的一个关键概念,超参数调优

在了解什么是超参数调优之前,我们需要先区分一下什么是参数和超参数。

  • 参数:是模型在训练过程中从数据中学习和优化得到的变量。例如,线性回归中的权重  和偏置 ,神经网络中的层间权重和偏置。

  • 超参数:是在模型开始训练之前,需要人为设定的变量,它们控制着模型的结构和训练过程。超参数的值不会在训练过程中自动学习。例如,学习率、正则化系数、神经网络的层数与节点数。

程序员学长

关注推荐系统、计算广告、大数据技术领域,专注个人能力提升

662篇原创内容

公众号

超参数调优,又称超参数优化,是机器学习领域一个至关重要的环节。

它指的是选择一组最优的超参数,使得机器学习模型在验证集上的性能指标(如准确率、F1分数、均方误差等)达到最佳的过程。

为什么需要超参数调优

  • 模型性能的关键

    超参数对模型的性能有着至关重要的影响。不同的超参数组合可以导致模型从欠拟合(Underfitting)到过拟合(Overfitting)的巨大差异。

  • 训练效率:某些超参数(如学习率、批次大小)直接影响训练的速度和收敛性。

  • 泛化能力:调优有助于平衡模型的偏差(Bias)和方差(Variance),找到一个既能很好地拟合训练数据,又具有强大泛化能力(在未见过的数据上表现良好)的模型。

常见的超参数调优方法

1.网格搜索

网格搜索是最简单、最基础的超参数调优方法。它预先为每个待调优的超参数设定一个有限的、离散的取值集合。

然后,它会穷举所有这些超参数取值集合的笛卡尔积,生成所有可能的超参数组合。

对于每一种组合,模型都会用交叉验证进行训练和评估。最终,选择性能最佳的那组超参数。

步骤

  1. 定义每个超参数的取值列表。

  2. 生成所有可能的组合(形成一个“网格”)。

  3. 遍历网格中的每一点

    • 使用该点对应的超参数组合训练模型。

    • 在验证集上或通过交叉验证评估模型性能。

  4. 返回性能最佳的超参数组合。

优点

  • 简单易懂,易于实现。

  • 如果最优解位于网格点上,一定能找到。

缺点

  • 计算开销巨大:随着超参数数量(维度)和每个参数取值数量的增加,搜索空间呈指数级增长,导致训练时间过长。

  • 效率低下:它在每个维度上都是“均匀”地搜索,很多计算资源可能浪费在模型性能很差的区域。如果最优值落在两个网格点之间,它也无法找到。

import numpy as np
from scipy.stats import randint, uniform
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV, train_test_split
from sklearn.metrics import accuracy_score

# 1. 加载数据
iris = load_iris()
X, y = iris.data, iris.target

# 2. 划分训练集和测试集 (用于最终评估,调优过程使用交叉验证)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 3. 定义基础模型
model = RandomForestClassifier(random_state=42)

# 4. 交叉验证折数
cv_folds = 5

print("--- 1. 网格搜索 (Grid Search) 示例 ---")

# 定义超参数网格
# n_estimators: 树的数量
# max_depth: 树的最大深度
param_grid_gs = {
    'n_estimators': [50, 100, 200],  # 3个取值
    'max_depth': [5, 10, None],      # 3个取值
    'criterion': ['gini', 'entropy'] # 2个取值
}

# 总组合数: 3 * 3 * 2 = 18 种组合

# 初始化 GridSearchCV
grid_search = GridSearchCV(
    estimator=model,          # 待优化的模型
    param_grid=param_grid_gs, # 超参数网格
    scoring='accuracy',       # 评估指标
    cv=cv_folds,              # 交叉验证折数
    verbose=1,                # 详细输出
    n_jobs=-1                 # 使用所有核心进行并行计算
)

# 开始训练和搜索
grid_search.fit(X_train, y_train)

# 输出结果
print("\n网格搜索最优参数:", grid_search.best_params_)
print("网格搜索最佳交叉验证分数: {:.4f}".format(grid_search.best_score_))

# 使用最优参数在测试集上评估
best_model_gs = grid_search.best_estimator_
y_pred_gs = best_model_gs.predict(X_test)
print("测试集准确率: {:.4f}".format(accuracy_score(y_test, y_pred_gs)))
2.随机搜索

随机搜索不像网格搜索那样对所有组合进行遍历。它为每个超参数定义一个连续或离散的取值范围(分布)。

然后,在给定的预算(如迭代次数或总时间)内,随机采样超参数组合,并评估模型性能。

为什么有效

有研究表明,在许多情况下,少数几个超参数对最终性能的影响远大于其他超参数。

随机搜索倾向于更密集地探索少数重要超参数的不同值,而不是均匀地探索整个高维空间。

这意味着在相同的计算预算下,随机搜索很大概率比网格搜索找到更好的结果。

步骤

  1. 定义每个超参数的搜索范围(可以是离散集合或连续分布)。

  2. 设定总共尝试的次数 。

  3. 重复  次

    • 从每个超参数的分布中随机采样,形成一组超参数组合。

    • 使用该组合训练和评估模型。

  4. 返回性能最佳的超参数组合。

优点

  • 效率更高:相比网格搜索,随机搜索在大多数情况下能更快地找到接近最优的解。

  • 易于实现:同样简单,且只需设置一个总次数 ,不依赖于参数数量。

缺点

  • 无法保证最优:由于是随机采样,它不能保证找到全局最优解。

print("\n--- 2. 随机搜索 (Random Search) 示例 ---")

# 定义超参数分布
# n_estimators: 在 50 到 250 之间随机取整数
# max_depth: 在 3 到 20 之间随机取整数 (或 None)
# min_samples_split: 在 2 到 11 之间随机取整数

param_dist_rs = {
    'n_estimators': randint(50, 250),           
    'max_depth': randint(3, 20),
    'min_samples_split': randint(2, 11),
    'max_features': uniform(0.5, 0.5) # 在 0.5 到 0.5+0.5=1.0 之间随机取浮点数
}

# 设定采样的组合数量
n_iter_search = 20

# 初始化 RandomizedSearchCV
random_search = RandomizedSearchCV(
    estimator=model,
    param_distributions=param_dist_rs, # 超参数分布
    n_iter=n_iter_search,              # 采样的组合数
    scoring='accuracy',
    cv=cv_folds,
    verbose=1,
    random_state=42,                   # 保证结果可复现
    n_jobs=-1
)

# 开始训练和搜索
random_search.fit(X_train, y_train)

# 输出结果
print("\n随机搜索最优参数:", random_search.best_params_)
print("随机搜索最佳交叉验证分数: {:.4f}".format(random_search.best_score_))

# 使用最优参数在测试集上评估
best_model_rs = random_search.best_estimator_
y_pred_rs = best_model_rs.predict(X_test)
print("测试集准确率: {:.4f}".format(accuracy_score(y_test, y_pred_rs)))
3.贝叶斯优化

贝叶斯优化是一种基于模型的优化方法,它旨在用尽可能少的评估次数找到全局最优解。

它通过构建一个代理模型来近似目标函数 ,并利用采集函数指导下一次采样,使搜索更加智能。

  1. 代理模型

    根据已有的历史评估结果(超参数和对应性能),用于拟合超参数配置与模型性能之间的未知函数关系。

    常用的代理模型是高斯过程,它能提供关于预测值的不确定性估计。

  2. 采集函数

    基于代理模型的预测,它决定了下一次应该评估哪个超参数组合。

    采集函数的目标是平衡探索(在不确定性高的区域采样)和利用(在预测性能好的区域采样)。

    常见的采集函数有:预期提升、概率提升和上下置信界等。

步骤

  1. 初始采样:随机选择若干组超参数 ,计算其性能 ;

  2. 建立代理模型:利用已观测数据  拟合一个概率模型(如高斯过程);

  3. 计算采集函数:找到使采集函数最大化的超参数组合 。

  4. 评估:在  上训练模型并获得真实的性能 。

  5. 更新: 将  加入历史评估结果集。

  6. 重复步骤 2-5 直到达到停止条件。

优点

  • 效率极高:尤其适用于评估成本高昂(训练时间长)的复杂模型。它倾向于用更少的迭代次数找到最优解。

  • 智能搜索:能够根据历史信息进行有目的地搜索。

缺点

  • 实现复杂:比网格搜索和随机搜索复杂得多,理解和实现贝叶斯优化更为复杂。

from bayes_opt import BayesianOptimization
# 1. 定义目标函数:将超参数配置转换为模型性能指标
def rf_cv_score(n_estimators, max_depth, min_samples_split):
    """
    贝叶斯优化将尝试最大化此函数的返回值。
    注意:超参数必须以浮点数形式传递,需要在函数内部转换为整数(如果需要)。
    """
    
    # 转换超参数类型
    n_estimators = int(n_estimators)
    max_depth = int(max_depth)
    min_samples_split = int(min_samples_split)
    
    # 定义模型
    model_bo = RandomForestClassifier(
        n_estimators=n_estimators,
        max_depth=max_depth,
        min_samples_split=min_samples_split,
        random_state=42,
        n_jobs=-1
    )
    
    # 执行交叉验证
    from sklearn.model_selection import cross_val_score
    score = cross_val_score(model_bo, X_train, y_train, cv=cv_folds, scoring='accuracy').mean()
    
    return score

# 2. 定义超参数搜索范围 (pbounds: Parameter Bounds)
# n_estimators: [50, 250]
# max_depth: [3, 20]
# min_samples_split: [2, 11]
pbounds = {
    'n_estimators': (50, 250), 
    'max_depth': (3, 20),
    'min_samples_split': (2, 11),
}

# 3. 初始化贝叶斯优化器
optimizer = BayesianOptimization(
    f=rf_cv_score, # 目标函数
    pbounds=pbounds, # 搜索范围
    random_state=42,
    verbose=0        # 设置为 0 则不打印每次迭代的详细信息
)

# 4. 开始优化
# init_points: 初始随机探索的次数
# n_iter: 贝叶斯优化迭代的次数 (利用代理模型进行智能探索)
optimizer.maximize(
    init_points=5,
    n_iter=15 
)

# 输出结果
print("\n贝叶斯优化最优参数:", optimizer.max['params'])
print("贝叶斯优化最佳交叉验证分数: {:.4f}".format(optimizer.max['target']))

# 提取最优参数并四舍五入
best_params_bo = {k: int(v) if k in ['n_estimators', 'max_depth', 'min_samples_split'] else v 
                  for k, v in optimizer.max['params'].items()}

# 使用最优参数在测试集上评估
best_model_bo = RandomForestClassifier(**best_params_bo, random_state=42)
best_model_bo.fit(X_train, y_train)
y_pred_bo = best_model_bo.predict(X_test)
print("测试集准确率: {:.4f}".format(accuracy_score(y_test, y_pred_bo)))

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐