24.1 PCA: skála, vetítés és megőrzött variancia

24.1.1 Miért kell a PCA-t skálázással együtt tárgyalni?

A Northstar prediktív projektben a Modellcsalád választása tudományos jegyzőkönyvvel modellválasztási jegyzőkönyv logisztikus regressziót is vizsgált a késő szállítás előrejelzésére. A lineáris modell viselkedése akkor értelmezhető jól, ha a numerikus bemenetek összehasonlítható skálán vannak. Mielőtt azonban a skálázást vakon elvégeznénk, szeretnénk látni, hogy a rendelésenkénti numerikus jellemzők – distance_km, basket_minor, quantity_sum – hány tényleges szóródási irányt hordoznak, és melyik változó uralja az egyes irányokat. Ez az alfejezet a Sajátérték, SVD és dimenziócsökkentés előképe szinguláris felbontás előképét alakítja konkrét tanítótranszformációvá.

Bemenet: egy X∈ℝn×pX \in \mathbb{R}^{n \times p} numerikus mátrix; megfigyelési egység itt a rendelés, nn a sorok száma, pp a jellemzők száma. A labor tiszta orders.csv fájljában n=2400n=2400 sor érhető el, a példában a három közvetlenül rendelkezésre álló numerikus oszlopot használjuk. Kimenet: k≤pk \le p főkomponens (principal component, PC) iránya, a pontok komponens-koordinátái (score-ok) és a komponensenkénti megmagyarázott varianciahányad. Ellenőrzés: a komponens-score oszlopok páronként korrelálatlanok, a varianciahányadok összege pp komponens esetén 11, és a csökkentett térből visszaállított adat hibája a kihagyott varianciával egyezik.

Előzetes becslés. A basket_minor EUR-cent egységben mért kosárérték varianciája több nagyságrenddel nagyobb, mint a kilométerben mért távolságé vagy a darabszámé. Ezért nyers skálán az első főkomponenst várhatóan a basket_minor egyedül uralja. Standardizálás után minden változó egységnyi varianciával indul, így a komponensek a változók közötti korrelációs szerkezetet mutatják, nem a mértékegységből fakadó szóródáskülönbséget.

24.1.2 Fogalmi alap: centering, scaling, SVD

A centering (centrálás) azt jelenti, hogy minden oszlopból kivonjuk az oszlopátlagot. A jj-edik oszlop átlaga

x‾j=1n∑i=1nxij, \bar{x}_j = \frac{1}{n}\sum_{i=1}^{n} x_{ij},

a centrált mátrix eleme pedig x̃ij=xij−x‾j\tilde{x}_{ij} = x_{ij} - \bar{x}_j. A centrált adat minden oszlopának átlaga 00.

A scaling (skálázás) legegyszerűbb változata a standardizálás vagy z-score transzformáció. A jj-edik oszlop korrigált mintaszórása

sj=1n−1∑i=1n(xij−x‾j)2, s_j = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}\left(x_{ij} - \bar{x}_j\right)^2},

a standardizált elem pedig zij=(xij−x‾j)/sjz_{ij} = (x_{ij} - \bar{x}_j)/s_j. Ekkor minden oszlop átlaga 00, szórása 11; a mértékegység eltűnik, a változók összehasonlíthatóvá válnak. A StandardScaler a scikit-learnben pontosan ezt a két lépést végzi.

A Sajátérték, SVD és dimenziócsökkentés előképe alfejezetben megismert SVD szerint a centrált mátrix felbontható

X̃=UΣV⊤, \tilde{X} = U \Sigma V^\top,

ahol U∈ℝn×rU \in \mathbb{R}^{n \times r} bal szingulárvektorok mátrixa, Σ∈ℝr×r\Sigma \in \mathbb{R}^{r \times r} diagonális, a főátlón a σ1≥σ2≥…≥σr>0\sigma_1 \ge \sigma_2 \ge \dots \ge \sigma_r > 0 szinguláris értékekkel, V∈ℝp×rV \in \mathbb{R}^{p \times r} pedig a jobb szingulárvektorok mátrixa; rr az adatmátrix rangja. A VV oszlopai a pontfelhő fő irányai, vagyis a főkomponens-irányok. A komponens-koordináták mátrixa

T=X̃V=UΣ, T = \tilde{X} V = U \Sigma,

amelynek oszlopai a score-ok. A jj-edik irányra vetített adat varianciája

λj=σj2n−1. \lambda_j = \frac{\sigma_j^2}{n-1}.

