MODULE 02 — Lagrange Multipliers & KKT

Lagrange 乘子與 KKT:圍欄上的最低點

玩完這關,你會把最佳化 01 關那個「谷底的梯度是 0」升級成「被圍欄夾住的時候,梯度不是 0,而是跟圍欄的法向量平行」, 而且你會知道那個比例 λ 不是計算殘渣 —— 它就是影子價格:多給你一單位資源,答案能改善多少。

① 這關在解什麼問題

上一關留下的洞

最佳化 01 關給你的判斷法是「谷底的梯度是 0」 —— 一句話複述:站在真正的最低點,你往任何方向踏一步都不會更低,所以那一點的 ∇f(往上最陡的方向)長度是 0。 這條規則很好用,因為它把「找最低點」變成「解一條方程式 ∇f = 0」。

問題是:你真實碰到的問題幾乎都有約束。預算就這麼多、產能就這麼多、 錢加起來要等於 100%、權重不能是負的。 而只要有約束,答案就幾乎永遠卡在圍欄上,不會停在那個自由的谷底。

而圍欄上那一點的 ∇f 不是 0。你手上唯一的工具,在你最需要它的地方失效了。 這關就是把工具修好。

先講具體的麻煩,不要先講數學。

你管兩條產線。A 線的產量記作 xB 線記作 y(單位:萬件/月)。 工廠有一個「最順的跑法」:A 線 2 萬件、B 線 1 萬件。偏離它就會多花錢 —— 開太慢養不起固定成本,開太快要加班、要搶共用的瓶頸工站。 這個「多花的錢」就是你的目標函數 f(x, y),它的谷底在 (2, 1),谷底的成本是 0。

到這裡最佳化 01 關的工具還夠用:答案就是谷底,做 (2, 1) 就好。然後業務進來了:

「這個月客戶要 6 萬件,兩條線加起來要湊到 6。」

現在 (2, 1) 只有 3 萬件,不合法。你被迫離開谷底。而且你有無窮多個選擇:

你不能一個一個試。你需要一條方程式,直接把答案解出來 —— 而且你還想知道第二件事:如果客戶願意少要一點,你能省多少? 這兩件事是同一個答案的兩面,那個答案叫 Lagrange 乘子 λ; 把它推廣到「不能超過 / 至少要」這種不等式,那套規則叫 KKT 條件

這關不會做的事

不推完整證明。Lagrange 的嚴格版本要談正則性條件(constraint qualification)、要談二階條件、 要區分必要與充分。這些都是真的,但它們不會讓你更會用這個工具。

這關的目標只有一個:看得懂、用得下去。你要能看著一張圖說出「所以這裡的 λ 是 2.73, 意思是客戶多要一萬件我就多花 2.73 萬」,然後在自己的問題上照著做一次。

② 先把符號攤開,再看一個最小的例子

本頁所有符號,一次講完

這一頁只有九個符號。每一個都在下表講清楚是什麼、唸什麼、現在的值是多少 (「現在的值」欄是 的預設狀態,由頁面載入時實際算出來填進去的)。

符號唸作它是什麼現在的值
x, y你能決定的兩個數字:A 線與 B 線的產量(萬件)。合起來寫成一個向量 v = (x, y)
f(v)f目標函數:你想壓到最小的東西。這裡是「偏離最順跑法多花的成本」(萬元)
mm無約束的谷底:不管訂單的話最省的跑法。這裡固定是 (2, 1)f(m) = 0(2, 1)
AA地形矩陣:決定這個碗有多陡、往哪邊拉長。跟機率統計 05 關的橢圓是同一種二次型,只是這裡它量的是成本而不是距離[[1, 0.6], [0.6, 2]]
∇fnabla f
/梯度
往上最陡的方向(一個向量)。長度=那個方向有多陡。最佳化 01 關的主角
g(v)g約束函數:被限制的那個量。這裡 g(v) = x + y=總產量
bb約束的常數:客戶要的量。約束就寫成 g(v) = b(剛好)或 g(v) ≥ b(至少)
∇gnabla g約束的法向量:垂直於那條圍欄、指向「這個量變大」的方向。g = x + y 的話它永遠是 (1, 1)(1, 1)
λlambda
/乘子
本關主角∇f∇g 的幾倍。也等於「b 多一單位,最低成本上升多少」
φ(b)phi of b最優值函數:在約束 g = b 之下能達到的最低成本。b 一變它就變
cc等高線的高度f(v) = c 這一圈上的每一點成本都是 c。最佳化 01 關的等高線見 ④
顏色語意(全站統一)
= 第一個東西:目標的梯度 ∇f   = 第二個東西:約束(那條圍欄與它的法向量 ∇g
= 結果:你現在站的點 / 最優解   = 誤差:沿線還沒榨完的下降量
= 主軸:等高線與最優解的軌跡(兩者都是 A 的二次型幾何)   = 資料點:⑥ 暴力撒出來的驗證點
跟最佳化 01 關的地形差在哪

最佳化 01 關的碗寫成 f(v) = ½(v − m)TA(v − m),這關用同一個寫法、同一個 ½, 只是係數換成 A = [[1, 0.6], [0.6, 2]]、谷底換成 m = (2, 1) —— 同族的橢圓碗,係數略不同,換數字是為了讓答案落在畫面好看的位置。

留著 ½ 的理由很實際:這樣梯度剛好沒有多餘的 2∇f(v) = A(v − m),等一下算 λ 的時候可以直接用眼睛除。

兩個寫法上的小差別,先講明白免得你以為看錯:

  • 最佳化 01 關把谷底叫 c這關叫 m —— 因為這一頁的 c 已經被「等高線的高度」佔走了。
  • 最佳化 01 關把梯度的長度寫成 ‖∇f‖,這一頁寫 |∇f|同一個意思(一個向量有多長)。

最小的例子:先砍到一維

兩條產線太多,先只留一條。成本 f(x) = (x − 2)²,谷底在 x = 2, 導數 f′(x) = 2(x − 2),在谷底是 0。最佳化 01 關的規則成立。 (這個一維例子刻意不放 ½ —— 寫成 (x − 2)² 比較好認。 下一節回到二維就會把 ½ 補回來,那時候梯度才會乾淨。)

現在加約束:客戶要 5 萬件,就這一條線,所以 x 一定要等於 5。

f′(5) = 2 × (5 − 2) = 6 ≠ 0 最低點的導數不是 0。而且你完全沒有別的選擇 —— 可行的點只有一個。

這就是整關的病灶,用最小的劑量呈現:「導數為 0」描述的是「往兩邊走都不會更好」, 但約束把你能走的方向拿掉了。在一維的等式約束下,能走的方向剩下零個,所以導數是幾都無所謂。

那 6 是垃圾嗎?不是。它就是 λ。意思是: 客戶如果只要 4 萬件,你大約能省 6 萬元(實際上省 f(5) − f(4) = 9 − 4 = 5, 差別是因為 6 只在「小幅度」下準 —— 這件事 會現場量給你看)。

所以這一段的結論是:約束下的最優點,梯度不是 0,而是「剩下的那股力氣全被圍欄擋掉了」。 擋掉多少,就是 λ接下來回到二維 —— 那裡你還有一個方向可以走,事情才真的有趣。

③ 把問題擺出來:只能沿著一條線走

回到兩條產線。地形是同一個橢圓碗,谷底在 m = (2, 1)。約束是一行字:

g(v) = x + y = b      這一節固定 b = 6 「兩條線加起來要剛好 6 萬件」。這在 (x, y) 平面上是一條直線 —— 你的所有合法選擇,就是這條線上的點。

注意這條約束跟 最佳化 03 關的 w1 + w2 + w3 = 1同一種東西:幾個數字加起來等於一個常數。那關是「錢分完」,這關是「訂單湊滿」。

這張圖在幹嘛
你會看到什麼:
左圖是成本地形:淡細圈是背景等高線(越內圈越便宜), 紫實線圈是你現在站的那一圈,橘直線是約束 x + y = 6, 小灰點是無約束谷底 m綠點是你。 藍箭頭∇f(往上最陡), 紅箭頭是「把藍箭頭壓到線上、再反向」=沿線還能往下走多少
右圖沿著線走的高度剖面:橫軸是 A 線產量 x(B 線自動是 6 − x),縱軸是成本。 左圖那條線被拉直、攤平成右圖這條曲線。
你可以動什麼:
拉滑桿,或直接用滑鼠拖左圖的綠點(它只能沿著橘線滑,拖到哪都會被拉回線上)。 右圖的光標會同步跑。
要看出什麼:
把綠點滑到紅箭頭縮成 0 的位置 —— 那就是最低點,右圖的光標剛好落在曲線谷底。 然後看讀數盤:那裡的 ∇f 長度不是 0(大約 3.87), 只有「沿線那一份」是 0。這就是最佳化 01 關的規則失效、而新規則接手的那一刻。
橘線上的每一點都合法,線外的點都不合法。谷底 m 在線的左下方 —— 它不合法,你到不了。
同一條線,攤平成一維。這條曲線是拋物線(因為 f 是二次的),所以只有一個谷底,不會有第二個。
A 線產量 x1.00
B 線自動補到 6:y = 6 − x。這就是「等式約束」的實際意思 —— 兩個數字只剩一個自由度。
所以這一段的結論是

在最低點 (4.333, 1.667)沿線的一維斜率是 0(左右踏一步都變貴), 但二維的 ∇f 不是 0(它是 (2.733, 2.733),長度 3.87)。

那個沒被抵銷掉的 ∇f 指向哪裡?它剛好垂直於約束線。 這不是巧合 —— 如果它有一點點沿著線的分量,你就還能沿線往下走一點,那就還沒到最低點。 「沿線分量 = 0」就是「∇f 完全垂直於線」。 把這件事變成幾何。

順便:我偷偷加了兩條約束,你有發現嗎

滑桿只讓 x 走到 0 和 6。那其實是兩條不等式約束: x ≥ 0y ≥ 0(產量不能是負的)。 但最低點在 (4.333, 1.667),兩個數字都是正的 —— 這兩條約束完全沒在擋你。 有約束、但不作用。 就在講這種約束該怎麼處理, 以及它為什麼跟 最佳化 03 關的「不能放空」是同一件事。

④ 相切:從碰不到,到剛好碰一點,到穿過去

③ 是「站在線上找最低」。這一節換一個看法,而它是整關的核心。

不要動點,改成動等高線。把 f(v) = c 這一圈的高度 c 從很小慢慢拉大,看它跟約束線的關係怎麼變:

一句話

從「碰不到」到「穿過去」的臨界點,就是只碰一點的那一刻 —— 那一點就是最低點, 而那裡兩條線相切。

「相切」講的是兩條曲線在該點的方向一樣。既然方向一樣,它們的法向量(垂直方向)也一樣 —— 等高線的法向量就是 ∇f,約束線的法向量就是 ∇g。 所以相切 ⇔ ∇f∇g 平行

「相切」的正式定義(切線=割線在 h → 0 的極限位置)在 微積分 · 導數關 ② 為什麼垂直於等高線在多變量關 ②這一節用幾何就講得完,那兩頁是補課用的。

這張圖在幹嘛
你會看到什麼:
紫圈是你正在控制的那條等高線 f = c橘線是約束 x + y = 6綠點是它們的交點(0 個、1 個或 2 個)。 每個交點上會長出兩支箭頭:∇f∇g兩支箭頭只畫方向,長度統一, 因為這一節只在乎它們平不平行。
你可以動什麼:
滑桿控制等高線的高度 c,從 0 拉到 8。 「跳到相切」會把 c 設成正好的臨界值。
要看出什麼:
盯著讀數盤的交點個數|sin θ|(兩支箭頭夾角的正弦,也就是外積除以長度)。 交點 2 個的時候,兩支箭頭明顯歪開、|sin θ| 是零點幾; 交點縮成 1 個的那一瞬間,兩支箭頭疊在一起、|sin θ| 掉到 0
紫圈是「花 c 元能走到的所有地方」的邊界。它第一次碰到橘線的那一刻,就是「湊滿 6 萬件」的最低價。
等高線高度 c(成本)2.00
從 0 慢慢往右拉。注意「交點 0 個」持續很久 —— 那段的意思都是「這個價錢辦不到」。
為什麼「只碰一點」一定是最低的

c 想成預算。f(v) ≤ c 是一個實心橢圓(碗被水位 c 淹掉的那一塊), 邊界就是等高線。你要的是「花得起、又碰得到約束線」的最小 c

c 太小 → 橢圓整個在線的一側,碰不到。慢慢放大 → 橢圓長大。 它第一次觸到那條線的時候,只會摸到一點(因為再小一點就完全碰不到)。 那個 c 就是最小可行成本 φ(b),那一點就是最優解。

這正是 最佳化 03 關那句「風險等高線第一次碰到可行域」的一般版本。 當時只給了你這張圖的直覺,現在你知道它其實是一個可以寫成方程式的條件。

⑤ 乘子 λ:兩支向量的比例,也是影子價格

④ 得到的條件是「兩支法向量平行」。平行寫成數學,就是一支是另一支的倍數

∇f(v) = λ · ∇g(v) ∇f(v) —— 目標函數在 v 這一點的梯度,往上最陡的方向。這關 = A(v − m)
∇g(v) —— 約束函數的梯度,就是那條圍欄的法向量。這關 g = x + y,所以永遠是 (1, 1)
λ —— 那個倍數(一個純量,可正可負)。這就是 Lagrange 乘子
整條式子讀作:「在最優點,目標的爬坡方向完全被圍欄的法向量吃掉,一點都沒剩。」

加上約束本身,你就有一組方程式,未知數是 x, y, λ

∇f(v) = λ ∇g(v)  (2 條,一個座標一條)
g(v) = b        (1 條) 3 條方程式、3 個未知數 —— 這就是「解得出來」的意思。λ 多出來的那個未知數不是負擔, 它是你多拿到的一個答案

這一節再往下會用中央差分(把 b 往左右各推 0.001)現場量 φ 的斜率來驗 λ。那個動作叫導數, 定義與「為什麼要推兩邊而不是一邊」在微積分 · 導數關 ②; 而「λ 是切線、走遠就不準」的量化版在同關 ④ 線性近似

先用眼睛除一次

在 ③ 的最低點 (4.333, 1.667),我們算過 ∇f = (2.733, 2.733), 而 ∇g = (1, 1)。兩個座標各除一次:

λ = 2.733 ÷ 1 = 2.733 (x 那一格)
λ = 2.733 ÷ 1 = 2.733 (y 那一格) 兩格算出同一個數 —— 這正是「平行」的意思。如果兩格算出不同的數,那兩支向量就不平行,那一點就不是最優解。

那 2.733 是什麼單位?

這是 λ 最有用、也最常被跳過的一段。

φ(b) 是「客戶要 b 萬件時,你最少要花多少」。它是一條曲線。 λ 就是這條曲線在當前 b 的斜率。

λ = dφ/db 白話:客戶多要一單位,你的最低成本會多多少。經濟學管這個叫影子價格(shadow price): 那條約束對你來說「值」多少。
這張圖在幹嘛
你會看到什麼:
左圖是地形俯視:橘線是約束,它會隨滑桿平移; 綠點是當前的最優解;紫虛線是最優解隨 b 移動掃出來的軌跡(是一條直線,不是巧合); 藍箭頭橘箭頭是最優解上的 ∇f∇g,永遠疊在一起。
右圖φ(b) 這條最低成本曲線:橫軸是客戶要的量 b,縱軸是你的最低成本。 綠點是當前的 (b, φ(b)),穿過它的藍直線就是斜率為 λ 的切線。
你可以動什麼:
滑桿平移約束線(改客戶要的量 b,從 0 拉到 8)。左右兩圖同時更新。
要看出什麼:
右邊讀數盤有兩個要對照的數字:公式算的 λ數值微分量出來的斜率(把 b 往兩邊各推 0.001,重新求一次最優值再除)。 兩者永遠相等到小數第六位以上 —— 這不是我在講故事,是頁面現場算給你看。 另外注意最下面那行「多接一整萬件實際多花」跟 λ 差了一成多: 影子價格只在小幅度下成立。
橘線平移的時候,綠點沿著紫虛線滑 —— 而藍、橘兩支箭頭從頭到尾都是平行的。這就是最優性條件一直被滿足的樣子。
這條曲線在 b = 3 觸底(那裡剛好穿過谷底、成本 0)。左邊斜率是負的,右邊是正的 —— 這就是 λ 的正負。
客戶要的量 b6.00
b = 3 是分水嶺:那裡約束線剛好穿過谷底 mλ = 0。
三組數字,實測對答案

把上面那個滑桿拉到 b = 4 / 6 / 8,讀數盤會給你這三行(下表是同一份程式算出來的,你可以逐格核對):

b最低成本 φ(b)λ(公式)數值斜率(Δb = ±0.001)誤差多接一整單位:成本實際變化
40.4555560.9111110.911111< 10−9 %1.366667(差 50%)
64.1000002.7333332.733333< 10−9 %3.188889(差 16.7%)
811.3888894.5555564.555556< 10−9 %5.011111(差 10.0%)

「數值斜率」那一欄是真的重新求解算出來的:頁面對 b ± 0.001 各跑一次 沿線的三分搜尋(完全不用 λ 的公式),拿到兩個最低成本再相減除以 0.002。 兩邊對得上,才代表「λ 是斜率」這句話是真的,不是我抄來的。

最後一欄是刻意放大步長:客戶一次多要一整萬件。這時 λ 就低估了實際成本 (因為 φ 是往上彎的),而且 b 越小低估越嚴重。影子價格是切線,不是弦。

λ 的正負在講什麼

λ > 0

約束在往上推你

客戶要的量比谷底的 3 還多(b > 3)。b 再變大,成本跟著變大。 這條約束在花你的錢,你會想跟客戶談少一點。

λ = 0

約束剛好貼在谷底

b = 3:約束線正好穿過 m。你不用離開谷底就達標,成本 0。 這一點很關鍵 —— 的「約束不作用」就是這個狀態的延伸。

λ < 0

約束在往下壓你

b < 3:客戶要的比你最順的跑法還,你被迫減產、也要花錢。 這時候 b 變大反而讓你更便宜,所以斜率是負的。

注意這裡的 λ 可正可負,因為約束是等式(你被釘在線上,兩邊都不能動)。 等 ⑥ 換成不等式,λ只能 ≥ 0 了 —— 原因很直白,等下就看到。

⑥ KKT:把「必須剛好」換成「不能越過」

現實裡的約束很少是「剛好等於」。業務更常說的是:

「這個月至少要出 b 萬件,多出無妨。」

g(v) = x + y ≥ b 可行的不再是一條線,而是那條線的一整側(含線上)—— 一個半平面。 你可以站在圍欄上,也可以站在圍欄後面,就是不能越過去。

這一改,事情反而變簡單了,因為只剩兩種可能:

① 沒碰到:約束不作用

谷底 m 本來就在合法區裡(b ≤ 3)。 那你根本不用理這條約束 —— 答案跟沒約束一樣,就是 m = (2, 1),成本 0。

這時 ∇f = 0,所以 λ = 0最佳化 01 關的老規則直接可用。

② 碰到了:退化成等式

谷底不合法(b > 3),你被推到圍欄上。 最優解一定正好落在線上(往裡面多走一步只會更貴),所以整個問題退化成 ③④⑤ 的等式版本。

這時 λ > 0:圍欄真的在往上推你。

兩行合起來就是「互補鬆弛」

先給「鬆弛量」一個名字:鬆弛量 = g(v) − b,意思是「你比要求多做了多少」。 合法就代表鬆弛量 ≥ 0。

λ · (g(v) − b) = 0 讀作:乘子鬆弛量這兩個數,至少有一個一定是 0。 情況 ①:鬆弛量 > 0(你已經達標而且還有餘裕),所以 λ 必須是 0。 情況 ②:λ > 0(圍欄在推你),所以鬆弛量必須是 0(你正好卡在線上)。

白話:一條不等式約束,不是綁住你、就是沒作用,不會有中間狀態。 沒有「有點擋到你」這種事 —— 要嘛你貼在圍欄上、它有價格;要嘛你離它有距離、它免費。

把三件事寫在一起,就是這關的最終工具 —— KKT 條件(Karush–Kuhn–Tucker,三個人的姓):

ⓐ 停滯: ∇f(v) = λ ∇g(v)
ⓑ 符號: λ ≥ 0,且 g(v) ≥ b(合法)
ⓒ 互補鬆弛: λ · (g(v) − b) = 0 三條合起來把「無窮多個候選」壓成「幾個可以逐一檢查的情況」。 λ 為什麼只能 ≥ 0:約束是「至少 b」,所以它只會把你往 g 變大的方向推。 推的力氣不可能是負的 —— 負的意思是「約束在幫你」,那它就不是約束了。
這張圖在幹嘛
你會看到什麼:
底的那一片是可行區x + y ≥ b), 橘線是它的邊界;灰點是谷底 m綠點是 KKT 解;紫圈是穿過解的等高線; 青色小點暴力驗證:在畫布裡用固定種子撒點、只留合法的,看有沒有人比綠點更便宜。
你可以動什麼:
滑桿把圍欄推來推去(改 b)。四顆按鈕跳到代表性位置。 「再撒一次」換一個隨機種子(種子值顯示在讀數盤最後一行,同一顆種子永遠給同一批點)。
要看出什麼:
把 b 從 0 慢慢拉到 8,盯著讀數盤那三行 KKT 條件的燈號,以及 λ 和鬆弛量的乘積永遠是 0。 在 b = 3 之前,綠點完全不動(黏在谷底)、λ 一直是 0; 過了 3 之後綠點才開始被推走、λ 才開始長大。
注意 b ≤ 3 的時候,橘線在谷底的左下方 —— 谷底在合法區裡面,圍欄根本沒碰到它。
至少要出 b 萬件2.00
把它慢慢往右拉過 3.00,看綠點是在哪一刻「開始動」的。
順便看清楚暴力法為什麼不行

