7.4 Numerikus stabilitás és jelentésmegőrző implementáció

7.4.1 1. Cél és induló kérdés

Ez az alfejezet a 7.3. Bináris, multilabel és regressziós cél alfejezetben bevezetett loss-szerződéseket vizsgálja a lebegőpontos implementáció oldaláról. Cél, hogy a hallgató meg tudja mondani, miért ad egy naiv képlet véges helyett NaN vagy inf értéket nagy logitokra, és hogyan őrizhető meg a matematikai jelentés a cross-entropy, a BCE és a kapcsolódó számítások során. Előismeret: alapvető Python/NumPy, logits és target alakok, softmax és BCEWithLogits definíciója, valamint a float32 alapvető korlátai.

Induló kérdés: ha z=[500,501,−500]z=[500, 501, -500], akkor a σ(z)i=ezi/∑jezj\sigma(z)_i=e^{z_i}/\sum_j e^{z_j} képlet matematikailag jól definiált. Miért számíthat mégis NaN-t egy float32 NumPy implementáció, és melyik egyetlen kivonás teszi stabillá?

7.4.2 2. Naiv softmax, overflow és a log-softmax

Legyen Z∈ℝN×KZ\in\mathbb R^{N\times K} logit-mátrix, egy sor z∈ℝKz\in\mathbb R^K. A softmax definíciója:

σ(z)i=ezi∑j=1Kezj,i=1,…,K. \sigma(z)_i=\frac{e^{z_i}}{\sum_{j=1}^{K}e^{z_j}},\qquad i=1,\ldots,K.

A cross-entropy logitsból közvetlenül a log-softmax negáltját használja:

logsoftmax(z)i=logσ(z)i=zi−log∑j=1Kezj. \operatorname{logsoftmax}(z)_i=\log \sigma(z)_i =z_i-\log\sum_{j=1}^{K}e^{z_j}.

Programozói értelemben a két alak nem ugyanazt a műveleti sorrendet jelenti. A naiv út először exponenciálist számol: float32-ben e500≈1,4⋅10217e^{500}\approx 1{,}4\cdot10^{217}, ami meghaladja a legnagyobb normális float32 értéket (≈3,4⋅1038\approx 3{,}4\cdot10^{38}), ezért inf lesz. A számláló és a nevező is inf, a hányados NaN; a logaritmus sem menti meg. Az underflow ellentétes irányú: e−500e^{-500} float32-ben 0, így a log0\log 0 -inf. Mindkettő az exponenciális függvény dinamikus tartományából fakad, nem a loss matematikai hibájából.

7.4.3 3. Logsumexp konstanseltolási azonosság

A stabilitás alapja egy azonosság, amelyet most tételként mondunk ki és bizonyítunk. Jelölje

lse(z)=log∑j=1Kezj \operatorname{lse}(z)=\log\sum_{j=1}^{K}e^{z_j}

a log-sum-exp függvényt.

Tétel (logsumexp eltolási azonosság). Legyen z=(z1,…,zK)∈ℝKz=(z_1,\ldots,z_K)\in\mathbb R^K és c∈ℝc\in\mathbb R. Ekkor

lse(z)=c+log∑j=1Kezj−c. \operatorname{lse}(z)=c+\log\sum_{j=1}^{K}e^{z_j-c}.

Bizonyítás. A jobb oldalból indulunk ki. A kitevő azonossága miatt ezj−c=e−cezje^{z_j-c}=e^{-c}e^{z_j}, tehát

c+log∑jezj−c=c+log(e−c∑jezj). c+\log\sum_{j}e^{z_j-c} =c+\log\!\left(e^{-c}\sum_{j}e^{z_j}\right).

A logaritmus szorzat-azonossága szerint a log a szorzatból összeg lesz:

c+loge−c+log∑jezj. c+\log e^{-c}+\log\sum_{j}e^{z_j}.

Mivel a természetes logaritmus és az exponenciális egymás inverze, loge−c=−c\log e^{-c}=-c. Így

c−c+lse(z)=lse(z). c-c+\operatorname{lse}(z)=\operatorname{lse}(z).

A feltételek pontosan a következők: zjz_j véges valós számok, cc véges valós, a logaritmus argumentuma pozitív — ez teljesül, mert et>0e^t>0 minden t∈ℝt\in\mathbb R-re. A bizonyítás befejezett.

