JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

前向誤差與後向誤差

用兩種誠實的方式去問「我的答案錯得有多離譜?」——一種量輸出端的落差,另一種量你得把輸入擾動多少,才能讓你算出的答案恰好正確。學會「往回想」,是讓往後整個數值分析豁然開朗的那一個關鍵心態轉變。

對同一個錯答案的兩個問法

你手上有一個問題,它的真實答案是 y,而電腦交給你的是一個近似值 y_hat。最直覺的問法是:y_hat 離 y 有多遠?這個落差就是 前向誤差——你在輸出端看得到的誤差。它正是你真正在乎的東西,卻往往正是你量不到的,因為如果你早知道真實的 y,當初根本不必去跑這個演算法。前幾篇把 條件數 一路鋪了起來;現在我們來認識這個故事的另一半。

於是數值分析師改問一個更狡猾的問題:我算出的答案 y_hat,是「哪一個相鄰問題」的精確答案?想像把輸入資料輕輕推一下,剛好讓 y_hat 成為被推過後那個問題的完全正確解。這個最小推動量的大小,就是 後向誤差。它把整個視角翻轉過來:不再問「我的輸出錯多少?」,而是問「我的輸入得錯多少,才會讓這個輸出變成對的?」

為什麼後向誤差才是你真正算得出來的

後向問法的魔力在於:你常常不必知道真實的 y 就能回答它。以線性方程組 A x = b 為例。你的求解器回傳 x_hat。你無法把它跟精確的 x 比較,但你可以用手邊已有的算術去算出 殘差 r = b - A x_hat。殘差小,就代表 x_hat 恰好是稍受擾動的方程組 A x = b - r 的精確解,也就是同一個矩陣、右端略微不同。你量得到的殘差,在縮放意義下,正是你想要的後向誤差。

這正是為什麼後向誤差是這個領域的實用通貨。前向誤差需要那個未知的答案;後向誤差往往只需要資料與你算出的結果,而這兩者都在你手上。一個後向誤差極小的求解器,給出的是一個強而可驗證的承諾:「我沒有解你那個問題,但我解了一個在你資料本身的不確定度層級上、與它無法區分的問題。」對於來自帶雜訊的感測器、或某個被四捨五入的常數的資料而言,這往往就是你所能擁有的全部真相。

一個小小的實作範例

假設真正的問題是計算 sqrt(2),所以 y = 1.41421356...。你的程式回傳 y_hat = 1.41421。前向誤差就是單純的輸出落差,約 3.6e-6。現在問後向問題:1.41421 是哪個數的精確平方根?它正是 1.41421^2 = 1.99998... 的精確平方根。換句話說,你的答案是 1.99998(而非 2.0)的完全正確平方根。後向誤差——也就是輸入得移動多遠——約為 1e-5。你對一個幾乎正確的問題,算出了一個毫無瑕疵的答案。

true input x = 2.0          exact output  y     = 1.41421356...
computed   y_hat = 1.41421

forward error  = |y_hat - y|          = 3.6e-6   (gap in the OUTPUT)
backward error = |x - y_hat^2|        = 1.0e-5   (gap in the INPUT)
               y_hat = sqrt(x + delta_x),  delta_x = -1.0e-5
同一個近似值,分別用前向(在輸出端)與後向(在輸入端)來度量。

注意這兩個數並不相同——這裡是 1e-5 對上 3.6e-6——而它們之間的比值並非偶然。它由平方根在 x = 2 附近對輸入有多敏感所決定,而那正是一個 條件數。這個比值是連接兩個世界的橋樑,也是我們接下來要拼出的那條主規則的核心。

把兩者綁在一起的主規則

這就是本階梯所圍繞的那一條不等式,經驗誤差界:前向誤差 <= 條件數 x 後向誤差(全都以相對量計)。條件數屬於問題;後向誤差由演算法產生。把兩者相乘,你就用兩個各自掌握得住的量,替你真正想要的東西——前向誤差——定了上界。平方根那個例子就服從它:一個 1e-5 的微小後向誤差,被問題的敏感度放大,得到你看到的那個小前向誤差。

把這條規則讀成一份預算。你的演算法只掌管它那一半:把後向誤差壓到接近 單位捨入——雙精度 下約是幾倍 1e-16——它就已經做到了一個演算法所能做的一切。條件數則被你拿到的那個問題鎖死——你沒得討價還價。如果這個數是 1e8,規則就說:不論程式多麼無瑕,你都可能在約 16 位誠實的十進位數字裡損失約 8 位。這條界,正是為什麼「我的程式正確」和「我的答案準確」是兩個真正分開的主張。

  1. 估計你算出的答案的後向誤差——對 A x = b,把殘差縮放:||r|| / (||A|| ||x_hat||)。這是演算法的成績單。
  2. 估計問題的條件數——對 A x = b 就是 矩陣條件數 cond(A),也就是 A 把相對擾動放大多少倍。
  3. 把兩者相乘得到前向誤差的估計:x 的相對誤差約為 cond(A) x(後向誤差)。這就是你對準確度的誠實預期。
  4. 解讀它:乘積小就信任那些位數;乘積大則警告你,就算演算法表現完美,答案仍可能很糟。

兩種看起來相同、其實不同的失敗

現在分開這兩種誤差的回報來了:它能診斷一個答案為什麼糟。假設你的前向誤差很大。這條規則只給出兩名嫌犯。要嘛後向誤差很大——你的演算法不穩定、草率、把本該吸收的捨入給放大了——這是你的錯,換個更好的方法就能修。要嘛後向誤差極小、但條件數極大——是問題本身 病態,在這個精度下地球上沒有任何演算法能做得更好。同一個症狀,相反的解方。

這也是為什麼在病態問題上追逐一個「更準確」的函式庫是白費力氣——你把問題的罪過怪到了演算法頭上。誠實的做法是分別算出後向誤差與條件數。如果後向誤差已經接近捨入水準,換求解器一點好處都換不到;你得改去重新表述問題、蒐集條件較好的資料,或改用更高精度來擴大那份正被條件數吞噬的位數預算。先診斷,再治療。

一個總能達成微小後向誤差的演算法——它算出的每個答案,都是某個只與你的問題相差一個捨入級推動量的問題的精確解——便贏得這領域最高的讚譽:它是 後向穩定的。這件事、以及為什麼它是你能合理要求的極限,正是下一篇要去的地方。