青色那些點是「隨機撒、只留合法的」。約束不作用的時候(b ≤ 3), 最優解在區域內部,隨機撒點還算撒得到附近,暴力法的最好成績離真答案不遠。

約束一作用(b > 3),最優解躲在邊界線上 —— 一條線的面積是 0, 隨機撒點撒不到。你會看到暴力法的最好成績跟 KKT 解差一截,而且怎麼加點都補不上。 這就是為什麼要解方程式,而不是亂試。

回頭看最佳化 03 關那兩條約束

現在你有工具了,回去看 最佳化 03 關的投資組合問題。那裡有兩條約束, 而它們正好是這一關的兩種型別:

最佳化 03 關的約束型別作用狀況意思
w1 + w2 + w3 = 1
錢要分完
等式 永遠作用 你被釘在那張平面上,沒有「不作用」的選項。所以它的乘子可正可負, 就像 λ
wi ≥ 0
不能放空
不等式 有時作用 某一檔的最佳權重本來就是正的 → 這條不作用、乘子 0; 本來想放空(負的)→ 這條作用,那一檔被釘在 0、乘子 > 0。
回去對照一下

最佳化 03 關 ⑦ 那個解法裡有一行提示,它會告訴你 「有幾檔權重被壓到 0 —— 『不能放空』這條約束正在作用」,或者反過來說 「三檔權重都是正的,『不能放空』沒有在擋你」。

