约束优化惩罚函数法:外点法、内点法与乘子法实战避坑
1. 从一个真实的调参事故说起惩罚函数法到底解决什么问题约束优化方法里的惩罚函数法说白了就是一套把带约束的难题硬掰成无约束的简单题的手法。我第一次在生产项目里用它是给一条机械臂做轨迹规划目标函数是能耗约束是关节角度限位和末端精度。当时我图省事直接把约束乘上一个很大的系数加进目标函数结果求解器返回了一个看起来收敛的点——约束违反量 0.03 弧度能耗还比预期低了 8%。我一度以为捡到了便宜直到把这条轨迹喂给运动学正解末端偏差 4 厘米整条线废掉。那次事故让我老老实实回头把惩罚函数法的数学底子重新捋了一遍。惩罚函数法Penalty Function Method的核心思想并不复杂原问题难在约束那就在目标函数里加一个违规罚款项只要你不满足约束目标值就变差满足约束罚款为零。于是带约束问题变成了一串无约束问题可以直接交给牛顿法、拟牛顿法、共轭梯度法这些成熟工具去啃。这套方法适合谁如果你在做工程优化、参数标定、控制律整定、结构轻量化、机器学习里的带约束训练或者你正在用scipy.optimize、MATLAB 的fmincon、CasADi 这类工具却对它们背后的收敛行为一头雾水那这篇内容就是给你写的。它不需要你有多深的实分析功底但需要你对梯度、Hessian、数值精度这些概念不陌生。下面我会从外点法讲到内点法再讲到乘子法最后给一份可以照着抄的完整代码和一份我自己踩坑攒出来的排查表。2. 先把问题摆正惩罚函数法的定位与数学直觉2.1 约束优化问题的标准形式与几何直觉工程里遇到的约束优化问题标准化之后基本都长这样minimize f(x) subject to g_i(x) 0, i 1, 2, ..., m h_j(x) 0, j 1, 2, ..., p其中x ∈ R^n是决策变量f是目标函数g是不等式约束h是等式约束。可行域就是所有约束同时成立的点的集合几何上是若干曲面围出来的一块区域。这里有个非常关键的认知最优解通常落在可行域的边界上而不是内部。原因很直观——如果最优点在内部说明约束根本没起作用那它同时也是无约束问题的极小点这类情况在工程里反而是少数。绝大多数时候最优点被一个或几个约束顶在边界上这些约束叫起作用约束active constraints。惩罚函数法的所有设计细节本质上都是在处理怎么让迭代点稳稳地贴到这条边界上。打个生活化的比方你要在一片有围栏的草地上找一个最低点。无约束优化相当于让你随便走闭着眼往低处滚就行带约束优化相当于围栏不能翻你只能沿着围栏内侧找。惩罚函数法的思路是把围栏换成一圈高压电网你一旦靠得太近就被电一下被电的代价换算成高度于是整个地形变成了一片连续起伏的山谷你照样可以闭着眼滚只是会自动避开电网。外点法是把电网架在围栏外侧你从外面滚过来越靠近越高内点法是把电网架在围栏内侧你只能从里面滚越靠近边界越陡。2.2 把约束折算进目标函数惩罚的基本逻辑惩罚函数法的构造方式是定义一个增广目标函数P(x, σ) f(x) σ · Φ(x)其中σ 0叫罚因子penalty parameterΦ(x)叫惩罚项penalty term。Φ(x)必须满足三个条件当x可行时Φ(x) 0当x不可行时Φ(x) 0Φ(x)随着违反程度单调增加。最常见的两种惩罚项形式是形式表达式光滑性特点二次惩罚Φ Σ[max(0,g_i)]² Σ h_j²C¹ 连续可微数值稳定收敛慢需要 σ→∞精确惩罚L1Φ Σmax(0,g_i) Σ混合形式不等式用 L1等式用二次部分光滑工程折中常见做法二次惩罚的好处是处处可导可以直接丢给 BFGS 或者 L-BFGS代价是只有 σ→∞ 时才能得到精确最优解。我上面那次事故就是因为 σ 取了个看起来很大的 1e4结果约束违反量还留在 1e-3 量级换算成角度就是 0.03 弧度。L1 惩罚也叫精确罚函数理论上更漂亮只要σ max|λ*|λ*是最优乘子一次求解就能得到精确解。但它有个致命的工程问题——在g_i(x) 0处不可导梯度会跳变用拟牛顿法求解时步长会剧烈震荡收敛变得很随机。我在实际项目里的做法通常是先用二次惩罚跑出粗略解和乘子估计再用 L1 或乘子法精修。2.3 三类主流变体的定位与选型逻辑惩罚函数法发展到现在工程上真正在用的主要是三支外点法Exterior Penalty Method又叫 SUMTSequential Unconstrained Minimization Technique。从可行域外部或任意点出发让 σ 逐步增大迭代点从外面一点点逼近边界。优点是对初始点没有要求等式约束和不等式约束都能处理缺点是 σ 大之后数值条件数恶化严重。内点法Interior Point / Barrier Method又叫障碍函数法。从可行域内部出发在边界内侧立一道势垒越靠近边界势垒越高随着参数趋近于零迭代点从里面贴到边界上。优点是中间迭代点始终可行工程上很受欢迎比如化工过程优化一旦中间点不可行就意味着设备越限缺点是必须要有严格可行的初始点而且对等式约束处理起来很别扭。乘子法Augmented Lagrangian / Multiplier Method。它在惩罚项里额外加入乘子的估计使得不需要 σ→∞ 就能收敛到精确解是真正工业级的方案。很多商业求解器和scipy的 SLSQP、trust-constr 内部都吸收了这个思想。选型上我的一般原则是入门验证用外点法过程约束必须全程可行用内点法要精度和效率用乘子法。下面按这个顺序逐一拆开讲。3. 外点罚函数法工程上最常用的入门方案3.1 罚函数的构造方式与几种常见写法外点法的标准构造是P(x, σ) f(x) σ · [ Σ_{i1}^{m} (max(0, g_i(x)))² Σ_{j1}^{p} (h_j(x))² ]注意不等式这里的max(0, g_i(x))只有当约束被违反g_i 0时惩罚才生效满足约束时罚款为零。这个max操作带来的一个细节是(max(0, g))²在g 0处是 C¹ 连续的一阶导连续二阶导跳变所以可以直接丢给拟牛顿法但不能用依赖精确 Hessian 的牛顿法。等式约束的平方惩罚有个众所周知的坑因为h²在h → 0时梯度也趋于零等式约束的收敛速度会明显慢于不等式约束同样 σ 下违反量大概是不等式的平方根关系。实践中我通常给等式约束配一个更大的初始 σ或者干脆把等式拆成一对不等式h ≤ 0和-h ≤ 0用 L1 惩罚来处理。还有一种写法是用罚因子的幂次P(x, σ_k) f(x) σ_k · Φ(x)序列{σ_k}满足σ_1 σ_2 ...且σ_k → ∞。为什么必须趋向无穷因为从 KKT 条件可以推出来只有当惩罚项的梯度足够硬才能把∇f平衡掉。3.2 序列无约束极小化SUMT的完整流程外点法的完整算法流程是这样的第 1 步给定初始点x⁰可以是任意点甚至不可行点初始罚因子σ₁ 0增长系数c 1通常取 2 到 10收敛容差ε 0令k 1。第 2 步以x^{k-1}为初值求解无约束问题min P(x, σ_k)得到x^k。这一步可以用任何无约束优化算法工程上最常用的是 BFGS 或 L-BFGS。第 3 步检查约束违反量是否满足max{ max_i max(0,g_i(x^k)), max_j |h_j(x^k)| } ε。满足就停输出x^k不满足就继续。第 4 步更新σ_{k1} c · σ_kk k 1回到第 2 步。这里有个容易被忽略但很重要的工程技巧每一步都用上一步的解作为热启动。因为σ_k和σ_{k1}之间差别不算太离谱x^k通常已经在x^{k1}的邻域里热启动能把无约束求解的迭代次数砍掉一大半。我实测下来冷启动跑 5 轮 SUMT 大约要 400 次函数评估热启动能压到 120 次左右。3.3 罚因子 σ 的增长策略为什么必须是渐进式抬高这是外点法里最考验经验的地方。σ 增长太快太慢都是坑。σ 增长太慢迭代轮数暴涨。因为违反量大致按O(1/σ)衰减不等式约束要让违反量从 1e-1 降到 1e-8σ 得跨越 7 个数量级。如果c 2那就是 23 轮每轮都要解一个无约束问题总开销很可观。σ 增长太快数值病态立刻出现。惩罚项的 Hessian 贡献是σ · ∇Φ的量级随着 σ 增大增广目标函数的 Hessian 条件数按O(σ)增长。当 σ 达到 1e8 以上双精度浮点的有效位数只剩 8 位求解器会在梯度计算里丢掉全部有效信息报出精度损失或者返回一个假收敛点。我前面那次事故本质上就是这个——σ 取 1e4 时条件数已经到 1e4 量级BFGS 的逆 Hessian 近似彻底失真。我的经验取值是场景初始 σ增长系数 c终止 σ 上限变量已归一化到 O(1)1.0101e8目标函数量级 O(1e3)1e-3~1e-25~101e6目标函数量级 O(1e6)1e-63~51e3有等式约束按上面再乘 10101e9核心原则是初始 σ 应该让惩罚项和目标函数在同一个数量级。判断方法很简单在初始点算一下f(x⁰)和Φ(x⁰)如果σ₁ · Φ(x⁰)比|f(x⁰)|小了三个数量级以上说明罚得太轻第一轮基本等于没约束反过来如果大了三个数量级说明第一轮就被罚因子主导求解器几乎只看得见约束会走得很慢。我养成的习惯是每轮打印f、σΦ和违反量三个数看它们的比例关系一眼就能判断参数是否合理。注意不要迷信σ 越大解越准。当 σ 超过某个阈值后增加它带来的是数值噪声而不是精度提升。真正想突破这个天花板就得上乘子法。4. 内点罚函数法只能从可行域内部逼近的那条路4.1 障碍函数的两种常用形式内点法只处理不等式约束构造的是障碍函数barrier functionB(x, r) f(x) r · Σ_{i1}^{m} I(g_i(x))常用的I(·)有两种倒数障碍I(g) -1/g定义域g 0。对数障碍I(g) -ln(-g)定义域g 0。两者的区别在于趋近边界的速度。倒数障碍在g → 0⁻时以1/g发散对数障碍以ln发散后者发散得更温和数值上更友好所以现代内点法包括scipy的trust-constr基本都用对数形式。倒数障碍的优点是形式简单、解析梯度好写早期 MATLAB 代码里很常见。参数r叫障碍因子序列满足r_1 r_2 ... 0且r_k → 0。r越大障碍越厚迭代点离边界越远r越小障碍越薄迭代点越贴近边界。理论保证是在适当条件下x(r)构成一条从可行域内部指向最优点的光滑路径也就是常说的中心路径central path。4.2 初始内点的获取这一步最容易被低估内点法最大的门槛是必须有一个严格可行的初始点即所有g_i(x⁰) 0严格成立。工程问题里这个点往往不好找尤其是可行域很窄的时候。我常用的三种办法第一种是问题本身自带。比如参数标定问题物理上的标称值通常就在可行域内部化工过程优化的稳态工作点一般也满足操作区间约束。这种情况下直接用就行。第二种是构造辅助问题。定义一个最大化裕度的问题max s, s.t. g_i(x) s ≤ 0先求这个得到的x就是严格可行点。这本质上是一个线性或非线性可行性问题用scipy.optimize.linprog或minimize都能解。第三种是罚函数法反过来帮忙。先用外点法跑几轮拿到一个接近可行的点再人为往里收缩一点点。这招在工程上很好用因为外点法对初值没要求跑几轮就有个不错的起点了。我试过一个气动外形优化问题可行域窄到必须用这招外点法先跑 3 轮拿到一个违反量 1e-3 的点然后沿着约束梯度反方向缩 1e-2就得到了严格内点。4.3 内点法与外点法的横向对比这两种方法经常被拿出来比我把关键差异整理成表维度外点法内点法初始点要求任意点均可必须严格可行中间迭代点一般不可行始终可行等式约束支持不能直接处理参数趋势σ → ∞r → 0⁺收敛速度线性受 σ 影响大线性到超线性取决于 r 更新数值风险σ 过大导致病态r 过小导致边界附近溢出典型适用场景一般非线性规划过程优化、参数需全程合法需要特别提醒的一点内点法里r不能降得太快。如果r_{k1} r_k / 100这种断崖式下降x^k还在中心路径的外侧下一个子问题的解会跑到很接近边界的地方数值上非常危险。稳妥的做法是r_{k1} r_k / 5到r_k / 10配合热启动。对数障碍下我一般用 0.2 到 0.5 的收缩比例。还有一个实操细节对数障碍优化出来的解通常是保守解也就是略微远离边界的可行解。如果你的工程需求是尽量贴近边界比如轻量化设计要压到应力上限障碍法就需要把r降到很小这时候数值精度又是问题。这类需求我更推荐乘子法。5. 乘子法跳出罚因子必须无穷大这个死结5.1 外点法的软肋σ → ∞ 带来的数值病态外点法的根本矛盾在于想要精确解就必须 σ → ∞但数值精度不允许 σ 无限大。这个矛盾能不能绕过去能。乘子法的洞察是KKT 条件里起作用约束的梯度是乘数倍的。∇f Σλ_i∇g_i Σμ_j∇h_j 0。如果我们在惩罚项里预先加上乘子的估计那么满足 KKT 条件需要的惩罚力度就小得多了。换句话说乘子承担了把梯度平衡掉的大部分工作罚因子只需要负责把约束违反量压到零。这就是增广拉格朗日方法Augmented Lagrangian Method也叫 PHR 方法Powell-Hestenes-Rockafellar的核心。5.2 增广拉格朗日函数的构造对于只含等式约束的问题增广拉格朗日函数是L_A(x, λ, σ) f(x) Σ_j λ_j h_j(x) (σ/2) Σ_j h_j(x)²对比一下外点法外点法只有(σ/2)Σh²乘子项是凭空多出来的。这个项的作用是在每个子问题里就把一阶最优性信息带进去。对于不等式约束形式稍复杂一点用的是 Powell-Hestenes-Rockafellar 的写法L_A(x, λ, σ) f(x) (1/(2σ)) Σ_i { [max(0, λ_i σ g_i(x))]² - λ_i² }注意这里涉及到的是(σ/2)还是(1/(2σ))的写法差异很多教材符号不统一容易看晕。我一开始就被这个坑过——按照σ/2的形式实现不等式部分跑出来的结果完全不对。判断标准很简单σ 增大时惩罚的强度应该增强而不是减弱。用(1/(2σ))的时候展开后max(0, λ σg)² /(2σ)在g 0时的主项是σ g²/2确实是随 σ 增强的这就对了。乘子的迭代更新规则是λ_j^{k1} λ_j^k σ_k · h_j(x^k) 等式约束 λ_i^{k1} max(0, λ_i^k σ_k · g_i(x^k)) 不等式约束投影保号看这个更新式它其实就是在做梯度上升——乘子在 KKT 条件的意义下本身就应该等于约束对目标的影子价格。这个形式还能看出为什么叫乘子法它同时迭代原变量x和对偶变量λ本质上是在做对偶上升。5.3 罚因子的更新策略与收敛判据乘子法最大的好处是罚因子不需要趋于无穷。实践中最常用的策略是自适应更新。每轮迭代结束后检查约束违反量有没有按预期下降if violation_k 0.25 * violation_{k-1}: σ_{k1} 2 * σ_k else: σ_{k1} σ_k这个 0.25 的阈值来自经验——如果违反量下降不到 4 倍说明单纯靠乘子更新推不动了需要加大惩罚力度。用这个策略σ 通常收敛到几百到几千量级就稳住了从来不需要冲到 1e10。我在一个 40 变量的结构优化问题上实测外点法需要 σ 到 1e9 才能把违反量压到 1e-6而乘子法 σ 到 800 就达到了同等精度而且总迭代次数少了一半。收敛判据建议同时看三个量约束违反量max |g_i^|,max |h_j|小于容差相邻两轮的目标函数变化|f_k - f_{k-1}| / max(1, |f_k|)小于容差乘子变化max |λ^{k1} - λ^k| / max(1, |λ^k|)小于容差。前两个是常规的第三个是乘子法特有的。乘子稳定下来意味着对偶问题收敛了这比单看目标函数可靠得多。6. 手把手代码实操二维约束问题完整复现6.1 问题设定与理论最优解我用一个教科书级的例子来跑通全流程这个例子在文献里被引用了无数次好处是有精确解可以对照minimize f(x) (x1 - 2)² (x2 - 1)² subject to h(x) x1 - 2·x2 1 0 g(x) x1²/4 x2² - 1 0几何上f的极小点在(2, 1)但那里不满足约束。等式约束是一条直线不等式约束是一个椭圆。先手算一遍建立信任。由等式约束得x1 2x2 - 1代入目标函数f (2x2 - 3)² (x2 - 1)² 5x2² - 14x2 10再代入不等式约束(2x2-1)²/4 x2² - 1 2x2² - x2 - 0.75 ≤ 0。先看不等号取等的情况2x2² - x2 - 0.75 0解得x2 (1 ± √7)/4取正根x2 ≈ 0.9114于是x1 ≈ 0.8229。验证它确实是最优目标函数在x2 1.4处取无条件极小但 1.4 超出了不等式的根 0.9114所以不等式确实起作用。最优解就是x* ≈ (0.8229, 0.9114)f* ≈ 1.3934。顺便求出乘子后面校核要用。一阶条件∇f λ∇h μ∇g 0∇f (-2.3542, -0.1772) ∇h (1, -2) ∇g (0.4115, 1.8228)解第一个分量-2.3542 λ 0.4115μ 0第二个分量-0.1772 - 2λ 1.8228μ 0。联立解得μ ≈ 1.847λ ≈ 1.594。这个μ值后面有用——它决定了 L1 精确罚函数的临界罚因子。6.2 外点罚函数法的完整 Python 实现代码我写得尽量直白不玩花活重点是每个参数都能对上前面讲的道理import numpy as np from scipy.optimize import minimize def objective(x): return (x[0] - 2.0)**2 (x[1] - 1.0)**2 def eq_cons(x): return x[0] - 2.0*x[1] 1.0 def ineq_cons(x): return x[0]**2/4.0 x[1]**2 - 1.0 def augmented(x, sigma): 二次惩罚的增广目标函数 g_viol max(0.0, ineq_cons(x)) # 只在违反时惩罚 h_val eq_cons(x) return objective(x) sigma * (g_viol**2 h_val**2) def violation(x): 最大约束违反量用作收敛判据 return max(max(0.0, ineq_cons(x)), abs(eq_cons(x))) # ---------- SUMT 主循环 ---------- x0 np.array([2.0, 1.0]) # 从无约束最优点出发故意不可行 sigma 1.0 c_growth 10.0 tol 1e-6 x_cur x0.copy() print(f{k:3} {sigma:10} {x1:10} {x2:10} f{violation:12} {f(x):12}) for k in range(1, 12): res minimize( augmented, x_cur, args(sigma,), methodBFGS, options{gtol: 1e-10, maxiter: 500} ) x_cur res.x v violation(x_cur) print(f{k:3} {sigma:10.1e} {x_cur[0]:10.4f} f{x_cur[1]:10.4f} {v:12.3e} {objective(x_cur):12.6f}) if v tol: print(f\n收敛于第 {k} 轮sigma {sigma:.1e}) break sigma * c_growth这段代码有几个细节值得单独说。g_viol max(0.0, ineq_cons(x))这一步看起来平淡但它保证了可行时惩罚项严格为零。如果图省事写成ineq_cons(x)**2那即使在可行域内部g 0也会贡献正的惩罚值相当于在可行域内部人为抬高地形最优点会被推向边界甚至推出可行域。这是个新手高频错误我在代码评审里至少见过五次。x0我故意选在(2, 1)也就是无约束最优点这是个完全不可行的点。外点法的好处就是这里——它不挑初值。gtol1e-10是无约束子问题的梯度容差。这个值不能太松因为随着 σ 增大增广函数的梯度尺度也在变如果容差设成 1e-6到了 σ 1e6 那一轮求解器可能在真正的极小点还差得远的时候就宣布收敛了。6.3 运行结果与迭代记录解读跑出来的结果大概是这样数值随求解器版本略有浮动kσx1x2约束违反量f(x)11e01.15201.07604.89e-010.724921e10.88400.94208.27e-021.248831e20.82980.91498.90e-031.376641e30.82360.91189.02e-041.391751e40.82300.91159.13e-051.393361e50.82290.91159.20e-061.393471e60.82290.91149.27e-071.3934几个观察点都是可以直接拿去指导实践的第一违反量的衰减确实近似O(1/σ)。从 σ 1 到 σ 1e2违反量从 4.89e-1 降到 8.90e-3跨了两个数量级 σ 对应两个数量级违反量。这个线性关系是二次惩罚的标志。这意味着想让违反量降到 1e-6σ 得有 1e6 的量级和我前面说的一致。第二目标函数值从下方逼近真值。第 1 轮f 0.7249远小于真值 1.3934。这是因为迭代点还飘在可行域外那里的f本身就更小。随着 σ 增大f单调上升并收敛到 1.3934。所以外点法的一个诊断信号是目标函数值应该是单调逼近的如果出现来回震荡说明 σ 增长太快或者初值太差。第三第 2 轮的x1是 0.8840比第 1 轮的 1.1520 反而更远了。第一次看这个序列容易困惑其实是因为第 1 轮的解主要在满足等式约束h残差小不等式违反得很厉害第 2 轮 σ 增大后不等式违反被压下来x沿着直线往椭圆边界上靠x1因此下降。这条路径挺有意思——迭代点不是在可行域里走而是在不可行但越来越贴近的方向上走。需要提醒的是到了第 6、7 轮σ 已经到 1e5 和 1e6BFGS 的逆 Hessian 近似开始明显失真我这边的实测是在 σ 1e8 左右 BFGS 会开始报Desired error not necessarily achieved due to precision loss。这不是代码 bug是方法本身的极限。6.4 用乘子法对照验证同样的精度小三个数量级的 σ把同一个问题用增广拉格朗日方法再跑一次对比会有说服力def aug_lagrangian(x, lam, sigma): g ineq_cons(x) h eq_cons(x) val objective(x) lam[0]*h 0.5*sigma*h**2 # 等式部分 t lam[1] sigma * g # PHR 不等式部分 if t 0: val (t*t - lam[1]*lam[1]) / (2.0*sigma) return val lam np.array([0.0, 0.0]) # 乘子初值从零开始 sigma 1.0 x_cur np.array([2.0, 1.0]) tol 1e-6 prev_v np.inf for k in range(1, 25): res minimize(aug_lagrangian, x_cur, args(lam, sigma), methodBFGS, options{gtol: 1e-10}) x_cur res.x g_val ineq_cons(x_cur) h_val eq_cons(x_cur) v max(max(0.0, g_val), abs(h_val)) # 乘子更新 lam[0] lam[0] sigma * h_val lam[1] max(0.0, lam[1] sigma * g_val) if v 0.25 * prev_v: # 下降不够快加大惩罚 sigma * 2.0 prev_v v if v tol: print(f乘子法 {k} 轮收敛: x {x_cur}, fsigma {sigma:.1f}, lam {lam}) break跑出来大概 8 到 12 轮收敛最终 σ 停在 256 或者 512λ收敛到(1.594, 1.847)附近。这个结果和我前面手算的解析乘子完全吻合是个很好的自检信号。如果乘子迭代收敛到的值和你的解析解差得远八成是实现里符号搞错了或者不等式部分的1/(2σ)写成了σ/2。把两个方法摆在一起对比指标外点法乘子法达到 1e-6 精度所需 σ1e6512总迭代轮数7约 10每轮无约束求解的迭代次数随 σ 增大明显增加基本稳定最终乘子估计无有且可验证数值稳定性后期出现精度损失告警全程稳定乘子法轮数多一点但每轮子问题好解得多总函数评估次数反而更少。更关键的是它给出了乘子——在工程里乘子就是影子价格能直接回答如果把这个约束放宽 1%目标函数能改善多少这个问题。这个信息在参数敏感性分析里价值极高外点法给不出来。7. 常见问题排查实录与避坑清单7.1 典型症状与排查路径速查下面这张表是我这几年攒下来的基本覆盖了 90% 的报错场景症状大概率原因处理办法求解器报 precision lossσ 过大条件数爆炸降低 σ 上限改用乘子法约束违反量降不下去σ 初值太小或增长太慢提高初始 σ增长系数改到 5~10目标函数来回震荡不收敛σ 增长过快子问题没解透增长系数降到 2~3收紧子问题 gtol迭代点跑出可行域很远惩罚项写成了g²而非max(0,g)²检查惩罚项构造等式约束始终差一点点平方惩罚对等式太软σ 单独放大或改用乘子法内点法第一步就报不可行初始点不严格可行先解可行性辅助问题内点法中途数值溢出r 降得太快贴到边界r 收缩比改为 0.2~0.5解出来看着对但物理上荒谬约束量纲不统一按数值大小失衡每个约束乘归一化尺度乘子法震荡乘子更新步长过大减小 σ或对乘子更新加阻尼结果依赖初值问题非凸落到不同局部解多初值重启取最优7.2 只有踩过才懂的五条实操心得第一条约束归一化比调参重要十倍。我做过一个结构优化问题三个约束分别是应力量级 1e8 Pa、位移量级 1e-3 m、频率量级 1e1 Hz。不归一化的话应力约束的违反量在数值上比位移大 11 个数量级惩罚项完全被应力主导位移约束形同虚设。处理办法是把每个约束除以其容许值g_norm g / g_allow让所有约束都变成量级 1 的相对违反。这一步做完同样的 σ 序列收敛速度快了一倍多。第二条别把 σ 的终值和收敛容差绑死。很多人习惯写σ 到 1e8 就停这是错的。停止条件应该看违反量σ 只是手段。我见过一个案例σ 到 1e8 时违反量还挂在 1e-4原因是这个问题的乘子特别大二次惩罚把它压不下来正确的做法是换 L1 或者乘子法而不是继续加 σ。第三条精确罚函数的临界罚因子可以先估算再取值。L1 惩罚需要σ max|λ*|。虽然不知道精确乘子但可以用二次惩罚跑一轮用λ ≈ σ·g做粗略估计。我上面那个例子里取 σ 50 就足够了真值 1.847。工程上我一般取估计值的 5 到 10 倍留裕度这样一次求解就能拿到精确解省掉整条 SUMT 序列。第四条L1 罚函数配拟牛顿法要用光滑化技巧。|g|在零点不可导直接用会让 BFGS 的线搜索反复失败。我在实际项目里用这个近似|g| ≈ sqrt(g² ε²)ε 取 1e-8 到 1e-6。这个函数处处可导在|g| ε时几乎等于|g|在零点附近被平滑成一个宽度 ε 的小球。实测下来收敛稳定性提升非常明显代价是最优解会有O(ε)量级的偏差。ε 取 1e-8偏差完全在工程容差内。第五条热启动 缩小子问题迭代上限的组合最省时间。这条听起来反直觉但很有用。SUMT 早期轮次的解不需要太精确因为下一轮 σ 变大后这个解马上就会被洗掉。我的做法是前几轮把子问题的maxiter限死在 20 到 50gtol放到 1e-6等到违反量降到 1e-3 以下再放开到gtol1e-10。这套组合在一个 60 变量的气动优化问题上省了大概 40% 的总时间最终精度完全一样。注意如果问题是高度非凸的SUMT 的每一轮子问题本身也可能有多个局部解。这时候热启动反而可能把你锁在一个坏分支上。稳妥做法是每隔 3 到 4 轮用当前解加一个小扰动重启一次子问题取目标值更好的那个。8. 一点个人体会惩罚函数法这套东西表面上看是数学实际用起来更像调参手艺。我最早接触它的时候满脑子想的是找一个万能的 σ后来才明白根本不存在——每个问题的目标函数量级、约束尺度、乘子大小都不一样σ 的合适区间能差六七个数量级。真正的转折点是理解了乘子的意义。乘子就是约束的影子价格它告诉你这个约束值多少钱。一旦你从调参数让方法收敛切换到估计影子价格让方法收敛很多原来玄学的东西就变得可预测了。这也是为什么我现在做任何约束优化第一件事都是先用二次惩罚跑几轮估一下乘子量级再决定后面的参数策略。最后一个技巧如果你的问题规模不大变量在 100 个以内别急着上复杂的算法先用外点法把整条路径打出来看看。f、σΦ、违反量这三条曲线画在一起问题的结构一目了然——是约束太强、目标太陡还是尺度失衡看一眼就知道。我在实际工作中靠这个习惯避开的坑比靠读论文避开的还多。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →