Ez az alfejezet a zajos bemenet és a tiszta célkép közötti páros szerződést vizsgálja. Feltételezzük a 29.1. Lineáris és nemlineáris autoencoder autoencoder-alapfogalmait: az encoder, decoder, latent tér és rekonstrukciós cél ismert. Nincs szükség haladó statisztikai előismeretre; a várható érték és a feltételes eloszlás alapjait az 5.2. Várható érték, variancia és likelihood vezette be.
Induló kérdés: egy 24×24-es szürke képre additív Gauss-zajt teszünk, majd egy modellnek a zajos képből a tiszta képet kell visszaadnia. Mi a legkisebb elérhető per-pixel négyzetes hiba? A válasz nem nulla, és a korrupciós modell, a cél definíciója és a tanító/target szétválasztás nélkül nem is értelmes.
Tiszta kép (clean image). Egyetlen megfigyelés , a GlyphLab esetében , , és minden pixelérték a intervallumba esik. Egy batch ; az absztrakt képletekben oszlopvektorként, dimenzióval jelenik meg.
Korrupció (corruption). A leképezés egy tiszta képhez és egy zajrealizációhoz rendeli a zajos megfigyelést: A tér a zaj értékkészlete. A korrupció lehet véletlen, de a tanítási folyamatban a zajrealizációt explicite kezelni kell.
Zajeloszlás (noise distribution). A korrupció által indukált feltételes eloszlás . A leggyakoribb laborváltozat az additív Gauss-zaj: ahol a klipelés elemenként . A klipelés miatt a megfigyelt peremeloszlása már nem tiszta Gauss, hanem csonkolt/cenzorált eloszlás.
Denoiser. A függvény a zajos képet a tiszta kép becslésére képezi: . A 29.1. Lineáris és nemlineáris autoencoder alfejezetben bevezetett encoder–decoder szerkezet természetes választás: az encoder a zajos képet latens reprezentációba képezi, a decoder onnan rekonstruál; a kimenet alakja megegyezik a bemenet alakjával.
Rekonstrukciós veszteség (reconstruction loss). A
denoising cél a tiszta target és a becsült kép közötti négyzetes
eltérés. Egyetlen páron
a teljes batch-en pedig az átlag:
A per-pixel MSE ettől egy
osztóban tér el; a levezetésekben a (1) alakot használjuk, és mindig
kimondjuk, melyik normalizálás szerepel. PyTorch
MSELoss(reduction='mean') az elemenkénti átlagot adja,
ezért ha a (1) alakot akarjuk, a veszteséget
helyett
átlagként kell kezelni; a gradiensskálát a tanulási ráta
kompenzálhatja.
Szivárgás (leakage). A tanító, validációs vagy tesztelési folyamatban a teszt splithez tartozó tiszta target vagy a teszt zajrealizációjára vonatkozó információ bejut a modellparaméterekbe vagy a modellválasztásba. A denoising beállításban a legsúlyosabb szivárgás az, ha egy tiszta tesztkép tanítási targetként szerepel: ekkor a teszt nem a korrupció eltávolítását méri, hanem a targetek memorizálását.
A denoising feladat valószínűségi megfogalmazása a várható négyzetes hiba minimalizálása. Legyen a tiszta kép véletlen vektor, a korrupt megfigyelés. A cél az összes mérhető függvény fölött:
Tétel (MSE-optimális denoiser). Tegyük fel, hogy . Ekkor az a minimalizálja a (2) célfüggvényt, amelyre (a feltételes várható érték alkalmas regular verziója), és a minimum
Bizonyítás. Legyen , és írjuk fel a különbséget: A normanégyzetet kifejtve: Vegyük mindkét oldal várható értékét. A kereszttag feltételes várható értékét kell megvizsgálni: $$\mathbb E\bigl[(X-m(Y))^\top(m(Y)-g(Y))\bigr] =\mathbb E\Bigl[\mathbb E\bigl[(X-m(Y))^\top(m(Y)-g(Y))\mid Y\bigr]\Bigr].$$ A feltételes várható érték tulajdonsága miatt az -tól függő kiemelhető: mert a feltételes várható érték definíciója szerint. A másodmomentum-feltétel és a Cauchy–Schwarz-egyenlőtlenség biztosítja, hogy a várható értékek végesek és a kiemelés jogszerű. Így és egyenlőség pontosan akkor áll, ha majdnem mindenütt. ∎
A tétel elméleti optimumot ad, de nem konstruktív: a általában nem zárt alakú, ezért a gyakorlatban paraméteres közelítést használunk. Az (1) empirikus veszteség a (2) várható cél Monte Carlo-közelítése, ha a párok független minták a közös korrupciós eloszlásból.
Additív Gauss-zaj esete. Legyen , ahol független -től, és a klipelést egyelőre elhagyjuk. A sűrűség: $$p_{Y\mid X}(y\mid x)=\prod_{j=1}^d \frac{1}{\sqrt{2\pi\sigma^2}}\exp\Bigl(-\frac{(y_j-x_j)^2}{2\sigma^2}\Bigr).$$ A Bayes-szabállyal a posterior: $$p_{X\mid Y}(x\mid y)\propto p_X(x)\exp\Bigl(-\frac{1}{2\sigma^2}\lVert y-x\rVert_2^2\Bigr),$$ ahol a tiszta képek eloszlása. A Bayes-optimális denoiser tehát Ez általában nem és nem is lineáris függvénye -nak. A célfüggvény a Tétel szerint: a klipeléses változatban pedig mellett ugyanez a képlet, de a eloszlása a csonkolás miatt különbözik a tiszta Gauss-esettől.
Legyen , egyenlő valószínűséggel, független, és . A Bayes-optimális denoiser a posterior várható érték: ahol a standard normális sűrűség. A pontban és , így Az identikus leképezés -et adna, a triviális átlag ; az optimális becslés a -ra húz, mert a megfigyelés közelebb van a móduszú esethez. A Tétel garantálja, hogy semmilyen más -vel nem érhető el kisebb várható négyzetes hiba ennél a zajmodellnél.
A labor kizárólag a GlyphLab saját, procedurális adatát használja. A cél: additív Gauss-korrupcióval párokat készíteni, a teszt zajrealizációját külön seeddel generálni, és a szivárgást szisztematikusan ellenőrizni. A modell tanítását nem végezzük el ebben a laborban; a párok előállítása és a baseline kiértékelés az átadandó eredmény.
import csv
import numpy as np
from pathlib import Path
DATA = Path("data")
SIGMA = 0.25
TRAIN_SEED = 20261020
VAL_SEED = 20261021
TEST_SEED = 20261022
def read_split_indices(metadata_path: Path) -> dict[str, np.ndarray]:
"""A metadata.csv split oszlopából indexeket olvas be int64-ként."""
idx: dict[str, list[int]] = {"train": [], "val": [], "test": []}
with metadata_path.open(newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
idx[row["split"]].append(int(row["index"]))
return {k: np.asarray(v, dtype=np.int64) for k, v in idx.items()}
def add_gaussian_noise(
clean: np.ndarray, sigma: float, seed: int
) -> tuple[np.ndarray, np.ndarray]:
"""Additív Gauss-zaj, majd elemenkénti [0,1] klipelés.
clean: (M, C, H, W) float32, [0,1]
Vissza: (noisy, noise), mindkettő float32, noisy [0,1].
"""
rng = np.random.default_rng(seed)
noise = rng.normal(0.0, sigma, clean.shape).astype(np.float32)
noisy = np.clip(clean + noise, 0.0, 1.0).astype(np.float32)
return noisy, noise
# 1. Bemenet és split
images = np.load(DATA / "images.npy") # (360,1,24,24) float32
meta_idx = read_split_indices(DATA / "metadata.csv")
train = images[meta_idx["train"]]
val = images[meta_idx["val"]]
test = images[meta_idx["test"]]
# 2. Korrupció: train/val/test külön zajseeddel
train_noisy, train_noise = add_gaussian_noise(train, SIGMA, TRAIN_SEED)
val_noisy, val_noise = add_gaussian_noise(val, SIGMA, VAL_SEED)
test_noisy, test_noise = add_gaussian_noise(test, SIGMA, TEST_SEED)
# 3. Alak- és tartománykontroll
for name, arr in [("train_noisy", train_noisy), ("val_noisy", val_noisy),
("test_noisy", test_noisy)]:
assert arr.shape == train.shape, name
assert np.isfinite(arr).all(), name
assert 0.0 <= arr.min() and arr.max() <= 1.0, name
# 4. Leakage-ellenőrzés: a tiszta testképek nem lehetnek train targetek
assert len(set(meta_idx["train"]).intersection(meta_idx["test"])) == 0
# 5. Baseline-ok, per-pixel MSE-ként
def mse(a: np.ndarray, b: np.ndarray) -> float:
return float(np.mean((a - b) ** 2))
identity_mse = mse(test_noisy, test) # a zajos input mint predikció
const_pred = np.full_like(test, np.mean(train)) # tanítási átlag konstansként
const_mse = mse(const_pred, test)
noise_energy = float(np.mean(test_noise ** 2)) # a vizsgált zaj átlagos energiája
print(f"shapes: train_noisy={train_noisy.shape}, test_noisy={test_noisy.shape}")
print(f"dtype: {train_noisy.dtype}, device: CPU (NumPy)")
print(f"identity baseline MSE={identity_mse:.6f}")
print(f"const baseline MSE={const_mse:.6f}")
print(f"noise energy={noise_energy:.6f}")A futáskor a data/images.npy és
data/metadata.csv fájloknak a munkakönyvtár
data almappájában kell lenniük. Futtatási parancs:
python denoising_pairs.pyEllenőrzés és értelmezés. Az identikus baseline per-pixel MSE-je várhatóan körül mozog, de a klipelés a szélső pixeleknél csökkenti a hibát, ezért a tényleges érték kisebb lehet. A konstans baseline a tiszta képek varianciáját méri: ha a képek intenzitása erősen változó, ez nagyobb -nél; ha a képek közel konstansak, kisebb. A zajenergia a ténylegesen hozzáadott négyzetes átlaga, és csak a klipelés nélküli zajt méri. A denoisernek az identikus és a konstans baseline alá kell mennie; a sikeres tanulás szükséges feltétele, hogy a validációs MSE az identikus baseline alatt legyen.
A kimenet a fenti kód futtatása nélkül nem mért érték; a megadott képletek és a GlyphLab szerződés alapján számított szemléltetésként az identikus baseline , a konstans baseline a tiszta képek szórásától függ, a zajenergia körüli.
F1 (fogalmi, nehézség 1). Definiálja a corruption, denoising és leakage fogalmát a denoising tanítási protokollban. Magyarázza el, miért vezetne az identikus leképezéshez az a hibás cél, amely a zajos képet önmagával hasonlítja. A válaszban adjon egy kis ellenpéldát: legyen , és mutassa meg, hogy az minimalizálója az identitás.
F2 (levezetési, nehézség 2). Legyen , , , és , ahol független. Vezesse le a Bayes-optimális denoiser képletét, számítsa ki értékét, és adja meg a feltételes minimális négyzetes hibát esetén. A megoldásban a közös normáló konstans kiesését mutassa ki.
F3 (implementációs, nehézség 2). Írjon
Python-függvényt, amely a GlyphLab data/images.npy
tömbjéből készít zajos–tiszta párokat: a train, val, test splithez külön
seedet használ, additív Gauss-zajt ad
mellett, és a zajos képet a
intervallumba klipeli. A függvény ellenőrizze, hogy a kimeneti zajos
tömb alakja megegyezik a bemenettel, minden érték véges, és a test split
tiszta célja nem szerepel a train targetek között.
F4 (hibakeresési, nehézség 2). Az alábbi kódrészlet hibás:
train_clean = images[train_idx]
test_clean = images[test_idx]
train_noisy = add_gaussian_noise(train_clean, SIGMA, TRAIN_SEED)[0]
target = np.concatenate([train_clean, test_clean], axis=0) # ???
model.fit(train_noisy, target)Lokalizálja a hibát, adja meg a következményét, és javítsa ki úgy, hogy a tanítás után a teszt splitre vonatkozó MSE független maradjon. A javított kódban mutassa be az ellenőrzést is.
F5 (kísérlettervezési, nehézség 3). Tervezzen kísérletet annak mérésére, hogy egy denoiser hogyan általánosít egy új, a tanításnál nem látott zajszintre. A tanítás zajjal történik, a teszt és mellett is futtatandó. Adja meg a megfigyelési egységet, a baseline-okat, a metrikát, a split- és seed-szabályt, valamint a következtetés érvényességi határát.
F1. A corruption a leképezés, amely tiszta képből zajos megfigyelést ad; a denoising a zajos megfigyelésből a tiszta kép becslése; a leakage a teszt splithez tartozó tiszta cél vagy zajrealizáció-információ bejutása a tanítási folyamatba. Ha a cél a zajos kép önmaga, azaz , akkor a cél minimális értéke nulla, és a identitás eléri. Ezért a modell nem tanul zajtalanítást, csak identitást. Az ellenpélda a fenti képletben közvetlen: bármely megoldás nulla hibát ad, függetlenül a zaj mértékétől.
F2. A posterior a Bayes-szabállyal, a közös konstanssal: A denoiser . A normális sűrűség közös szorzója kiesik; csak az exponenciális tagokat kell számolni: Súlyozatlanul a súlyok és ; a normalizálás után Így A feltételes minimális négyzetes hiba a posterior variancia: Alakellenőrzés: a kimenet skalár, a posterior valószínűségek összege egy, a variancia nemnegatív.
F3. A megoldás a labor kódjának megfelelő függvény:
def make_noisy_pairs(images, meta, sigma, train_seed, val_seed, test_seed):
idx = read_split_indices(meta)
train_clean = images[idx["train"]]
val_clean = images[idx["val"]]
test_clean = images[idx["test"]]
def noisy(clean, seed):
rng = np.random.default_rng(seed)
n = rng.normal(0.0, sigma, clean.shape).astype(np.float32)
return np.clip(clean + n, 0.0, 1.0).astype(np.float32), n.astype(np.float32)
train_noisy, train_noise = noisy(train_clean, train_seed)
val_noisy, val_noise = noisy(val_clean, val_seed)
test_noisy, test_noise = noisy(test_clean, test_seed)
assert train_noisy.shape == train_clean.shape
assert np.isfinite(train_noisy).all()
assert train_noisy.min() >= 0.0 and train_noisy.max() <= 1.0
assert len(set(idx["train"]).intersection(idx["test"])) == 0
return (train_noisy, train_clean), (val_noisy, val_clean), (test_noisy, test_clean)Használat: a meta lehet a data/metadata.csv
elérési út vagy egy DataFrame; itt a read_split_indices a
CSV-t olvassa. A tolerancia a klipelésnél nem szükséges, mert a
np.clip pontosan a megadott határokat adja; a shape-eket
egész számokként ellenőrizzük.
F4. A hiba az
np.concatenate([train_clean, test_clean]) sorban van: a
tiszta tesztképek bekerülnek a tanítási targetek közé. Következmény: a
modell a teszt tiszta céljának memorizálása révén hamisan alacsony teszt
MSE-t érhet el, miközben új zajos mintán gyengén teljesít; a kiértékelés
torzított. Javítás: csak a train splitből készítsünk tanítópárokat, a
test célját kizárólag a végső tesztnél használjuk:
train_clean = images[train_idx]
train_noisy = add_gaussian_noise(train_clean, SIGMA, TRAIN_SEED)[0]
model.fit(train_noisy, train_clean)
test_noisy = add_gaussian_noise(images[test_idx], SIGMA, TEST_SEED)[0]
test_mse = np.mean((model.predict(test_noisy) - images[test_idx]) ** 2)
assert not np.array_equal(model.predict(test_noisy), images[test_idx])Az ellenőrzés: a train_idx és test_idx
diszjunktságát a labor 4. lépése garantálja; a teszt MSE-t csak egyszer,
a modell kiválasztása után számítjuk.
F5. Egy lehetséges terv: a megfigyelési egység a
GlyphLab scene_id-je, mert a két nézet ugyanannak a
jelenetnek a zajos változata; a split scene-szinten rétegzett. A modell
tanítása
zajjal, a validáció
mellett választja a legjobb checkpointot. A teszteléshez a tiszta test
splitet három külön zajrealizációval korrumpáljuk:
,
,
,
mindegyikhez külön seedet használva. Baseline-ok: az identikus
és a tanítási átlag konstans prediktor, minden zajszinten; a metrika a
per-pixel MSE és a PSNR
.
A kiértékelést a modell rögzítése után egyszer futtatjuk. Érvényességi
határ: az eredmény csak a választott zajcsaládra és a szintetikus
GlyphLab adatra vonatkozik; a klipelés miatt a magas
esetén a tényleges zaj torzul, ezért a
eredmény nem ekvivalens egy valós érzékelő zajával.
A leggyakoribb hiba az, hogy a tiszta tesztkép tanítási targetként megjelenik. Tegyük fel, hogy a 60 tesztképből 10 tiszta targetet véletlenül a train célhoz csatolunk. Egy olyan modell, amely a train párokat memorizálja, a teszt ezen 10 elemére nulla közeli MSE-t adhat, míg a maradék 50 elemre a denoising teljesítménye gyenge. Az átlagos teszt MSE így optimistán alacsony lesz, de nem a zajtalanítás minőségét méri. A helyes protokoll: a train generálásakor kizárólag a train split képeit használjuk, a test tiszta célját pedig csak a végső kiértékelésben töltjük be.
Ellenőrzendő: a target mindig a tiszta kép, a veszteség redukciója explicite rögzített, a zajrealizációk seedjei a splitenként elkülönülnek. A 28.2. Sziámi és triplet cél triplet céljával párhuzamos: ott a pozitív pár közelítése, itt a zajos–tiszta pár közvetlen rekonstrukciója a cél; margin és negatív pár hiányában a denoising collapse-je a konstans prediktor, nem a konstans embedding. A 25.2. Multilabel és hierarchikus cél megfigyelési maszkjának tanulsága: ha tudjuk, mely pixelek korruptak, a loss súlyozható; itt minden pixel megfigyelt, ezért a maszk nem szükséges. A 17.2. AlexNet és a kísérleti kontextus kísérleti kontextusa emlékeztet: a zajmodell és a kiértékelési feltételek rögzítése nélkül az eredmény nem általánosítható.
A denoising cél a zajos megfigyelésből a tiszta kép becslése, a korrupciós leképezés rögzítése mellett. A várható négyzetes hibát a feltételes várható érték minimalizálja; a tétel feltétele . Az additív Gauss-zaj modelljében a posterior a tiszta kép priorjától függ, ezért az optimum általában nem zárt alakú. Az empirikus veszteség az átlagos rekonstrukciós hiba, per-pixel MSE-re az elemszámmal normalizálva. A labor a GlyphLab adatra külön train/val/test zajseeddel állít elő párokat; a tiszta tesztcél tanítási targetként való megjelenése szivárgás, és a teszt MSE-t torzítja.
A 29.3. Regularizált latent reprezentáció egység a regularizált latent reprezentációt tárgyalja: ott a denoising célhoz hasonló rekonstrukciós veszteség mellé egy latent-térbeli büntetőtag kerül, amely a reprezentáció eloszlását szabályozza. A most bevezetett zajos–tiszta páros szerződés és a szivárgásvizsgálat ott is kötelező kiindulópont.