當時那句話只是描述現象。現在你知道它在講的是互補鬆弛: 「權重被釘在 0」=鬆弛量為 0 =那條約束的乘子 > 0; 「權重是正的」=鬆弛量 > 0 =那條約束的乘子 = 0。 去把那個滑桿拉一遍,看提示怎麼在兩句話之間切換 —— 你現在看得懂它為什麼只有兩種說法了。

⑦ 數學長怎樣:當工具用的操作手冊

先給程式版。這就是這一頁所有數字的來源,沒有省略:

// ---- 設定:目標與約束 ----
// f(v) = ½·(v − m)ᵀ A (v − m)      目標:要壓到最小的成本
// ∇f(v) = A(v − m)                  梯度(留了 ½ 所以沒有多餘的 2)
// g(v) = pᵀv                        約束的那個量;這裡 p = (1, 1),g = x + y
// ∇g(v) = p                         線性約束的梯度是常數向量

// ---- 等式約束 g(v) = b:直接解 ----
// 條件 ⓐ:A(v − m) = λ·p    →  v = m + λ·A⁻¹p
// 代進約束:pᵀm + λ·(pᵀA⁻¹p) = b
const s   = dot(p, matVec(inv(A), p));   // 只跟 A、p 有關的常數,算一次就好
const lam = (b - dot(p, m)) / s;         // ← 乘子
const v   = add(m, scale(matVec(inv(A), p), lam));   // ← 最優解
const phi = f(v);                        // ← 最低成本