Stabil választás. Válasszuk c=maxjzjc=\max_j z_j értéket, és jelölje mm ezt a maximumot. Ekkor zj−m≤0z_j-m\le 0, ezért 0<ezj−m≤10<e^{z_j-m}\le 1, és az összeg s=∑jezj−ms=\sum_j e^{z_j-m} a [1,K][1,K] intervallumba esik. A logs\log s véges, nem overflowol. A gyakorlatban tehát

lse(z)=m+logs,s=∑j=1Kezj−m, \operatorname{lse}(z)=m+\log s,\qquad s=\sum_{j=1}^{K}e^{z_j-m},

a log-softmax pedig

logsoftmax(z)i=(zi−m)−logs. \operatorname{logsoftmax}(z)_i=(z_i-m)-\log s.

A softmax maga, ha a predikciót is ki akarjuk írni,

σ(z)i=ezi−ms, \sigma(z)_i=\frac{e^{z_i-m}}{s},

ugyancsak a max-eltolással stabil.

7.4.4 4. A CrossEntropyLoss bemeneti és redukciós konvenciói

A PyTorch nn.CrossEntropyLoss dokumentációja [D07] szerint a számítás log-softmax és negatív log-likelihood kombinációja; a függvény logitsot vár, tehát a hívó nem alkalmaz előtte softmaxot, és osztályindex-céloknál a cél int64 tömbje (N,)(N,) alakú; alternatívaként (N,K)(N,K) alakú, osztályonkénti valószínűségi cél is megadható megfelelő lebegőpontos típussal. Az egyszerű főútban az int64, (N,)(N,) alakot használjuk. A dokumentáció által garantált reduction érték mean, sum és none; az itteni példák mean-t használnak.

A laborban a könyvtári kimenetet float64-es, külön megírt számításhoz hasonlítjuk: ez a dokumentált bemeneti és redukciós viselkedés numerikus ellenőrzése, nem a könyvtár belső implementációjának bizonyítása. A matematikai azonosság, amelyet fent bizonyítottunk, a stabil implementáció alapja.

7.4.5 5. log1p és a BCEWithLogits stabil alakja

A 7.3. Bináris, multilabel és regressziós cél alfejezetben bevezetett BCEWithLogits veszteség naiv alakja

ℓ(z,y)=−ylogσ(z)−(1−y)log(1−σ(z)). \ell(z,y)=-y\log\sigma(z)-(1-y)\log(1-\sigma(z)).

Ha zz nagyon negatív, σ(z)\sigma(z) float32-ben nullává válik, és a log\log -inf-et ad; ha zz nagy pozitív, a 1−σ(z)1-\sigma(z) tag nullázódik. A stabil formula az yy-tól függő algebrai átalakítást használ:

ℓ(z,y)=(1−y)z+softplus(−z), \ell(z,y)=(1-y)z+\operatorname{softplus}(-z),

ahol

softplus(t)=log(1+et). \operatorname{softplus}(t)=\log(1+e^t).

A softplus\operatorname{softplus} számítására a

softplus(t)=max(t,0)+log(1+e−|t|) \operatorname{softplus}(t)=\max(t,0)+\log(1+e^{-|t|})

alak praktikus, mert e−|t|≤1e^{-|t|}\le 1 miatt a logaritmus argumentuma [1,2][1,2]-ben van. Az e−|t|e^{-|t|} azonban kis pozitív tt-re majdnem nulla, és a 1+1+ hozzáadása után a log elveszítheti a kis tagot. Erre való a log1p függvény:

log1p(x)=log(1+x), \operatorname{log1p}(x)=\log(1+x),

amely kis |x||x| esetén lényegesen pontosabb, mint a naiv np.log(1+x), mert a könyvtár közvetlenül a xx tag alapján, nem az 1+x1+x kerekített értékéből számol. Például x=10−16x=10^{-16} esetén log1p(x) körülbelül 10−1610^{-16}, míg 1+x float64-ben kerekítve 11, a log pedig 00. A BCEWithLogits stabil implementációjának levezetése a 7.3. Bináris, multilabel és regressziós cél feladatanyagában szerepelt; itt a log1p a megvalósítási eszköz.

7.4.6 6. Clipping: vágás a jelentés megőrzésére

A clipping vágás azt jelenti, hogy egy értéket korlátos intervallumba szorítunk. Ha a felhasználó a predikált valószínűséget pp-ként naplózza, és abból később logp\log p-t vagy entrópiát számol, akkor a p=0p=0 érték -inf-et adna. Egy lehetséges gyakorlat

