基于Jupyter 实现python对拉格朗日和KKT条件求极值

python对拉格朗日和KKT条件求极值

    • 一、拉格朗日乘子法和KKT条件是两种最常用的方法概念
    • 二、手工推导方法
    • 三、利用python代码实现
    • 四、分析

一、拉格朗日乘子法和KKT条件是两种最常用的方法概念

求解最优化问题中,拉格朗日乘子法和KKT条件是两种最常用的方法。在有等式约束时使用拉格朗日乘子法,在有不等式约束时使用KKT条件。这个最优化问题指某一函数在作用域上的全局最小值(最小值与最大值可以相互转换)。

最优化问题通常有三种情况(这里说两种):

  1. 无约束条件
    求解办法是求导等于0得到极值点。将结果带回原函数验证。

  2. 等式约束条件
    设目标函数f(x),约束条件hk(x),
    min f(x)  s.t. hk(x)=0   (k = 1,2…l)
    l表示有l个约束条件。
    该类问题解决办法是消元法或拉格朗日法。消元法简单,这里讲拉格朗日法,后面提到的KKT条件是对拉格朗日乘子法的泛化。

例子:
椭球内接长方体的最大体积,即求 f(x,y,z) = 8xyz 的最大值。
方法1:消元法
根据条件消去z,然后带入函数转化为无条件极值问题。(有时这种方法麻烦,甚至解不出来)
方法2:拉格朗日乘法
思想:通过引入拉格朗日乘子将含有n个变量和k个约束条件的约束优化问题转化为含有(n+k)个变量的无约束优化问题。
首先定义拉格朗日函数F(x),f(x)为目标函数,h(x)为约束条件:
(λk是各个约束条件的待定系数)
对各个变量求偏导:

方程组的解就可能是最优化值,将结果带回原方程验证。
上面的函数,通过拉格朗日乘数法将问题转化为:

对 F(x,y,z,λ) 求偏导得到

联立前面3个方程得到 bx = ay 和 az = cx,带入第4个方程:

带入原函数得到最大体积:

为什么这么做是最优解?
举个例子:min f(x,y)  s.t.  g(x,y) = c
画出 z = f(x,y)的等高线

绿线是约束g(x,y) = c的轨迹。蓝线是f(x,y)的等高线。箭头表示斜率,和等高线的发现平行。从梯度的方向看,d1>d2(梯度下降法越接近目标,步长越小,前进越慢)。
在没有约束条件,f(x,y)的最小值是落在最里面等高线内部的某一点。加上约束条件的 f(x,y) 最小值是 f(x,y)的等高线和约束线相切的位置,因为如果只是相交意味着还存在其它的等高线在该等高线的内部或外部,使新的等高线与目标函数的交点值更大或更小,只有等高线与目标函数的曲线相切时,取到最优值。

从图看出,想让目标函数 f(x,y) 的等高线和约束 g(x,y) 相切,则他们切点的梯度在一条直线上( f 和 g 斜率平行)。
即在最优解的时候:∇f(x,y)=λ(∇g(x,y)-C) (∇为梯度算子,即:f(x)的梯度 = λ*g(x)的梯度,λ是非0常数)
即:▽[f(x,y)+λ(g(x,y)−c)]=0  (λ≠0)
那么拉格朗日函数: F(x,y)=f(x,y)+λ(g(x,y)−c) 在达到极值时与 f(x,y) 相等,因为 F(x,y) 达到极值时 g(x,y)-c 总等于0。
min(F(x,λ))取得极小值时其导数为0,即f(x)和h(x)的梯度共线。
简单的说,在F(x,λ)取最优解的时候,即F(x,λ)取极值的时候(导数为0,▽[f(x,y)+λ(g(x,y)−c)]=0)。f(x)与g(x)梯度共线,此时就是在条件约束g(x)下,f(x)的最优解。

  1. 不等式约束
    设目标函数f(x),不等式约束为g(x),等式约束条件h(x)。此时的约束优化问题描述如下:

我们定义不等式约束下的拉格朗日函数L:

其中f(x)是目标函数,hj(x)是第j个等式约束条件,λj是对应的约束系数,gk(x)是第k个不等式约束,uk是对应的约束系数。
不等式约束常用的方法是KKT条件,同样的,把所有的不等式约束、等式约束和目标函数全部写为一个式子L(a, b, x)= f(x) + ag(x)+bh(x)
KKT条件是说最优值必须满足以下条件:
L(a, b, x)对x求导为零;
h(x) =0;
a*g(x) = 0;
求取这些等式之后就能得到候选最优值。其中第三个式子,因为g(x)<=0,如果要满足这个等式,必须a=0或者g(x)=0。

在这里插入图片描述

二、手工推导方法

基于Jupyter 实现python对拉格朗日和KKT条件求极值_第1张图片

三、利用python代码实现

from sympy import *
x1,x2,k = symbols('x1,x2,k')
f = 60-10*x1-4*x2+(x1)**2+(x2)**2-x1*x2
g = x1+x2-8

#构造拉格朗日等式
L=f-k*g


#求导,构造KKT条件
dx1 = diff(L, x1)   # 对x1求偏导
print("dx1=",dx1)

dx2 = diff(L,x2)   #对x2求偏导
print("dx2=",dx2)

dk = diff(L,k)   #对k求偏导
print("dk=",dk)
dx1= -k + 2*x1 - x2 - 10
dx2= -k - x1 + 2*x2 - 4
dk= -x1 - x2 + 8
#求出个变量解
m= solve([dx1,dx2,dk],[x1,x2,k])   
print(m)
{x1: 5, x2: 3, k: -3}
#给变量重新赋值
x1=m[x1]
x2=m[x2]
k=m[k]
#计算方程的值
f = 60-10*x1-4*x2+(x1)**2+(x2)**2-x1*x2
print("方程的极小值为:",f)
方程的极小值为: 17

四、分析

同过手工推导以及python代码实现的结果一致,极小值都是17,可以验证python代码的正确性。此次实验还是很繁琐的,复杂在用python的推导的过程。

参考链接:
https://www.cnblogs.com/keye/p/10916118.html
https://www.cnblogs.com/ty123/p/10529541.html

你可能感兴趣的:(python学习,人工智能与机器学习)