// ---- 不等式約束 g(v) ≥ b:檢查兩種情況 ----
if (dot(p, m) >= b) {
  // 情況 ①:谷底本來就合法 → 約束不作用
  return { v: m, lam: 0 };               // 鬆弛量 > 0,所以 λ = 0
} else {
  // 情況 ②:谷底不合法 → 退化成等式,重用上面那三行
  return { v: v, lam: lam };             // 鬆弛量 = 0,所以 λ > 0
}

符號版。三條 KKT 條件,逐字翻譯:

最小化 f(v),受限於 g(v) ≥ b

ⓐ  ∇f(v*) = λ ∇g(v*)
ⓑ  λ ≥ 0, g(v*) ≥ b
ⓒ  λ · (g(v*) − b) = 0 v* —— 上標星號=「最優的那個 v」,跟一般的 v 區分開。
ⓐ 停滯(stationarity) —— 目標的爬坡方向被圍欄的法向量整條吃掉,沒有沿著可行方向的殘量。
ⓑ 符號與可行 —— 乘子不能是負的(不等式只會單向推你),而且答案本身要合法。
ⓒ 互補鬆弛 —— λ 和鬆弛量至少一個是 0。「不是綁住你、就是沒作用」。
多條約束的話,每一條配自己的 λi,ⓐ 變成 ∇f = Σi λi ∇gi,ⓒ 每一條各自成立。
為什麼叫「乘子」

