一、前言
在以往的推文中,我们详细地介绍了求解混合整数规划(Mixed Integer Programming, MIP)的高级方法:Benders Decomposition。
经典的Benders Decomposition一般要求子问题为线性规划。但是,在很多案例中,子问题却包含0-1变量或者整数变量。此时,经典的Benders Decomposition不再适用。幸运的是,J.N. Hooker和G. Ottosson在2003年将经典的Benders Decomposition进行了拓展,提出了著名的Logic-based Benders Decomposition,
使其适用于任意形式的子问题。该理论发表在数学规划领域的顶级期刊《Mathematical Programming》上。本文就来详细介绍Logic-based Benders Decomposition的理论及Python调用Gurobi实现。
Hooker J N, Ottosson G. Logic-based Benders decomposition[J]. Mathematical Programming, 2003, 96(1): 33-60.
为了让大家更好地理解Logic-based Benders Decomposition,我们首先对经典的Benders Decomposition进行简要回顾,然后逐步过渡到Logic-based Benders Decomposition。为了便于大家继续深入学习,本文也推荐了若干篇关于Logic-based Benders Decomposition的参考文献,见[1-4]。
1 Benders Decomposition回顾
考虑下面的数学规划问题:
在上述模型中,是0-1型或整型的复杂变量,其中是指一系列约束的集合,表示的可行域;而是连续型的简单变量。
Benders Decomposition是把变量和分离开,其中和分别属于不同的可行域和。因此,上述问题可以被抽象地表示为:
根据之前推文的介绍[https://mp.weixin.qq.com/s/7LpHBPvedDknWP7iRAS_zA],
我们可以将原问题分解为下面的形式:
1.1 主问题
其中是子问题对偶问题的极点,是子问题对偶问题的极射线。
1.2 子问题
子问题的目的是生成主问题中评估取值的辅助变量的的下界。子问题的而具体形式为:
子问题的对偶问题为:
但是,如果子问题的决策变量是离散变量,例如0-1变量或者整数变量,则传统的Benders分解不再奏效。此时,需要用到
Logic-based Benders Decomposition。
2 Logic-based Benders Decomposition
2.1 大体思想
我们将Benders Decomposition研究的模型的抽象形式抄写下来:
设想,如果将的值固定为,然后去寻找的情况下,使得缩减后的问题的目标函数最小的可行的(即求解子问题)。我们穷举所有的,然后在所有的下,找到对应的使得最小的可行的。然后我们计算出所有情况下的的取值,从中选出最小的方案,就等价于成功求解了原问题。但是这种穷举的方法比较笨拙。我们可以采用更聪明的办法来避免穷举。
这种聪明的方法就是,从子问题推理得到一个子问题目标函数的下界。这样就可以割去一部分被优超(Dominate)的,从而避免穷举。这个下界,就对应传统Benders Decomposition中的Benders cut,只不过在Logic-based Benders Decomposition中,需要对这个cut的形式进行推广。
假设我们将固定为某个测试值(trial value) ,可以通过求解下面的子问题(Subproblem)来生成一个成本的下界。Subproblem的具体形式为:
如果我们不直接求解Subproblem,而是求解Subproblem的推断对偶(Inference Dual):(注意,在后文no-good cut的版本中,是直接求解Subproblem)
推断对偶的目的是:如果假设,我们能基于Subproblem的原始约束,推断出Subproblem的下界是多少。如果是推断对偶的最优值,则我们可以获得的Subproblem的最紧的下界就是。
将推断对偶应用到Benders Decomposition中,其具体的做法就是,我们可以通过求解Subproblem的推断对偶,得到下面的一个cut(其中表示对的评估):
这个cut为提供了一个下界。它也被称为Benders cut。
基于此,主问题(Master problem)可以被重构为下面的形式:
其中,是到目前为止被尝试的的取值,在最后一次的迭代中,我们固定了.
一般来讲,这个主问题是原始问题的松弛版本,因为它的约束(Benders cuts)只代表了一部分的取值下推导出来的的下界。实际上,很有可能还有其他的可以得出更紧的下界。
注意:在上述的介绍中,我们并没有给出的具体表达式,而是只给出了其抽象表达式。
2.2 一个简单的例子
下面以一个简单的例子来解释Logic-based Benders Decomposition的原理。下文的例子来源于文献[2]。
这个例子是一个非常简单的最短路问题。这个非常简单的案例可以直接观察出最优解,从而可以帮助我们更直观地理解理论。
假设我们希望以尽可能低的价格从家到达一个偏远的村庄(图1)。这一地区的四个城市都可以乘飞机到达,但到达相应的城市后,我们必须乘坐火车、公共汽车或者共享汽车来继续出行。
图1:最短路的例子:来自参考文献2
下面用Benders Decomposition来解决这个问题。
假设 代表乘飞机到达的城市, 代表从城市到村庄的路线选择。总成本为 ,包括从家到城市 的机票费用(到四个城市的机票价格分别为100美元、200美元、200美元和100美元)加上路线 的成本。
首先,我们可以通过观察得到该案例的最短路径为:,总成本为:$100+80+150=330。
下面的表格汇总了用Logic-based Benders Decomposition求解该问题的迭代步骤。
图2:迭代过程:来自参考文献2
下面来详细解释一下。
初始情况下,主问题仅有关于的定义域的约束。可以任意选择 。
接下来,我们来推导当时的子问题的推断对偶。我们通过研究地图,可以找到从到村庄的成本的下界。我们发现,从 到村庄必须通过,否则无法到达。因此,经过 的费用是机票加上从机场到的费用,加上从到村庄的费用:330。但与此同时,我们也注意到,从 和 去村庄也必须经过。因此,总成本的下界可以表述如下:
- 这里需要注意,为什么主问题都固定了,Bender cut 中还需要判断时的情况呢?是因为加回主问题的cut 中,变成了决策变量,在求解主问题之前,当前主问题的最优解中的取值其实是尚未知晓的,我们需要保证加回主问题的cut 对于所有可能的的取值都能使其对应的目标值大于等于其对应的识别的下界。
更新完成后,回到主问题,在Benders cut 的条件下,最小化成本。可得解是 y = city 4, z*=100。
接下来,我们固定 ,求解子问题的推断对偶,得到了第二个Benders cut :,其中
这里,为什么是呢?实际上是因为,之前已经添加过的情况了,所以这里就可以忽略了。
继续求解主问题,我们会发现最便宜的选择是,成本为$330。
我们回到了之前考虑过的的子问题。发现330美元是最好的下界,此时我们获得了原问题的最优解:
,总成本为:$100+80+150=330。
算法在找到最小成本的路径后终止,并且只检查了的四个可能值中的两个。
最终,主问题的形式可以 被写为下面的形式:
这里,我们再来尝试通过简单推导得到上述数学规划模型的最优解:下面是求解过程:
if y = city4,则可以得到
z >= 0
z >= 330 * 0 + 350 * 0 + 100 * 1
z >= 350 * 1
得到 z = 350
if y = city1,则可以得到
z >= 0
z >= 330 * 1 + 350 * 0 + 100 * 0
z >= 350 * 0
得到 z = 330
其他情况类似。
最终,得到z* = 330
上述模型也可以写成下面的形式:
3 Inference duality理论介绍
本节将简要介绍LBBD中的重要理论:Inference duality。Inference duality是对线性规划的对偶理论的推广。该理论适用于任意形式的数学规划模型[1-2]。在线性规划的场景下,原问题的Inference dual等价于传统的对偶,但是在整数规划下,模型不再有传统意义上的对偶,需要用Inference duality来获得其推断对偶。
3.1 参考文献及概述
- 参考文献:
[1] J.N. Hooker and G. Ottosson. 2003. Logic-based Benders decomposition. Math. Program., Ser. A 96, 1 (April 2003), 33–60. https://doi.org/10.1007/s10107-003-0375-9
图3:Logic-based Benders decomposition
- Benders Decomposition通过变量的取值的试验值,找到与这些值一致的最佳解。在这个过程中可以获知其他试验解决方案的质量,并使用这些信息来减少为了找到最优解而必须枚举的解的数量。这种策略可以被描述为
从错误中学习。 - 推广Benders Decomposition的关键在于将其从线性优化推广到任何类型的优化问题。这通过将LP对偶推广到
推断对偶(Inference dual)来实现。而通过从约束中推断出最优值处的有效界,则能构造出这样的对偶。 - Logic-based Benders Decomposition最有前景的应用是作为优化和约束规划问题相结合的框架。
3.2 Inference duality
Inference duality,即推断对偶。
我们先考虑一个general的优化问题:
这里,是一些general的约束定义的可行域。而和不同,其可以是一系列0-1向量定义的域,也可以是实数向量定义的域;也就是说,这里可以是一个离散的域,比如说是一个整数向量的可行空间。举一个的具体例子,.
基于此,假设和是两个命题。定义下面的概念:
若对于任意的,都成立,则命题对于任意的也成立。我们将这个关系写为
因此,原问题的对偶推断(Inference duality)就可以定义为:
经过Inference duality,原本的目标函数转化成了上面的形式。这里需要解释一下:
我们的对偶问题,就是要找到的最紧的下界。这与经典Benders Decomposition是一致的。在线性规划中,对偶和推断对偶是等价的。
在子问题为线性规划的经典的Benders Decomposition中,子问题的原问题为,子问题的对偶问题为。根据弱对偶性,我们有,因此对偶问题就是原问题的一个下界。如果强对偶成立,则有.
在general的情况,也是如此,由于整数规划并不存在像线性规划一样的对偶,因此其推断对偶的目标函数不像线性规划那样可以直接写出来,但是终究必定有一个值,我们可以用去估计这个值。我们就是希望构造子问题的原问题的Inference duality,想找到这个的最紧的下界,来加回到主问题中,即:
但是,如何得到的具体形式,是最具挑战性的地方。
3.3 线性规划问题的Inference duality
对于一个线性规划问题:
其推断对偶(Inference dual)可以被描述为
更准确的说,一个定义在上的可行的方程组,从语义上可以推出。这等价于当且仅当存在一个实数向量,满足
或者,其替代描述为,存在某个,使得替代不等式优超了不等式。
找到的方法,就是求解下面的数学规划:
上述模型等价于
而上述形式正是原问题线性规划的对偶问题。这也证明了线性规划的对偶就等价于其推断对偶。
3.4 0-1规划问题的Inference duality
对于如下形式的原问题:
我们固定了,从而可以得到子问题:
如果,那这个子问题是可以直接利用线性规划的对偶进行处理的。
但是如果,那么这个子问题就无法使用传统的线性规划的对偶的方法进行处理。想要获得目标函数的下界,就不能用弱对偶定理来对偶获得,而需要通过推断对偶(Inference dual)来获得,也就是等价于下面的方法:
根据前文的介绍,我们需要使用给定的,生成一个决策变量的函数。这个函数给出了的一个下界。
例如,给定,我们生成了一个cut:
这里,其实就是。
算法的过程如下。在每次迭代中,累计生成的Benders cuts组成了主问题的约束:
其中,是累计获得的的试验值。的下一个试验值通过求解主问题得到。如果子问题的最优值等于,则算法以最优值终止;否则,一个新的Benders cut 将被添加到子问题中。至此,重复上述过程。
下面的伪代码展示了广义Benders Decomposition的基本思路。在传统的Benders中,Benders cut通过dual value生成;但是在广义的问题中,Benders cut则是通过Inference dual生成。
图4:Logic-based Benders decomposition的伪代码(来自wenxian [2])
关于广义Benders Decomposition,有如下定理:
.假设:在广义Benders算法的每次迭代中,界的函数满足以下条件:(B1)Benders cut 有效,也就是说,原问题的任意可行解都满足:则有:如果算法在主问题中以有限最优解终止,则原问题有一个值为的最优解;如果以不可行的主问题终止,则原问题是不可行的;如果以不可行的子问题终止,则原问题是无界的。
.假设:在广义Benders算法的每次迭代中,界的函数满足(B1)和以下条件:(B2),其中是子问题的解。则有:如果是有限的,且子问题得到最优解,则广义Benders算法终止。
补充说明:一般情况下,即使子问题总是得到最优解,算法也不需要终止。考虑下面的问题:
该问题的最优解为。为了与算法保持一致,对,令:
那么,在初始情况下,,则主问题的解服从序列,则该算法永远不会终止。
但是,如果的定义域是有限的,则算法必须终止,因为只能定义有限数量的子问题。因为在除了最后一次迭代之外的每一次迭代中都必须增加,所以经过有限多次迭代才能达到最优值。
3.5 经典Benders decomposition再回顾
经典的Benders方法适用于子问题为线性的问题:
其中,是函数的向量。则子问题为:
根据经典的强对偶性,子问题的对偶为:
注意,子问题或者其对偶至少有一个需要是可行的。
如果子问题的对偶有一个有限解,则提供了一个推理过程(线性组合)来获得原问题的目标函数值的有效界。
当时取等号。
而获得任意的界的关键在于,观察到相同的在任意的对偶中都是可行的。因此,这样相同的过程(即相同的)对任意提供了一个界:
这就是经典Benders cut。很容易证明,当对偶不可行或无界时,可以得到如下的cut:
其中是一条射线,通过求解如下模型得到:
4 No-good cuts理论介绍与代码实现
4.1 大体思想
由于Inference dual一般推导起来比较复杂 ,所以实际中,可以选择使用No-good cuts来实现Logic-based Benders Decomposition。No-good cut与Inference dual的思想有巨大的区别。Inference dual是通过推断对偶评估目标函数的最紧的下界,而No-good cut是通过添加cut,将当前的非最优解割去或者排除不可行解。
首先,考虑下面的整数规划:
其中,, , , 。
我们引入连续变量来表示第二项成本。则Benders主问题()可以被重构为:
求解主问题,可以得到最优解, 接下来,子问题为:
根据子问题,可以生成2类Benders cut。
第一类:Benders Feasibility Cut
若不可行,则说明提供了不切当的。因此,需要将不恰当的割去,使得下一次提供恰当的。因此这类No-good cuts为:
这个cut的含义为:割去上一次导致不可行的。这个cut保证了,新提供的相较上一次的旧的,至少有一个决策变量的取值发生了变化。因此保证了下一次一定会产生不同的。
第二类:Benders Optimality Cut
若可行,并且的最优值的值大于,即,则需要添加下面的No-good cuts:
其中,是原问题在上的投影问题的下界。这个cut是用来提升提升的下界。下面来分析上面的cut。
如果,即下一次的x不发生变化,则上述cut为
即:的下界会被提升。
如果,即下一次的x发生变化,但仅有一个决策变量变化,则上述cut为
如果,即下一次的x发生变化,但仅有2个决策变量变化,则上述cut为
综上,上述cut不会割去最优解。
当时,原问题得到了最优解,最优值为.
注意,**No-good cut方法的最坏情况,是将主问题的所有可行解全部枚举一遍,由于整数规划的可行解的数量是有限的,所以Logic-based Benders Decomposition算法一定可以在有限步内收敛到最优解。**但是实际案例中,一般只会探索一部分主问题的可行解。
4.2 代码实现
这里,我们采用No-good cuts方法,通过Python3.7.9调用Gurobi10.0.2,对2.2中的小例子进行了代码实现。
- 例子回顾
假设有人希望以尽可能便宜的价格到达一个偏远的村庄。这一地区的四个城市都可以乘飞机到达,但从那里,人们必须通过火车、公共汽车和租赁汽车的组合来出行。为了用Benders分解来解决这个问题,假设代表乘飞机到达的城市,代表从那里到村庄的路线。总成本是到的机票(分别是到四个城市的100美元、200美元、200美元和100美元) 加上穿越路线的成本。
图1:最短路的例子:来自参考文献2
我们用 0 表示,用 5 表示。上述模型可以表示为:
Benders 主问题:
得到解之后,Benders 子问题:
迭代过程:
第一步迭代,,子问题目标函数250,因此产生的 cut 为:
第二步迭代,,子问题目标函数230,因此产生的 cut 为:
…… ……
完整的迭代过程如下:
LBBD求解案例的详细迭代过程
- 主体代码(完整代码获取方式见文末):
class LBBD(object):def __init__(self):NoneNoneNone0100001000000def set_instance(self):05def build_MIP_model(self):""" 设置算例"""""" 创建MIP模型对象"""'LBBD example'"""创建决策变量 """forin""" 创建目标函数 """forin""" 创建约束条件 """forinforin01if1if1if1'org'elif-1'des'else0'inter'""" 求解模型 """""" 输出模型的最优解 """"%6s: %5.1f""ObjVal"forin":"def LBBD_solver(self):""" 初始化 x_MP_previous """""" 构建LBBD主问题 """""" 求解LBBD主问题 """""" 更新主问题的解 """""" 计算 LB """10021while0""" 构建或者更新Benders子问题 """""" 求解BendersSP """""" 更新bounds """1002""" 展示求解日志 """""" 如果 Gap > 0, 就继续执行算法 """if0""" 生成Benders cuts """False""" 求解更新后的主问题 """""" 更新主问题的解 """""" 识别主问题的解是否变化,如果不变,则割去当前解 """ifTrueTrue""" 更新x_MP_sol_previous """""" 更新迭代的次数 """1""" LBBD 算法结束,打印结果 """def build_BendersMP(self):'BendersMP'0'eta'""" 添加第一题阶段的决策变量 """01020304forin0101f'x_{arc[0]}_{arc[1]}'""" 创建目标函数 """forin011""" 添加主问题的约束, 这里进需要添加从起点出发的约束即可 """forin0111"OutputFlag"0def initialize_x_MP_previous(self):def update_x_MP_sol(self):""" 更新主问题的解 """def check_x_MP_sol_no_change(self):""" 检查 x_MP_sol 是否变化 """def compute_LB(self):""" 该函数用于计算第二阶段的目标函数的全局下界. 该问题中很简单,只需要选出第二阶段的距离的最小值即可。 """15253545forindef build_BendersSP(self):ifNoneraise"x_MP_sol is not specified"'BendersSP'""" 添加第一题阶段的决策变量 """15253545forin0101f'x_{arc[0]}_{arc[1]}'""" 创建目标函数 """forin01""" 添加子问题的约束: 首先是 flow conservation的约束 """forin1234forin01if10forin01iff'flow_conservation_{node}'""" 必须到达目的地 """forin0111'end'"OutputFlag"0def generate_and_add_benders_cuts(self, x_MP_no_change=False):""" 生成benders cuts, 并且将其添加到bendersMP中 @param Q_opt: 子问题目前的最优解 生成的cut为 eta >= Q - (Q - LB) * (sum x + sum (1-x)) """if2orTrue""" 添加 Benders feasibility cut : sum x + sum (1-x) >= 1 """forin""" 将cut添加到主问题中 """1f'benders_fea_cut_{self.iter_cnt}'elif2""" 添加 Benders optimality cut """""" 将cut添加到主问题中 """f'benders_opt_cut_{self.iter_cnt}'def deploy_LBBD_log_head(self):"\n\n\n=========================================================="" Logic-Based Benders Decomposition ""=========================================================="' %5s |''iter'''' %6s |''MP_sol'''' %6s |''SP_sol'''' %5s |''MP_obj'''' %5s |''eta'''' %5s |''SP_obj'''' %5s |''UB'''' %5s |''LB'''' %5s |''Gap (%)'''def deploy_LBBD_log(self):' %5s '''# print(' %5s |' % 'MP_sol', end='')forinif0.5' = '' %12s '''# print(' %5s |' % 'SP_sol', end='')forinif0.5' = '' %12s '''' %6.1f '''' %6.1f '''' %6.1f '''' %6.1f '''' %6.1f '''' %6.1f '''def deploy_LBBD_summary(self):"\n\n\n=========================================================="" Logic-Based Benders Decomposition (summary) ""=========================================================="'%25s : %10s''Solution Method'"LBBD"'%25s : %10s''Status'"Optimal"'%25s : %10d''ObjVal''%25s : %10d''Gap''%25s : %10d''Iter_cnt''%25s : %10d''CPU_time'- 结果输出:
13301.00e-043.300000000000e+023.300000000000e+020.0000330.01.00.00.00.01.00.00.00.011.01.0100.00.0250.0350.0100.071.421.01.0150.050.0230.0330.0150.054.531.01.0270.070.0150.0330.0270.018.241.01.0330.0230.0230.0330.0330.00.0330050with05 小结
本文介绍了Logic-based Benders Decomposition算法,并阐述了求解此类问题的两种方式:推断对偶(Inference dual)和No-good cuts。在实际应用中,由于Inference dual推导起来较为复杂,因此现有的论文中往往使用No-good cuts来实现Logic-based Benders Decomposition。本文也基于一个小案例,对No-good cuts方法进行了代码实现。在未来的推文中,我们还将进一步介绍Inference dual中命题的可满足性问题的理论推导与应用,敬请期待。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。