p̃i=min(max(pi,ε),1−ε), \tilde p_i=\min\bigl(\max(p_i,\varepsilon),\;1-\varepsilon\bigr),

ahol ε\varepsilon kicsi, tipikusan 10−710^{-7} körüli. Ez a jelentéskészítés vágása: biztosítja, hogy a lementett predikció logaritmusa véges legyen.

Fontos határ: a tanítási loss logitjait nem szabad így vágni. Ha a cross-entropy bemenetét clipeljük, megváltozik a gradiens jelentése: a háló többé nem a valódi logits alapján tanul, és a kalibráció is torzul. A vágás tehát output-utófeldolgozás, nem a numerikus stabilitás tanítási trükkje. Ez heurisztika, mert az optimális ε\varepsilon feladatonként eltérhet; ellenőrzési tervet igényel.

7.4.7 7. Mixed precision előkép

A float16 számformátum tartománya jóval szűkebb: a legnagyobb normális érték ≈65504\approx65504, a legkisebb pozitív normális ≈6,1⋅10−5\approx6{,}1\cdot10^{-5}, a subnormalis tartomány még kisebb. Ezért float16-ban már z=12z=12 esetén e12≈162755e^{12}\approx162755, ami túlcsordul. A logsumexp max-eltolása itt még kritikusabb, mint float32-ben.

Az automatikus kevert pontosság (AMP) egy gyakori lépése a loss scaling: mivel a hátraterjesztés során a veszteség gradiense gyakran olyan kicsi, hogy float16-ban nullává válna, a veszteséget egy nagy skalárral megszorozzák a skálázott hátraterjesztéshez, majd a frissítés előtt visszaosztanak. Ez a jelen egységben csak előkép; a teljes GPU/AMP-labort a 24.3. Mixed precision és nagyobb batch tárgyalja. A cél itt az, hogy a hallgató tudja: a stabil matematikai formula a float16 környezetben is ugyanaz, de a tartományhatárok miatt az ellenőrzés szigorúbb.

7.4.8 8. Eredeti kézi kis példa

Legyen z=[500,501,−500]z=[500,501,-500], a helyes osztály indexe y=2y=2 (azaz a második komponens, mert a Python-indexelés 1-től számított második eleme az 501). A maximum m=501m=501. A max-eltolás után

z−m=[−1,0,−1001]. z-m=[-1,0,-1001].

Az exponenciális összeg:

s=e−1+e0+e−1001≈0,367879+1+0=1,367879. s=e^{-1}+e^{0}+e^{-1001} \approx 0{,}367879+1+0 =1{,}367879.

A log-sum-exp:

lse(z)=501+log1,367879≈501+0,313262=501,313262. \operatorname{lse}(z)=501+\log 1{,}367879 \approx501+0{,}313262 =501{,}313262.

A log-softmax:

z−lse(z)=[−1,313262,−0,313262,−1001,313262]. z-\operatorname{lse}(z)=[-1{,}313262,-0{,}313262,-1001{,}313262].

A második komponens negáltja, a veszteség ℓ≈0,313262\ell\approx0{,}313262. Ellenőrzésképp a softmax valószínűségek:

ez−ms≈[0,268941,0,731059,0], \frac{e^{z-m}}{s}\approx[0{,}268941,0{,}731059,0],

amelyek összege 11 kerekítési hibán belül. A naiv float32 softmax ezzel szemben inf/inf = NaN-t adna.

7.4.9 9. Labor: naiv NumPy, stabil NumPy és könyvtári loss

A labor célja a három implementáció összevetése egy f64 referencia ellen. A következő kimenet számított szemléltetés, nem mérési jegyzőkönyv; a kódot a laborban kell végrehajtani.

Munkakönyvtár: lab/. Futtatási parancs:

python n028_stability.py

A szkript:

import numpy as np
import torch
import torch.nn as nn

# Seed, dtype, device
rng = np.random.default_rng(20261028)
device = "cpu"

# Input: N=4, K=3 logits, float32; target int64
logits32 = np.array([
    [ 500.0,  501.0, -501.0],
    [   2.0,    1.0,    0.0],
    [-500.0,  500.0,  500.0],
    [  10.0,  -10.0,   20.0],
], dtype=np.float32)
target_np = np.array([1, 0, 2, 2], dtype=np.int64)