Ez a mennyiség az úgynevezett explained variance (megmagyarázott variancia): azt mutatja, mennyi szóródást visz a jj-edik komponens. A teljes variancia a centrált adat oszlopvarianciáinak összege, és egyenlő ∑jλj\sum_j \lambda_j-vel, ha minden pozitív szinguláris értéket megtartunk. A explained variance ratio (megmagyarázott varianciahányad) a

λj∑k=1pλk \frac{\lambda_j}{\sum_{k=1}^{p}\lambda_k}

érték; azt mondja meg, a teljes szóródás hányad részét fedi le az adott komponens. A scikit-learn PCA objektuma a components_ attribútumban a V⊤V^\top mátrixot tárolja, tehát annak jj-edik sora a jj-edik PC irány terhelése (loading); az explained_variance_ a λj\lambda_j-ket, az explained_variance_ratio_ a fenti hányadokat adja vissza.

Kézi kontroll. Legyen a nyers adat három pontja

X=[113223]. X = \begin{bmatrix} 1 & 1 \\ 3 & 2 \\ 2 & 3 \end{bmatrix}.

Az oszlopátlag (2,2)(2, 2), ezért

X̃=[−1−11001]. \tilde{X} = \begin{bmatrix} -1 & -1 \\ 1 & 0 \\ 0 & 1 \end{bmatrix}.

A mintakovariancia-mátrix

S=1n−1X̃⊤X̃=12[2112]=[10,50,51]. S = \frac{1}{n-1}\tilde{X}^\top\tilde{X} = \frac{1}{2} \begin{bmatrix} 2 & 1 \\ 1 & 2 \end{bmatrix} = \begin{bmatrix} 1 & 0{,}5 \\ 0{,}5 & 1 \end{bmatrix}.

A sajátértékek λ1=1,5\lambda_1 = 1{,}5 és λ2=0,5\lambda_2 = 0{,}5; a hozzájuk tartozó normalizált sajátvektorok

v1=12[11]≈[0,7070,707],v2=12[1−1]≈[0,707−0,707]. v_1 = \frac{1}{\sqrt{2}} \begin{bmatrix}1 \\ 1\end{bmatrix} \approx \begin{bmatrix}0{,}707 \\ 0{,}707\end{bmatrix}, \qquad v_2 = \frac{1}{\sqrt{2}} \begin{bmatrix}1 \\ -1\end{bmatrix} \approx \begin{bmatrix}0{,}707 \\ -0{,}707\end{bmatrix}.

A varianciahányadok: 1,5/(1,5+0,5)=0,751{,}5/(1{,}5+0{,}5)=0{,}75 és 0,5/2=0,250{,}5/2=0{,}25. A score-ok: az első pont első score-ja (−1)(0,707)+(−1)(0,707)=−1,414(-1)(0{,}707)+(-1)(0{,}707)=-1{,}414, a második ponté 0,7070{,}707, a harmadiké 0,7070{,}707; a második komponensen rendre 00, 0,7070{,}707 és −0,707-0{,}707. Ez a kis példa a későbbi kódos ellenőrzés aranyalapja.

A levezetésből látszik a kulcsgondolat: a PCA a centrált adat kovarianciaszerkezetének sajátirányait keresi. Ha az elemek mértékegysége nagyon eltérő, a kovarianciamátrixot a nagy abszolút értékű változó uralja. A standardizálás után viszont a kovarianciamátrix éppen a korrelációs mátrixszá válik, mert sj=1s_j = 1 minden jj-re.

24.1.3 Teljes minimális munkapélda

Az alábbi kód két PCA-t futtat ugyanazon a mesterségesen generált, Northstar-stílusú adaton: először nyers skálán, majd standardizálás után. A generálás determinisztikus, így az összehasonlítás megismételhető. A kimenet itt szemléltető, mert a szöveget író modell nem futtatta a kódot; a várt tartományokat a generáló képletből vezetjük le.

import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

# Determinisztikus, Northstar-stílusú rendelésadatok.
# Mértékegységek: distance_km [km], basket_minor [EUR-cent],
# quantity_sum [darab]; a kosárérték erősen összefügg a távolsággal.
rng = np.random.default_rng(42)
n = 300
distance_km = rng.normal(40, 15, n)
basket_minor = 2500 + 40 * distance_km + rng.normal(0, 100, n)
quantity_sum = rng.poisson(4, n) + 1
X = pd.DataFrame({
    "distance_km": distance_km,
    "basket_minor": basket_minor,
    "quantity_sum": quantity_sum.astype(float),
})

# Nyers PCA. A sklearn itt is centrál, de nem standardizál.
pca_raw = PCA(n_components=3)
T_raw = pca_raw.fit_transform(X)
print("Nyers loadings (soronként egy PC irány):")
print(pca_raw.components_)
print("Nyers arány:", pca_raw.explained_variance_ratio_)

