在开发EDA工具时,涉及到复杂算法的每个Flow,有很多的参数需要设定默认值。默认值和算法有关,不同默认值效果大不相同。并且,对于不同目标,默认值的设置也不相同。如果参数很多,很难通过经验进行默认值的设定。这类似一个多参数模型调优的问题。
问题:当我们遇到一个多参数的模型时,不同参数导致不同的后果,在可以通过打分确认好坏的情况下,如何选择最优的参数(获得更好的得分)。
对于多个参数的搜索,随着参数个数增多,会产生组合爆炸,是一个NP-Hard问题。
传统的做法就是 网格搜索和随机搜索.
所谓grid search 就是 穷举法,相当于是暴力破解,当然代价巨大。
random search 是随机挑选一些参数,这就有点撞大运了,没法保证拿到最优解,大概率会错过最优解。
好了,上面问题的解法是使用贝叶斯优化,那我们先看看它的工作原理:
首先,贝叶斯模型是基于概率的,如下图:A已发生的条件下B发生的概率。
在贝叶斯优化中,“模型”是一个概率模型,通常是高斯过程。当我们想优化一个函数(例如,机器学习模型的性能)但不知道它的确切形状时,我们可以使用贝叶斯优化。每次尝试都会为我们提供新的信息,帮助我们更好地理解这个函数,并指导我们在下一次应该在哪里尝试,以期望得到最好的结果。
贝叶斯优化的工作原理是:首先对目标函数的全局行为建立先验知识(通常用高斯过程来表示),然后通过观察目标函数在不同输入点的输出,更新这个先验知识,形成后验分布。基于后验分布,选择下一个采样点,这个选择既要考虑到之前观察到的最优值(即利用),又要考虑到全局尚未探索的区域(即探索)。这个选择的策略通常由所谓的采集函数(Acquisition Function)来定义,比如最常用的期望提升(Expected Improvement),这样,贝叶斯优化不仅可以有效地搜索超参数空间,还能根据已有的知识来引导搜索,避免了大量的无用尝试。
可以先不管先验后验这些难以理解的概念,简单的把贝叶斯优化理解为:贝叶斯优化利用已经观察到的点来决定下一步需要探索的点。如此,反复的迭代,直到找到最优(算法能找到的,不一定是全局最优点,也可能是局部最优。本文后续也采用此说法)的超参数。而如何决定下一步需要探索的点就有探索和利用这两种需要权衡的策略。
对于平均值大的是可利用的点,方差大的是值得探索的点.
如上原则,可选出下一步要尝试的点。减少尝试次数,尽快拿到最优解。
如果参数量过大,参数之间存在依赖……
首先,有开源的项目可用,不需要自已重写基础代码。
开源项目:https://scikit-optimize.github.io/stable/
这是一个Python的开源项目,详情可自行查看。
如果要把开源工具集成到实际项目中使用,还需要做一些工作。我们以EDA中Route过程的参数调优为例,来看一下工程化的步骤。
demo.sh 是运行模型(EDA工具中实际调用的程序)
demo.csv:这是指行的score结果(各种QoR指标)
main.py是主程序,优化器的所有参数在此设置。
Adapter.py 主要是要重写score()方法,确定评分规则。
Paras.json 是涉及的参数列表,包括每个参数的取值范围
最主要的参数定义,大多数是高级参数,需要深入理解。
比较常用的就是:
优化器运行次数,这个和参数个数相关。
选取score最高的参数组合。
可以查看不同参数的收敛情况:
如上图,可以看到,当尝试到第8次时,已经基本找到了最大值 ,说明很快找到了最优解。
但是,我们考虑一下,如果参数比较多,会怎么样?
那一定会非常的慢,还有,如果参数与参数之间的相关性很小,但放到一起来分析,那是没有必要的,所以,我们需要评估图,来评估参数之间的影响度。
评估图如下:
当然,这里的例子,参数太少,没啥意义。
主要是通过评估图,查看多个参数之间的相互影响度。将影响不大的,可以分开,变成多组参数,分多次来分析。
评估图还有一些别的用途,我也没研究清楚。后续再说。
上面的工具,要简单使用,是很好用的。但是如果参数超出10个或者超过20个,实际上就需要做参数的分类,确定出哪些参数对于性能的影响大,哪些影响小(分为主要参数和次要参数)。
另外,在多维空间中,我们通过评估图,可以找到大至的一个最优区域,这样,关键参数就可以减小取值范围,然后再对次数参数进行优化。这种解藕,可以简化搜索空间。相当于分层的贝叶斯优化方法,可以保证更加快速的到到最佳组合,最大化我们的计算资源的利用率。