# f64 kontroll: a float64 tartományban 500 még véges
z64 = logits32.astype(np.float64)
lse64 = np.log(np.exp(z64).sum(axis=1, keepdims=True))
logp64 = z64 - lse64
ref = -logp64[np.arange(logits32.shape[0]), target_np].mean()
print("f64 control:", ref)

def naive_nll(logits, target):
    # Szándékosan naiv: először softmax, majd log
    expz = np.exp(logits)                      # float32: overflow
    probs = expz / expz.sum(axis=1, keepdims=True)
    logp = np.log(probs)                       # 0-ból -inf
    return -logp[np.arange(logits.shape[0]), target].mean()

def stable_nll(logits, target):
    # Logsumexp max-eltolással, közvetlen log-softmax
    m = logits.max(axis=1, keepdims=True)
    shifted = logits - m
    s = np.exp(shifted).sum(axis=1, keepdims=True)
    lse = m + np.log(s)
    logp = logits - lse
    return -logp[np.arange(logits.shape[0]), target].mean()

loss_fn = nn.CrossEntropyLoss()
t_logits = torch.from_numpy(logits32).to(device)
t_target = torch.from_numpy(target_np).to(device)
with torch.no_grad():
    torch_mean = loss_fn(t_logits, t_target).item()

naive_val = naive_nll(logits32, target_np)
stable_val = stable_nll(logits32, target_np)
print("naive  float32:", naive_val)
print("stable float32:", stable_val)
print("torch  float32:", torch_mean)
print("abs stable-ref:", abs(stable_val - ref))
print("abs torch-ref :", abs(torch_mean - ref))

A naiv ág RuntimeWarning: overflow üzenetet ír ki, eredménye nan. A stabil és a torch eredménye várhatóan:

Implementáció dtype Érték (jelölt)
f64 kontroll float64 0.31326169
naiv NumPy float32 nan
stabil NumPy float32 0.31326166
PyTorch CrossEntropyLoss float32 0.31326169

Ellenőrzés és értelmezés. A szkriptben abs(stable_val - ref) < 1e-5 és abs(torch_mean - ref) < 1e-5 az elvárt tolerancia; ezt a hallgató assert-tel is ellenőrizheti. A naiv érték nem véges, tehát a stabilizálás nem kozmetikai javítás, hanem az eredmény létezését adja vissza. A torch és a stabil NumPy egyezése a float32 kerekítési hibahatáron belül van.

7.4.10 10. Gyakori tévedés ellenpéldával

Tévedés: „Ha a veszteség NaN, elég numerikus stabilizálást alkalmazni.” Ellenpélda: a hívó fél a logitsra már softmaxot alkalmaz, vagy a célvektort float32-ként adja át, és a veszteség még a stabil formulával is hibát jelez. Tekintsük a következő hibás hívást:

probs = torch.softmax(logits, dim=1)  # ez már valószínűség, nem logit
loss_fn(probs, target)                # szerződéssértés

A CrossEntropyLoss logitsot vár [D07]; a softmax dupla alkalmazása nemcsak rossz értéket ad, hanem a gradiens is elromlik. Ugyanígy, ha a target alakja (N,1)(N,1) helyett float32 (N,)(N,) egyes könyvtári verziókban, a hibát a szerződés ellenőrzése oldja meg, nem a stabilitási trükk. A stabil logsumexp tehát szükséges, de nem elégséges: előbb a matematikai szerződést kell helyesen teljesíteni.

7.4.11 11. Feladatok

F1 (fogalmi, nehézség 1). Adja meg az overflow és az underflow definícióját, és mutasson egy-egy konkrét float32 példát a softmax környékéről. Átadandó: saját megfogalmazású definíciók és két konkrét számérték.

F2 (levezetési, nehézség 2). Bizonyítsa be, hogy a softmax invariáns a konstans eltolásra: σ(z+c𝟏)i=σ(z)i\sigma(z+c\mathbf 1)_i=\sigma(z)_i minden c∈ℝc\in\mathbb R-re, ahol 𝟏\mathbf 1 a KK-dimenziós csupa egyes vektor. Ezután vezesse le a stabil logsoftmax\operatorname{logsoftmax} képletet a c=−maxjzjc=-\max_j z_j választással. Átadandó: teljes levezetés és a végképlet.