# Standardizált PCA.
scaler = StandardScaler()
Z = scaler.fit_transform(X)
pca_std = PCA(n_components=3)
T_std = pca_std.fit_transform(Z)
print("Standardizált loadings:")
print(pca_std.components_)
print("Standardizált arány:", pca_std.explained_variance_ratio_)

# Invariáns ellenőrzés: a score-ok off-diagonális kovarianciája nulla.
score_cov = np.cov(T_std.T)
print("Score kovariancia off-diag maximuma:",
      np.max(np.abs(score_cov - np.diag(np.diag(score_cov)))))

# Rekonstrukció egy komponenssel a standardizált térben.
pca_1 = PCA(n_components=1)
Z_score_1 = pca_1.fit_transform(Z)
Z_reconstructed = pca_1.inverse_transform(Z_score_1)
reconstruction_var = np.var(Z - Z_reconstructed, axis=0).sum()
print("Rekonstrukciós hiba teljes varianciája 1 komponens után:",
      reconstruction_var)
print("Kihagyott variancia a modell szerint:",
      pca_1.explained_variance_.sum() if False else
      pca_std.explained_variance_[1:].sum())

A kódban a PCA automatikusan elvégzi a centrálást az X-en, tehát kézzel nem kell az oszlopátlagot levonni. A várható eredmény: a nyers skálán a basket_minor szórása 608608 cent körüli, a distance_km szórása 1515 km, a quantity_sum szórása 22 darab körüli, így a nyers első komponens terhelése gyakorlatilag a basket_minor oszlopra koncentrálódik, és az első varianciahányad 0,990{,}99 fölött van. Standardizálás után a korrelációs mátrix első két változója közötti korreláció elméletileg 0,980{,}98 körüli, ezért az első PC terhelése a distance_km és a basket_minor irányában egyaránt 0,70{,}7 körüli, az első varianciahányad ≈(1+ρ)/3≈0,66\approx (1+\rho)/3 \approx 0{,}66, a második ≈0,005\approx 0{,}005, a harmadik ≈0,33\approx 0{,}33. Az utolsó ellenőrzés arra mutat rá, hogy a standardizált térbeli rekonstrukciós hiba varianciája éppen a kihagyott komponensek magyarázott varianciája: 33 változó esetén a teljes variancia 33, ebből 11 komponenssel λ1\lambda_1-et tartjuk meg, így a hiba λ2+λ3\lambda_2+\lambda_3.

A laborban ugyanez a gondolatmenet a tiszta orders.csv három numerikus oszlopán futtatható: distance_km, basket_minor, quantity_sum. A segment, region és acquisition_channel kategorikus oszlopok nem kerülnek közvetlenül a PCA-ba; a priority_flag bináris, ezért csak külön megfontolással szerepelhet. Ha az account_age_days jellemzőt is be akarjuk vonni, azt előbb a customers.csv signup_date oszlopából kell időhelyesen kiszámítani, ahogyan azt a CTE, window és előzményből számolt feature előzményaggregátumai mutatják. A laborfuttatás várt tendenciája ugyanaz: a nyers PCA-t a kosárérték uralja, a standardizált PCA a korrelációs szerkezetet emeli ki.

A kézi példa megmagyarázott varianciaaránya - A hárompontos kézi példa levezetett értékei, nem mérés.

24.1.4 Train-only fit és a predikciós szerződés

A PCA felügyelet nélküli, de a belőle épített transzformáció betanított paramétereket tartalmaz: az oszlopátlagokat, a szórásokat és a komponensirányokat. Ha ezeket a teljes adaton, a tesztet is beleértve tanuljuk meg, akkor a tesztből származó eloszlásinformáció szivárog a pipeline-ba. Ez ugyanaz a hibaosztály, amelyet a Predikciós szerződés és hibateszt predikciós szerződés és a Küszöb, kalibráció és ritka esemény foldon belüli resampling kizár. A train-only fit (csak tanítón illesztés) lépései a Northstar időbeli splitjénél a következők:

cutoff = pd.Timestamp("2025-05-01T00:00:00+00:00")
# Feltéve, hogy a s084 label-availability szűrés már megtörtént:
train_mask = (df["order_ts"] < cutoff) & (df["label_available_at"] < cutoff)
test_mask = df["order_ts"] >= cutoff
X_train = df.loc[train_mask, numeric_cols]
X_test = df.loc[test_mask, numeric_cols]

scaler = StandardScaler().fit(X_train)        # csak tanítón tanul
pca = PCA(n_components=2).fit(scaler.transform(X_train))

