A Vetítés és legkisebb négyzetek alfejezetben a legkisebb négyzetes illesztést úgy értelmeztük, mint merőleges vetítést az oszloptérre. Most a kérdés a következő: ha az adatpontok egy felhőt alkotnak valamilyen térben, mely irányok mentén terülnek el a legjobban? Ez azért fontos, mert a későbbi dimenziócsökkentés, illetve a főkomponens-elemzés előképét adja: egy sokdimenziós táblában gyakran néhány irány hordozza az eltérések nagy részét, a többi pedig szinte zaj.
A tanulási ellenőrzéshez nem a teljes Northstar rendeléseit, hanem egy ismert szerkezetű, mesterséges pontfelhőt használunk. Így a számított főirányt a generálásból ismert irányhoz tudjuk hasonlítani. A bemenet egy numerikus mátrix, ; a megfigyelési egység egy szimulált rendelés. A generálás:
Előzetes becslés: mivel
szórása sokkal nagyobb, a főiránynak az elforgatás után a
-os
irányban kell állnia, a második irány pedig arra merőleges. A
szinguláris értékek négyzeteinek és a mintaszámnak az aránya közelítőleg
a két generáló varianciát adja. A fejezet végére teljes
NumPy-kóddal ellenőrizzük ezt.
Legyen méretű: sorai megfigyelések, oszlopai numerikus jellemzők. A főirányoknak azt az irányt keressük, amely mentén a megfigyelések szóródnak, nem feltétlenül azt, amely mentén az értékek nagyok. Ezért először oszloponként kivonjuk az átlagot:
Itt az csupa egy vektor, pedig a oszlopátlag-vektor. A centrálás után minden oszlop átlaga nulla. A minta-kovarianciamátrix becslése:
aminek mérete . A főátlóban az egyes oszlopok szórásnégyzeteinek becslése, az átlón kívül kovarianciák állnak. A mértékegység vegyes lehet: ha az első oszlop kilométer, a második euró, akkor elemei , és egységekben vannak; ez már jelzi, hogy a nyers kovarianciamátrix irányai nem mértékegység-függetlenek.
Adott egységvektorra, azaz , a pontok vetülete , alakja . Ennek varianciája:
A cél tehát: találni olyan egységvektort, amelyre a másodfokú kifejezés a lehető legnagyobb. Ez vezet a sajátvektorokhoz.
Egy négyzetes, méretű mátrixra a vektor sajátvektor (eigenvector), ha a mátrixszorzás csak a hosszát skálázza:
A skalár a sajátérték (eigenvalue). A sajátvektor tehát olyan irány, amelyen a lineáris transzformáció nem forgat, csak nyújt vagy zsugorít. Például
Ekkor és ; a sajátértékek és , a sajátvektorok a koordinátatengelyek.
A minta-kovarianciamátrix szimmetrikus és pozitív szemidefinit. Itt a spektráltételt bizonyítás nélkül használjuk: egy szimmetrikus mátrix felbontható ortonormált sajátvektorokkal:
ahol oszlopai a sajátvektorok, pedig a sajátértékekből álló átlós mátrix. A legnagyobb sajátértékhez tartozó sajátvektor a kifejezés maximumát adja az egységgömbön; ez a legnagyobb varianciájú irány. A második legnagyobb sajátértékhez tartozó, az elsőre merőleges vektor adja az erre merőleges fő szóródási irányt.
A korábbi Vetítés és legkisebb négyzetek alfejezetből már ismerjük az mátrixot: ott a normálegyenletben jelent meg. A ranghiányos esetben a pszeudoinverz adott egyértelmű megoldást. Most ugyanannak a mátrixnak a főirányait, nem egy konkrét egyenlet megoldását keressük.
Bármely valós mátrix felbontható szinguláris értékek szerint (singular value decomposition, SVD):
A redukált felbontásban, ha :
Ha a centrált mátrix, akkor , . Mivel a példánkban, mérete , mérete , mérete . A és ortogonalitása miatt és . Ebből:
Összevetve a -re adott sajátfelbontással: a jobb szingulárvektorok megegyeznek a kovarianciamátrix sajátvektoraival, és a -edik főirányon a variancia
A szinguláris érték tehát nem szórás: a hossza úgy skálázódik, mint a centrált adat euklideszi normája. Ezt a mintapéldában ellenőrizzük.
Kézi kis kontroll: ha
akkor , tehát a szinguláris értékek és , a jobb szingulárvektorok a koordinátatengelyek, a rang .
A rang (rank) a lineárisan független oszlopok maximális száma. Az SVD-ben a rang a pozitív szinguláris értékek száma. Pontos lineáris függésnél a legkisebb szinguláris érték nulla:
ahol a harmadik oszlop az első kettő lineáris kombinációja, így a
rang legfeljebb
.
A gyakorlatban mérési zaj vagy lebegőpontos kerekítés miatt a nulla
helyett nagyon kicsi
jelenik meg. A np.linalg.matrix_rank ezért egy relatív
tűréssel dönt; a pontos mátrixnál a kis szinguláris érték a tűrés alatt
marad, a zajosnál viszont lehet, hogy a tűrés fölé kerül. A rang tehát
numerikus döntés is, nem csak algebrai fogalom. A Stabil numerika és érzékenység üzenete itt
visszatér: a mátrix skálázása megváltoztathatja, hogy ugyanaz a fizikai
eltérés mekkora szinguláris értékként jelenik meg.
A legnagyobb szinguláris érték megtartásával készíthető közelítés:
Az új koordináták, a főirányokra vetített értékek:
A rekonstrukció , és az elhagyott irányok hibáját a összeg méri. Ha a kis szinguláris értékek elhanyagolhatók, akkor a dimenziós pontfelhő jó közelítéssel dimenzióban ábrázolható. A formális PCA-modellhez ez a kép, de a skálázási és értelmezési döntéseket a PCA: skála, vetítés és megőrzött variancia fejezetre hagyjuk.
Az alábbi kód előállítja a forgatott felhőt, centrál, SVD-t számol, és ábrázolja a főirányokat. A futást helyben kell elvégezni; az itt mutatott értékek elméletileg várt, szemléltető értékek, nem előre rögzített mérési jegyzőkönyv.
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(20261020)
n = 240
# Látens jellemzők: egy nagy és egy kis szórású irány
z1 = rng.normal(loc=0.0, scale=2.0, size=n)
z2 = rng.normal(loc=0.0, scale=0.3, size=n)
# 35 fokos forgatás az (x1, x2) síkban
t_deg = 35.0
t = np.deg2rad(t_deg)
R = np.array([[np.cos(t), -np.sin(t)],
[np.sin(t), np.cos(t)]])
# Minden sor egy megfigyelés: shape (n, 2)
X = (R @ np.vstack([z1, z2])).T
# Centrálás, majd redukált SVD
Xc = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(Xc, full_matrices=False)
print("s === ", s)
print("Vt sorai:", Vt[0], Vt[1])
# Rekonstrukció: U * s oszloponként skáláz, majd Vt-vel szoroz
Xr = (U * s) @ Vt
print("max rekonstrukciós hiba:", np.max(np.abs(Xc - Xr)))
# Variancia a főirányokon
vars_axes = (s ** 2) / (n - 1)
print("varianciák a főirányokon:", vars_axes)
print("teljes variancia:", vars_axes.sum(),
Xc.var(axis=0, ddof=1).sum())
# Az első főirány szöge, előjeltől eltekintve
angle = np.rad2deg(np.arctan2(Vt[0, 1], Vt[0, 0])) % 180
print("első főirány szöge:", angle)
# Ábra
fig, ax = plt.subplots(figsize=(6.4, 6.4))
ax.scatter(Xc[:, 0], Xc[:, 1], s=18, alpha=0.5,
label="centrált megfigyelés")
for j, color in zip([0, 1], ["tab:red", "tab:blue"]):
sd = s[j] / np.sqrt(n - 1)
v = Vt[j] * 2.0 * sd
ax.arrow(0, 0, v[0], v[1], width=0.08, head_width=0.30,
length_includes_head=True, color=color,
label=f"{j + 1}. főirány, sd={sd:.3f}")
ax.axhline(0, color="gray", linewidth=0.8)
ax.axvline(0, color="gray", linewidth=0.8)
ax.set_xlabel("x1: centrált változó")
ax.set_ylabel("x2: centrált változó")
ax.set_title("SVD-főirányok a forgatott pontfelhőn")
ax.set_aspect("equal", adjustable="datalim")
ax.legend()
plt.savefig("s040_principal_directions.png", dpi=150,
bbox_inches="tight")A rang kérdését külön kis példán mutatjuk be. Ha a harmadik oszlop pontosan lineáris kombináció, a rang ; ha nagyon kis zajt adunk hozzá, a numerikus rang a tűrés függvényében lehet vagy :
x3 = -1.5 * X[:, 0] + 0.5 * X[:, 1] # pontos lineáris függés
X3 = np.column_stack([X, x3])
print("pontos rang:", np.linalg.matrix_rank(X3))
x3n = x3 + rng.normal(scale=1e-8, size=n)
X3n = np.column_stack([X, x3n])
print("zajos alap rang:", np.linalg.matrix_rank(X3n))
print("nagy tűrés melletti rang:",
np.linalg.matrix_rank(X3n, tol=1e-5))Az SVD matematikai azonossága miatt a rekonstrukciós hiba
értékének
alatt kell maradnia float64 esetén; ez az egyik stabil
ellenőrző invariáns. A vars_axes elemek elméletileg
-hez
és
-hez
esnek közel, mert a forgatás nem változtatja meg a teljes varianciát,
csak elforgatja a koordinátarendszert. Az angle várt értéke
fok, mert a nagyobb szóródású
tengelye a forgatás után ebben az irányban áll; a minta
véletlenszerűsége miatt néhány tizedfok eltérés várható. A második
főirány erre merőleges, tehát közelítőleg
fok.
A harmadik oszlopnál a pontos lineáris kombináció esetén a legkisebb
szinguláris érték gyakorlatilag nulla, és a numerikus rang
.
A
szórású zaj hatására a
körülbelül
nagyságrendű lesz, ami az alapértelmezett tűrés felett lehet, így a rang
hivatalosan
;
nagyobb tűréssel viszont ismét
-nek
tekintjük. A döntést tehát nem érdemes automatikusan a nyers
matrix_rank kimenetére bízni, hanem a szinguláris értékek
nagyságrendjét kell értelmezni. Ez a későbbi PCA-nál különösen
fontos.
Tekintse a következő, már centrált mátrixot:
Számítsa ki kézzel értékét. Melyek a sajátértékei? Melyik a főirány? Adja meg a szinguláris értékeket is.
Generáljon
megfigyelést
-os
forgatással,
és
összetevőkkel, np.random.default_rng(42) seeddel.
Centráljon, számítson SVD-t, majd ellenőrizze három feltételt: a
rekonstrukciós hiba
;
az első főirány szöge előjeltől eltekintve
-hoz
képest
fokon belül van; a
értékek
-hez
és
-höz
vannak közel.
Egy jegyzetfüzetben a szerző így számol főirányt:
C = X.T @ X
vals, vecs = np.linalg.eigh(C)
print(vecs[:, -1])A bemeneti mátrix egyik oszlopa , a másik . Az eredmény szerint a főirány szinte pontosan az irány, és a legnagyobb sajátérték óriási. Mi a fogalmi hiba, és mi a helyes számítás?
A 35 fokos alapfeladat mátrixát úgy skálázzák, hogy az első oszlopot -rel szorozzák, a másodikat -gyel. Számítsa ki ismét az SVD-t a centrált skálázott mátrixon. Hogyan változik a főirány? Magyarázza meg, miért nem invariáns az eredmény a mértékegységre.
A mátrix oszlopátlagai nullák, mert és .
A sajátértékek és , a sajátvektorok és . A főirány , mert . Az SVD-ből és , mert . A rang .
import numpy as np
rng = np.random.default_rng(42)
n = 150
z1 = rng.normal(0.0, 3.0, n)
z2 = rng.normal(0.0, 0.5, n)
t = np.deg2rad(60.0)
R = np.array([[np.cos(t), -np.sin(t)],
[np.sin(t), np.cos(t)]])
X = (R @ np.vstack([z1, z2])).T
Xc = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(Xc, full_matrices=False)
err = np.max(np.abs((U * s) @ Vt - Xc))
angle = np.rad2deg(np.arctan2(Vt[0, 1], Vt[0, 0])) % 180
vars_axes = s ** 2 / (n - 1)
print(err, angle, vars_axes)
# Ellenőrzés
assert err < 1e-10
assert abs(angle - 60.0) < 5.0
assert abs(vars_axes[0] - 9.0) < 1.0
assert abs(vars_axes[1] - 0.25) < 0.1A tűrések szándékosan bőek, mert kis mintán a becsült variancia jelentősen eltérhet a generáló paramétertől. A rekonstrukciós invariáns viszont szoros, mert az SVD matematikai azonosság, nem statisztikai becslés.
A hiba az, hogy a kód mátrixon dolgozik a centrálás nélkül. Az oszlop átlaga , ezért az mátrixban a nagyságrendű elem dominál. A számított „főirány” nem a szóródás, hanem az origótól mért nagy eltolás iránya. Helyesen:
Xc = X - X.mean(axis=0)
C = Xc.T @ Xc / (len(Xc) - 1)
vals, vecs = np.linalg.eigh(C)
print(vecs[:, -1]) # a legnagyobb szórás irányaA centrálás után az első oszlop varianciája -hez, a másodiké szintén -hez közeli, így a főirány már nem lesz triviálisan .
X_scaled = X.copy()
X_scaled[:, 0] *= 1000.0
X_scaled[:, 1] *= 0.01
Xc_s = X_scaled - X_scaled.mean(axis=0)
_, s_s, Vt_s = np.linalg.svd(Xc_s, full_matrices=False)
angle_s = np.rad2deg(np.arctan2(Vt_s[0, 1], Vt_s[0, 0])) % 180
vars_s = s_s ** 2 / (len(X_scaled) - 1)
print(angle_s, vars_s)Az első oszlop -szeresre növelése annak szórásnégyzetét -szorosára növeli; a második oszlop -szeresre csökkentése a másik varianciát -szeresére. Az új főirány ezért szinte pontosan az tengely, vagyis a szög fokhoz közeli. A tanulság: a nyers SVD-főirány nem mértékegység-független; a PCA előtt a PCA: skála, vetítés és megőrzött variancia fejezetben a standardizálási döntést expliciten meg kell hozni.
A legnagyobb varianciájú irány nem szükségképpen a cél szempontjából leghasznosabb irány. Generáljunk megfigyelést így:
Az első főirány szinte kétségtelenül lesz, mert a varianciája -szoros. Ha a feladat a célváltozó előrejelzése, akkor rossz választás, mert a generálás szerint független -tól. A korrelációban ezt az ellenpéldát mutatja: , míg . A főirány tehát a reprezentáció tömörségét méri, nem a célváltozóval való kapcsolatot. Ezért a dimenziócsökkentést soha nem helyettesítheti automatikusan a felügyelt feature-szelekció.
Egy másik gyakori félreértés: a „megmagyarázott variancia” arányt összekeverik a modellpontossággal. Nem minden eltérés számít a döntés szempontjából; a Dátumok, időzónák és kategóriák alfejezetben látott típuscédulák példája is mutatja, hogy az adatválasztás szemantikája elsődleges.
A válaszok a fenti szakaszokban találhatók; ha bármelyik nem megy, a megoldásokhoz érdemes visszalapozni.
PCA előtt az adatokat általában centrálni kell, és eltérő skálák esetén indokolt lehet a standardizálás. A megőrzött varianciaarány nem azonos a prediktív információ megőrzésével, a főkomponensek pedig önmagukban nem kapnak oksági értelmezést.
A szinguláris felbontás a centrált adatmátrixot ortogonális komponensekre bontja. A jobb szingulárvektorok a kovarianciamátrix sajátvektorai, tehát a pontfelhő főirányait adják; a értékek mutatják a vetített varianciát az egyes irányokon. A rang a pozitív szinguláris értékek száma, de numerikusan tűrésfüggő döntés. A legnagyobb varianciájú irány nem feltétlenül a legjobb prediktor, ezért a dimenziócsökkentés és a felügyelt feature-szelekció külön döntés. A későbbi PCA-fejezetben ehhez a skálázás és az értelmezési szerződés csatlakozik.