F3 (implementációs, nehézség 2). Írjon NumPy-függvényt, amely a BCEWithLogits veszteséget a stabil softplus-képlettel számolja, és hasonlítsa össze egy f64 referenciaimplementációval. Bemenet: z = np.array([[-1000.0, 0.0, 1000.0]], dtype=np.float32), y = np.array([[0.0, 0.5, 1.0]], dtype=np.float32). Átadandó: futtatható kód, a várt hiba <10−6<10^{-6} abszolút toleranciával.

F4 (hibakeresési, nehézség 2). Az alábbi kód hibás:

p = torch.softmax(logits, dim=1)
loss = nn.CrossEntropyLoss()(p, target_float)

Azonosítsa mindkét hibát, javítsa a kódot, és ellenőrizze a javítást a z=[1,2,3]z=[1,2,3], y=2y=2 kézi eseten. Átadandó: a hibák megnevezése, javított kód és a kézi ellenőrzés értéke.

F5 (kísérleti/transzfer, nehézség 3). Tervezzen kísérletet annak vizsgálatára, hogy a logits hőmérséklet-skálázása z/Tz/T hogyan hat a cross-entropy értékére és a predikciók szétterülésére egy rögzített CNN-modellen. Használja a GlyphLab val és test splitjét a scene_id megfigyelési egységgel, egy előre rögzített modell-állapottal. Adja meg a protokollt, a változó tényezőt, a baseline-t, a metrikát és a következtetési határt. Átadandó: egy lehetséges terv, nem feltétlenül futtatott eredmény.

7.4.12 12. Megoldások és indoklás

F1 megoldás. Overflow: olyan művelet, amelynek eredménye nagyobb a reprezentálható legnagyobb értéknél; float32 np.exp(500.0) inf-et ad. Underflow: az eredmény abszolút értéke kisebb a legkisebb reprezentálható normális értéknél, ezért nullává kerekítik; float32 np.exp(-500.0) 0.0 lesz. A softmax naiv nevezője overflowol, a számláló underflowolhat; az eredmény inf/inf=NaN vagy 0/0=NaN.

F2 megoldás. A bizonyítás:

σ(z+c𝟏)i=ezi+c∑jezj+c=eceziec∑jezj=σ(z)i. \sigma(z+c\mathbf 1)_i =\frac{e^{z_i+c}}{\sum_j e^{z_j+c}} =\frac{e^{c}e^{z_i}}{e^{c}\sum_j e^{z_j}} =\sigma(z)_i.

A második lépésben az exponenciális szorzat-azonosságát, a harmadikban a közös ece^c kiemelését használtuk; ec>0e^c>0, ezért a törlés megengedett. Ebből a logsoftmax\operatorname{logsoftmax} invarianciája is következik:

logsoftmax(z+c𝟏)=logsoftmax(z). \operatorname{logsoftmax}(z+c\mathbf 1)=\operatorname{logsoftmax}(z).

A stabil választás: m=maxjzjm=\max_j z_j, ekkor z−mz-m minden komponense nempozitív. Legyen s=∑jezj−ms=\sum_j e^{z_j-m}. A fenti invariancia miatt

σ(z)i=σ(z−m)i=ezi−ms, \sigma(z)_i=\sigma(z-m)_i=\frac{e^{z_i-m}}{s},

így

logsoftmax(z)i=(zi−m)−logs. \operatorname{logsoftmax}(z)_i=(z_i-m)-\log s.

A s∈[1,K]s\in[1,K] korlát miatt a logaritmus argumentuma véges és pozitív;

F3 megoldás.

import numpy as np

z = np.array([[-1000.0, 0.0, 1000.0]], dtype=np.float32)
y = np.array([[0.0, 0.5, 1.0]], dtype=np.float32)

def stable_bce_with_logits(z, y):
    # stabil softplus: max(t,0)+log1p(exp(-abs(t)))
    t = -z
    softplus = np.maximum(t, 0.0) + np.log1p(np.exp(-np.abs(t)))
    return (1.0 - y) * z + softplus

def ref_bce_with_logits(z, y):
    # f64 naiv matematikai definíció: z itt 1000-ig biztonságos
    z64 = z.astype(np.float64)
    y64 = y.astype(np.float64)
    sig = 1.0 / (1.0 + np.exp(-z64))
    return -y64 * np.log(sig) - (1.0 - y64) * np.log(1.0 - sig)