X_train_score = pca.transform(scaler.transform(X_train))
X_test_score = pca.transform(scaler.transform(X_test))   # tesztre csak transform

A fit hívás a tanító statisztikákat rögzíti; a tesztre már csak a transform vonatkozik. Így a teszt score-jai is a tanító átlagával és szórásával számítódnak, pontosan ahogyan a betanított modell élesben is viselkedne. Ugyanezt a szerződést érdemes egy Pipeline-ba zárni, hogy a keresztvalidáció foldjaiban se férhessen a teszt a tanításhoz.

24.1.5 Feladatok

  1. Értelmezés, kézi számítás. A szövegbeli hárompontos példán ellenőrizd, hogy a centrált mátrix oszlopainak empirikus varianciája 11 és 11, a két főkomponens score-oszlopainak kovarianciája 00, és a score-ok varianciája rendre λ1=1,5\lambda_1 = 1{,}5 és λ2=0,5\lambda_2 = 0{,}5. Számold ki kézzel a score-oszlopok értékeit a megadott v1v_1 és v2v_2 irányokkal.

  2. Megvalósítás. Futtasd le a fenti minimális példát. Ellenőrizd, hogy a nyers első komponensben a basket_minor terhelés abszolút értéke legalább 0,990{,}99, a standardizált első komponensben pedig a distance_km és a basket_minor terhelése egyaránt 0,650{,}65 és 0,750{,}75 közé esik. Ellenőrizd azt is, hogy az explained_variance_ratio_.sum() értéke 11 legyen 10−1210^{-12} tűréssel.

  3. Hibadiagnózis. Egy kolléga az időbeli split előtt a teljes orders.csv-re illeszti a StandardScaler-t, mondván: „a PCA felügyelet nélküli, itt nincs szivárgás.” Miért téves ez az érvelés akkor, ha a skálázott jellemzőket később logisztikus regresszióba adjuk? Milyen irányba torzíthat a jövőbeli eloszlásból származó átlag és szórás? Add meg a javítást Pipeline-nal.

  4. Megváltozott feltétel. Hagyd el a basket_minor oszlopot a generált adatból, és illeszd újra a PCA-t a maradék két oszlopra standardizálás után. Milyen komponensirányt és varianciahányadot vársz? Röviden indokold. Mi változik, ha a quantity_sum helyett a bináris priority_flag oszlopot teszed be változatlan problémakezeléssel?

24.1.6 Megoldások és indoklás

1. feladat megoldása. A centrált mátrix oszlopai (−1,1,0)(-1, 1, 0) és (−1,0,1)(-1, 0, 1); mindkettő átlaga 00, négyzetösszege 22, így a korrigált variancia 2/(3−1)=12/(3-1)=1. A score-ok:

T=X̃VT = \tilde{X}V, tehát az első komponensen a fent kiszámított −1,414-1{,}414, 0,7070{,}707, 0,7070{,}707; a másodikon 00, 0,7070{,}707, −0,707-0{,}707. Az első score-oszlop átlaga 00, varianciája [(−1,414)2+0,7072+0,7072]/2=(2+0,5+0,5)/2=1,5\left[(-1{,}414)^2 + 0{,}707^2 + 0{,}707^2\right]/2 = (2 + 0{,}5 + 0{,}5)/2 = 1{,}5; a másodiké (0+0,5+0,5)/2=0,5(0 + 0{,}5 + 0{,}5)/2 = 0{,}5. A két oszlop kovarianciája [(−1,414)(0)+(0,707)(0,707)+(0,707)(−0,707)]/2=(0+0,5−0,5)/2=0\left[(-1{,}414)(0) + (0{,}707)(0{,}707) + (0{,}707)(-0{,}707)\right]/2 = (0 + 0{,}5 - 0{,}5)/2 = 0. Az oszlopok tehát korrelálatlanok, a varianciák a sajátértékek.

2. feladat megoldása. A nyers PCA-nál a generálásból Var(basket)=402⋅152+1002=370000\text{Var(basket)} = 40^2\cdot 15^2 + 100^2 = 370\,000 cent², Var(distance)=225\text{Var(distance)} = 225 km², Var(quantity)≈4\text{Var(quantity)} \approx 4 darab². A kovarianciamátrix diagonális dominanciája miatt az első sajátvektor a basket_minor tengelyhez simul, így a terhelés abszolút értéke 0,9990{,}999 fölött várható. Standardizálás után a korreláció 40⋅15/225⋅370000≈600/608≈0,98640\cdot 15 / \sqrt{225 \cdot 370\,000} \approx 600/608 \approx 0{,}986; a korrelációs mátrix első 2×22\times2-es blokkjának domináns sajátvektora közel (0,707,0,707)(0{,}707, 0{,}707), ezért mindkét terhelés 0,650{,}65–0,750{,}75 közé esik. Az arányok összege a definícióból adódóan 11; numerikusan 10−1210^{-12} tűrésen belül kell maradnia. A tényleges futtatás során a mintavételezés miatt a terhelések ±0,05\pm 0{,}05-ön belüli eltérése elfogadható.

