22.2.3. Kód implementáció

2025.10.06.
AI Biztonság Blog

Az elmélet és a képlet tiszta sor, a környezetünk pedig készen áll. Most jön a lényeg: hogyan ültetjük át az FGSM matematikai formuláját – \(x’ = x + \epsilon \cdot \text{sign}(\nabla_x J(\theta, x, y))\) – működő Python kódba? Ahelyett, hogy egyben rád zúdítanám a kész függvényt, bontsuk le a folyamatot a leggyakoribb kérdések mentén, amelyek egy ilyen implementáció során felmerülnek.

Kapcsolati űrlap

AI Biztonság kérdésed van? Itt elérsz minket:

Milyen bemenetekre és kimenetekre van szükségünk?

Mielőtt egyetlen sort is írnánk, gondoljuk végig, mit kell a támadást generáló függvényünknek kapnia, és mit várunk el tőle. Egy logikusan felépített függvény drasztikusan leegyszerűsíti a későbbi munkát.

  • Bemenetek:
    • image: A PyTorch tenzor, ami a támadni kívánt bemeneti képet tartalmazza.
    • epsilon: A perturbáció mértékét szabályozó hiperparaméter (egy lebegőpontos szám).
    • data_grad: A veszteségfüggvény gradiense a bemeneti képhez képest. Ezt kell majd felhasználnunk a zaj generálásához.
  • Kimenet:
    • perturbed_image: Az eredeti képből generált, rosszindulatú zajjal ellátott új kép, szintén PyTorch tenzorként.

A függvényünk váza tehát valahogy így nézne ki:

# PyTorch importálása
import torch

def fgsm_attack(image, epsilon, data_grad):
 """
 FGSM támadás generálása a bemeneti kép és a gradient alapján.
 
 Argumentumok:
 image (torch.Tensor): Az eredeti, tiszta kép.
 epsilon (float): A perturbáció mértékét szabályozó érték.
 data_grad (torch.Tensor): A veszteség gradiense a bemeneti képhez képest.
 
 Visszatérési érték:
 torch.Tensor: A perturbált, adverzárius kép.
 """
 # ... az implementáció itt következik ...
 pass

Hogyan alkalmazzuk a képletet a gradientre?

Ez az FGSM lelke. A képlet a gradient előjelét (sign) kéri. A PyTorch szerencsére rendelkezik egy beépített függvénnyel erre: torch.sign(). Ez a függvény elemenként megállapítja egy tenzor elemeinek előjelét, és egy új tenzort ad vissza, amelyben -1, 0 vagy 1 értékek szerepelnek.

# A gradient előjelének meghatározása
sign_data_grad = data_grad.sign()

Ha ez megvan, a képlet következő lépése az előjel-tenzor megszorzása az epsilon értékkel, majd hozzáadása az eredeti képhez. Ez egy egyszerű elemenkénti szorzás és összeadás.

# A perturbált kép létrehozása a képlet alapján
perturbed_image = image + epsilon * sign_data_grad

Mi a helyzet a képi adatok érvényességével?

Itt jön egy kritikus, gyakorlati lépés, amit a puszta matematikai képlet nem tartalmaz. A képeink pixelértékei általában egy meghatározott tartományban vannak (pl. [0, 1] a normalizált képeknél, vagy [0, 255] az egész számoknál). A zaj hozzáadása után könnyen előfordulhat, hogy egyes pixelértékek „kilógnak” ebből a tartományból (pl. -0.1 vagy 1.2 lesz az értékük).

Ezek az érvénytelen értékek problémát okozhatnak a modellnek, és nem is reprezentálnak valós képet. A megoldás az, hogy a perturbált kép értékeit „levágjuk” (clamp) az érvényes tartományon belülre.

Figyelem! A clamping lépés elhagyása az egyik leggyakoribb hiba az adverzárius támadások implementálásakor. Mindig győződj meg róla, hogy a generált kép pixelértékei az eredeti adatokkal megegyező tartományban maradnak.

A PyTorch erre is kínál egy egyszerű megoldást, a torch.clamp() függvényt, amellyel megadhatjuk a minimális és maximális elfogadható értékeket.

# A pixelértékek visszaszorítása az érvényes [0,1] tartományba
perturbed_image = torch.clamp(perturbed_image, 0, 1)

A teljes FGSM generáló függvény egyben

Most, hogy minden lépést külön-külön megvizsgáltunk, rakjuk össze a teljes, működőképes függvényt. Ez a kód fogja képezni a következő fejezetben végrehajtott támadásunk alapját.

import torch

def fgsm_attack(image, epsilon, data_grad):
 """
 FGSM támadás generálása a bemeneti kép és a gradient alapján.
 Ez a függvény feltételezi, hogy a bemeneti kép pixelértékei a [0,1]
 tartományban vannak normalizálva.
 
 Argumentumok:
 image (torch.Tensor): Az eredeti, tiszta kép.
 epsilon (float): A perturbáció mértékét szabályozó érték.
 data_grad (torch.Tensor): A veszteség gradiense a bemeneti képhez képest.
 
 Visszatérési érték:
 torch.Tensor: A perturbált, adverzárius kép.
 """
 # 1. Lépés: A gradient előjelének összegyűjtése
 # Ez a lépés felel meg a sign(∇x J(...)) résznek a képletben.
 sign_data_grad = data_grad.sign()
 
 # 2. Lépés: A perturbált kép létrehozása az FGSM képlet alapján
 # x' = x + ε * sign(...)
 perturbed_image = image + epsilon * sign_data_grad
 
 # 3. Lépés: A kép értékeinek "clamping"-je, hogy a [0,1] tartományban maradjanak
 # Ez egy kritikus gyakorlati lépés az érvényes képi adatok biztosítására.
 perturbed_image = torch.clamp(perturbed_image, 0, 1)
 
 # 4. Lépés: A kész adverzárius kép visszaadása
 return perturbed_image
Bemeneti Gradient data_grad.sign() image + ε * sign torch.clamp(…, 0, 1) Adverzárius Kép

Az FGSM perturbáció generálásának folyamata a kódban.

Ezzel a tiszta és jól dokumentált függvénnyel a kezünkben már mindenünk megvan ahhoz, hogy a következő fejezetben ténylegesen végrehajtsuk a támadást egy betanított modellen, és kiértékeljük annak hatékonyságát.