如何配置算法
此页面描述了 Katib 支持的超参数 (HP) 调优算法以及如何配置它们。
HP 调优算法
Katib 目前支持多种用于 NAS 的搜索算法
网格搜索
Katib 中的算法名称为 grid
。
当所有变量都是离散的(而不是连续的)且可能性数量较低时,网格采样非常有用。网格搜索对所有可能性进行穷举组合搜索,这使得搜索过程即使对于中等规模的问题也非常漫长。
Katib 使用 Optuna 优化框架进行网格搜索。
设置名称 | 描述 | 示例 |
---|---|---|
random_state | [int]:将 random_state 设置为非 None 值以获得可重现的结果。 | 10 |
随机搜索
Katib 中的算法名称为 random
。
随机采样是网格搜索的替代方法,当要优化的离散变量数量很大且每次评估所需时间很长时使用。当所有参数都是离散的时,随机搜索执行不放回采样。因此,当无法进行组合探索时,随机搜索是最好的算法。如果连续变量数量很高,则应改用拟随机采样。
Katib 使用 Hyperopt、Goptuna 或 Optuna 优化框架进行随机搜索。
Katib 支持以下算法设置
设置名称 | 描述 | 示例 |
---|---|---|
random_state | [int]:将 random_state 设置为非 None 值以获得可重现的结果。 | 10 |
贝叶斯优化
Katib 中的算法名称为 bayesianoptimization
。
>贝叶斯优化方法使用高斯过程回归对搜索空间进行建模。该技术计算搜索空间中每个点的损失函数估计值及其不确定性。当搜索空间的维度数量较低时,该方法非常适用。由于该方法同时对预期损失和不确定性进行建模,因此搜索算法在几个步骤内即可收敛,这使得它成为评估参数配置所需时间较长时的良好选择。
Katib 使用 Scikit-Optimize 优化框架进行贝叶斯搜索。Scikit-Optimize 也称为 skopt
。
Katib 支持以下算法设置
设置名称 | 描述 | 示例 |
---|---|---|
base_estimator | [“GP”, “RF”, “ET”, “GBRT” 或 sklearn 回归器,默认值=“GP”]:应继承自 sklearn.base.RegressorMixin 。predict 方法应有一个可选的 return_std 参数,该参数与 E[Y | x] 一起返回 std(Y | x) 。如果 base_estimator 是 [“GP”, “RF”, “ET”, “GBRT”] 之一,系统将使用相应类型的默认代理模型。在 skopt 文档中了解更多信息。 | GP |
n_initial_points | [int, 默认值=10]:在使用 base_estimator 进行近似之前,使用初始化点评估 func 的次数。以 x0 形式提供的点计为初始化点。如果 len(x0) < n_initial_points ,系统将随机采样额外的点。在 skopt 文档中了解更多信息。 | 10 |
acq_func | [string, 默认值="gp_hedge" ]:在后验分布上最小化的函数。在 skopt 文档中了解更多信息。 | gp_hedge |
acq_optimizer | [string, “sampling” 或 “lbfgs”, 默认值=“auto”]:最小化采集函数的方法。系统使用通过 acq_optimizer 优化 acq_func 获得的最佳值来更新拟合模型。在 skopt 文档中了解更多信息。 | auto |
random_state | [int]:将 random_state 设置为非 None 值以获得可重现的结果。 | 10 |
Hyperband
Katib 中的算法名称为 hyperband
。
Katib 支持 Hyperband 优化框架。Hyperband 没有使用贝叶斯优化来选择配置,而是专注于早期停止作为优化资源分配的策略,从而最大化其可以评估的配置数量。Hyperband 还注重搜索速度。
Parzen 窗估计器树 (TPE)
Katib 中的算法名称为 tpe
。
Katib 使用 Hyperopt、Goptuna 或 Optuna 优化框架进行 TPE 搜索。
此方法提供了一种基于前向和后向梯度的搜索。
Katib 支持以下算法设置
设置名称 | 描述 | 示例 |
---|---|---|
n_EI_candidates | [int]:用于计算预期改进的候选样本数量。 | 25 |
random_state | [int]:将 random_state 设置为非 None 值以获得可重现的结果。 | 10 |
gamma | [float]:l(x) 和 g(x) 之间的分割阈值,请查看这篇论文中的公式 2。值必须在 (0, 1) 范围内。 | 0.25 |
prior_weight | [float]:计数的平滑因子,以避免出现概率为 0 的情况。值必须 > 0。 | 1.1 |
多元 TPE
Katib 中的算法名称为 multivariate-tpe
。
Katib 使用 Optuna 优化框架进行多元 TPE 搜索。
多元 TPE 是独立(默认)TPE 的改进版本。此方法在搜索空间中找到超参数之间的依赖关系。
Katib 支持以下算法设置
设置名称 | 描述 | 示例 |
---|---|---|
n_ei_candidates | [int]:用于计算预期改进的 Trial 数量。 | 25 |
random_state | [int]:将 random_state 设置为非 None 值以获得可重现的结果。 | 10 |
n_startup_trials | [int]:随机搜索算法生成超参数的初始 Trial 数量。 | 5 |
协方差矩阵自适应进化策略 (CMA-ES)
Katib 中的算法名称为 cmaes
。
Katib 使用 Goptuna 或 Optuna 优化框架进行 CMA-ES 搜索。
>协方差矩阵自适应进化策略是一种用于连续搜索空间优化问题的随机无导数数值优化算法。您还可以使用 IPOP-CMA-ES 和 BIPOP-CMA-ES,这些是当优化收敛到局部最小值时用于重新启动的变体算法。
Katib 支持以下算法设置
设置名称 | 描述 | 示例 |
---|---|---|
random_state | [int]:将 random_state 设置为非 None 值以获得可重现的结果。 | 10 |
sigma | [float]:CMA-ES 的初始标准差。 | 0.001 |
restart_strategy | [string, "none", "ipop", 或 "bipop", 默认值="none"]:当 CMA-ES 优化收敛到局部最小值时用于重新启动的策略。 | "ipop" |
Sobol 拟随机序列
Katib 中的算法名称为 sobol
。
Katib 使用 Goptuna 优化框架进行 Sobol 拟随机搜索。
>Sobol 拟随机序列是一种低差异序列。众所周知,Sobol 拟随机序列可以提供更好的均匀性。
基于种群的训练
Katib 中的算法名称为 pbt
。
请查阅基于种群的训练论文,了解有关该算法的更多详细信息。
PBT 服务需要具有 RWX 访问模式的持久卷声明 (Persistent Volume Claim),以便在 Suggestion 和 Trials 之间共享资源。目前,Katib Experiment 必须具有 resumePolicy: FromVolume
才能运行 PBT 算法。在此指南中了解有关恢复策略的更多信息。
Katib 支持以下算法设置
设置名称 | 描述 | 示例 |
---|---|---|
suggestion_trial_dir | 保存检查点的 Trial 容器内的位置 | /var/log/katib/checkpoints/ |
n_population | 每代 Trial seed 的数量 | 40 |
resample_probability | null(默认):将超参数扰动 0.8 或 1.2 倍。0-1:按指定概率对原始分布进行重采样 | 0.3 |
truncation_threshold | 用于修剪低性能 seed 的利用阈值 | 0.4 |
在 Katib 中使用自定义算法
您可以自己将 HP 调优算法添加到 Katib 中。Katib 的设计遵循 ask-and-tell
模式
- 请求一组新参数
- 转到 Experiment 并编程新参数
- 观察运行 Experiment 的结果
- 回到您的笔记本电脑并告诉优化器结果 1. 转到步骤 1
创建 Experiment 后,将创建一个作为 Kubernetes Deployment 的算法服务。然后 Katib 通过 GetSuggestions
gRPC 调用请求一组新参数。之后,Katib 根据这些集合创建新的 Trials 并观察结果。当 Trials 完成后,Katib 将完成的 Trials 的指标告知算法,并请求另一组新参数。
创建一个新的算法服务
新算法需要实现 api.proto 中定义的 Suggestion 服务。
一个示例算法如下所示
from pkg.apis.manager.v1beta1.python import api_pb2
from pkg.apis.manager.v1beta1.python import api_pb2_grpc
from pkg.suggestion.v1beta1.internal.search_space import HyperParameter, HyperParameterSearchSpace
from pkg.suggestion.v1beta1.internal.trial import Trial, Assignment
from pkg.suggestion.v1beta1.hyperopt.base_service import BaseHyperoptService
from pkg.suggestion.v1beta1.internal.base_health_service import HealthServicer
# Inherit SuggestionServicer and implement GetSuggestions.
class HyperoptService(
api_pb2_grpc.SuggestionServicer, HealthServicer):
def ValidateAlgorithmSettings(self, request, context):
# Optional, it is used to validate algorithm settings defined by users.
pass
def GetSuggestions(self, request, context):
# Convert the Experiment in GRPC request to the search space.
# search_space example:
# HyperParameterSearchSpace(
# goal: MAXIMIZE,
# params: [HyperParameter(name: param-1, type: INTEGER, min: 1, max: 5, step: 0),
# HyperParameter(name: param-2, type: CATEGORICAL, list: cat1, cat2, cat3),
# HyperParameter(name: param-3, type: DISCRETE, list: 3, 2, 6),
# HyperParameter(name: param-4, type: DOUBLE, min: 1, max: 5, step: )]
# )
search_space = HyperParameterSearchSpace.convert(request.experiment)
# Convert the Trials in GRPC request to the Trials in algorithm side.
# Trials example:
# [Trial(
# assignment: [Assignment(name=param-1, value=2),
# Assignment(name=param-2, value=cat1),
# Assignment(name=param-3, value=2),
# Assignment(name=param-4, value=3.44)],
# target_metric: Metric(name="metric-2" value="5643"),
# additional_metrics: [Metric(name=metric-1, value=435),
# Metric(name=metric-3, value=5643)],
# Trial(
# assignment: [Assignment(name=param-1, value=3),
# Assignment(name=param-2, value=cat2),
# Assignment(name=param-3, value=6),
# Assignment(name=param-4, value=4.44)],
# target_metric: Metric(name="metric-2" value="3242"),
# additional_metrics: [Metric(name=metric=1, value=123),
# Metric(name=metric-3, value=543)],
trials = Trial.convert(request.trials)
#--------------------------------------------------------------
# Your code here
# Implement the logic to generate new assignments for the given current request number.
# For example, if request.current_request_number is 2, you should return:
# [
# [Assignment(name=param-1, value=3),
# Assignment(name=param-2, value=cat2),
# Assignment(name=param-3, value=3),
# Assignment(name=param-4, value=3.22)
# ],
# [Assignment(name=param-1, value=4),
# Assignment(name=param-2, value=cat4),
# Assignment(name=param-3, value=2),
# Assignment(name=param-4, value=4.32)
# ],
# ]
list_of_assignments = your_logic(search_space, trials, request.current_request_number)
#--------------------------------------------------------------
# Convert list_of_assignments to
return api_pb2.GetSuggestionsReply(
trials=Assignment.generate(list_of_assignments)
)
构建算法服务的 Docker 镜像
您应该为您的算法服务构建 Docker 镜像,为此,在 cmd/suggestion
下添加一个新的 Docker 镜像,例如:cmd/suggestion/hyperopt。新的 gRPC 服务器应在端口 6789 上提供服务。
之后您可以从您的算法构建 Docker 镜像
docker build . -f cmd/suggestion/<PATH_TO_DOCKER> -t <DOCKER_IMAGE>
使用以下内容更新 Katib 配置
使用新算法实体更新Katib 配置
runtime:
suggestions:
- algorithmName: random
image: docker.io/kubeflowkatib/suggestion-hyperopt:$(KATIB_VERSION)
- algorithmName: tpe
image: docker.io/kubeflowkatib/suggestion-hyperopt:$(KATIB_VERSION)
+ - algorithmName: <new-algorithm-name>
+ image: <DOCKER_IMAGE>
将算法贡献给 Katib
如果您想将算法贡献给 Katib,您可以在 CI 中为其添加单元测试和/或 e2e 测试并提交 PR。
为算法添加单元测试
这是一个示例 test_hyperopt_service.py
import grpc
import grpc_testing
import unittest
from pkg.apis.manager.v1beta1.python import api_pb2_grpc
from pkg.apis.manager.v1beta1.python import api_pb2
from pkg.suggestion.v1beta1.hyperopt.service import HyperoptService
class TestHyperopt(unittest.TestCase):
def setUp(self):
servicers = {
api_pb2.DESCRIPTOR.services_by_name['Suggestion']: HyperoptService()
}
self.test_server = grpc_testing.server_from_dictionary(
servicers, grpc_testing.strict_real_time())
if __name__ == '__main__':
unittest.main()
您可以使用 grpc_testing
设置 gRPC 服务器,然后定义您自己的测试用例。