print(stable_bce_with_logits(z, y))
print(ref_bce_with_logits(z, y))
print(np.abs(stable_bce_with_logits(z, y) - ref_bce_with_logits(z, y)).max())

A várt eltérés <10−6<10^{-6}. A naiv f32 ágon a z=1000z=1000-hez tartozó σ=1\sigma=1, 1−σ=01-\sigma=0, log0\log0 -inf; a stabil formula ezt elkerüli.

F4 megoldás. A két hiba: (1) a CrossEntropyLoss logitsot vár, nem softmax-kimenetet; (2) a cél target_float float típusú, holott a függvény int64 osztályindexet vár. Javítás:

loss = nn.CrossEntropyLoss()(logits, target.long())

Ellenőrzés a z=[1,2,3]z=[1,2,3], y=2y=2 esetre: log-softmax [1−3.4076,2−3.4076,3−3.4076]≈[−2.4076,−1.4076,−0.4076][1-3.4076,2-3.4076,3-3.4076]\approx[-2.4076,-1.4076,-0.4076], a negált log-valószínűség 0.40760.4076. A torch mean reduction egy minta esetén ugyanezt adja.

F5 megoldásminta. Protokoll: rögzítsünk egy, a GlyphLab train-en tanított modell state_dict-jét; a val spliten (60,1,24,24)(60,1,24,24) képekre és int64 célokra gyűjtsük a logitsokat. Változó tényező: T∈{0.5,1.0,2.0,5.0}T\in\{0.5,1.0,2.0,5.0\}; a modell maga változatlan. Metrika: átlagos cross-entropy a skálázott logitsokból és a predikált valószínűség maximumának átlaga, kontrollként a val accuracy rögzítve. Baseline: T=1T=1 az eredeti logits. Kiértékelés: minden TT-re a val spliten számolt metrika; a kísérleti következtetés csak a hőmérséklet hatását állapíthatja meg a rögzített modellen, nem általános optimalitást. A test splitet legfeljebb egyszer, a protokoll végén szabad használni. Megfigyelési egység a scene_id, mert a két view függő.

7.4.13 13. Önellenőrzés és korábbi fogalom visszahívása

Ellenőrzési lista az alfejezethez: a logsumexp-tételben lépésenként indokoltuk a kitevő- és logaritmus-azonosságokat; a max-eltolás után a ss összeg [1,K][1,K]-ban van; a kézi példában a softmax összege 11 kerekítési hibán belül; a labor toleranciája explicit.

Visszahívás 7.3. Bináris, multilabel és regressziós cél alfejezetből: a BCEWithLogits gradiense σ(z)−y\sigma(z)-y, így a stabil formula nem változtatja meg a tanítási jelet. Visszahívás 6.4. Momentum és adaptív optimizálók alfejezetből: az Adam-epsilon a nevező stabilitását szolgálja, hasonló céllal, mint itt a max-eltolás; de a két számítási hely különböző. Visszahívás 3.4. Sajátérték, SVD és spektrális határ alfejezetből: ha egy lineáris réteg WW súlyának spektrális normája nagy, a logitok szórása megnőhet, és ezzel a softmax stabilitási tartaléka csökken; a spektrális norma hasznos diagnosztikai eszköz, de nem helyettesíti a stabil formulát.

7.4.14 Rövid összefoglaló

A naiv softmax és BCE implementáció nagy vagy kis logitokra inf, -inf vagy NaN értéket adhat, miközben a matematikai eredmény véges. A logsumexp konstanseltolási azonossága szerint lse(z)=c+log∑jezj−c\operatorname{lse}(z)=c+\log\sum_j e^{z_j-c}, és a c=maxjzjc=\max_j z_j választás biztosítja, hogy az exponenciális összeg [1,K][1,K]-ban maradjon. A log-softmax ebből közvetlenül számolható, a BCEWithLogits pedig a max(t,0)+log(1+e−|t|)\max(t,0)+\log(1+e^{-|t|}) softplus-képlettel és log1p-pel stabilizálható. A labor f64 kontrollon mutatja ki a naiv, a stabil és a könyvtári implementáció viszonyát.

7.4.15 Következő alkalmazás

A 8.1. Kockázat, mintavétel és baseline egységben a kockázat, a mintavétel és a baseline fogalmát építjük fel; ott azt vizsgáljuk, hogy egy stabilan kiszámolt veszteség milyen tartalmi állításokat tesz lehetővé a modell várható teljesítményéről.

7.4.16 Források