3. feladat megoldása. A standardizálás paramétereit a tanuló pipeline részének kell tekinteni, mert az átlag és a szórás a modell bemenetét alakítja. Ha a teszt jövőbeli eloszlásából is tanulunk, a teszt adatai már a tanulási folyamatban részt vesznek; ez ugyanaz a leakeage, mint a Predikciós szerződés és hibateszt alfejezetben tárgyalt szerződésszegés. A torzítás iránya jellemzően optimista kiértékelés: a modell olyan jövőbeli eloszlási információkhoz fér hozzá, amelyek éles használatkor nem állnának rendelkezésre. A javítás:

from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ("scaler", StandardScaler()),
    ("pca", PCA(n_components=2)),
])
pipeline.fit(X_train)          # mindkét lépés paramétere csak tanítón tanul
X_test_score = pipeline.transform(X_test)

4. feladat megoldása. A basket_minor elhagyása után a standardizált térben a két megmaradt változó korrelációja gyenge (a generálásban a quantity_sum független a distance_km-től), ezért a korrelációs mátrix körülbelül egységmátrix. A két sajátérték közel 11 és 11, a komponensirányok bizonytalanok, a varianciahányadok közel 0,50{,}5–0,50{,}5. A PCA itt nem ad értelmes dimenziócsökkentést, mert nincs domináns korrelációs tengely. A bináris priority_flag beillesztése hasonló óvatosságot igényel: a z-score alapú PCA a 0/10/1 változót is standardizálja, de az eredményül kapott komponens nehezen értelmezhető folytonos látens tengelyként, és a bináris változó marginális varianciája a ritka eseményeknél torzíthatja a súlyozást. Jobb ilyenkor előbb megfontolni, hogy a bináris változót külön kezeljük, vagy kevert típusú reprezentációt válasszunk.

24.1.7 Gyakori tévedés ellenpéldával

A leggyakoribb félreértés úgy szól: „a PCA kiválasztja a legfontosabb jellemzőket a predikcióhoz.” Ez téves. A PCA célváltozó nélkül, kizárólag a bemeneti variancia alapján rangsorol; egy alacsony varianciájú változó lehet determinisztikus, erős prediktor. Ellenpélda: legyen x1∼𝒩(0,10)x_1 \sim \mathcal{N}(0, 10) nagy szórású zaj, x2x_2 pedig olyan bináris jel, amely 2%2\% valószínűséggel 11, egyébként 00; legyen a cél y=x2y = x_2. A PCA első komponense x1x_1-et tartja meg, pedig x2x_2 tökéletesen prediktálja yy-t. A variancia tehát nem helyettesíti a felügyelt feature-szelekciót. Hasonlóképpen a komponensek nem feltétlenül oksági tényezők: a standardizált első PC például a távolság és a kosárérték lineáris kombinációja, nem egy beavatkozható fizikai mennyiség. Az oksági értelmezéshez az Oksági cél és megfigyelési torzítás kísérleti kerete kell.

A PCA komponenseinek előjele tetszőleges, ezért futások között azonos főkomponens ellentétes előjellel is megjelenhet. A komponensek korrelálatlansága a tanítási adatokra és a szokásos centrált PCA-definícióra vonatkozik.

24.1.8 Rövid összefoglaló

A PCA a centrált adatmátrix SVD-jéből nyeri a fő irányokat; a szinguláris értékekből számolt σj2/(n−1)\sigma_j^2/(n-1) mennyiség az adott komponens megmagyarázott varianciája, hányada pedig a teljes szóródásból való részesedés. Különböző mértékegységű oszlopoknál a nyers PCA-t a legnagyobb varianciájú változó uralja, ezért standardizálni kell – de a skálázó és a PCA paramétereit kizárólag a tanítórészen szabad illeszteni. Az inverz transzformáció rekonstrukciós hibája a kihagyott komponensek varianciája. A PCA varianciaalapú, nem célfüggő feature-szelekció. Következő alkalmazás: a Klaszterezés és stabilitás klaszterezés, ahol ugyanezt a komponens-teret használjuk a távolságalapú stabilitás vizsgálatához.