Ebben az alfejezetben azt a kérdést vizsgáljuk, hogy egy már tanított modell bemenetéhez hozzáadott, emberi szemmel alig észrevehető változás miként változtathatja meg a predikciót. A jelenséget először fenyegetési modellként (threat model) formalizáljuk, majd levezetjük az elsőrendű gradiensinformációból építkező FGSM (Fast Gradient Sign Method) lépést. A labor kizárólag saját, szintetikus GlyphLab-modellünkön mutat be kis, ellenőrzött perturbációt; külső rendszert nem támadunk.
Induló becslés: ha egy -es kép minden pixelét legfeljebb értékkel változtathatjuk meg, vajon megfordul-e az osztályozás? A kérdésre csak a konkrét modell és a konkrét kép ismeretében adható válasz, ezért előbb a fogalmakat rögzítjük.
Egy perturbációs vizsgálat tudományos állításként csak akkor értékelhető, ha megmondjuk, ki mit tud, mit módosíthat, és milyen céllal. Ezt a hármast nevezzük fenyegetési modellnek (threat model).
Definíció (white-box perturbációs támadó). Az osztályozó predikciót ad. A white-box támadó ismeri -t, a bemenet gradiensét és a címkét. Célja olyan előállítása, amelyre , miközben a perturbáció egy előre rögzített norm budget (norma költségvetés) korláton belül marad. A támadó nem fér hozzá a tanításhoz, a súlyokat nem módosítja, és az adatfelosztást nem ismeri — csak a kész modellt és az aktuális bemenetet.
A leggyakoribb budget az -norma:
Képeknél ez azt jelenti, hogy egyetlen pixel intenzitása sem változhat -nál többel; a GlyphLab képek a tartományban vannak, ezért legfeljebb intenzitásváltozást enged pixelenként. Az -budget, , ugyanakkora teljes energiájú, de térben simább perturbációt enged meg; a kettő nem felcserélhető, mert más támadóerőt jelent.
Három adattípust különböztetünk meg, mert más-más tudományos kérdésre válaszolnak:
corrupted_test.npy fájlja ilyen: ugyanazokat a
jelenet/nézet párokat tartalmazza, mint a clean test, csak fizikai
jellegű zajjal terhelve.A corrupt és az adversarial közötti határ a szándék és a modellinformáció felhasználása: a corrupt zaj nem a döntési határra irányul, az adversarial igen.
A levezetéshez egyetlen megfigyelésre van szükség: ha a veszteségfüggvény differenciálható, akkor kis perturbáció mellett a veszteség változását az elsőrendű Taylor-közelítés uralja.
Legyen a veszteségfüggvény (például egyetlen mintára vett cross-entropy), és a bemenet szerinti gradiens. A gradiens alakja megegyezik alakjával; képeknél NCHW tensorként is jelöli, de az absztrakt levezetésben oszlopvektorként kezeljük. A Taylor-közelítés:
A támadó a lineáris tagot akarja maximalizálni a feltétel mellett. Mivel a korlát koordinátánként független, a feladat szétesik:
Minden egyes tagra a maximumot a gradiens előjelét követő lépés adja: ha , akkor ; ha , akkor ; ha , a tag minden megengedett mellett nulla. Így a maximalizáló perturbáció elemenként:
Az itt konvenció, nem a ReLU-féle törésponti döntés; a gradiens pontosan nulla koordinátáinál a perturbáció nem változtatja az elsőrendű közelítést.
Lemma (FGSM irány elsőrendű optimalitása). Legyen differenciálható az pontban, és legyen . Ha , akkor
Egyenlőség akkor és csak akkor áll fenn, ha minden olyan -re, ahol ; a nulla gradiensű koordinátákon a perturbáció tetszőleges lehet. Ha , akkor minden megengedett azonos, nulla elsőrendű növekedést ad.
Bizonyítás. Minden -re , mert . Összegzés után . A felső korlát elérése: esetén , tehát az összeg egyenlő az értékkel. A megfordítás: ha egy koordinátán nem az előjelirány, akkor , ezért az összeg szigorúan kisebb. Ha , akkor és minden tag nulla.
A lemma azt mondja ki, hogy az elsőrendű közelítésben az -budget mellett a veszteséget leggyorsabban növelő irány a gradiens előjele. Ebből kapjuk az FGSM lépést:
Képeknél, ahol a bemenet a tartományra korlátozott, a perturbált képet visszavágjuk: . A a megengedett képtartomány betartását szolgálja, és nem része az elsőrendű irány levezetésének.
Fontos korlát: a lemma nem állítja, hogy az FGSM lépés globálisan maximalizálja a veszteséget, csak azt, hogy az elsőrendű közelítést maximalizálja. Ha a veszteségfelület erősen görbült, a gradiens előjele félrevezető lehet; erre a 31.3. Diffúziós zajmodell és CNN-denoiser alfejezetben látott diffúziós zajmodell is példa, ahol a zajpredikciós cél felülete nem lineáris.
Legyen a modell egy affin osztályozó -ból két logitba:
A bemenet , a helyes osztály , a perturbációs budget . A logitok:
A softmax valószínűségek:
A cross-entropy veszteség egyetlen mintára: . A bemenet szerinti gradiens — a 7.2. Softmax és többosztályos cross-entropy alfejezetben levezetett softmax-láncszabályból — , ahol :
Az FGSM perturbáció: , tehát
Az új logitok , az új veszteség . A veszteség nőtt, de a predikció még mindig a osztály, mert a margó maradt. Az elsőrendű közelítés jósolta növekedés ; a tényleges növekedés , ami a magasabb rendű tagok hatását mutatja.
A következő kód ugyanezt a számítást ellenőrzi float64 pontossággal:
# lab/n139/manual_fgsm.py
import numpy as np
x = np.array([0.5, 0.2, -0.3], dtype=np.float64)
W = np.array([[1.0, 0.0, -0.5],
[0.2, -1.0, 0.4]], dtype=np.float64)
b = np.array([0.1, -0.2], dtype=np.float64)
y = 0
eps = 0.1
def softmax(z):
e = np.exp(z - z.max())
return e / e.sum()
def ce_loss(z, y):
return -z[y] + np.log(np.exp(z).sum())
z = W @ x + b
p = softmax(z)
g = W.T @ (p - np.eye(2)[y])
delta = eps * np.sign(g)
x_adv = x + delta
z_adv = W @ x_adv + b
print("z =", z)
print("L =", ce_loss(z, y))
print("g =", g)
print("delta =", delta)
print("x_adv =", x_adv)
print("z_adv =", z_adv)
print("L_adv =", ce_loss(z_adv, y))
print("eps*||g||_1 =", eps * np.abs(g).sum())Az alábbi labor kizárólag a saját, szintetikus GlyphLab-adatunkon mutat be FGSM-et. A modell egy kis CNN, amelyet a laborban röviden tanítunk a train spliten; a demonstrációhoz a test split első képét használjuk. A kimenetet ez a kézirat számított példaként közli, nem rögzített futásként; a tényleges futás a laborban ellenőrizhető.
Futtatási környezet: CPU, float32, seed
,
munkakönyvtár lab/n139/. Az adatfájlok a szerződés szerint
lab/data/ alatt vannak, ezért a relatív út
../data/.
# lab/n139/fgsm_demo.py
"""FGSM demonstráció a GlyphLab egy képén. Nem támad külső rendszert."""
import numpy as np
import torch
import torch.nn as nn
SEED = 20261020
torch.manual_seed(SEED)
np.random.seed(SEED)
DTYPE = torch.float32
DEVICE = torch.device("cpu")
# --- GlyphLab betöltés a manifest szerződése alapján ---
data_dir = "../data"
images = np.load(f"{data_dir}/images.npy") # (360,1,24,24) float32
labels = np.load(f"{data_dir}/labels.npy") # (360,) int64
# A manifest szerint a train az első 240, a test az utolsó 60 kép;
# a metadata.csv split oszlopa ugyanezt a felosztást rögzíti.
X_train = torch.from_numpy(images[:240]).to(DEVICE).to(DTYPE)
y_train = torch.from_numpy(labels[:240]).to(DEVICE)
x_test = torch.from_numpy(images[300:301]).to(DEVICE).to(DTYPE)
y_test = torch.tensor([labels[300]], device=DEVICE)
class MiniCNN(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(1, 8, 3, padding=1), nn.ReLU(),
nn.AdaptiveAvgPool2d(4),
nn.Flatten(),
nn.Linear(8 * 4 * 4, 3)
)
def forward(self, x):
return self.net(x)
model = MiniCNN().to(DEVICE).to(DTYPE)
loss_fn = nn.CrossEntropyLoss()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
B = 32
for epoch in range(2):
perm = torch.randperm(X_train.shape[0])
for i in range(0, X_train.shape[0], B):
idx = perm[i:i+B]
xb, yb = X_train[idx], y_train[idx]
opt.zero_grad()
loss = loss_fn(model(xb), yb)
loss.backward()
opt.step()
def fgsm_perturbation(model, x, y, eps):
x = x.clone().detach().requires_grad_(True)
logits = model(x)
loss = loss_fn(logits, y)
model.zero_grad()
loss.backward()
grad = x.grad.detach()
delta = eps * torch.sign(grad)
x_adv = torch.clamp(x.detach() + delta, 0.0, 1.0)
return x_adv, delta, loss.item()
eps = 0.05
model.eval()
with torch.no_grad():
clean_logits = model(x_test)
clean_pred = clean_logits.argmax(dim=1).item()
x_adv, delta, clean_loss = fgsm_perturbation(model, x_test, y_test, eps)
with torch.no_grad():
adv_logits = model(x_adv)
adv_pred = adv_logits.argmax(dim=1).item()
adv_loss = loss_fn(adv_logits, y_test).item()
# Kontroll 1: véletlen perturbáció azonos L∞ budgettel
torch.manual_seed(SEED + 1)
rnd = torch.empty_like(x_test).uniform_(-eps, eps)
x_rnd = torch.clamp(x_test + rnd, 0.0, 1.0)
with torch.no_grad():
rnd_pred = model(x_rnd).argmax(dim=1).item()
# Kontroll 2: ellentétes gradiensirány (veszteségcsökkentő lépés)
x_neg = torch.clamp(x_test - delta, 0.0, 1.0)
with torch.no_grad():
neg_pred = model(x_neg).argmax(dim=1).item()
print(f"clean pred={clean_pred} loss={clean_loss:.4f}")
print(f"adv pred={adv_pred} loss={adv_loss:.4f}")
print(f"random pred={rnd_pred}")
print(f"neg pred={neg_pred}")
print(f"||delta||_inf={delta.abs().max().item():.4f}")Várt viselkedés (számított példa, nem rögzített
mérés): a rövid tanítás után a modell a kiválasztott test képen
jellemzően helyesen osztályoz; az FGSM a veszteséget növeli, és gyakran,
de nem garantáltan megfordítja a predikciót. A véletlen perturbáció
azonos budget mellett általában nem változtatja meg a döntést, mert nem
a döntési határra irányul. Az ellentétes gradiensirány csökkenti a
veszteséget. A ||delta||_inf értéknek
-nek
kell lennie, kivéve ha a clip beavatkozott a
határoknál.
Definíció (pontonkénti -robusztusság). Az osztályozó az ponton -robusztus az -normában, ha minden olyan mellett, amelyre , teljesül . A modell adversarial accuracy-ja egy teszthalmazon az -budgetű támadóval szemben:
ahol az adott támadó által előállított perturbált bemenet. Ez a mérőszám attól a konkrét támadóalgoritmustól függ, amellyel -et előállítjuk; ezért az FGSM-mel mért adversarial accuracy és a PGD-vel mért érték nem ugyanazt a robusztusságot méri.
A három tesztfajta összehasonlítása páros szerkezetű: a GlyphLab clean, corrupt és adversarial tesztje ugyanazokra a jelenet/nézet párokra épül, ezért az accuracy-k különbsége páros összehasonlításként értékelhető. A bizonytalanság számításához a 23.3. Páros összehasonlítás és csoportszintű intervallum alfejezetben bevezetett class-stratified scene bootstrap a helyes eljárás, nem a képenkénti független mintavétel.
F1 (fogalmi, nehézség 1). Magyarázd el, mi a különbség a clean, corrupt és adversarial tesztadatok között a GlyphLab keretében. Mindegyikre adj egy konkrét példát, és mondd meg, melyik milyen tudományos kérdésre válaszol. Elvárt átadandó eredmény: legfeljebb fél oldalas, három bekezdésből álló magyarázat, mindhárom esetre explicit példával.
F2 (levezetési, nehézség 2). Bizonyítsd be az -normás változatot: ha és , akkor az elsőrendű tagot a perturbáció maximalizálja, és a maximum . Mi történik, ha ? Elvárt átadandó eredmény: teljes levezetés a Cauchy–Schwarz-egyenlőtlenség alkalmazásával, az egyenlőség feltételének indoklásával.
F3 (implementációs, nehézség 1). Írj Python
függvényt fgsm_affine(W, b, x, y, eps) néven, amely a fenti
affin osztályozón kiszámítja az FGSM perturbációt, és visszaadja az
(x_adv, loss_orig, loss_adv, grad) négyest. Ellenőrizd a
kézi példán a megadott paraméterekkel; a numerikus tolerancia
.
Elvárt átadandó eredmény: futtatható kód, amely kiírja a négy értéket,
és az ellenőrzés sikerét jelzi.
F4 (hibakeresési, nehézség 2). Az alábbi kódrészlet egy hibát tartalmaz:
x_adv = x.clone().detach().requires_grad_(True)
loss = loss_fn(model(x_adv), y)
loss.backward()
delta = eps * torch.sign(x_adv.grad)
x_final = torch.clamp(x_adv + delta, 0.0, 1.0)Itt x az eredeti, nem perturbált kép. Mi a hiba, mi a
következménye, és hogyan javítanád? Elvárt átadandó eredmény: a hiba
lokalizálása, a következmény leírása egy-két mondatban, és a javított
kódrészlet.
F5 (kísérleti, nehézség 3). Tervezz kísérletet a GlyphLab-on, amely összehasonlítja a clean accuracy-t, az FGSM-perturbált teszt accuracy-ját és a corrupt teszt accuracy-ját. Add meg: a kutatási kérdést, a splitet, a modellt, a baseline-t, a változó tényezőt, a metrikát, a futtatási erőforrást és a következtetés korlátait. Elvárt átadandó eredmény: protokollterv az 1.2. Szakmailag megalapozott modellezési állítások alfejezetben bevezetett kísérleti napló szerkezetében, legalább egy explicit érvényességi korláttal.
M1. A clean adat az eredeti, módosítatlan
tesztbemenet; például a data/images.npy test splitjének
első képe. Azt a kérdést válaszolja meg, hogy a modell általában milyen
pontosan osztályoz. A corrupt adat természetes, nem ellenséges romlást
tartalmaz; a GlyphLab corrupted_test.npy fájlja zajt és
fényességgradienst ad ugyanazokhoz a jelenet/nézet párokhoz. Azt
vizsgálja, hogy a modell mennyire érzékeny a fizikai jellegű
képromlásra. Az adversarial adat a modell gradiensét felhasználva, a
hibázás céljával készült perturbáció; például a fenti FGSM-mel generált
.
Azt vizsgálja, hogy egy white-box támadó milyen könnyen tudja
megváltoztatni a döntést. A három mérés három különböző kockázatot
jellemez, ezért nem helyettesíthetik egymást.
M2. A Cauchy–Schwarz-egyenlőtlenséggel . Az egyenlőség feltétele, hogy és párhuzamosak és azonos irányúak; mivel , a választás eléri a korlátot, és valóban . Ha , akkor a felső korlát nulla, és minden megengedett nulla elsőrendű növekedést ad; a másodrendű tagok dönthetik el, melyik irány növeli a veszteséget.
M3. A megoldás a kézi példát közvetlenül újrahasznosítja:
import numpy as np
def fgsm_affine(W, b, x, y, eps):
x = np.asarray(x, dtype=np.float64)
W = np.asarray(W, dtype=np.float64)
b = np.asarray(b, dtype=np.float64)
def softmax(z):
e = np.exp(z - z.max())
return e / e.sum()
def ce(z, y):
return -z[y] + np.log(np.exp(z).sum())
z = W @ x + b
p = softmax(z)
g = W.T @ (p - np.eye(W.shape[0])[y])
delta = eps * np.sign(g)
x_adv = x + delta
return x_adv, ce(z, y), ce(W @ x_adv + b, y), g
W = np.array([[1.0, 0.0, -0.5], [0.2, -1.0, 0.4]])
b = np.array([0.1, -0.2])
x = np.array([0.5, 0.2, -0.3])
y = 0
eps = 0.1
x_adv, L0, L1, g = fgsm_affine(W, b, x, y, eps)
print(x_adv, L0, L1, g)
# Ellenőrzés: L1 > L0 és ||x_adv - x||_inf <= eps + 1e-12
assert L1 > L0
assert np.abs(x_adv - x).max() <= eps + 1e-12Az x_adv várható értéke
,
a veszteségek
és
körül, a gradiens
körül, a tolerancia
.
M4. A hiba az, hogy a gradiens a perturbált ponton készül, miközben a perturbációt is -re alkalmazzák. Az eredmény egy kétlépéses, második lépésében már eltérített irány, nem pedig az eredeti ponton vett FGSM. Következmény: az irány már nem az -beli veszteség növelésére irányul, ezért a veszteség növekedése kisebb lehet, és a támadás hatékonysága romlik. A javítás: a gradienset az eredeti -en kell kiszámítani, majd azonnal perturbálni:
x_grad = x.clone().detach().requires_grad_(True)
loss = loss_fn(model(x_grad), y)
loss.backward()
delta = eps * torch.sign(x_grad.grad)
x_adv = torch.clamp(x + delta, 0.0, 1.0)M5. Lehetséges terv: Kérdés: mekkora a különbség a clean, corrupt és FGSM-perturbált test accuracy között a GlyphLab modellen? Split: a rögzített train/val/test; a modellt csak trainen tanítjuk, a val a korai leállításra, a test csak a végén kerül kiértékelésre — a corrupt és adversarial teszt ugyanazon a test spliten. Modell: a laborban használt MiniCNN. Baseline: a clean test accuracy. Változó tényező: a bemenet típusa (clean, corrupt, FGSM ). Metrika: scene-egységen átlagolt accuracy, a bizonytalanságot class-stratified scene bootstrap adja. Erőforrás: CPU, float32, seed . Következtetési korlát: az eredmény a szintetikus GlyphLab-eloszlásra és a választott FGSM-támadóra vonatkozik; nem állít semmit a természetes képek robusztusságáról, és egyetlen FGSM-eredmény nem bizonyítja a modell ellenállását erősebb, például iterált támadókkal szemben.
A leggyakoribb tévedés: egy FGSM-teszten elért siker nem minden támadóval szembeni robusztussági bizonyítás. Az FGSM egyetlen lépést tesz a gradiens előjelirányában; ha a veszteségfelület nem lineáris, egy iterált, vetített gradiensű módszer (például PGD) ugyanazon budget mellett lényegesen nagyobb veszteségnövekedést találhat. Ellenpélda: tekintsük az veszteségfüggvényt az pontban, mellett. Itt , tehát az FGSM perturbáció nulla, és a veszteség nem nő. Egy támadó azonban a irányt választva veszteséget ér el. Az FGSM tehát nem találta meg a támadási irányt, miközben létezik hatékony perturbáció. A modell értékelésekor ezért mindig meg kell nevezni a támadóalgoritmust, és az egyetlen támadóval mért adversarial accuracy nem általános robusztussági garancia.
Rövid önellenőrzés: (1) A gradiens alakja megegyezik-e a bemenet alakjával? (2) A perturbáció minden koordinátája legfeljebb abszolút értékű-e? (3) A clip után a kép a tartományban marad-e? (4) A megfogalmazott állítás az elsőrendű közelítésre vagy a tényleges veszteségre vonatkozik? Visszahívás a 35.2. Magyarázat sanity check alfejezetből: a sanity check arra figyelmeztet, hogy a gradiensalapú jelzések akkor is változhatnak, ha a modell nem használja a címkét; az FGSM-nél ez azért fontos, mert a gradiens informatív voltát ugyanúgy ellenőrizni kell. A 34.3. Pruning és struktúra alfejezetből: a strukturált pruning megváltoztatja a döntési határt, ezért a perturbációs vizsgálatot mindig a végleges, nem pedig a közbenső modellen kell elvégezni.
Az adversarial példa olyan bemenet, amelyet a modell hibázásának céljával, a megengedett perturbációs budgeten belül választottak. A white-box fenyegetési modell rögzíti, hogy a támadó ismeri a paramétereket és a gradienst. Az FGSM az elsőrendű Taylor-közelítésből vezethető le: az -korlát mellett a veszteséget növelő irány a gradiens előjele, amelynek maximális elsőrendű növekedése . A kézi példa igazolta a lépést, a labor pedig saját GlyphLab-modellen mutatott kontrollperturbációt. A clean, corrupt és adversarial accuracy más kockázatot mér; egyetlen FGSM-eredmény nem bizonyít általános robusztusságot.
A 35.4. Korlátok, eredet és döntési felelősség egységben ezeket a perturbációs eredményeket a korlátok, az adateredet és a döntési felelősség keretébe helyezzük: milyen feltételek mellett lehet egy modellt magas tétű alkalmazásban használni, és mit kell dokumentálni az ismert támadási felületekről.