因為它在傳統寫法裡是乘在約束前面的那個數。把目標和約束綁成一個函數:

L(v, λ) = f(v) − λ · (g(v) − b) L 叫 Lagrangian。對 v 偏微分設 0 就得到條件 ⓐ;對 λ 偏微分設 0 就得到約束 g(v) = b 本身。 兩條規則被塞進一個函數的「梯度為 0」裡 —— 最佳化 01 關的老工具又能用了,代價是多養一個變數 λ

這個包裝很漂亮,但你不需要它才能做事。你需要的是那三條 KKT 條件,和「λ 是影子價格」這句話。

⑧ 工程師的「原來如此」

雲端配額:λ 就是「再加一張 GPU 值多少」

你在排訓練任務。目標是壓總完成時間,約束是「這個 team 的 GPU 配額只有 8 張」。 排程器解完之後,除了「哪個 job 放哪張卡」之外,它手上還有一個數字:GPU 這條約束的 λ

那個 λ 直接回答了你去跟 infra 吵架時需要的東西:配額從 8 張加到 9 張,總完成時間會少多少。 如果 λ 幾乎是 0,代表你的瓶頸根本不是 GPU(可能是資料載入或某個序列依賴)—— 這時候再買卡是純浪費,而且這件事你光看 utilization 圖是看不出來的。

反過來,如果 λ 很大,你就有一個可以寫進提案的數字: 「多一張卡每天省 X 分鐘 pipeline 時間」。這比「感覺卡不夠」有力一百倍。 雲端計費、rate limit、connection pool 大小、Kubernetes resource quota —— 全都是同一個結構。

SVM 的支援向量,就是「碰到約束那幾點」

SVM 要找一條把兩類分開、而且離兩邊都盡量遠的線。寫成最佳化問題: 目標是最小化 ½·|w|²|w| 就是那個係數向量的長度, 壓小它等價於把邊界推寬), 約束是每一個訓練樣本一條不等式:「這一點要落在自己那一側,而且離邊界至少一個單位」。

一萬筆資料就是一萬條不等式,每一條配一個 λi。 互補鬆弛說:只有「正好貼在邊界上」的那幾點,λi 才會大於 0; 其他所有點的 λi 都是 0。

而最終解 w = Σi λi yi xi —— λi = 0 的點在這個加總裡貢獻 0。 所以一萬筆資料裡,真正決定那條線的可能只有幾十筆。那幾十筆就叫支援向量(support vector)

這也解釋了兩件 SVM 的實務現象:模型可以存得很小(只留支援向量); 以及刪掉一個非支援向量,模型完全不變(它的 λ 本來就是 0,刪了沒差)。 「support」這個字就是從互補鬆弛來的。

踩坑

坑 1:λ 不是答案,是代價

新手最常犯的錯:解完方程式,把 λ 當成答案的一部分交出去。 不是。答案是 v*(你要做什麼),λ 是價格(那條約束值多少)。

兩者單位都不一樣。這一頁 v* 的單位是「萬件」, λ 的單位是「萬元 ÷ 萬件」=每萬件的邊際成本。 把價格當數量交出去,下游一定爆。

坑 2:非凸的地形上,KKT 只給候選,不給保證

這一頁的地形是凸的(一個碗),所以 KKT 條件找到就是全域最優。 這是運氣好,不是常態。

