学习黑盒优化算法CMA和RandomSearch,借助阿里达摩院MindOpt的RABBO榜单【系列2/4】

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: 这次达摩院MindOpt优化求解器团队出的「开发者福利」黑盒优化RABBO V1.0看起来挺有意思的,什么是黑盒优化呢?怎么借助这个榜单学习这个技术呢?RABBO内有内置CMA(CMA-ES, 协方差矩阵自适应进化策略)和Random Search(随机搜索),让我们来学一学~~

1  黑盒优化的概念

上一篇《新手一步步学习黑盒优化算法,借助达摩院MindOpt的RABBO榜单【系列1/4】》中给大家介绍了黑盒优化的概念、和怎么借助阿里达摩院的RABBO“动手”。这篇将着重学习黑盒优化的算法。

先复习一下上篇关于雨黑盒优化的内容:
什么是黑盒优化?
「黑盒优化问题」泛指目标函数难以从数学上解析表达,缺少可直接利用的梯度信息,仅可利用目标函数输入和对应输出函数值进行最优解搜索的优化问题。
这类问题要怎么优化求解?
可以 通过获取不同控制参数(输入变量)对应的系统表现,来推断和搜寻优化解。架构就像下面的图这样,我们假设我们要求解的问题不好描述,就把这个问题做成一个仿真系统,这个系统可以根据输入的变量值(“候选解”)来给出对应的评价(“观测值”),然后黑盒优化算法,就是接上这个仿真系统,通过不断地去提供候选解和得到观测值,来搜索可能的最优表现的候选解。
image.png
图注: 云栖大会上王孟昌博士 https://yunqi.aliyun.com/2021/agenda/session170 直播回放大约20分钟的时候截图

2 RABBO中的算法:CMA方法、randomSearch方法。

根据网页介绍:https://tianchi.aliyun.com/specials/promotion/BlackBox 的指引,下载了RABBO仓库。可以参看上一篇文档《【系列1/4】》指导来下载。RABBO的algorithms文件夹里面有两个内置的算法,在目录 blackbox_starter_kit -> algorithms文件夹下面。
image.png

2.1 CMA(CMA-ES, 协方差矩阵自适应进化策略)

2.1.1 cma 背景知识

点开RABBO文件夹下面的blackbox_starter_kit/algorithms/cmaes/cma_algorithm.py文件,可以看到第13行写着:

import cma.purecma as purecma

看来这个代码示例是利用cma工具包来操作的。在安装的requirements.txt文件中需要cma
可在这儿查看cma的一些介绍:https://www.cnpython.com/pypi/cma (英文的在这儿:https://pypi.org/project/cma/)。

