smac的应用:auto-sklearn

SMAC(Sequential Model-based Algorithm Configuration),即基于序列模型的算法配置,被广泛应用在各个自动机器学习框架上,如微软的nnihttps://github.com/microsoft/nni),auto-sklearn等。其中auto-sklearn的超参搜索模块完全由smac算法构建。要想理解auto-sklearn,首先就要理解smac

下面介绍smac的应用场景:AutoML,应用主体:auto-sklearn

随着最近人工智能技术的大热,机器学习、深度学习逐渐进入了人们的视野,在各种工具(如Tensorflow,sklearn)开源的加持下,机器学习技术逐渐有普及化、全民化的趋势。但是,以普通的表格数据(行为样本点,列为特征)为例,要找到一个表现最好的Pipeline(即从数据处理,特征处理,最后到estimator判别),需要花费人们大量的时间去尝试。一般来说,数据科学家需要获取数据的一些基本统计量,做一些图表来判断数据的模式,再根据自己的经验挑选数据处理,特征处理的方法,最后选择一个estimator(在分类任务中为分类器classifier,在回归任务中为回归器regressor)。可是这个过程一个是耗时,还有更加依赖数据科学家的经验,提升了机器学习的门槛。

人们注意到了这一矛盾,提出了自动机器学习(AutoML)的概念。在2015年一个AutoML框架auto-sklearn横空出世,引来人们争相关注:
smac源码分析(1):初探smac_sed

auto-sklearn架构

  • auto-sklearn论文与代码
paper http://papers.nips.cc/paper/5872-efficient-and-robust-automated-machine-learning.pdf
code https://github.com/automl/auto-sklearn