地形一旦不凸(多個谷、鞍點、山脊),KKT 就退化成必要條件: 最優解一定滿足它,但滿足它的點不一定是最優解 —— 可能是局部最小、可能是鞍點、 甚至可能是局部最大(那裡的梯度也跟法向量平行)。

實務上的意思:你解出一組 KKT 點之後,還要逐個算 f 值比大小,或者從多個起點重跑。 「解出來了」不等於「找到最好的了」。凸不凸這件事要在動手前先確認,不是解完再說。

坑 3:約束寫錯比目標寫錯難發現

目標函數寫錯,答案通常會很離譜,你一眼就看出來。約束寫錯不會 —— 它會給你一個「看起來很合理」的錯答案。

最常見的三種:不等式方向反了 打成 ,可行區換到另一邊, 答案跑到對面去但依然「像個答案」);漏了一條(例如忘了寫非負,於是解裡出現負產量、 而下游系統很可能默默把它當 0 處理);單位不一致(一邊萬件、一邊件,差一萬倍, 約束變成幾乎不作用,λ 也就一直是 0)。

自保方式很簡單:解完之後把每條約束逐條代回去印出來 —— 印 g(v*)、 印鬆弛量、印 λ。這一頁的讀數盤就是在示範這件事。 如果每條約束的 λ 都是 0,先別高興,那通常代表你的約束根本沒接上。

坑 4:影子價格只在小幅度下成立

λ切線斜率,是「無窮小的一步」的兌換率。 拿它去估「配額直接加一倍」會錯得很難看。

那張表就是證據:b = 6 時 λ = 2.733, 但實際多接一整萬件要多花 3.189低估 16.7%;b = 4 時更慘,低估 50%。 而且方向是系統性的:凸目標的 φ 往上彎,所以 λ 永遠低估大步的成本。

更糟的情況是「作用集」變了:幅度一大,可能有另一條原本不作用的約束突然開始擋你, 這時 λ整個跳掉,連「低估」都談不上(φ 在那裡有折點,斜率不連續)。 所以拿 λ 做決策時,一律講清楚適用範圍: 「在目前配置附近,每多一張卡值 X」,不要講成「每張卡都值 X」。

⑨ 這關回答了什麼

為什麼有約束時梯度不是 0?

「梯度為 0」的真正含意是「往任何方向走都不會更低」。它成立的前提是:你真的可以往任何方向走

約束把方向拿掉了。在 x + y = 6 這條線上,你只剩兩個方向可走(沿線前後), 原本垂直於線的那兩個方向是違法的。所以最優點只需要滿足「沿線走不會更低」, 垂直方向剩下多少陡度,都無所謂 —— 因為你不能往那邊走

的一維例子是最乾淨的版本:x 必須等於 5, 可走的方向剩零個,所以 f′(5) = 6 完全不影響「5 就是答案」。 則是二維版:最低點的 ∇f = (2.733, 2.733)、長度 3.87, 但它沿線的分量是 0

相切為什麼是最優的條件?

兩種講法,同一件事。

幾何講法:f(v) ≤ c 想成「花 c 元能走到的範圍」,是一個實心橢圓。 把 c 從小放大,橢圓長大,它第一次觸到約束線的那一刻只會摸到一點 —— 再小一點就完全碰不到。 那個 c 就是最小可行成本。 的滑桿讓你親手看交點從 0 個變 1 個變 2 個。

代數講法:如果等高線在該點穿過(不是相切)約束線, 那等高線和約束線就會在該點附近分出「比較低的那一側」,你沿線往那邊走就能更便宜 —— 所以那一點不是最優。 只有相切時,沿線兩邊都是往上,才走不掉。

而相切 ⇔ 兩條曲線方向相同 ⇔ 兩條的法向量平行 ⇔ ∇f ∥ ∇g。 ④ 的讀數盤現場算兩支的 |sin θ|:把 c 拉到 8(交點 2 個)時是 0.44460.7420,拉到 5 是 0.26820.3738;按「跳到相切」的那一刻,它掉到 0(外積是 10−16 量級的浮點殘渣,讀數盤直接印給你看)。

λ 是什麼意思?

兩個意思,都要記住。

算的時候它是比例:∇f = λ∇gλ 就是「目標的梯度是約束法向量的幾倍」。 讓你直接用眼睛除:(2.733, 2.733) ÷ (1, 1),兩格都是 2.733。

用的時候它是價格:λ = dφ/db, 「約束的常數 b 多一單位,最低成本會多多少」。這叫影子價格。 ⑤ 的讀數盤把「公式的 λ」跟「重新求解再做數值微分得到的斜率」並排, 兩者對到小數第六位以上 —— 這不是類比,是同一個數字。

工程上第二個意思更有用:它直接回答「這條限制解開一點值不值得」。 λ ≈ 0 的約束,放寬它是白花錢。

λ 的正負代表什麼?

