A 28.1. Embedding, távolság és normalizálás egységben definiáltuk az embeddinget mint leképezést, az L2-normalizálást és a koszinusz-hasonlóságot. Most azt vizsgáljuk, milyen veszteségfüggvények tanítanak be olyan teret, amelyben a hasonlósági kérdések közvetlenül, euklideszi vagy koszinusz-távolsággal megválaszolhatók. Induló kérdés: ha egy minibatchben három kép van, honnan tudjuk, melyik kettőt kell közelíteni, és mi történik, ha a „negatív” címke téves?
1. definíció (sziámi, siamese). Legyen egy megosztott paraméterű hálózat, ahol közös. Két vagy több bemenetre a reprezentációk , ugyanazzal a -val készülnek. A sziámi elnevezés tehát a súlymegosztási és tanítási mintára vonatkozik, nem egy külön rétegtípusra. A 28.1. Embedding, távolság és normalizálás jelölését követve a képbemenet alakja , a batchelt embedding , ahol egy megfigyelés egy sor. A közös következménye, hogy a három ág hibájának gradiense a láncszabály szerint ugyanabban a paramétertérben adódik össze.
2. definíció (pár). Egy pár , ahol pozitív pár, ha és azonos osztályú; negatív pár, ha eltérő. A pozitív/negatív fogalmát a rendelkezésre álló felügyelt címke definiálja, nem a pillanatnyi beágyazási távolság.
3. definíció (triplet). Egy triplet rendezett hármas, ahol anchor, pozitív: ; negatív: . A triplet három, batchbeli sorra hivatkozik: .
Jelölje az euklideszi távolságot. A squared forma, amellyel dolgozunk, .
4. definíció (kontrasztív cél, contrastive loss). Legyen a párra és margin a távolságnégyzet egységében:
ahol . A batchveszteség a párok átlaga. A pozitív tag a közelséget, a negatív tag azt bünteti, ha .
5. definíció (triplet margin loss, squared változat). Legyen margin a távolságnégyzet egységében:
Ez a [P16] absztraktjában ellenőrzött állítás szerint ahhoz az elvhez illeszkedik, hogy a beágyazást közvetlenül triplet-megkötésekkel tanítsuk; a képlet pontos FaceNet-változatához a teljes cikk releváns szakasza olvasandó. A népszerű keretrendszerek beépített triplet loss implementációi eltérhetnek abban, hogy a távolságot vagy annak négyzetét használják, ezért beépített osztály használata előtt a helyi API-referenciát ellenőrizni kell.
1. lemma (a squared triplet loss zérus halmaza). Legyen és . Ekkor
Ha a feltétel nem teljesül, azaz , akkor
Bizonyítás (saját levezetés). Legyen . A definíciója szerint , ha , és , ha . A feltétel átrendezve ; a feltétel a fordított szigorú egyenlőtlenség, és ekkor miatt az érték is pozitív. Az egyenlőségi határon , tehát a veszteség zérus, de a marginpont nem sima pont.
Következmény (kapcsolat a koszinusszal). Ha mind egységnormájú, a 28.1. Embedding, távolság és normalizálás alfejezetben levezetett alapján a zérus feltétel:
A margin tehát normalizált beágyazásban a pozitív koszinusz-hasonlóság felett előnyt követel meg.
Kézi számpélda. Legyen , .
Az 1. példa mutatja, hogy a negatívnak nem kell messzinek lennie, csak az margót teljesítenie kell; a 2. példa aktív, kemény tripletet mutat.
Ha a triplet aktív, sima. A négyzetes norma deriváltjából esetén:
Minden gradiens -beli vektor, alakja megegyezik a megfelelő bemenet alakjával. A pontban a nem klasszikusan differenciálható, ugyanúgy, ahogy a ReLU nullpontja a 10.3. Gradiensellenőrzés és numerikus hibamérce gradient checkben; a fenti képletek csak az aktív tartományra érvényesek.
6. definíció (aktív és kemény negatív). A triplet aktív, ha a marginfeltétel sérül: , azaz . Ezen belül szokás kemény negatívnak nevezni a esetet; a tartomány a négyzetes távolságú veszteség szokásos semi-hard (félig kemény) megfelelője. A terminológia a konkrét mining-eljárás szerint változhat. A mining-stratégiák közül a batch-all minden lehetséges tripletet képez a batchben és átlagol, a batch-hard csak a legkeményebbeket tartja meg; ez utóbbi kisebb számítású, de nagyobb gradiensvarianciájú heurisztika. Az egyes szakirodalmi és szoftveres megoldások a semi-hard és hard eseteket eltérő mining-konvenció szerint definiálhatják, ezért a használt kritériumot a kísérleti protokollban rögzíteni kell.
7. definíció (collapse, összeomlás). Azt mondjuk, hogy a beágyazás collapse-olt, ha létezik , amelyre minden vizsgált bemenetre .
Állítás. mellett a triplet loss nem lehet zérus egy teljes mértékben collapse-olt beágyazásra, mert , így . Ezzel szemben esetén a konstans leképezésre ; tehát egy margin nélküli squared triplet cél elfogadná a haszontalan, teljesen egybeeső reprezentációt. Ez a margin szükségességének formális indoka a squared változatra; az állítás saját levezetés a lemma következményeként. A margin nem elégséges garancia: ha a mintavételezés csak távoli negatívokat ad, a hálózat alacsony rangú, finom szerkezet nélküli megoldást is találhat.
A labor célja a squared triplet veszteség numerikus kontrollja és
annak igazolása, hogy a három ágon átfolyó közös paraméter gradiense a
három ág gradiensének összege. A PyTorch-kód a könyv lab
munkakönyvtárából futtatható. A kimenetet nem ebben a kéziratban
futtattuk; a numerikus értékek a képletekből számolt várható
szemléltetések.
# lab/n110_triplet_lab.py
# Futtatás a labor könyvtárból: cd lab && python n110_triplet_lab.py
import torch
torch.manual_seed(20261020)
dtype = torch.float32
device = torch.device("cpu")
def triplet_squared_loss(z_a, z_p, z_n, margin):
# z_a, z_p, z_n: (N, d); margin: float >= 0; reduction: atlag
d_ap = ((z_a - z_p) ** 2).sum(dim=1)
d_an = ((z_a - z_n) ** 2).sum(dim=1)
return torch.clamp(d_ap - d_an + margin, min=0.0).mean()
# 1. Kezi kontroll a ket numerikus peldara
z_a = torch.tensor([[0.0, 0.0]], dtype=dtype, device=device)
z_p = torch.tensor([[1.0, 0.0]], dtype=dtype, device=device)
z_n = torch.tensor([[2.0, 0.0]], dtype=dtype, device=device)
loss_zero = triplet_squared_loss(z_a, z_p, z_n, 0.2)
print("loss_zero =", loss_zero.item(), "(varhato 0.0)")
z_a2 = torch.tensor([[0.0, 0.0]], dtype=dtype, device=device)
z_p2 = torch.tensor([[2.0, 0.0]], dtype=dtype, device=device)
z_n2 = torch.tensor([[0.0, 1.0]], dtype=dtype, device=device)
loss_pos = triplet_squared_loss(z_a2, z_p2, z_n2, 0.2)
print("loss_pos =", loss_pos.item(), "(varhato 3.2)")
class Backbone(torch.nn.Module):
def __init__(self):
super().__init__()
self.fc = torch.nn.Linear(3, 2, bias=False)
def forward(self, x):
# x: (N, 3), kimenet: (N, 2)
return self.fc(x)
# Rogzitett, kezzel beallitott suly; az x bemenetek leaf tenzorok,
# ezert a gradiens csak a sulyra folyik.
W_data = torch.tensor([[0.5, -0.2, 0.1],
[0.3, 0.4, -0.1]], dtype=dtype, device=device)
x_a = torch.tensor([[1.0, 0.0, 0.0]], dtype=dtype, device=device)
x_p = torch.tensor([[0.0, 1.0, 0.0]], dtype=dtype, device=device)
x_n = torch.tensor([[0.0, 0.0, 1.0]], dtype=dtype, device=device)
net = Backbone().to(device)
with torch.no_grad():
net.fc.weight.copy_(W_data)
z_a = net(x_a)
z_p = net(x_p)
z_n = net(x_n)
loss = triplet_squared_loss(z_a, z_p, z_n, 0.2)
loss.backward()
grad_shared = {name: p.grad.detach().clone()
for name, p in net.named_parameters() if p.grad is not None}
# Harom fuggetlen masolat ugyanazzal a sulyvektorral;
# az agankent szamitott gradiensek osszeget hasonlitjuk a megosztotthoz.
nets = [Backbone().to(device) for _ in range(3)]
for net_i in nets:
with torch.no_grad():
net_i.fc.weight.copy_(W_data)
z_list = [nets[0](x_a), nets[1](x_p), nets[2](x_n)]
loss_split = triplet_squared_loss(*z_list, 0.2)
loss_split.backward()
grad_sum = {}
for net_i in nets:
for name, p in net_i.named_parameters():
if p.grad is None:
continue
g = p.grad.detach()
grad_sum[name] = grad_sum.get(name, torch.zeros_like(g)) + g
max_diff = max((grad_shared[name] - grad_sum[name]).abs().max().item()
for name in grad_shared)
print("loss =", loss.item())
print("max gradiens diff =", max_diff)
print("megosztott gradiens egyezik:", max_diff < 1e-6)A várt loss érték a három, lineárisan kivetített
bemenettől függ; a döntő kontroll a max_diff. Ha
max_diff < 1e-6, akkor az autograd valóban a három ág
VJP-jének összegét rendeli a közös súlyhoz; ez a PyTorch gráfbeli
súlymegosztás következménye, és a labor ezt numerikusan, független
referenciaként ellenőrzi.
F1 (fogalmi, nehézség 1). Magyarázza el, mi a különbség a sziámi súlymegosztás és két független hálózat között a triplet cél szempontjából. Miért nem elegendő két külön paraméterezésű háló az anchor–pozitív és anchor–negatív pár feldolgozásához? Átadandó: legfeljebb öt mondatos indoklás a gradiensösszegzés következményével.
F2 (levezetési, nehézség 2). Vezesse le a squared triplet loss aktív esetének gradiensét a változókra. Ellenőrizze, hogy minden gradiens -beli, és hogy . Átadandó: minden lépés a négyzetes norma deriváltjából.
F3 (implementációs, nehézség 2). Írjon
pair_squared_loss(z_i, z_j, y, m) függvényt a kontrasztív
célra NumPy vagy PyTorch használatával. Ellenőrizze az
,
,
,
párra, hogy a kimenet
;
az
,
ugyanezen vektorokra a kimenet
.
Átadandó: kód és a két kimenet.
F4 (hibakeresési, nehézség 2). Az alábbi kód a triplet veszteséget akarja kiszámítani, de egy olyan tripletre is nullát ad, ahol a negatív közelebb van az anchorhoz, mint a pozitív. Keresse meg a hibát, írja le a következményt, és javítsa:
d_ap = ((z_a - z_p) ** 2).sum(dim=1)
d_an = ((z_a - z_n) ** 2).sum(dim=1)
loss = torch.relu(d_an - d_ap + margin).mean() # hiba ittÁtadandó: a hiba oka, a hibás viselkedés egy konkrét numerikus példán, és a javított sor.
F5 (kísérleti/transzfer, nehézség 3). Tervezzen a
GlyphLab train/val/test felosztásán triplet-alapú reprezentációtanulást,
amelynek célja, hogy a test képre a train-képek közül az azonos
orientációjúak legyenek a legközelebb. Adja meg a triplet-képzés
szabályát a scene_id figyelembevételével, egy baseline-t,
egy retrieval-metrikát és két érvényességi korlátot. Átadandó:
protokollterv, nem szükséges kód.
F1. A sziámi hálózatban mindhárom ág ugyanazt a paramétert használja, ezért a triplet veszteség egyetlen közös térben értelmezi a pozitív közelséget és a negatív távolságot. Két független hálózat esetén az anchor–pozitív út és az anchor–negatív út külön transzformációt tanulhatna; a hasonlósági döntéshez nem lenne közös metrika. A közös gradiensére a három ág hibájának VJP-je összegződik: ha az autograd gráfban ugyanaz a súly három lineáris úton szerepel, a láncszabály összeadja az ágakat.
F2. Legyen , tehát . A gradiensére . Az első tag szerinti gradiense , a másodiké . Összegük:
A szerinti derivált csak az első tagból származik: . A szerinti derivált csak a második tagból: . Alakellenőrzés: mindhárom kifejezés -beli, mert ; a veszteség skalár. A konstans deriváltja nulla.
F3. Egy lehetséges megoldás:
import torch
def pair_squared_loss(z_i, z_j, y, m):
d2 = ((z_i - z_j) ** 2).sum()
return y * d2 + (1 - y) * torch.clamp(m - d2, min=0.0)
z_i = torch.tensor([0.0, 0.0])
z_j = torch.tensor([1.0, 0.0])
print(pair_squared_loss(z_i, z_j, 1.0, 0.5).item()) # 1.0
print(pair_squared_loss(z_i, z_j, 0.0, 0.5).item()) # 0.0, ezért esetén ; esetén , tehát a clamp nulla.
F4. A hibás kód a két távolságnégyzetet felcseréli:
relu(d_an - d_ap + margin) akkor büntet, ha a negatív túl
távol van. Konkrét példa:
,
,
mellett a helyes loss
,
a hibás kód viszont
-t
ad, tehát a legkeményebb tripletet figyelmen kívül hagyja. Következmény:
a modell nem kap nyomást a negatív eltávolítására. Javítás:
loss = torch.relu(d_ap - d_an + margin).mean()F5. Egy lehetséges terv. Kérdés: ad-e a triplet
beágyazás jobb retrieval-t, mint a nyers intenzitásvektor? Megfigyelési
egység: jelenet (scene_id), nem kép. Split: a rögzített
GlyphLab train/val/test; triplet csak trainből, a val a
mining-margin kiválasztására, a test csak a
záróértékelésre. Triplet-képzés: batch-all, az anchorhoz minden azonos
osztályú elem pozitív, minden eltérő osztályú negatív; a két view
ugyanazon osztályba tartozik, ezért pozitív párként is szerepelhet.
Baseline: 24×24-es, L2-normalizált nyers intenzitásvektor, euklideszi
távolsággal. Keresett tényező: margin az
halmazból. Metrika: query = test, gallery = train; recall@1 és mAP@5 a
scene-független felosztás miatt. A [D15] metrika-averaging protokollja
az implementáció előtt ellenőrizendő. Korlátok: három osztály,
méret és
kép; az eredmény nem általánosítható természetes képre, a triplet
tanulás pedig nem garantál cluster-tisztaságot.
A rossz negatívdefiníció hamis negatívokat hoz létre, és hibás reprezentációs nyomást ad. Legyen , , , . A párkonstrukció hibásan -t negatívnak címkézi, pedig valójában az anchor osztályába tartozik. Ekkor , , a triplet loss , és a gradiens a -t lefelé tolja, növelve az azonos osztályú pontok távolságát. A hálózat tehát megtanulja szétválasztani azokat a pontokat, amelyeket a valódi szemantika szerint közelíteni kellene. A következmény nem csupán nagyobb veszteség: a downstream osztályozási vagy keresési pontosság is romolhat. A helyes gyakorlat a pár- és triplet-generálást a megbízható osztálycímkére, nem a jelenlegi beágyazási távolságra alapozni.
A margin dimenzióját mindig tisztázni kell: a squared triplet és a fenti kontrasztív egyaránt távolságnégyzet egységű. A pont nem sima; a gradiensképletek csak esetén érvényesek, ugyanúgy, mint a ReLU nullpontjánál. Visszahívás a 28.1. Embedding, távolság és normalizálás alfejezetből: egységnormájú vektorokra , ezért a triplet margin normalizált térben a pozitív feletti koszinusz-előnyt ír elő. A 16.2. Pooling, downsampling és aliasing tanulsága szerint a pooling és downsampling a finom térbeli pozíciót eltüntetheti; a triplet feladathoz használt embedding ezért a backbone kimenetén, nem a global average pooling utáni túl alacsony dimenziós vektoron értelmezendő.
A sziámi szerkezet két vagy több bemenetet ugyanazon a paramétervektoron futtat, ezért a triplet cél közös beágyazási teret kényszerít. A kontrasztív párveszteség a pozitív párokat közelíti, a negatívakat margón túl nyomja. A squared triplet veszteség ; a lemma szerint zérus akkor és csak akkor, ha . A ellenpélda megmutatja, hogy margin nélkül a konstans embedding nulla veszteségű collapse. A hard negative a margót sértő negatív; a rossz negatívdefiníció hamis negatívval hibás nyomást ad. A labor a megosztott súly ágonkénti gradiensösszegzését numerikusan ellenőrzi.
A 28.3. Retrieval-értékelés és adatfelosztás egységben a megtanult beágyazás retrieval-értékelését építjük fel: a galéria és query definícióját, a recall@k, mAP és csoportszintű aggregálás helyes használatát, valamint a [D15] forrásban ellenőrizendő averaging-protokollt.