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

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 这次达摩院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)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的优化算法及其应用
【10月更文挑战第8天】 本文将探讨深度学习中常用的优化算法,包括梯度下降法、Adam和RMSProp等,介绍这些算法的基本原理与应用场景。通过实例分析,帮助读者更好地理解和应用这些优化算法,提高深度学习模型的训练效率与性能。
106 63
|
8天前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
29 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
9天前
|
算法
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
32 2
动态规划算法学习三:0-1背包问题
|
4天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
4天前
|
机器学习/深度学习 人工智能 算法
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
23 0
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
|
7天前
|
机器学习/深度学习 算法 数据挖掘
基于GWO灰狼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了基于分组卷积神经网络(GroupCNN)和灰狼优化(GWO)的时间序列回归预测算法。算法运行效果良好,无水印展示。使用Matlab2022a开发,提供完整代码及详细中文注释。GroupCNN通过分组卷积减少计算成本,GWO则优化超参数,提高预测性能。项目包含操作步骤视频,方便用户快速上手。
|
8天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于WOA鲸鱼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了一种基于WOA优化的GroupCNN分组卷积网络时间序列预测算法。使用Matlab2022a开发,提供无水印运行效果预览及核心代码(含中文注释)。算法通过WOA优化网络结构与超参数,结合分组卷积技术,有效提升预测精度与效率。分组卷积减少了计算成本,而WOA则模拟鲸鱼捕食行为进行优化,适用于多种连续优化问题。
|
9天前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
37 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
9天前
|
算法
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
40 0
动态规划算法学习二:最长公共子序列
|
9天前
|
缓存 负载均衡 算法
nginx学习:配置文件详解,负载均衡三种算法学习,上接nginx实操篇
Nginx 是一款高性能的 HTTP 和反向代理服务器,也是一个通用的 TCP/UDP 代理服务器,以及一个邮件代理服务器和通用的 HTTP 缓存服务器。
17 0
nginx学习:配置文件详解,负载均衡三种算法学习,上接nginx实操篇