A data scientist munkában gyakori, hogy egy függvény értékét egy közeli pontból közelítjük: például a veszteségfüggvény meredekségét egy kis lépésközzel becsüljük, vagy egy modellpredikció simaságát ellenőrizzük. A kérdés az, hogy meddig érdemes csökkenteni a lépésközt. Ebben az alfejezetben az függvényt vizsgáljuk az pont körül, és két közelítést számolunk:
A bemenet a
lépésköz-sorozat, a megfigyelési egység egy-egy függvénykiértékelés pár,
a kimenet pedig a közelítés abszolút hibája. Előzetes becslés: a
különbségi hányados hibája nagy
-ra
körülbelül
;
nagyon kis
-ra
viszont a lebegőpontos kerekítési hiba nőni fog, így az optimális
lépésköz
nagyságrendű lesz. A cél annak felismerése, hogy a véges
float64 pontosság hol szab határt a matematikai
közelítésnek.
A lépésköz (step size, increment) egy pozitív valós szám, amellyel az pontból elmozdulunk: . A értékekből sorozatot képezhetünk, például alakban. Az sorozat konvergenciája azt jelenti, hogy van olyan határérték, hogy az eltérés minden hibakorláthoz található küszöbtől kezdve kisebb marad. Képletben:
A függvényhatárérték hasonló, csak folytonos változóval fogalmaz: azt jelenti, hogy minden -hoz van , hogy esetén . Numerikusan ez egy közelítési folyamat: a hiba addig csökken, amíg el nem érjük a számábrázolás határát.
A példánkban az
kifejezésre . Az exponenciális függvény Taylor-közelítéséből , ezért
$$B(h)=e^1\cdot\frac{e^h-1}{h}=e^1\Bigl(1+\frac{h}{2}+\frac{h^2}{6}+\cdots\Bigr).$$
A matematikai közelítési hiba tehát nagy -nál dominánsan
ami lineárisan csökken, ahogy
-t
csökkentjük. A Lebegőpont, hiányzó érték és stabil
számítás alfejezetben bevezetett float64 azonban
közbeszól.
A float64 relatív hibája
(gépi epszilon). A
kivonás numerikusan érzékeny: a számláló nagysága körülbelül
,
a kivonás abszolút kerekítési hibája pedig
nagyságrendű. A hányados numerikus hibája:
Ez csökkenésével nő. A teljes hiba két ellentétes irányú tag összege: a matematikai hiba , a kerekítési hiba . Az optimum ott van, ahol a két tag egyenlő:
A folytonosság azt jelenti, hogy kis bemeneti változás kis kimeneti változást okoz. Képletben:
Numerikusan:
-nél
;
-nál
.
A hiba arányos
-val.
A float64-ben azonban
pontosan
,
mert a mantissza nem tud különbséget tenni; ekkor a közelítés hibája
nullára ugrik, de nem azért, mert a függvény tökéletesebb lett, hanem
mert a gépi bemenet már nem változott. A folytonossági határátmenet
tehát befagy a véges pontosságnál.
A következő kód a két közelítést számolja ki, és ábrázolja a
különbségi hányados hibáját. A kód a lab projekt Python
3.12 környezetében futtatható; csak numpy és
matplotlib szükséges, adatfájl nem.
import math
import numpy as np
import matplotlib.pyplot as plt
x0 = 1.0
exact = math.exp(x0) # e^1: határérték és meredekség is
# Logaritmikusan csökkenő lépésköz: 1e-1, 1e-2, ..., 1e-16
hs = np.geomspace(1e-1, 1e-16, 16) # shape: (16,)
# 1) Függvényérték-közelítés: e^(x0+h) közelítse e^x0-t
continuity_err = np.abs(np.exp(x0 + hs) - exact) # shape: (16,)
# 2) Előremutató különbségi hányados naiv kivonással
fd = (np.exp(x0 + hs) - exact) / hs
fd_err = np.abs(fd - exact)
# 3) Stabil változat: expm1(h) = e^h - 1 pontosan kiszámítva
fd_stable = exact * np.expm1(hs) / hs
fd_stable_err = np.abs(fd_stable - exact)
print("h |e^(x0+h)-e| |fd-e| |fd_stable-e|")
for h, c, f, fs in zip(hs, continuity_err, fd_err, fd_stable_err):
print(f"{h:8.1e} {c:15.6e} {f:15.6e} {fs:15.6e}")
plt.figure(figsize=(7, 4))
plt.loglog(hs, fd_err, "o-", label="naiv (exp(x+h)-exp(x))/h")
plt.loglog(hs, fd_stable_err, "s--", label="stabil expm1(h)/h")
plt.xlabel("h lépésköz")
plt.ylabel("abszolút hiba")
plt.title("Különbségi hányados hibája: a túl kis lépésköz árt")
plt.grid(True, which="both", ls="--", alpha=0.5)
plt.legend()
plt.tight_layout()
plt.savefig("s035_step_error.png", dpi=150)
plt.show()A kimenet gépenként kissé eltérhet, de a trend minden IEEE-754
float64 környezetben azonos. Szemléltető várt sorok:
h |e^(x0+h)-e| |fd-e| |fd_stable-e|
1.0e-01 2.9e-01 1.4e-01 1.4e-01
1.0e-04 2.7e-04 1.4e-04 1.4e-04
1.0e-08 2.7e-08 9.1e-08 1.4e-08
1.0e-12 2.7e-12 6.0e-04 1.4e-12
1.0e-16 0.0e+00 2.7e+00 0.0e+00
A naiv változat hibája
körül a legkisebb, majd nő; a stabil expm1 változat a
kivonási kerekítést elkerülve a matematikai
szinten marad, amíg a lépésköz ábrázolása nem csorbul. Az alábbi chart a
naiv hiba
-transzformált
pontjait mutatja:
A pontok a csökkenő, majd növekvő hibát mutatják; a tanuló futtatása adja a tényleges, gépen mért értékeket.
A függvényérték-közelítés hibája a folytonosság miatt monoton csökken, majd -nál nulla lesz, mert a gépen . A különbségi hányados hibája ezzel szemben U alakú:
Az optimum körülbelül
,
amit a táblázat is tükröz:
-nál
a naiv hiba
,
míg
-nál
már
.
A stabil expm1 változat azért pontosabb, mert a
értéket közvetlenül, a kivonási kerekítés nélkül számolja, így a hibája
-nél
is
marad. A véges pontosság határa tehát nem maga a határérték, hanem az
adott számítási út érzékenysége.
Értelmezés. Miért ad a függvényérték-közelítés -nál nulla hibát, és miért helytelen ebből azt állítani, hogy a valós exponenciális függvény gépi kiértékelése hiba nélkül folytonos?
Implementáció. Írd át a kódot a szimmetrikus különbségi hányadosra: . Számold ki a hibát és között, és ábrázold. Mekkora az optimum nagyságrendje? (Segítség: a matematikai hiba most , a numerikus hiba továbbra is nagyságrendű.)
Hibadiagnózis. Egy kolléga azt írja: „A derivált
közelítésnél
-nel
a hiba nagyobb, mint
-nál,
ezért a kód hibás.” Melyik jelenség magyarázza ezt, és hogyan
ellenőriznéd a naiv kivonás helyett expm1-gyel?
Megváltoztatott feltétel. Végezd el ugyanezt a mérést függvénnyel az pontban, naiv különbségi hányadossal, mellett. A pontos derivált . Körülbelül mekkora hibát vársz, és miért dominál itt már a kerekítés jobban, mint az exponenciális esetben?
Transzfer. A Northstar distance_km
értékekből egy szállítási időre vonatkozó jellemző meredekségét
szeretnéd numerikusan közelíteni. A távolságok 5 és 300 km közöttiek, a
függvényértékeket lebegőpontos kilométerben tároljuk. Milyen
lépésköz-nagyságrendet javasolnál a numerikus differenciához, és miért
nem érdemes
km-t használni?
a float64-ben pontosan
,
ezért np.exp(1 + 1e-16) az np.exp(1.0) értékét
adja, a különbség nulla. Ez az ábrázolás tulajdonsága, nem a valós
függvényé: valós számokkal
lenne. A nulla hiba tehát a bemenet eldurvulásából fakad, nem a
folytonosság tökéletes gépi reprodukciójából.
A szimmetrikus hányadosra
,
ezért a matematikai hiba
nagyságrendű. A numerikus hiba továbbra is
nagyságrendű. Az optimum:
,
azaz
.
A szimmetrikus hányados tehát nagyobb lépésközzel is pontosabb, mint az
előremutató, de a túl kis
itt is ront. A kódban
fd = (np.exp(x0 + hs) - np.exp(x0 - hs)) / (2 * hs) átírást
kell elvégezni.
A jelenség a kivonási kerekítés.
-nél
a számláló
abszolút kerekítési hibája
nagyságrendű, a számláló maga pedig
;
a hányados hibája
,
ami nagyobb, mint a
-nál
várható
matematikai hiba. Ellenőrzés: ugyanazon
-ra
a stabil fd_stable = exact * np.expm1(h) / h hibája
-nél
is
nagyságrendű marad, tehát ha a naiv hiba nagy, a kivonási kerekítés
okozza, nem a kód logikája.
esetén , . A forward diff matematikai hibája . A kerekítési hiba , tehát -nál a kerekítési hiba már egy nagyságrenddel nagyobb a matematikai hibánál. A logaritmus lassabb változása miatt a hasznos jel kisebb, így a kerekítés hamarabb elnyomja.
A derivált közelítéséhez az abszolút nagyságrendje szabja a kivonási problémát. Az optimális körülbelül nagyságrendű, azaz 5 km-nél km, 300 km-nél km. Gyakorlati mérési bizonytalanság mellett érdemes – km nagyságrendű lépésközt használni, és ellenőrizni, hogy a hiba -val arányosan csökken-e. A km lépésköz a kivonási kerekítési hibát a hasznos jel fölé emeli, ezért nem javasolt.
A leggyakoribb tévedés: „minél kisebb a lépésköz, annál pontosabb a
numerikus közelítés.” Ellenpélda:
-nál
a naiv különbségi hányados hibája
,
miközben
-nál
a hiba csak
.
Ennek oka a kivonási kerekítés, amelyet a Lebegőpont, hiányzó érték és stabil számítás
alfejezetben a float64 relatív hibájánál vezettünk be. A
hibát a lépésköz csökkentése két oldalról befolyásolja: a matematikai
közelítési hiba csökken, a kerekítési hiba nő. Az optimum ott van, ahol
a kettő egyensúlyba kerül. A függvényérték-közelítésnél a túl kis
nem ront, hanem a bemenet befagy; a különbségi hányadosnál viszont a
kivonás felnagyítja a hibát. A konvergencia matematikai fogalma
tetszőleges pontosságot ígér, de a float64 csak ennek
közelítését tudja szolgáltatni.
expm1 miért nem old meg minden véges pontossági
problémát, csak kitolja a határt?distance_km esetén
km lépésközt használni numerikus deriváláshoz?Rövid válaszok: a sorozat elemei egy diszkrét
mentén közelítenek, a függvényhatárérték
folytonos változóval fogalmaz; a nagy
-s
szakaszon a hiba
,
kis
-nál
;
a kerekítési dominanciát a hiba növekedése vagy zajossága jelzi; az
expm1 a
kivonást elkerüli, de maga
is kerekített; a
km lépésköz a kivonási hibát a hasznos jel fölé emeli.
A numerikus különbségi hányados hibája nem feltétlenül csökken monoton a lépésköz csökkentésével. A csonkítási hiba és a lebegőpontos kerekítés közötti kompromisszumot például középső differenciával, több lépésközt összehasonlítva lehet megfigyelni.
A határérték közelítési folyamat: a sorozat vagy függvényérték
hibáját egyre kisebb lépésközzel csökkentjük, és a matematikai
konvergencia azt ígéri, hogy a hiba tetszőlegesen kicsi lehet. A
float64 véges pontossága azonban a lépésköz csökkentését
megfordítja: a kivonási kerekítés a különbségi hányados hibáját egy pont
után növeli. Az optimális lépésköz ott van, ahol a matematikai és a
kerekítési hiba egyensúlyba kerül; az expm1-féle stabil
átírás kitolja ezt a határt. A folytonosság szemlélete szerint kis
bemeneti változás kis kimeneti változást ad, de ha a bemenet maga már
nem változtatható a gépen, a határátmenet befagy. A következő
alfejezetben ezt a meredekséget numerikus deriváltként használjuk, ahol
a Meredekség és numerikus derivált a
hibaképletet és a stabil alternatívákat mélyíti el.