ML 的容器运行时的并行超参数优化 (HPO)¶
Snowflake ML 超参数优化 (HPO) API 是一个与模型无关的框架,可对模型进行高效、并行的超参数调整。您可以使用任何开源框架或算法。您也可以使用 Snowflake ML APIs。
如今,此 API 可在配置为使用 Snowpark Container Services (SPCS) 上的容器运行时的 Snowflake 笔记本内使用。创建这样一个笔记本 之后,可以执行以下操作:
使用任何开源包训练模型,并使用此 API 分配超参数调整过程
使用 Snowflake ML 分布式训练 APIs 训练模型,并扩展 HPO,同时也扩展每个训练运行
HPO 工作负载由笔记本启动,在 Snowpark Container Services 内的 CPU 或 GPU 实例上执行,并扩展到 SPCS 计算池中单个节点上可用的核心(CPUs 或 GPUs)。
并行化的 HPO API 具有以下优势:
单个 API 可自动处理在多个资源之间分配训练的所有复杂问题
利用开源 ML 框架或 Snowflake ML 建模 APIs,几乎能使用任何框架或算法进行训练
一系列调整和采样选项,包括贝叶斯和随机搜索算法,以及各种连续和非连续采样函数
与 Snowflake 的其他功能紧密集成;例如,通过 Snowflake 数据集或数据帧高效引入数据,以及自动获取 ML 沿袭
优化模型的超参数¶
使用 Snowflake ML HPO API 调整模型。以下步骤说明了这一过程:
引入数据。
使用搜索算法定义用于优化超参数的策略。
定义超参数的采样方式。
配置调谐器。
从每个训练作业中获取超参数和训练指标。
启动训练作业。
获得训练作业结果。
以下各部分将介绍前面的步骤。有关示例,请参阅 容器运行时 HPO 示例 (https://github.com/Snowflake-Labs/sf-samples/blob/main/samples/ml/container_runtime_hpo/hpo_example.ipynb)。
引入数据¶
使用 dataset_map
对象将数据引入 HPO API。dataset_map
对象是一个字典,用于将训练或测试数据集与其对应的 Snowflake DataConnector 对象配对。dataset_map
对象将传递给训练函数。以下是 dataset_map
对象示例:
dataset_map = {
"x_train": DataConnector.from_dataframe(session.create_dataframe(X_train)),
"y_train": DataConnector.from_dataframe(
session.create_dataframe(y_train.to_frame())
),
"x_test": DataConnector.from_dataframe(session.create_dataframe(X_test)),
"y_test": DataConnector.from_dataframe(
session.create_dataframe(y_test.to_frame())
),
}
定义搜索算法¶
定义用于探索超参数空间的搜索算法。算法利用之前的试验结果来确定如何配置超参数。您可以使用以下搜索算法:
网格搜索 搜索您定义的超参数值网格。HPO API 对超参数的每种可能组合进行评估。以下是超参数网格示例:
search_space = { "n_estimators": [50, 51], "max_depth": [4, 5]), "learning_rate": [0.01, 0.3], } In the preceding example, each parameter has two possible values. There are 8 (2 * 2 * 2) possible combinations of hyperparameters.
贝叶斯优化 使用概率模型来确定下一组要评估的超参数。算法利用之前的试验结果来确定如何配置超参数。有关贝叶斯优化的更多信息,请参阅 贝叶斯优化 (https://github.com/bayesian-optimization/BayesianOptimization)。
随机搜索 随机对超参数空间进行采样。这种方法简单有效,尤其适用于大型或混合(连续或离散)搜索空间。
您可以使用以下代码定义搜索算法:
from entities import search_algorithm
search_alg = search_algorithm.BayesOpt()
search_alg = search_algorithm.RandomSearch()
search_alg = search_algorithm.GridSearch()
定义超参数采样¶
在每次试验中使用搜索空间函数来定义超参数采样方法。用它们来描述超参数的取值范围和类型。
以下是可用的采样功能:
uniform(
lower
,upper
):在 lower 和 upper 之间均匀采样一个连续值。该功能特别适用于调节诸如丢弃率或正则化强度等参数。loguniform(
lower
,upper
):该函数采用对数空间采样,特别适用于取值范围跨越多个数量级的参数(如学习率)。randint(
lower
,``upper``):在 lower(包含)和 upper(不包含)之间均匀采样一个整数。适用于离散参数,如层数。choice(options):从提供的列表中随机选择一个值。常用于分类参数。
以下示例展示了如何使用均匀函数定义搜索空间:
search_space = {
"n_estimators": tune.uniform(50, 200),
"max_depth": tune.uniform(3, 10),
"learning_rate": tune.uniform(0.01, 0.3),
}
配置调谐器¶
使用 TunerConfig
对象配置调谐器。在该对象中,您可以指定要优化的指标、优化模式和其他执行参数。以下是可用的配置选项:
指标 您要优化的性能指标,如准确度或损耗。
模式 用于确定目标是最大化还是最小化指标(
"max"
或"min"
)。搜索算法 指定探索超参数空间的策略。
试验次数 设置要评估的超参数配置总数。
并发 定义可以同时运行多少个试验。
下面的示例代码使用贝叶斯优化库,在五次试验中最大限度地提高模型的准确性。
from snowflake.ml.modeling import tune
tuner_config = tune.TunerConfig(
metric="accuracy",
mode="max",
search_alg=search_algorithm.BayesOpt(
utility_kwargs={"kind": "ucb", "kappa": 2.5, "xi": 0.0}
),
num_trials=5,
max_concurrent_trials=1,
)
获取超参数和训练指标¶
Snowflake ML HPO API 需要每次训练运行的训练指标和超参数,以便有效优化超参数。使用 TunerContext
对象获取超参数和训练指标。下面的示例创建了一个训练函数,用于获取超参数和训练指标:
def train_func():
tuner_context = get_tuner_context()
config = tuner_context.get_hyper_params()
dm = tuner_context.get_dataset_map()
...
tuner_context.report(metrics={"accuracy": accuracy}, model=model)
启动训练作业¶
使用 Tuner
对象启动训练作业。Tuner
对象将训练函数、搜索空间和调谐器配置作为实参。以下示例展示了如何启动训练作业:
from snowflake.ml.modeling import tune
tuner = tune.Tuner(train_func, search_space, tuner_config)
tuner_results = tuner.run(dataset_map=dataset_map)
前面的代码将训练函数分配到可用的资源上。它收集和总结试验结果,并确定性能最佳的配置。
获得训练作业成果¶
所有试验完成后,``TunerResults`` 对象将整合每次试验的结果数据。它提供了对性能指标、最佳配置和最佳模型的结构化访问。
以下是该对象的可用属性:
results:一个包含每次试验的评估指标和配置参数的 Pandas DataFrame。
best_result:一个总结性能最佳的试验的 DataFrame 行。
best_model:与最佳试验相关的模型实例(如适用)。
以下代码可以得出结果、最佳模型和最佳结果:
print(tuner_results.results)
print(tuner_results.best_model)
print(tuner_results.best_result)
API 参考¶
调谐器¶
以下是调谐器模块的导入语句:
from snowflake.ml.modeling.tune import Tuner
调谐器类是与容器运行时 HPO API 交互的主要接口。要运行 HPO 作业,请使用以下代码初始化调谐器对象,并调用带有数据集的运行方法。
class Tuner:
def __init__(
self,
train_func: Callable,
search_space: SearchSpace,
tuner_config: TunerConfig,
)
def run(
self, dataset_map: Optional[Dict[str, DataConnector]] = None
) -> TunerResults
SearchSpace¶
以下是搜索空间的导入语句:
from entities.search_space import uniform, choice, loguniform, randint
以下代码定义搜索空间函数:
def uniform(lower: float, upper: float)
"""
Sample a float value uniformly between lower and upper.
Use for parameters where all values in range are equally likely to be optimal.
Examples: dropout rates (0.1 to 0.5), batch normalization momentum (0.1 to 0.9).
"""
def loguniform(lower: float, upper: float) -> float:
"""
Sample a float value uniformly in log space between lower and upper.
Use for parameters spanning several orders of magnitude.
Examples: learning rates (1e-5 to 1e-1), regularization strengths (1e-4 to 1e-1).
"""
def randint(lower: int, upper: int) -> int:
"""
Sample an integer value uniformly between lower(inclusive) and upper(exclusive).
Use for discrete parameters with a range of values.
Examples: number of layers, number of epochs, number of estimators.
"""
def choice(options: List[Union[float, int, str]]) -> Union[float, int, str]:
"""
Sample a value uniformly from the given options.
Use for categorical parameters or discrete options.
Examples: activation functions ['relu', 'tanh', 'sigmoid']
"""
TunerConfig¶
以下是 TunerConfig 模块的导入语句:
from snowflake.ml.modeling.tune import TunerConfig
使用以下代码定义调谐器的配置类:
class TunerConfig:
"""
Configuration class for the tuning process.
Attributes:
metric (str): The name of the metric to optimize. This should correspond
to a key in the metrics dictionary reported by the training function.
mode (str): The optimization mode for the metric. Must be either "min"
for minimization or "max" for maximization.
search_alg (SearchAlgorithm): The search algorithm to use for
exploring the hyperparameter space. Defaults to random search.
num_trials (int): The maximum number of parameter configurations to
try. Defaults to 5
max_concurrent_trials (Optional[int]): The maximum number of concurrently running trials per node. If not specified, it defaults to the total number of nodes in the cluster. This value must be a positive
integer if provided.
Example:
>>> from entities import search_algorithm >>> from snowflake.ml.modeling.tune import TunerConfig
>>> config = TunerConfig(
... metric="accuracy",
... mode="max",
... num_trials=5,
... max_concurrent_trials=1
... )
"""
SearchAlgorithm¶
以下是搜索算法的导入语句:
from entities.search_algorithm import BayesOpt, RandomSearch, GridSearch
以下代码创建了一个贝叶斯优化搜索算法对象:
@dataclass
class BayesOpt():
"""
Bayesian Optimization class that encapsulates parameters for the acquisition function.
This class is designed to facilitate Bayesian optimization by configuring
the acquisition function through a dictionary of keyword arguments.
Attributes:
utility_kwargs (Optional[Dict[str, Any]]):
A dictionary specifying parameters for the utility (acquisition) function.
If not provided, it defaults to:
{
'kind': 'ucb', # Upper Confidence Bound acquisition strategy
'kappa': 2.576, # Exploration parameter for UCB
'xi': 0.0 # Exploitation parameter
}
"""
utility_kwargs: Optional[Dict[str, Any]] = None
以下代码创建一个随机搜索算法对象:
@dataclass
class RandomSearch():
The default and most basic way to do hyperparameter search is via random search.
Attributes:
Seed or NumPy random generator for reproducible results. If set to None (default), the global generator (np.random) is used.
random_state: Optional[int] = None
TunerResults¶
以下是 TunerResults 模块的导入语句:
from entities.tuner_results import TunerResults
以下代码创建了 TunerResults 对象:
@dataclass
class TunerResults:
results: pd.DataFrame
best_result: pd.DataFrame
best_model: Optional[Any]
get_tuner_context¶
以下是 get_tuner_context
模块的导入语句:
from snowflake.ml.modeling.tune import get_tuner_context
该辅助方法专为在训练函数内部调用而设计。它返回一个 TunerContext 对象,该对象封装了多个对运行试验有用的字段,包括:
由 HPO 框架为当前试验选择的超参数。
训练所需的数据集。
这是一个用于上报指标的辅助函数,可指导 HPO 框架推荐下一组超参数。
以下代码将创建一个调谐器上下文对象:
class TunerContext:
"""
A centralized context class for managing trial configuration, reporting, and dataset information.
"""
def get_hyper_params(self) -> Dict[str, Any]:
"""
Retrieve the configuration dictionary.
Returns:
Dict[str, Any]: The configuration dictionary for the trial.
"""
return self._hyper_params
def report(self, metrics: Dict[str, Any], model: Optional[Any] = None) -> None:
"""
Report metrics and optionally the model if provided.
This method is used to report the performance metrics of a model and, if provided, the model itself.
The reported metrics will be used to guide the next set of hyperparameters selection in the
optimization process.
Args:
metrics (Dict[str, Any]): A dictionary containing the performance metrics of the model.
The keys are metric names, and the values are the corresponding metric values.
model (Optional[Any], optional): The trained model to be reported. Defaults to None.
Returns:
None: This method doesn't return anything.
"""
def get_dataset_map(self) -> Optional[Dict[str, Type[DataConnector]]]:
"""
Retrieve the dataset mapping.
Returns:
Optional[Dict[str, Type[DataConnector]]]: A mapping of dataset names to DataConnector types, if available.
"""
return self._dataset_map
限制¶
贝叶斯优化需要连续的搜索空间,并且只适用于均匀采样函数。它与使用 tune.randint
或 tune.choice
方法采样的离散参数不兼容。要绕过这一限制,要么使用 tune.uniform
并转换训练函数中的参数,要么改用可同时处理离散空间和连续空间的采样算法,如 tune.RandomSearch
。
故障排除¶
错误消息 |
可能原因 |
可能的解决方案 |
---|---|---|
无效的搜索空间配置:BayesOpt 要求所有采样函数都是“均匀”类型。 |
贝叶斯优化只适用于均匀采样,不适用于离散采样。(请参阅上面的 限制)。 |
|
CPU 资源不足。需要:16,可用:8。所需资源和可用资源的数量可能有所不同。 |
|
按照错误信息提供的指导进行操作。 |
GPU 资源不足。需要:4,可用:2。可参阅 CPU 或 GPU。所需资源和可用资源的数量可能有所不同。 |
|
按照错误信息提供的指导进行操作。 |