一种求解困难(非凸、病态、多模、粗糙、带噪声)连续优化问题的随机数值优化算法,使用Python实现。( 参考
其中pycma模块提供了独立的两个CMA-ES算法实现,即cma.CMAEvolutionStrategy类和cma.purecma.CMAES类。

再百度搜索一下CMA-ES(协方差矩阵自适应进化策略)会有很多的理论的教程。

  • 对多元高斯分布进行采样得到新解,使用其中较好的解更新高斯分布的参数,最大熵原理。(参考
  • 这是一篇论文里面的CMA-ES的总体方案;第一步取值;第二步挑精英值;第三步,更新均值μ;第四步更新步长σ需要的演化路径;第五步更新步长;第六步更新path2 需要的C的演化路径;第七步我个人觉得不需要?;第八步更新协方差矩阵。(参考

2.1.2  读代码

来看看RABBO中的示例代码。首先看blackbox_starter_kit/algorithms/submission/submission_algorithm.py ,与cma_algorithm.py相似。submission_algorithm.py中注释掉的代码部分,是4种功能的参考:

  1. 提取问题相关接口和参数。
  2. 实现一个循环采样的逻辑直到完成指定数量的采样并且消耗完指定的优化时长。
  3. 在循环采样过程中使用相应api评估函数值。
  4. 在循环采样过程中使用相应api评估约束违背情况。

image.png
图注:这个截图的文件里,我添加了cma算法代码进去,所以行号和长度不一样。

然后再看cma_algorithm.py

  • 后面有几个自定义函数def convert_to_real_config(self):def get_bounds(self, real_config):def convert_to_origin_param_dict(self, suggest_x):def update_best(self, param, fval, directs):,做参数数据等的计算和更新工作。
  • 算法迭代的内容在def optimize(self, prob_info, query_budget, time_budget):中。

这个示例代码里,用CMA库去做RABBO库中的问题求解的主要步骤如下:
(1) 首先,初始化参数,参数用处后面会体现。

    def __init__(self, sigma=0.3, penalty_coeff=10e8, penalty_thresh=10e-3, **kwargs):
        """Build wrapper class to use an optimizer in benchmark.

        Parameters
        ----------

        """
        AbstractAlgorithm.__init__(self)
        self._sigma = sigma
        self.name = "CMA"
        self.penalty_coeff = penalty_coeff
        self.penalty_thresh = penalty_thresh # 0

(2) 然后对输入的一番处理。
_query_budget_time_budget(次数和时间预算)用于后面迭代次数的控制,在examples/experiment_example.py中会用到。time_budget时间单位在optimize()中注释的是秒。
image.png
(3) 创建一个CMAES的实例

        x0 = np.asarray(lb+(ub-lb)/2)    # initial point for CMA
        sigma = np.asarray(self._sigma)     #  standard deviation (step size)
        es = purecma.CMAES(x0, sigma)    # create CMAES instance

(4)开始迭代优化

  • 最外层while循环是用_query_budget_time_budget(次数和时间预算)控制的。

    • 每次通过es.ask()来获取一组推荐解Xes.tell(X, fitlist)来更新ES。 这里要注意CMA默认的算法direction是minimization的,因此fitlist赋值时候需要转换一下。

      • X循环内,

        • 采用 funcval = self._problem(suggest_x_ori) 来获得待优化问题的评价evaluate,
        • 采用violations, fg = self._constr_eval(suggest_x_ori) 来评估约束的违反情况,并根据惩罚penalty调整评价。
      • 如果没有违反约束(fg),则把def update_best(self, param, fval, directs)结果更新。

(5)返回结果:return self.best_param, self.best_fval
核心部分的代码copy如下:

        ct = 0
        while self._query_budget - ct > 0 or self._time_budget - tm >0:    # loop until both query budget and time budget are used
            X = es.ask()  # deliver candidate solutions
            fitlist=[]
            for x in X:
                fg = True
                suggest_x_ori = self.convert_to_origin_param_dict(x)
                self.para_list.append(suggest_x_ori)
                funcval = self._problem(suggest_x_ori)
                self.func_list.append(funcval)
                aug_funcval = copy.deepcopy(funcval)

                if self._constraint_types != []:    
                    penalty = 0
                    violations, fg = self._constr_eval(suggest_x_ori)
                    self.constraint_viol.append(violations)
                    
                    for j in range(len(violations)):
                        if self._constraint_types[j] == 'lt':
                            penalty = max(violations[j], 0) * self.penalty_coeff
                        elif self._constraint_types[j] == 'eq':
                            penalty = abs(violations) * self.penalty_coeff
                        for k in range(len(funcval)):
                            if self._directions[k] == "minimize":
                                aug_funcval[k] += penalty
                            elif self._directions[k] == "maximize":
                                aug_funcval[k] -= penalty
                    self.aug_func_list.append(aug_funcval)

                if self._directions[0] == "maximize": # for now only consider single objective
                    fitlist.append(-aug_funcval[0])
                elif self._directions[0] == "minimize":
                    fitlist.append(aug_funcval[0])

                # if penalty > self.penalty_thresh * abs(funcval[k]): # use customized judgement
                if fg: # fg = True means feasible
                    self.update_best(suggest_x_ori, funcval, self._directions)
                else:
                    # print("The current point is infeasible.")
                    print(".") # yoyo add 

                ct += len(funcval)
            es.tell(X, fitlist)  # all the work is done here
            tm = time.time() - st
            
        if self.best_param == []:
            print("No feasible points have been found.")
            self.best_fval = []
        else:
            print("Best value found:\n\tf(x) = {}\nObserved at:\n\tx = {}".format(self.best_fval, self.best_param))
        return self.best_param, self.best_fval

2.2 Random Search(随机搜索)

2.2.1 Random Search 背景知识

RandomSearch的代码在RABBO的blackbox_starter_kit/algorithms/random_search/random_search_algorithm.py文件。

继续百度一下Random Search,又会有一堆材料。随机搜索,如字面意思,就是随机撒点来搜索。

  • 可参考深度学习里面超参调优的random Search方法例子(参考)。
  • 查询资料的时候就会看到GridSearch和RandomSearch等算法的科普。目前业界用得比较多的分别是Grid search、Random search和Bayesian Optimization,业界公认的Random search效果会比Grid search好。(参考

2.2.2 读代码

继续读RABBO的random_search_algorithm.py文件。挺多代码内容是一样的。类似前面的内容分解代码:
(1) 首先,初始化参数,用处后面会体现。random search与前面的CMA不同的地方是sigma换成了n_suggestion,意思是采样点的个数,在后面的代码里可以看出用途。

    def __init__(self, n_suggestion=1, penalty_coeff=10e8, penalty_thresh=10e-3, **kwargs):
        """Build wrapper class to use an optimizer in benchmark.

        Parameters
        ----------

        """
        AbstractAlgorithm.__init__(self)
        self._n_suggestions = n_suggestion
        self.name = "Random search"
        self.penalty_coeff = penalty_coeff
        self.penalty_thresh = penalty_thresh # 0

(2)然后对输入的一番处理。类似之前CMA的代码。
(3)开始迭代优化,大部分内容与前面CMA代码解析相似,除了红色标记部分。

  • 最外层while循环是用_query_budget_time_budget(次数和时间预算)控制的。

    • _n_suggestions(多个个采样点)的循环内,默认是1个

      • 采用numpy生成随机推荐值cand_x = np.random.uniform(lb, ub, size=(1, len(lb)))
      • 采用 funcval = self._problem(suggest_x_ori) 来获得待优化问题的评价evaluate,
      • 采用violations, fg = self._constr_eval(suggest_x_ori) 来评估约束的违反情况,并根据惩罚penalty调整评价。
      • 如果没有违反约束(fg),则把def update_best(self, param, fval, directs)结果更新。

(4)返回结果:return self.best_param, self.best_fval
核心部分的代码copy如下:

        while self._query_budget - ct > 0 or self._time_budget - tm >0:
            ct += self._n_suggestions
            for i in range(self._n_suggestions):
                fg = True
                cand_x = np.random.uniform(lb, ub, size=(1, len(lb)))
                suggest_x_ori = self.convert_to_origin_param_dict(cand_x.tolist()[0])
                self.para_list.append(suggest_x_ori)
                funcval = self._problem(suggest_x_ori)
                self.func_list.append(funcval)
                
                if self._constraint_types != []:
                    aug_funcval = copy.deepcopy(funcval)
                    penalty = 0
                    violations, fg = self._constr_eval(suggest_x_ori)
                    print('fg','feasible' if fg==True else 'infeasible')
                    self.cosntraint_viol.append(violations)
                    
                    for j in range(len(violations)):
                        if self._constraint_types[j] == 'lt':
                            penalty = max(violations[j], 0) * self.penalty_coeff
                        elif self._constraint_types[j] == 'eq':
                            penalty = abs(violations) * self.penalty_coeff
                        for k in range(len(funcval)):
                            if self._directions[k] == "minimize":
                                aug_funcval[k] += penalty
                            elif self._directions[k] == "maximize":
                                aug_funcval[k] -= penalty
                    self.aug_func_list.append(aug_funcval)


                # print(self.cosntraint_viol)
                if fg:
                    self.update_best(suggest_x_ori, funcval, self._directions)
                else:
                    print("The point is infeasible.")
            tm = time.time() - st
        if self.best_param == []:
            print("No feasible points have been found.")
            self.best_fval = []
        else:
            print("Best value found:\n\tf(x) = {}\nObserved at:\n\tx = {}".format(self.best_fval, self.best_param))      
        return self.best_param, self.best_fval

2.3 两种方法结果对比

2.3.1 肉眼对比

怎么评判算法的好坏呢?RABBO里面提供了个评测思路:对于单一问题,从采样次数和优化时间两个维度考察算法表现,分别记录:

  1. 算法在给定采样次数内得到的最优解,
  2. 算法在给定优化时间内得到的最优解。

也就是根据最后的目标值f(x) 和运算耗时来评估。

这里我们把RABBO自带的两种方法的输出结果整理一下,得到如下的结果对比表格。问题的f(x)值不是都是越大越好的,需要根据优化方向来定的,可以去对应问题的Python文件看到directions
跑实验给定的参数可在examples/experiment_example.py中修改,我用的是默认的(根据后面RABBO线上测评的结果看,是与线上测评的次数设置不一样的,大家根据需要修改):

exp.run(n_exps=2, query_budget=200, time_budget=1.5)

表注:CMA 和 RandomSearch算法效果对比

Problem CMA Random Search
Rover(D60) ["maximize"] f(x) = [0.6208424134973445] f(x) = [-7.616628618190374]
Time_cost:4.069791 Time_cost:1.501341
f(x) = [-2.208531434656983] f(x) = [-5.440533957191118]
Time_cost:4.035468 Time_cost:1.504337,
Ackley20 ["minimize"] f(x) = [0.15557523403160234] f(x) = [19.210505137025393]
Time_cost:1.526049, Time_cost:1.502744,
f(x) = [0.09328943244080223] f(x) = [19.65136222860868]
Time_cost:1.529486, Time_cost:1.521297,
Rastrigin20D["minimize"] f(x) = [4.561892672012249] f(x) = [182.66992495064426]
Time_cost:1.516006, Time_cost:1.522427,
f(x) = [3.468608199278009] f(x) = [173.0407459158929]
Time_cost:1.539840, Time_cost:1.511754,
Windfarm ["maximize"] No feasible points No feasible points
Time_cost:21.864100, Time_cost:14.613770,
No feasible points No feasible points
Time_cost:21.935938, Time_cost:15.937529,

从上表可以看出,目标值f(x)结果是CMA的结果更优秀呢,虽然在有些问题上耗时久一些。不过风场的这个Windfarm问题两个算法都解不出来,可以感觉到这两种方法都有些弱呢。

2.3.2 提交到RABBO线上测评

上一篇《【系列1/4】》中给大家介绍了怎么打包镜像评测,
最近RABBO的线上评测题有修改,测评的时间非常的长,要好几个小时。
根据评分规则,totalScore是越高越好,据说是不知道上限有多高的,因为有些问题没有人知道最优解是多少。windfarm那个问题没有解出来,拉低了平均分很多呀。smelting的问题只有在线上测评时候有,看起来也很难解呢…………

RABBO线上测评结果截图如下(结果里面的日志是有测评平台输出和我们自己编代码里的print输出):
image.png

image.png

RABBO线上评测给出的成绩score整理成表格如下:

problem testcma testRandomSearch 对比分析
synthetic_score 1.0622723286900113 0.00036699951336779913 随机搜索可能目标值太差了,溃败
smelting_score 0.047487121954627516 0.000045399929762484854 随机搜索这个评分应该是没有求解出来
windfarm_score 0.000045399929762484854 0.000045399929762484854 随机搜索这个评分应该是没有求解出来
rover_score 1.0220139967289352 0.951324606382894 差异不大
totalScore 0.532954711826 0.237945601439 线上评测也是CMA算法更好

3 其他的算法

从前面的测评结果看,CMA(CMA-ES, 协方差矩阵自适应进化策略)要比random Search(随机搜索)要优秀,不过对于RABBO中给出的smeltingwindfarm问题都不能好的求解,根据云栖大会MindOpt团队的视频分享( https://yunqi.aliyun.com/2021/agenda/session170 直播回放),他们的求解器性能应该很不错的。还有更好的算法值得去发掘、学习和研究。

从前面的搜索中,大家应该能看到很多其他的优化算法。有一篇写的挺不错的:《贝叶斯优化: 一种更好的超参数调优方式》https://zhuanlan.zhihu.com/p/29779000

自动调参算法:说到自动调参算法,大家可能已经知道了Grid search(网格搜索)、Random search(随机搜索),还有Genetic algorithm(遗传算法)、Paticle Swarm Optimization(粒子群优化)、Bayesian Optimization(贝叶斯优化)、TPE、SMAC等。

像遗传算法和PSO这些经典黑盒优化算法,我归类为群体优化算法,也不是特别适合模型超参数调优场景,因为需要有足够多的初始样本点,并且优化效率不是特别高,本文也不再详细叙述。

目前业界用得比较多的分别是Grid search、Random search和Bayesian Optimization。

大家也可以多利用互联网搜索,多学习一下。
后面我也会找一个经典的方法改造一下参与测评,欢迎交流。

4 其他文

后面,立个flag,将补充更多文章,学会黑盒优化。
借助阿里达摩院的MindOpt的RABBO榜单系列:
1.《新手一步步学习黑盒优化算法,借助达摩院MindOpt的RABBO榜单【系列1/4】
2.(本篇)《学习黑盒优化榜单RABBO提供的两个参考算法CMA和RandomSearch

  1. todo:《改编一个自己的黑盒优化算法或开源算法》
  2. todo:《将我要解决的业务问题建模成RABBO的问题格式,然后去求解》

image.png

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
17天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
|
13天前
|
机器学习/深度学习 人工智能 自然语言处理
【EMNLP2024】基于多轮课程学习的大语言模型蒸馏算法 TAPIR
阿里云人工智能平台 PAI 与复旦大学王鹏教授团队合作,在自然语言处理顶级会议 EMNLP 2024 上发表论文《Distilling Instruction-following Abilities of Large Language Models with Task-aware Curriculum Planning》。
|
17天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
17天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
17天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
17天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
17天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构的基本概念;算法的基本概念、特性以及时间复杂度、空间复杂度等举例说明;【含常见的报错问题及其对应的解决方法】
|
17天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!