Ez az egység a záró vizsga levezetési része. A cél nem új réteg bemutatása, hanem annak ellenőrzése, hogy a hallgató egy korábban nem látott, kis neurális gráfot önállóan definiál, teljes alakhelyes deriváltját levezeti, és a levezetést numerikus kontrollal megerősíti. A feladat a 36.4. IX. ellenőrző pont: lektorálható modellállítás lektorálható csomagjának matematikai komponensét, a 37.4. Rétegből kis tanulómodell integrált saját réteget és a 38.4. Mentett modellből független inference inference-szerződés pontosságigényét egyetlen vizsgapéldában kapcsolja össze.
Előismeret: a 36.4. IX. ellenőrző pont: lektorálható modellállítás hatkomponensű bírálati kerete, a 37.4. Rétegből kis tanulómodell alfejezetben rögzített modellszintű véges differenciás ellenőrzés, a 38.4. Mentett modellből független inference visszatöltési tolerancia fogalma és a 39.4. Alkalmazási riport és átadás alkalmazási riport határkijelölése. Visszautalás: a 28.1. Embedding, távolság és normalizálás normalizálási azonossága, a 36.1. Állításból kísérleti terv hipotézis/kontroll elválasztása és a 39.1. Feladat- és annotációs döntés annotációs konvenció.
Induló kérdés: elegendő-e egy loss.backward() sor
sikeres lefutása annak bizonyításához, hogy egy új gráf deriváltját
értjük? Becslés: nem. A könyvtári hívás a láncszabály minden lépését
elrejti; a vizsga ezért kézzel levezetett, alakellenőrzött és float64
véges differenciával megerősített megoldást vár.
A vizsgafeladat a következő, a könyvben ebben az összeállításban még nem szereplő kompozíciót elemzi. A bemenet , ahol a térbeli hossz. Az első lépés 1D keresztkorreláció (cross-correlation, kernelmegfordítás nélkül) szimmetrikus zero paddinggel, stride 1-gyel, dilation 1-gyel, groups=1-gyel:
ahol a párnázott bemenet, , , , és
A második lépés elemenkénti ReLU: , azaz . A harmadik lépés globális átlag-pooling (global average pooling, GAP) a térbeli dimenzió felett:
A negyedik lépés L2-normalizálás:
ahol . A veszteség egy rögzített, egységnormájú célvektorra, , , megfigyelés átlagában:
A regularizáló tag csak a konvolúciós súlyra vonatkozik, a biasra
nem;
.
A
konvenció gradiense
.
A ReLU nullponti deriváltját itt 0-nak definiáljuk, ez a PyTorch
F.relu viselkedése is; a nullpontot a numerikus
ellenőrzésben kifejezetten elkerüljük.
1. lemma (az új kompozíció teljes gradiense). Tegyük fel, hogy , , , stride=dilation=1, a padding szimmetrikus zero padding, minden -re, és a ReLU nullponti deriváltja 0. Ekkor minden olyan pontban, ahol egyetlen sem nulla, a gradiensei a következők.
A (iv)–(vi) képletekben nincs újabb szorzó: a batch-átlag tényezője egyszer, az (i) lépésben lép be, és onnan a , gradiensekkel öröklődik. Ezért két azonos mintából álló batch () átlagolt veszteségének súly- és biasgradiense megegyezik az egymintás () értékkel; ha a (iv)–(v) összegeket még egyszer -nel osztanánk, a gradiens -nél a felére esne.
majd a paddinget levágjuk: minden esetén.
Bizonyítás. (i) Jelölje . A hányadosszabályból a parciális derivált:
$$ \frac{\partial f_c}{\partial x_j} =\frac{\delta_{cj}\|x\|_2-x_c x_j/\|x\|_2}{\|x\|_2^2} =\frac{1}{\|x\|_2}\Bigl(\delta_{cj}-\frac{x_c x_j}{\|x\|_2^2}\Bigr). $$
Mivel és az egységmátrix eleme, ezért a Jacobi-mátrix
$$ J_f(x)=\frac{1}{\|x\|_2}\Bigl(I_{C_{out}}-uu^\top\Bigr). $$
A mátrix szimmetrikus, tehát a VJP a 4.3. Jacobi-mátrix, láncszabály és VJP konvenciója szerint . A belső veszteségre . Innen az (i) képlet; az faktor a batch-átlag lineáris láncszabályából jön. (ii) A GAP lineáris leképezés, minden -re , tehát a VJP a konstans szorzó. (iii) A ReLU a kimondott konvencióval minden pontban klasszikusan differenciálható, deriváltja vagy ; a nullpontot a lemma feltétele kizárja. (iv)–(v) A keresztkorreláció a elemeiben lineáris, ezért a láncszabály a gradiensét a megfelelő szegmensével súlyozza; a tag a deriváltja. (vi) Ugyanaz a linearitás az input irányában: egy adott elem minden olyan hármasban szerepel, amelyre , azaz . A -feltétel a kimeneti index érvényességét biztosítja; a padding zónákon kívül eső tagok a zero padding miatt eleve nem járulnak hozzá. A padding levágása az utolsó lépés definíció szerint. Alakellenőrzés: , , , , .
Ellenpélda a feltétel szükségességére. Két dolgot
kell elkülöníteni. (1) A normalizáló függvény önmagában:
a nullában nem definiált, és nem is terjeszthető ki folytonosan:
esetén
,
így a nulla körüli középső differencia
.
(2) A teljes gráfban ez a pont nem így érhető el:
minden komponense nemnegatív, és
pontosan akkor, ha minden
.
Ha minden
,
akkor kis perturbációra is
marad, a veszteség egy egész környezetben csak a könyvtár konvenciójától
függ (a PyTorch F.normalize a nullvektorra
-t,
így
-et
ad); ugrás nincs, de a gradiens sem informatív. Ugrás csak akkor van, ha
a
pontban valamelyik
,
azaz a ReLU aktív halmaza a perturbációval változik. Példa:
,
,
,
,
,
,
,
,
,
.
Ekkor
és
.
Ha
,
akkor
,
és
;
ha
,
akkor
,
és
.
A középső differencia
,
ami
mellett
,
,
(float64-ben futtatva), tehát nem konvergál. Itt a két kizárt eset, a
és a
,
együtt fordul elő; a lemma mindkét feltétele szükséges, és egyik sem
technikai részlet.
Legyen , , , , , , . Bemenet és súly:
A párnázott bemenet , . A keresztkorreláció:
tehát alakja . ReLU után:
A GAP:
A cél . A veszteség:
A lemma szerint , ezért
A GAP osztása után, az két nulláját a ReLU-gradiens lenullázza:
Innen a biasgradiens:
A súlygradiens a szegmenseivel:
A párnázott bemeneti gradiens :
a padding levágása után:
Az egzakt törtek a kézi ellenőrzés rögzített várt értékei. A következő kód ugyanezt a levezetést a PyTorch autogradjával veti össze float64-ben.
Munkakönyvtár: n157_lab/.
Fájl: derive_check.py.
Futtatás:
python n157_lab/derive_check.py. A főút kis helyi CPU,
float64, seed rögzített. A kód a fenti gráfot építi fel, kiszámolja az
autograd gradienseket, majd saját központi differenciával ellenőrzi a
súly-, bias- és bemeneti gradiens minden elemét. Ezután a lemma (i)–(vi)
képleteit teljes kézi backwardként (manual_backward) is
megvalósítja, és (a) a 4. szakasz példáján, illetve két azonos
mintájából álló batch-en, (b) egy független, nem degenerált batch-en
(,
,
,
,
,
,
,
nem kollineáris
és
)
veti össze az autograddal és a központi differenciával. A (b) rész azért
kell, mert a 4. szakasz példája
,
és
mellett nem mutatná ki sem a hibás
-et,
sem a regularizációs tag hibáját.
import torch
import torch.nn.functional as F
torch.manual_seed(20261020)
dtype = torch.float64
device = torch.device("cpu")
eps = 1.0e-6
atol = 1.0e-7
X = torch.tensor([[[1.0, 2.0, 1.0]]], dtype=dtype, device=device, requires_grad=True)
K = torch.tensor([[[1.0, 1.0]], [[1.0, -1.0]]], dtype=dtype, device=device, requires_grad=True)
b = torch.tensor([0.0, 0.0], dtype=dtype, device=device, requires_grad=True)
t = torch.tensor([1.0, 0.0], dtype=dtype, device=device)
def forward(xv, kv, bv):
z = F.conv1d(xv, kv, bv, padding=1) # (1,2,4)
a = F.relu(z) # (1,2,4)
h = a.mean(dim=2) # (1,2)
return z, a, h
def loss_fn(xv, kv, bv):
_, _, h = forward(xv, kv, bv)
u = F.normalize(h.squeeze(0), dim=0) # (2,)
return 1.0 - torch.dot(u, t)
Z, A, h = forward(X, K, b)
assert not torch.any(Z == 0), "ReLU-nullpont: a kozponti differencia itt ervenytelen"
loss = loss_fn(X, K, b)
loss.backward()
print("Z=", Z.detach().numpy())
print("h=", h.detach().numpy())
print("loss=", loss.item())
print("gX=", X.grad.detach().numpy())
print("gK=", K.grad.detach().numpy())
print("gb=", b.grad.detach().numpy())
def central_diff(param, grad):
flat = param.detach().flatten().clone()
max_abs = 0.0
for i in range(flat.numel()):
orig = flat[i].item()
flat[i] = orig + eps
if param is K:
lp = loss_fn(X, flat.view_as(K), b)
elif param is b:
lp = loss_fn(X, K, flat.view_as(b))
else:
lp = loss_fn(flat.view_as(X), K, b)
flat[i] = orig - eps
if param is K:
lm = loss_fn(X, flat.view_as(K), b)
elif param is b:
lm = loss_fn(X, K, flat.view_as(b))
else:
lm = loss_fn(flat.view_as(X), K, b)
flat[i] = orig
fd = (lp - lm).item() / (2 * eps)
max_abs = max(max_abs, abs(fd - grad.detach().flatten()[i].item()))
return max_abs
print("max fd diff K: %.3e" % central_diff(K, K.grad))
print("max fd diff b: %.3e" % central_diff(b, b.grad))
print("max fd diff X: %.3e" % central_diff(X, X.grad))
assert central_diff(K, K.grad) < atol
assert central_diff(b, b.grad) < atol
assert central_diff(X, X.grad) < atol
# A lemma (i)-(vi) teljes kézi backwardja, általános N, C_in, C_out, k, p és lambda mellett.
def manual_backward(xv, kv, bv, tv, lam, pad):
with torch.no_grad():
z = F.conv1d(xv, kv, bv, padding=pad) # (N,C_out,L_out)
h = F.relu(z).mean(dim=2) # (N,C_out)
n_batch, l_out, k = h.shape[0], z.shape[2], kv.shape[2]
nrm = h.norm(dim=1, keepdim=True)
u = h / nrm
g_h = -(tv - (u @ tv)[:, None] * u) / (n_batch * nrm) # (i), az egyetlen 1/N
g_z = (g_h[:, :, None] / l_out) * (z > 0) # (ii)-(iii)
x_pad = F.pad(xv, (pad, pad)) # (N,C_in,L+2p)
g_b = g_z.sum(dim=(0, 2)) # (iv)
g_k = torch.stack([torch.einsum("noj,nij->oi", g_z, x_pad[:, :, r:r + l_out])
for r in range(k)], dim=2) + lam * kv # (v)
g_xp = torch.zeros_like(x_pad)
for r in range(k): # (vi): s = j + r
g_xp[:, :, r:r + l_out] += torch.einsum("noj,oi->nij", g_z, kv[:, :, r])
return g_k, g_b, g_xp[:, :, pad:pad + xv.shape[2]] # padding levágása
def general_loss(xv, kv, bv, tv, lam, pad):
h = F.relu(F.conv1d(xv, kv, bv, padding=pad)).mean(dim=2)
u = h / h.norm(dim=1, keepdim=True)
return (1.0 - u @ tv).mean() + 0.5 * lam * (kv ** 2).sum()
def fd_grad(fun, args, which):
"""Központi differencia az args[which] tenzor minden elemére."""
base = args[which]
g = torch.zeros_like(base)
for i in range(base.numel()):
plus, minus = base.clone(), base.clone()
plus.view(-1)[i] += eps
minus.view(-1)[i] -= eps
ap, am = list(args), list(args)
ap[which], am[which] = plus, minus
g.view(-1)[i] = (fun(*ap) - fun(*am)) / (2 * eps)
return g
# (a) A 4. szakasz példája (N=1, lambda=0) és két azonos mintája (N=2): a gradiens nem feleződhet.
gK1, gb1, gX1 = manual_backward(X.detach(), K.detach(), b.detach(), t, 0.0, 1)
assert torch.allclose(gK1, K.grad) and torch.allclose(gb1, b.grad) and torch.allclose(gX1, X.grad)
gK2, gb2, gX2 = manual_backward(torch.cat([X.detach()] * 2), K.detach(), b.detach(), t, 0.0, 1)
assert torch.allclose(gK2, gK1) and torch.allclose(gb2, gb1)
assert torch.allclose(gX2, torch.cat([gX1] * 2) / 2) # mintánként 1/N: a bemeneti gradiens feleződik
print("(a) kezi backward == autograd; N=2 azonos mintaval K, b gradiens valtozatlan")
# (b) Független kontroll: N=3, C_in=2, C_out=3, L=5, k=3, p=1, lambda=0.03, nem kollineáris t és h.
gen = torch.Generator().manual_seed(7)
Xr = torch.randn(3, 2, 5, generator=gen, dtype=dtype) + 1.0
Kr = torch.randn(3, 2, 3, generator=gen, dtype=dtype) * 0.5
br = torch.tensor([0.2, 0.1, 0.3], dtype=dtype)
tr = torch.tensor([0.6, 0.0, 0.8], dtype=dtype)
lam, pad = 0.03, 1
Zr = F.conv1d(Xr, Kr, br, padding=pad)
hr = F.relu(Zr).mean(dim=2)
cos_ht = (hr / hr.norm(dim=1, keepdim=True)) @ tr
print("min|Z| = %.3f, min||h|| = %.3f, cos(h,t) =" % (Zr.abs().min(), hr.norm(dim=1).min()),
cos_ht.numpy().round(4))
assert Zr.abs().min() > 100 * eps and hr.norm(dim=1).min() > 0 # nincs ReLU-töréspont, h != 0
assert cos_ht.abs().max() < 0.99 and (hr > 0).sum(dim=1).min() >= 2 # nem degenerált irány
args = [Xr.clone().requires_grad_(), Kr.clone().requires_grad_(), br.clone().requires_grad_(), tr, lam, pad]
general_loss(*args).backward()
manual = manual_backward(Xr, Kr, br, tr, lam, pad) # (gK, gb, gX)
auto = (args[1].grad, args[2].grad, args[0].grad)
fdiff = (fd_grad(general_loss, [Xr, Kr, br, tr, lam, pad], 1),
fd_grad(general_loss, [Xr, Kr, br, tr, lam, pad], 2),
fd_grad(general_loss, [Xr, Kr, br, tr, lam, pad], 0))
for name, m, a, f in zip(("K", "b", "X"), manual, auto, fdiff):
d_auto, d_fd = (m - a).abs().max().item(), (m - f).abs().max().item()
print("(b) %s: max|kezi-autograd| = %.1e, max|kezi-fd| = %.1e, max|g| = %.3f"
% (name, d_auto, d_fd, m.abs().max().item()))
assert d_auto < 1e-12 and d_fd < atol and m.abs().max() > 1e-3Futási jegyzőkönyv és értelmezés. A lab-profilban
(PyTorch 2.14.1+cpu, float64) a szkript minden assert-en
átment. A kimenet a 4. szakasz kézi értékeit reprodukálja:
loss
,
gX
,
gb
.
(a) A teljes kézi backward a 4. szakasz példáján egyezik az autograddal;
két azonos mintából álló
batch-en a
és
gradiense változatlan, tehát nem feleződik, a bemeneti gradiens viszont
mintánként a felére esik, mert minden minta
-e
külön változó, és mindegyik
súllyal szerepel. (b) A független kontrollban
,
,
;
a kézi és az autograd gradiens legnagyobb eltérése
,
a kézi gradiens és a központi differencia eltérése
(),
()
és
().
A három max fd diff sor az autograd és a központi
differencia abszolút eltérése:
(),
()
és
(),
mind az atol=10^{-7} korlát alatt.
A kódban a Z==0 ellenőrzés szándékosan megelőzi a
numerikus differenciát: a ReLU nullpontjában az autograd által
választott 0 meredekség konvenció, a központi differencia viszont az
ottani féloldali ugrás miatt nem konvergál a klasszikus deriválthoz. A
választott pontban minden
elem
,
,
vagy
,
tehát a vizsgálat sima (l. 10.3. Gradiensellenőrzés
és numerikus hibamérce); a (b) részben az assert
legalább
távolságot követel meg a nullától.
F1 (fogalmi, nehézség 1). Magyarázza meg, miért nem lineáris a teljes leképezés, és nevezze meg a két pontot, ahol a linearitás elromlik. Adjon konkrét számpéldát: a 4. szakasz -ére és -ére mi a érték, és miért cáfolja ez a linearitást? Elvárt átadandó: legfeljebb fél oldal indoklás a két ponttal és a két veszteségértékkel.
F2 (levezetési, nehézség 2). A 4. szakasz numerikus példáján, mellett vezesse le a és értékét a lemma (iv) és (vi) képletéből. Adja meg az egzakt törteket és a közelítő tizedes értékeket öt jegyre, majd ellenőrizze az alakokat: , . Elvárt átadandó: a levezetés minden közbülső összeggel.
F3 (implementációs, nehézség 2). Írjon NumPy függvényt, amely a lemmát a , , , , , , esetre implementálja: a forward, a , a , , , kiszámítását. Válasszon olyan bemenetet, amelyen egyetlen elem sem nulla, és nem párhuzamos -vel. Hasonlítsa össze a PyTorch autograd gradienseivel azonos bemeneten, abszolút toleranciával. Degenerációs részfeladat: mutassa meg, hogy és esetén , a veszteség lokálisan konstans, és minden gradiens nulla; magyarázza meg, miért nem ellenőrzi ez az eset az implementációt. Elvárt átadandó: futtatható kód, a teszteset és a toleranciaellenőrzés kimenete, valamint a degenerációs eset indoklása.
F4 (hibakeresési, nehézség 2). Az alábbi hibás gradiensszámítás a normalizálási lépést egyszerűsíti:
def wrong_grad_h(h, t):
return -t / np.linalg.norm(h)Magyarázza meg, melyik komponensben és miért tér el ez a lemma (i) képletétől, számítsa ki a hibás és a helyes -t a 4. szakasz adataira, és mutassa meg, hogy a központi differencia a helyes értéket erősíti meg. Elvárt átadandó: ok, következmény, javított képlet és a numerikus összevetés.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen kontrollált kísérletet annak eldöntésére, hogy a GlyphLab 38.4. Mentett modellből független inference checkpointjának embeddingjein az L2-normalizálás javítja-e a jelenetszintű euklideszi retrieval mAP@5-öt a nyers embeddinghez képest. Ne futtassa; adja meg a hipotézist, a kontrollt, a változó tényezőt, a metrikát, a jelenet-alapú splitet és bootstrap egységet, valamint a következtetési korlátokat. Elvárt átadandó: preregisztrált terv a 36.1. Állításból kísérleti terv sablonja szerint.
F1 megoldás. Egy leképezés akkor lineáris, ha minden és esetén. Két töréspont van. (1) A ReLU: és , de , tehát a szuperpozíció elromlik. (2) Az L2-normalizálás skálainvariáns: minden -ra, ezért nem lehet lineáris. A 4. szakaszban . A bemenetre a pozitív ágban is duplázódik, ezért , ami ugyanazt az -t adja: . Ha a leképezés lineáris lenne, teljesülne; a valós egyenlőség cáfolja a linearitást.
F2 megoldás. A a 4. szakaszban adott. A biasgradiens az oszloponkénti összeg: ; , tehát . A bemeneti gradienshez a párnázott összeget számoljuk az párokkal. A két kernelsorát és a két sorát használva:
ahol például -re a tag és a tag összege , ahol . A padding levágása () a fenti hármast adja. Az alakok ellenőrzése: két koordináta, egy csatorna, három térbeli index, konzisztens a lemma (iv) és (vi) képletével.
F3 megoldás. A lemmát a konkrét 1D esetre megvalósító NumPy kód:
import numpy as np
import torch
import torch.nn.functional as F
def manual_1d(X, K, b, t):
# X: (1,1,L); K: (C_out,1,2); b: (C_out,); t: (C_out,), ||t||=1; p=1, N=1, lambda=0
L, C = X.shape[2], K.shape[0]
Xp = np.concatenate([np.zeros(1), X[0, 0], np.zeros(1)]) # p=1
Lout = L + 2 - 2 + 1
Z = np.array([[b[o] + np.sum(K[o, 0] * Xp[j:j + 2]) for j in range(Lout)] for o in range(C)])
assert np.all(Z != 0), "ReLU-nullpont"
h = np.maximum(Z, 0.0).mean(axis=1) # (C_out,)
hnorm = np.linalg.norm(h)
assert hnorm > 0, "||h|| = 0: a lemma nem alkalmazható"
u = h / hnorm
loss = 1.0 - u @ t
gh = -(t - (u @ t) * u) / hnorm # (i), N=1
gZ = (gh[:, None] / Lout) * (Z > 0) # (ii)-(iii)
gb = gZ.sum(axis=1) # (iv)
gK = np.array([[[np.sum(gZ[o] * Xp[r:r + Lout]) for r in range(2)]] for o in range(C)]) # (v)
gXp = np.zeros(L + 2)
for s in range(L + 2): # (vi)
for o in range(C):
for r in range(2):
if 0 <= s - r < Lout:
gXp[s] += gZ[o, s - r] * K[o, 0, r]
return loss, gXp[1:1 + L][None, None, :], gK, gb
def torch_ref(X, K, b, t):
Xt, Kt, bt = (torch.tensor(a, requires_grad=True) for a in (X, K, b))
h = F.relu(F.conv1d(Xt, Kt, bt, padding=1)).mean(dim=2)[0]
loss = 1.0 - F.normalize(h, dim=0) @ torch.tensor(t)
loss.backward()
return loss.item(), Xt.grad.numpy(), Kt.grad.numpy(), bt.grad.numpy()
X = np.array([[[1.0, 2.0, -1.0, 3.0]]])
K = np.array([[[1.0, 0.5]], [[-0.5, 1.0]]])
b = np.array([0.1, 0.2])
t = np.array([0.6, 0.8])
for name, m, a in zip(("loss", "gX", "gK", "gb"), manual_1d(X, K, b, t), torch_ref(X, K, b, t)):
print(name, "max abs diff = %.1e" % np.max(np.abs(np.asarray(m) - np.asarray(a))))
assert np.allclose(m, a, rtol=0, atol=1e-10)
gX = manual_1d(X, K, b, t)[1]
assert np.abs(gX).max() > 1e-3 # nem nulla: a teszt nem triviális
# Degenerációs eset: C_out = 1, h > 0 => u = 1, a veszteség lokálisan konstans, minden gradiens 0.
_, gX1, gK1, gb1 = manual_1d(X, K[:1], b[:1], np.array([1.0]))
print("C_out=1: max|grad| =", max(np.abs(g).max() for g in (gX1, gK1, gb1)))A választott bemeneten , , tehát nincs ReLU-nullpont, a második csatornában az aktív halmaz nem teljes; és , azaz nem párhuzamos -vel. A lab-profilban (PyTorch 2.14.1+cpu, float64) a kézi és az autograd érték legnagyobb eltérése (loss), (), () és (), és , tehát a teszt nem a nullgradienst hasonlítja össze. A degenerációs esetben , , ezért minden kis perturbációra, konstans, és a lemma (i) szerint ; a futás is -t ad. Egy ilyen teszt bármely, nullát visszaadó hibás implementációt is elfogadna, ezért ellenőrzésre nem alkalmas.
F4 megoldás. A hiba oka: a wrong_grad_h
kihagyja a normalizálás Jacobi-mátrixának
vetületi tagját, mintha a célgradiens közvetlenül a normalizálatlan
-ra
hatna. Következmény: a céllal merőleges irányú komponens gradiense
eltűnik. A 4. szakaszban a helyes
,
a hibás pedig
.
Az első komponens közel 17-szeres hibája mellett a második komponens
nulla, miközben a központi differencia a
perturbációjára
értéket ad. A javítás a lemma (i) teljes képlete:
;
ezzel mindkét komponens egyezik a numerikus kontrollal.
F5 megoldásminta. Hipotézis: a GlyphLab test-szettjén a normalizált embedding euklideszi retrieval mAP@5-e magasabb, mint a nyers embeddingé. Adat és egység: test 60 kép, 30 scene, minden scenenek két view-ja; a független bootstrap egység a scene_id. Változó tényező: L2-normalizálás alkalmazása a kinyert embeddingre; a modell súlyai rögzítettek, nincs újratanítás. Metrika: mindkét irányú view-query/gallery átlagolt mAP@5; a per-scene bootstrap CI a 36.2. Ismétlések és eredménybizonytalanság szerint. Döntési szabály: a normalizált mAP CI-ja a nyers CI fölött van. Korlátok: a GlyphLab toy adat, nem természetes képi benchmark; az eredmény csak ehhez az adatszerződéshez és ehhez a checkpointhoz kötött; a 28.1. Embedding, távolság és normalizálás azonossága miatt a koszinusz-rangsor önmagában nem változik, ezért a metrikának kifejezetten euklideszi távolságot kell használnia.
Tipikus tévedés. A memorizált API-hívás nem
helyettesíti az önálló levezetést. Ha a hallgató a
F.normalize hívása után a célgradienst változatlanul
továbbítja, azaz
-t
ír, a 4. szakaszban
-t
kapna a helyes
helyett. Az első komponens hibája körülbelül 35-szörös, a második
komponens pedig teljesen hiányzik. A központi differencia azonnal
kimutatja az eltérést; ezért a vizsgán a levezetés és a numerikus
kontroll együtt kötelező, nem felcserélhető.
Önellenőrzés. Minden levezetési lépésben: (a) a tenzoralakok egyeznek-e a lemma kimeneti alakjaival; (b) a , peremfeltételek teljesülnek-e; (c) a ReLU nullpontot elkerültük-e; (d) a tag csak a súlygradiensbe került-e; (e) a központi differencia a megadott lépésközzel és toleranciával egyezik-e. A nullvektor normalizálása a lemmán kívül eső, nem differenciálható eset; a gyakorlatban a GAP utáni nulla csatornavektort külön esetként kell kezelni.
Visszahívás. A 28.1. Embedding, távolság és normalizálás levezetése szerint egységnormájú vektorokra a koszinusz-hasonlóság és a négyzetes euklideszi távolság rendezése ekvivalens; ezért a jelen feladat cosinus-vesztesége és az euklideszi retrieval a normalizálás után ugyanazt a geometriát használja. A 36.1. Állításból kísérleti terv tanulsága, hogy a kísérleti hipotézist a futás előtt kell rögzíteni; az F5 terv éppen ezt a preregisztrációt gyakorolja. A 39.1. Feladat- és annotációs döntés annotációs konvenciója pedig arra emlékeztet, hogy a cél alakja és az üres eset definíciója a feladat szerves része: itt a target egységnormájú vektor, és a eset kizárása ugyanolyan szerződéses feltétel, mint a bbox üres célja.
A vizsga egy új gráfot vezetett le: 1D keresztkorreláció, ReLU, globális átlag-pooling, L2-normalizálás, cosinus-veszteség és L2-súlyregularizálás kompozícióját. Az 1. lemma rögzíti a peremfeltételeket, és lépésenként igazolja a nemlineáris, pooling- és konvolúciós VJP-ket. Az ellenpélda a nulla normájú esetben mutatja a differenciálhatóság határát. A kézi példa egzakt törtekkel, a labor float64 központi differenciával ellenőriz. Az öt feladat a fogalmi cáfolattól a preregisztrált kísérleti tervig terjed, és a memorizált API-hívás helyett a levezetés és a numerikus kontroll együttesét kéri számon.
A 40.2. Kísérleti és hibakeresési vizsga egység a kísérleti és hibakeresési vizsgát rendezi csomaggá: egy rögzített modellen és adaton kell diagnózist adni, hibás gradiens- vagy adatfolyamot lokalizálni, és a javítást kontrollált újrafuttatással igazolni.