最近笔者在调研AutoML,于是对auto-sklearn进行了调研。笔者发现,auto-sklearn是用automl团队(https://www.automl.org)编写,这个团队在构建这个自动机器学习框架时使用了大量自己开发的库,库与库之间功能内聚,相互隔离,调用关系为一个有向无环图:
smac源码分析(1):初探smac_sed_02
橙色字体标记的smac就是我们今天的主角。在分析清楚auto-sklearn的项目架构后,我下载了smac的源代码(https://github.com/automl/SMAC3),开始对其进行分析。

smac源码分析(1):初探smac_搜索_03在AutoML系统中,最为关键的就是根据当前的多组超参数组合(如svm核函数的选择,惩罚系数C的选择)与模型表现(如分类任务中的准确率),给出一个在当前条件下预计最优的超参数,这就是贝叶斯优化(Bayesian Optimizer)。与网格搜索与随机搜索不同,贝叶斯优化能在更短的时间内找到更好的超参数。
smac源码分析(1):初探smac_搜索_04

来源: borgwang.github.io

目前应用最广的是SMBO(Sequential Model-Based Optimazation),即基于模型的序列优化。目前有三种常见的SMBO算法:基于高斯过程回归的序列超参优化、基于随机森林算法代理的序列超参优化(即 smac),基于TPE算法代理的序列超参优化。

smac 快速上手

在终端中执行以下命令,开启第一个例子

git clone  https://github.com/automl/SMAC3
cd SMAC3 && pip install .
cd examples
python SMAC4HPO_svm.py

让我们看到SMAC4HPO_svm.py这个样例代码。
首先定义了一个输入为cfg(超参配置),输出为模型表现的函数svm_from_cfg

def svm_from_cfg(cfg):
    """ 创建一个基于配置的SVM模型,并且通过交叉验证在鸢尾花数据集上评价该模型
    Parameters:
    -----------
    cfg: 超参配置 (ConfigSpace.ConfigurationSpace.Configuration)
    Returns:
    --------
    A crossvalidated mean score for the svm on the loaded data-set.
    svm在数据集上交叉验证的平均值
    """
    # 通过字典解析过滤掉值为None的键值对
    cfg = {k : cfg[k] for k in cfg if cfg[k]}
    # We translate boolean values:
    cfg["shrinking"] = True if cfg["shrinking"] == "true" else False
    # And for gamma, we set it to a fixed value or to "auto" (if used)
    if "gamma" in cfg:
        cfg["gamma"] = cfg["gamma_value"] if cfg["gamma"] == "value" else "auto"
        cfg.pop("gamma_value", None)  # Remove "gamma_value"

    clf = svm.SVC(**cfg, random_state=42)

    scores = cross_val_score(clf, iris.data, iris.target, cv=5)
    return 1-np.mean(scores)  # Minimize!

接下来是配置超参空间

# Build Configuration Space which defines all parameters and their ranges
cs = ConfigurationSpace()

# We define a few possible types of SVM-kernels and add them as "kernel" to our cs
kernel = CategoricalHyperparameter("kernel", ["linear", "rbf", "poly", "sigmoid"], default_value="poly")
cs.add_hyperparameter(kernel)

# There are some hyperparameters shared by all kernels
C = UniformFloatHyperparameter("C", 0.001, 1000.0, default_value=1.0)
shrinking = CategoricalHyperparameter("shrinking", ["true", "false"], default_value="true")
cs.add_hyperparameters([C, shrinking])

# Others are kernel-specific, so we can add conditions to limit the searchspace
degree = UniformIntegerHyperparameter("degree", 1, 5, default_value=3)     # Only used by kernel poly
coef0 = UniformFloatHyperparameter("coef0", 0.0, 10.0, default_value=0.0)  # poly, sigmoid
cs.add_hyperparameters([degree, coef0])
use_degree = InCondition(child=degree, parent=kernel, values=["poly"])
use_coef0 = InCondition(child=coef0, parent=kernel, values=["poly", "sigmoid"])
cs.add_conditions([use_degree, use_coef0])

# This also works for parameters that are a mix of categorical and values from a range of numbers
# For example, gamma can be either "auto" or a fixed float
gamma = CategoricalHyperparameter("gamma", ["auto", "value"], default_value="auto")  # only rbf, poly, sigmoid
gamma_value = UniformFloatHyperparameter("gamma_value", 0.0001, 8, default_value=1)
cs.add_hyperparameters([gamma, gamma_value])
# We only activate gamma_value if gamma is set to "value"
cs.add_condition(InCondition(child=gamma_value, parent=gamma, values=["value"]))
# And again we can restrict the use of gamma in general to the choice of the kernel
cs.add_condition(InCondition(child=gamma, parent=kernel, values=["rbf", "poly", "sigmoid"]))

可以看到在配置超参空间时除了可以配置超参的默认值、值域之外,还能配置超参的Condition,即条件。例如,svm有["linear", "rbf", "poly", "sigmoid"]四种核函数,但只有["rbf", "poly", "sigmoid"]这三种核函数含有gamma参数。

# Scenario object  (方案对象)
scenario = Scenario({"run_obj": "quality",   # 有{runtime,quality}两种选项
                     "runcount-limit": 50,   # max. number of function evaluations; for this example set to a low number
                     "cs": cs,               # configuration space
                     "deterministic": "true"
                     })

# Example call of the function
# It returns: Status, Cost, Runtime, Additional Infos
def_value = svm_from_cfg(cs.get_default_configuration())
print("Default Value: %.2f" % (def_value))

# Optimize, using a SMAC-object
print("Optimizing! Depending on your machine, this might take a few minutes.")
smac = SMAC4HPO(scenario=scenario, rng=np.random.RandomState(42),
        tae_runner=svm_from_cfg)

# incumbent(现任者,与挑战者challenger相对),表示能让模型表现最优的配置
incumbent = smac.optimize()  
# 最优的模型表现
inc_value = svm_from_cfg(incumbent)

print("Optimized Value: %.2f" % (inc_value))


# We can also validate our results (though this makes a lot more sense with instances)
smac.validate(config_mode='inc',      # We can choose which configurations to evaluate
              #instance_mode='train+test',  # Defines what instances to validate
              repetitions=100,        # Ignored, unless you set "deterministic" to "false" in line 95
n_jobs=1) # How many cores to use in parallel for optimization

我们看到最后这几行代码,先是构建一个方案对象(Scenario)表示搜索配置,然后将scenario与目标函数作为参数传入SMAC4HPO返回一个smac对象,调用optimize()得到一个最优超参。

smac 项目架构

$ tree smac -L 1
.
├── configspace/
├── epm/
├── facade/
├── initial_design/
├── intensification/
├── optimizer/
├── runhistory/
├── scenario/
├── stats/
├── tae/
└── utils/

目录名 释义
configspace 超参空间
epm 随机森林代理模型
facade 一些装饰类
initial_design 搜索空间的初始化方法
intensification 在challenger中选出最优配置
optimizer 优化器,包含acquisition,smbo多个重要文件
runhistory 对运行历史的记录
scenario 搜索方案配置
tae Target Algorithm Evaluator,目标算法评价器

在下一期推送中,笔者将继续为大家分析smac源码,探究smac是如何用随机森林代理模型对超参空间进行优化的。

在下一期推送中,笔者将继续为大家分析smac源码,探究smac是如何用随机森林代理模型对超参空间进行优化的。


欢迎关注公众号"人工智能源码阅读"(aicodereview)
smac源码分析(1):初探smac_github_05