等式約束λ 可正可負,它就是 φ(b) 的斜率的正負:

  • λ > 0:b 變大會讓你更貴 —— 約束在花你的錢,你想跟客戶談少一點。這一頁是 b > 3 的情況。
  • λ = 0:約束剛好貼在無約束谷底上,鬆不鬆它都一樣。這一頁是 b = 3
  • λ < 0:b 變大反而更便宜 —— 約束是從另一邊壓你(要求太少、迫使你減產)。這一頁是 b < 3

不等式約束g ≥ b)下 λ 只能 ≥ 0。 原因很直白:不等式只會單向推你。如果算出來 λ < 0,那代表「約束在幫你」, 意思是你其實想往合法區內部走 —— 那這條約束根本沒在擋,正確答案是把它當不作用、令 λ = 0。 所以 λ < 0 在不等式問題裡不是一個答案,是一個「你猜錯作用集了」的訊號。

不等式約束跟等式差在哪?

差在「約束有沒有在作用」變成一件要判斷的事

等式g(v) = b):你被釘在那條線上,永遠作用,沒有選擇。 乘子可正可負。最佳化 03 關的「錢要分完」就是這種。

不等式g(v) ≥ b):可行區是一整片半平面。可能有兩種結局 —— 谷底本來就在裡面(不作用,答案跟沒約束一樣,λ = 0), 或谷底在外面(作用,退化成等式,λ > 0)。乘子只能 ≥ 0。 最佳化 03 關的「不能放空」就是這種。

所以解不等式問題的實際流程是:先猜哪些約束在作用,把它們當等式解, 然後檢查 ⓑⓒ 有沒有被違反;違反了就換一組猜。真正的 solver 管這個叫 active-set method最佳化 03 關 ⑥ 那個「取區間端點還是取拋物線頂點」的判斷就是它的小型手工版。

互補鬆弛白話怎麼講?

一條不等式約束,不是綁住你、就是沒作用,不會有中間狀態。

拆成兩半來記:

  • 離圍欄有距離(鬆弛量 > 0)→ 那它現在對你免費λ = 0
  • 有價格λ > 0)→ 那你一定正貼在圍欄上,鬆弛量 = 0。

寫成一行就是 λ · (g(v) − b) = 0 —— 兩個非負的數乘起來是 0,就代表至少一個是 0。

生活版:房租上限對你有沒有影響?如果你本來就只想付上限以下的錢,那條規定對你不存在(免費)。 如果它把你卡住了,那你一定正好付在上限(貼著)。不會出現「上限有點影響我但我沒付到上限」這種狀態。

的讀數盤永遠印著 λ × 鬆弛量, 你把滑桿從頭拉到尾,那個乘積一直是 0,只是「哪一個因子是 0」會在 b = 3 換手。

λ 真的可以直接從畫面上讀出來嗎?

可以,而且這一頁刻意設計成讀得出來。因為約束是 g = x + y, 它的梯度永遠是 (1, 1) —— 除以 1 等於不用除。

所以在 的讀數盤,你看到 ∇f = (2.733, 2.733) 的那一刻, λ 就是 2.733,不需要任何額外計算。兩個分量必須相等, 不相等就代表那一點不是最優解(兩支向量不平行)。

換成別的約束(例如 最佳化 01 關的 x + 2y = b∇g = (1, 2))就得真的除:λ = ∇fx / 1 = ∇fy / 2, 兩式要算出同一個數。這仍然是一個很好用的手動檢查:拿你 solver 吐出來的解, 算一次 ∇f,看它是不是 ∇g 的倍數。不是,就是哪裡錯了。

那我什麼時候該自己寫 KKT,什麼時候直接叫 solver?

約束(一兩條)而且結構簡單(線性)時,自己解通常更好:你會拿到閉式解, 沒有迭代、不會不收斂、不用調參數,而且順手拿到 λ 那段程式碼總共十行,就是完整解法。

約束一多(幾十條以上、混著等式與不等式),「猜哪些在作用」的組合數會爆炸, 這時候用現成的 QP / 內點法 solver。但你還是需要這一關 —— 因為 solver 會把 λ(常常叫 dualduals)一起回傳給你, 而看不看得懂那組數字,決定了你是在用 solver 還是在拜 solver

另外,λ 也是最好的 debug 訊號:所有 λ 都是 0 → 約束大概沒接上;某條 λ 大得離譜 → 那條約束可能單位寫錯或本身矛盾。

這關留下什麼洞 → 下一關補

工具齊了。你現在手上有:目標函數與等高線(最佳化 01 關)、梯度為 0(最佳化 01 關)、 相切條件、乘子 λ、影子價格、KKT 三條件、互補鬆弛(這關)。

但這一頁的問題是我編給你的:兩條產線、一條線性約束、一個漂亮的碗。 下一關把它套在一個真實形狀的問題上 —— 最佳化 03 關的投資組合:目標是 wTCw(用的是 03 關的共變異數矩陣), 約束是 Σw = 1(等式,永遠作用)和 w ≥ 0(不等式,有時作用)。

去的時候帶著這一關的眼睛:看到「相切」就想到 ∇f = λ∇g, 看到「某檔權重被壓到 0」就想到互補鬆弛。那頁的每一張圖你都已經看得懂了。