Matris Ayrıştırmaları: Eigendecomposition, SVD, PCA ve LoRA'nın Sırrı
Bir matrisi 'DNA'sına' ayırma sanatı. Eigendecomposition (özdeğer) ve SVD (tekil değer) ayrıştırmaları, PCA'nın SVD ile sıfırdan inşası, LoRA'nın matematiksel temeli — neden düşük-rank güncelleme yeter? Embedding compression pratiği.
Şükrü Yusuf KAYA
42 dakikalık okuma
Orta🧬 Bu derste bir matrisi 'genom'una ayırmayı öğreneceğiz
Lineer cebirin en güzel teorem'lerinden biri: her gerçek matris SVD ile üç matrise ayrılabilir. Bu, sadece teorik bir gerçek değil — LoRA, PCA, embedding compression, image compression, recommender sistemler hepsi bu teoremin pratik karşılığı. 42 dakika sonra LoRA paper'ını okurken 'Aha, bu sadece truncated SVD'ymiş' diyeceksin.
Ders Haritası#
- Eigendecomposition: bir kare matrisin "ekseni" ne demek?
- Diagonalization: hesap kolaylığı
- SVD: her matris için geçerli teorem
- SVD ⇔ Eigendecomp ilişkisi
- PCA'yı SVD ile sıfırdan inşa
- Truncated SVD ve düşük-rank yaklaşım
- LoRA'nın matematiksel temeli
- Embedding compression pratiği
- Sayısal stabilite: condition number, pseudoinverse
1. Eigendecomposition — Bir Matrisin "Özel Eksenleri"#
Bir kare matris (n×n), aslında bir lineer dönüşüm'dür: girdi vektörünü alıp başka bir vektöre eşler. Çoğu vektörü bu dönüşüm karıştırır — yön değişir, uzunluk değişir.
AAma bazı vektörler özel: dönüşümden sonra yönü değişmez, sadece uzunluğu değişir.
Bu özel vektörlere eigenvector (özvektör), uzunluk değişim katsayısına eigenvalue (özdeğer) denir.
Burada eigenvector, eigenvalue.
vλSezgi: dönen Dünya'nın ekseni#
Dünya kendi etrafında döner. Çoğu noktayı hareket ettirir. Ama kuzey ve güney kutuplarındaki noktalar sabit kalır (sadece dönme ekseni üzerinde). Bu eksen = Dünya'nın "eigenvector"'ü.
Matematiksel olarak: 3D rotasyon matrisinin eigenvector'ü dönme eksenidir. Eigenvalue = 1 (çünkü uzunluk korunur).
Bir matrisin tüm eigenvalue'larını bul#
Karakteristik polinom:
Bu n. dereceden bir polinom; çözümleri eigenvalue'lar. Her eigenvalue için çözerek eigenvector bulunur.
(A - λI) v = 0n × npython
import torch # Basit 2x2 örnek — simetrikA = torch.tensor([[4., 2.], [2., 3.]]) eigenvalues, eigenvectors = torch.linalg.eig(A)print("Eigenvalues:", eigenvalues.real) # tensor([5.5616, 1.4384])print("Eigenvectors:")print(eigenvectors.real)# tensor([[ 0.7882, -0.6154],# [ 0.6154, 0.7882]]) # Doğrulama: A @ v ≈ λ * vv1 = eigenvectors[:, 0].reallam1 = eigenvalues[0].realprint("A v1 =", A @ v1) # tensor([4.3835, 3.4221])print("λ v1 =", lam1 * v1) # tensor([4.3835, 3.4221]) ✓ # Eigenvalue'lar bize geometrik bilgi verir:# - Pozitif → eksende uzatma# - Negatif → ters çevirme# - Tüm |λ| < 1 → her uygulamada vektör küçülür (contraction)# - Tüm |λ| = 1 → izometri (uzunluk korunur, örn. rotation)# - Bazı |λ| > 1 → bazı yönlerde uzatmaEigendecomposition — PyTorch ile.
💡 Simetrik matrisler altın gibidir
Bir matris simetrik ise (A = A^T) ve gerçekse: (1) tüm eigenvalue'lar gerçek, (2) eigenvector'lar ortogonal (birbirine dik). LLM'de kovaryans, gram matrisi, attention'da bazı yapılar simetriktir. `torch.linalg.eigh` (h = Hermitian) bu durumda daha hızlı ve sayısal olarak stabil.
2. SVD — Singular Value Decomposition#
Eigendecomposition güzel ama sadece kare matrisler için tanımlı (ve hatta her kare matris için garanti çalışmıyor).
SVD ise her matris için çalışır — kare olmayan, full-rank olmayan, kompleks bile.
burada:
- ∈ ℝ^{m×n}
A - ∈ ℝ^{m×m}, ortogonal (sütunları sol singular vector'lar)
U - ∈ ℝ^{m×n}, diagonal, non-negative (singular value'lar)
Σ - ∈ ℝ^{n×n}, ortogonal (sütunları sağ singular vector'lar)
V
Geometrik yorum: Her lineer dönüşüm üç adıma ayrılabilir:
- : rotasyon (sağda)
V^T - : eksenlerde ölçekleme
Σ - : rotasyon (solda)
U
Yani rotation → scaling → rotation.
Singular value'lar = matrisin "önem dereceleri"#
Σσ_1 ≥ σ_2 ≥ ... ≥ σ_r ≥ 0- en büyük: matrisin en baskın "yönü"
σ_1 - : o yönde "bilgi yok"
σ_i = 0
Matrisin rank'i = sıfır olmayan singular value'ların sayısıdır.
SVD'nin "toplam" formu#
Yukarıdaki ikinci formül kritik: bir matris, r tane rank-1 matrisin ağırlıklı toplamıdır.
İlk terim "en önemli bilgi", ikinci "ikinci önemli", vs.
LoRA'nın temeli: Eğer son k terimi küçükse (σ küçük), onları atıp matrisi yaklaşık olarak tutabiliriz. Buna truncated SVD denir.
python
import torch # (4, 3) bir matristorch.manual_seed(0)A = torch.randn(4, 3)print("A =")print(A) U, S, Vh = torch.linalg.svd(A, full_matrices=False)# Vh = V^T (PyTorch konvansiyonu) print("\nU shape:", U.shape) # (4, 3)print("S (singular values):", S) # (3,)print("V^T shape:", Vh.shape) # (3, 3) # A'yı geri inşa etA_reconstructed = U @ torch.diag(S) @ Vhprint("\nReconstruction error:", (A - A_reconstructed).norm().item()) # ~0 # rank-1 dekompozisyona ayır# A = sigma_1 * u_1 * v_1^T + sigma_2 * u_2 * v_2^T + ...A_rank1 = S[0] * torch.outer(U[:, 0], Vh[0, :])A_rank2 = A_rank1 + S[1] * torch.outer(U[:, 1], Vh[1, :])A_rank3 = A_rank2 + S[2] * torch.outer(U[:, 2], Vh[2, :]) print("\nrank-1 yaklaşım hatası:", (A - A_rank1).norm().item())print("rank-2 yaklaşım hatası:", (A - A_rank2).norm().item())print("rank-3 yaklaşım hatası:", (A - A_rank3).norm().item()) # ~0SVD ile matris ayrıştırma + adım adım rank reconstruction.
3. Eigendecomposition ⇔ SVD İlişkisi#
Bu ikisinin ilişkisi cebirsel olarak güzel:
- (n × n simetrik PSD matris) eigendecomposition'u: eigenvalue'lar
A^T A, eigenvector'larσ_i²(V'nin sütunları).v_i - (m × m simetrik PSD): eigenvalue'lar yine
A A^T, eigenvector'larσ_i²(U'nun sütunları).u_i
Yani: SVD = Gram matrisinin (A^T A) eigendecomposition'unun karekökü.
Bu, SVD'yi sayısal olarak hesaplamanın bir yolu (modern algoritmalar bunu daha akıllı yapıyor ama mantık aynı).
4. PCA (Principal Component Analysis) — SVD ile Sıfırdan#
PCA, verinin en yüksek varyans yönlerini bulup boyut indirme tekniğidir. SVD'nin uygulamasıdır.
Algoritma#
Verilen veri matrisi ∈ ℝ^{n × d} (n örnek, d özellik):
X- Merkezle: her sütundan ortalama çıkar.
X_c = X - mean(X, axis=0) - SVD:
X_c = U Σ V^T - 'nin sütunları = principal component'lar (özellik uzayındaki ana yönler)
V - 'nin köşegeni = her component'in varyansının karekökü
Σ - Boyut indirme: ilk k component'i tut:
X_reduced = X_c @ V[:, :k]
Sezgi#
X'in satırları bir bulut. PCA o bulutun en uzun eksenini bulur (en yüksek varyans), sonra ikinci en uzun (ilkine dik), vs. İlk k eksene projekte et → boyut k'ye iner ama maximum bilgi korunur.
LLM'de PCA?#
- Embedding compression: 1536-d OpenAI embedding'i 768-d'ye sıkıştırmak
- Visualization: high-d embedding'leri 2D'de plot
- Outlier detection: PCA'dan sapan örnekler anormal
- Whitening: input'u decorrelate etmek (eski ML)
python
import torchimport matplotlib.pyplot as plt torch.manual_seed(42) # Sentetik veri: 2D Gaussian, x ekseninde daha genişN = 200data = torch.randn(N, 2) * torch.tensor([3.0, 0.5])# Hafif rotationtheta = 0.5R = torch.tensor([[torch.cos(torch.tensor(theta)), -torch.sin(torch.tensor(theta))], [torch.sin(torch.tensor(theta)), torch.cos(torch.tensor(theta))]])data = data @ R.T # 1. Merkezlemean = data.mean(dim=0)X_c = data - mean # 2. SVDU, S, Vh = torch.linalg.svd(X_c, full_matrices=False) # 3. Principal componentsprint("Variance per component:", (S ** 2 / (N - 1)))print("First PC direction:", Vh[0])print("Second PC direction:", Vh[1]) # 4. 1D'ye indirX_reduced = X_c @ Vh.T[:, 0:1] # (N, 1)print("Reduced shape:", X_reduced.shape) # 5. Geri yansıt (lossy)X_restored = X_reduced @ Vh[0:1, :] + meanprint("Reconstruction error:", (data - X_restored).norm().item()) # Görselleştirme (Colab/Jupyter'da)# plt.scatter(data[:, 0], data[:, 1], alpha=0.5, label="Original")# plt.scatter(X_restored[:, 0], X_restored[:, 1], alpha=0.5, label="1D Projection")# plt.legend()# plt.axis('equal')# plt.show()PCA'yı SVD ile sıfırdan, 200 satır 2D veri üzerinde.
5. Truncated SVD ve Düşük-Rank Yaklaşım#
Eckart-Young teoremi: Bir matrisi en iyi rank-k ile yaklaşıklamak istersen, SVD'yi yap ve ilk k singular value'yu tut.
A_kASıkıştırma oranı#
Tam matris: m × n parametre.
Rank-k SVD: m·k + k + k·n ≈ k(m + n) parametre.
Örnek: A = 4096×4096 (16.7M parametre). k=16: 16·(4096+4096) = 131K parametre. %99 sıkışma.
Hata: — atılan singular value'ların karelerinin toplamı.
||A - A_k||_F² = σ_{k+1}² + σ_{k+2}² + ...python
import torch # Bir "doğal" düşük-rank matris simüle et:# Gerçek rank 5, ama 64x64 boyutundatorch.manual_seed(0)A_low = torch.randn(64, 5) @ torch.randn(5, 64) # rank ≤ 5A_noise = A_low + 0.1 * torch.randn(64, 64) # gürültü ekle # SVDU, S, Vh = torch.linalg.svd(A_noise, full_matrices=False) # Singular value spectrum'unu yazdırprint("İlk 10 singular value:")print(S[:10].tolist())# İlk 5 büyük, sonra hızla düşer — "elbow" rank=5 civarında # Truncated reconstructiondef truncate_svd(U, S, Vh, k): return U[:, :k] @ torch.diag(S[:k]) @ Vh[:k, :] for k in [1, 3, 5, 10, 20]: A_k = truncate_svd(U, S, Vh, k) err = (A_noise - A_k).norm() / A_noise.norm() print(f"k={k:2d}: relative error = {err*100:.2f}%") # Çıktı (yaklaşık):# k= 1: relative error = 80.50%# k= 3: relative error = 40.10%# k= 5: relative error = 12.40% <- elbow# k=10: relative error = 11.95%# k=20: relative error = 11.85%Truncated SVD ile spektrum analizi ve elbow tespiti.
6. LoRA'nın Sırrı: Hipotez ve Matematik#
LoRA (Low-Rank Adaptation, Hu et al. 2021) modern LLM fine-tuning'inin omurgası. Çoğu kişi onu "küçük matrisli LoRA" diye geçiyor; ama altında zarif bir hipotez var.
Hipotez#
"Pre-trained büyük modelin fine-tuning sırasında parametre değişimi (ΔW) düşük intrinsik rank'lıdır."
Yani, bir downstream task için pre-trained modeli adapte ederken, ağırlık değişimi tam-rank değil, küçük bir rank'la temsil edilebilir.
Matematiksel yapı#
Pre-trained weight: . Fine-tuning sırasında 'yi öğrenmek yerine, onu iki düşük-rank matrise ayır:
W ∈ ℝ^{d × d}ΔWr ≪ dInference'ta 'yı önceden hesaplayıp 'ye ekleyebilirsin (zero latency overhead).
BAWParametre tasarrufu#
Llama 8B'nin weight'i 4096×4096 = 16.7M parametre.
LoRA rank=16: A (16×4096) + B (4096×16) = 131K parametre.
Tasarruf: %99.2.
q_projNeden çalışıyor?#
Empirik gözlem (Hu et al. 2021): veya çoğu görevde yeterli. Bu, fine-tuning hareketinin gerçekten düşük-boyutlu manifold'da olduğunu gösteriyor.
r=4r=8SVD bağlantısı#
LoRA ≠ SVD ama akrabası. SVD bir matrisi düşük-rank ile yaklaşır; LoRA güncellemenin düşük-rank olduğunu öğreniyor. SVD'yi başlangıç initialization olarak kullanan PiSSA (2024) varyantı bu bağlantıyı kullanıyor.
python
import torchimport torch.nn as nn class LoRALinear(nn.Module): """Pre-trained Linear üzerine LoRA adapter.""" def __init__(self, base_layer: nn.Linear, rank: int = 16, alpha: float = 32): super().__init__() self.base = base_layer # Base'i dondur for p in self.base.parameters(): p.requires_grad = False d_out, d_in = base_layer.weight.shape # A: küçük → büyük; init: gaussian self.A = nn.Parameter(torch.randn(rank, d_in) * 0.01) # B: büyük → küçük; init: sıfır → başta ΔW = 0 self.B = nn.Parameter(torch.zeros(d_out, rank)) self.rank = rank self.alpha = alpha self.scaling = alpha / rank def forward(self, x): # Base output out = self.base(x) # LoRA update: x @ A^T @ B^T (ya da B @ A operasyonunu transpose'lı yaz) lora_out = (x @ self.A.T) @ self.B.T return out + lora_out * self.scaling # Testbase = nn.Linear(4096, 4096, bias=False)lora = LoRALinear(base, rank=16)print("Base params:", sum(p.numel() for p in base.parameters())) # 16.7Mprint("LoRA trainable:", sum(p.numel() for p in lora.parameters() if p.requires_grad)) # 131K x = torch.randn(2, 8, 4096)y = lora(x)print(y.shape) # (2, 8, 4096) # Başlangıçta y = base(x) çünkü B = 0 → ΔW = 0y_base = base(x)print(torch.allclose(y, y_base)) # True (eğitim başlangıcında)LoRA'yı sıfırdan PyTorch'ta — sadece 30 satır.
💡 LoRA'nın 'aha' momenti
Modül 21'de LoRA'yı production seviyesinde işleyeceğiz. Ama matematiksel iskelet şuradan: SVD bize 'her matris düşük-rank ile yaklaşıklanabilir' diyor. LoRA bunu 'fine-tuning sırasındaki güncelleme' için uyguluyor. Eğer ΔW gerçekten düşük-rank ise, o zaman tam-matris öğrenmek savurganlık. Bu içgörü 100M+ kez fine-tune edildi.
7. Rank, Condition Number, Sayısal Stabilite#
Rank#
Bir matrisin rank'i = lineer bağımsız satır (veya sütun) sayısı = sıfır olmayan singular value sayısı.
- Full-rank: m × n matris için min(m,n) rank
- Rank-deficient: daha az
- Pratikte "yaklaşık rank": çok küçük singular value'lar sayılmaz
Condition number#
En büyük ve en küçük (sıfır olmayan) singular value oranı. Sayısal hesaplamaların stabilitesini ölçer:
- κ = 1: ideal (orthogonal matrix)
- κ < 10: iyi
- κ > 10^6: sayısal olarak tehlikeli (FP32 hassasiyeti yetersiz olabilir)
- κ = ∞: singular matrix
LLM'de niye önemli?#
- Attention matrisi bazen kondisyonsuz olabilir → loss spike
- Fine-tuning'de condition number izlenir
- Mixed precision (FP16) ile çalışırken iyi-kondisyonlu matrisler şart
python
import torch # İyi kondisyonlu matrisA_good = torch.randn(100, 100)print("Good κ:", torch.linalg.cond(A_good).item()) # ~100-1000 (rastgele Gaussian) # Kötü kondisyonlu — son satır birinciye yakınA_bad = torch.randn(100, 100)A_bad[-1] = A_bad[0] + 1e-8 * torch.randn(100) # neredeyse aynıprint("Bad κ:", torch.linalg.cond(A_bad).item()) # 10^7+ — tehlikeli # Hilbert matrisi — klasik kötü kondisyonlu örnekH = torch.tensor([[1 / (i + j + 1) for j in range(5)] for i in range(5)])print("Hilbert(5) κ:", torch.linalg.cond(H).item()) # 4.8e5 — zaten 5x5'te kötüCondition number — iyi vs kötü matris.
8. Pseudoinverse — Square Olmayan Matrislerin "Tersi"#
Square olmayan veya singular bir matrisin tersi yok. Ama pseudoinverse (Moore-Penrose) her zaman var:
Σ^+LLM'de: Bir over-determined linear regression çözmek (least squares). en küçük veren çözüm.
x = A^+ b||Ax - b||²import torch A = torch.randn(100, 5) b = torch.randn(100) # x = (A^T A)^-1 A^T b ya da pinv ile x = torch.linalg.pinv(A) @ b print(x.shape) # (5,)
9. Mini Egzersizler#
-
Eigendecomp 2x2:. Eigenvalue'ları el ile bul (karakteristik polinom). Sonra eigenvector'ları.
A = [[2, 1], [1, 2]] -
SVD shape oyunu:shape (1000, 50).
A,U,Σ'nin tam shape'leri ne? Truncated rank-10'da hangi shape'ler?V -
PCA elbow: 1000 örneklik 100-d veri. PCA yaparsın, 90% varyansı korumak için kaç component yeter? (İpucu: cumulative explained variance.)
-
LoRA tasarrufu: 70B Llama modelinde tüm attention katmanları LoRA rank-32 ile fine-tune edilir. Toplam eğitilebilir parametre sayısı? (İpucu: Llama 70B'de 80 layer, her layer 4 attention proj, hidden dim 8192.)
-
Condition number: Bir LoRA matrisinin condition number'ı 10^9. Bu sorun mu? Neden? Nasıl tespit ederdin?
Bu Derste Neler Öğrendik?#
✓ Eigendecomposition: bir matrisin "özel eksenleri" (eigenvector) + "ölçek faktörü" (eigenvalue)
✓ SVD: her matris için garantili ayrıştırması
✓ SVD'nin rank-1 toplamı formu — LoRA'nın matematiksel iskeleti
✓ PCA = SVD (merkezlenmiş veri üzerinde)
✓ Truncated SVD: en iyi düşük-rank yaklaşım (Eckart-Young)
✓ LoRA: ΔW'nin düşük-rank olduğu hipotezi, %99+ parametre tasarrufu
✓ Rank, condition number: sayısal stabilite göstergeleri
✓ Pseudoinverse: square-olmayan/singular matrisler için tersin yerine
A = UΣV^TSıradaki Ders#
1.3 — Türev, Gradient ve Matrix Calculus
Geri yayılım (backpropagation) aslında zincir kuralının tekrarlı uygulanmasıdır. Gradient, Jacobian, Hessian; numerator vs denominator layout; cross-entropy + softmax'ın türevinin neden bu kadar zarif olduğunu göreceksin.
Sık Sorulan Sorular
**Eigendecomposition**: simetrik veya kare matrisler için, eigenvector ve eigenvalue'lar geometrik analiz gerekirken (örn. principal axes, mode analysis). **SVD**: her matris için — kare olmayan, rank-deficient, fine-tuning update'leri. Pratikte LLM mühendisinin %95'i SVD kullanır. Eigendecomposition sadece Hessian analizinde, optimization landscape çalışmalarında karşımıza çıkar.
Yorumlar & Soru-Cevap
(0)Yorum yazmak için giriş yap.
Yorumlar yükleniyor...
İlgili İçerikler
Modül 0: Kurs Çerçevesi ve Atölye Kurulumu
LLM Engineer Kimdir? Junior'dan Staff'a Yapay Zekâ Mühendisliği Kariyer Haritası
Öğrenmeye BaşlaModül 0: Kurs Çerçevesi ve Atölye Kurulumu
Kurs Felsefesi: Neden Bu Yol, Neden Bu Sıra — 8 Aylık Müfredatın İskeleti
Öğrenmeye BaşlaModül 0: Kurs Çerçevesi ve Atölye Kurulumu
Atölye Kurulumu: uv, PyTorch 2.5+, CUDA, WSL2, Mac MPS, Triton, FlashAttention, Nsight
Öğrenmeye BaşlaBağlantılı Pillar Konular