PCA로 공분산 구조를 줄이는 방법: 고유값 분해와 로딩 해석

PCA의 공분산 행렬, 고유값 분해, SVD, 설명분산비와 주성분 로딩 해석을 실무 관점에서 정리한다.

2026-08-14 · 최초 발행 2024-04-29

분산 구조를 보존하며 차원을 줄이는 PCA

PCA(Principal Component Analysis)는 평균 중심화된 데이터 행렬 X(n×d)에서 분산이 가장 큰 직교 축을 찾는 차원 축소 기법이다. 설명 가능한 분산이 큰 성분부터 남겨 데이터의 상관 구조를 압축하고, 잡음과 다중공선성의 영향을 줄이는 데 쓴다.

핵심은 공분산 행렬을 분해해 변수들이 함께 움직이는 방향을 찾는 데 있다. 각 주성분은 서로 직교하므로, 선택한 성분만으로도 원래 데이터의 주요 변동을 저차원 좌표에 담을 수 있다.

공분산 행렬은 다음처럼 계산한다.

  • C = (1/(n−1)) XᵀX 또는 표준화된 데이터 Z의 상관 행렬 R 사용
  • 대각 원소는 각 변수의 분산이고, 비대각 원소는 변수 간 공분산이다.
  • 공분산의 부호와 크기는 변수 사이 선형 관계의 방향과 강도를 나타낸다.

고유값 분해는 C = VΛVᵀ로 쓸 수 있다. Λ의 고유값 λ_i는 i번째 주성분이 설명하는 분산이고, V의 열벡터 v_i는 그 주성분의 방향이다. 설명분산비는 λ_i / Σ_j λ_j이며, 누적 비율을 기준으로 남길 성분 수 k를 결정한다.

SVD로도 같은 구조를 구할 수 있다. X = UΣVᵀ에서 V의 열벡터는 주성분 방향이며, Σ²/(n−1)가 고유값에 해당한다. n < d인 경우, 희소하거나 대규모인 데이터, 수치 안정성이 중요한 경우에는 SVD 기반 구현이 적합하다.

전처리와 행렬 선택이 해석을 좌우한다

변수 단위가 섞여 있다면 z-score 표준화가 필요하다. 표준화하지 않으면 분산이 큰 변수가 주성분을 지배한다. 변수의 단위, 범위, 의미를 기준으로 공분산 행렬과 상관 행렬을 선택하며, 표준화한 뒤의 공분산 행렬은 상관 행렬과 같다.

주성분 수는 누적 설명분산비(예: ≥ 90~95%), 스크리 플롯의 엘보 지점, 교차검증 기반 재구성 오차를 함께 보며 정한다. 규제, 비용, 해석 가능성도 k를 정하는 조건이다.

로딩은 두 방식으로 정의할 수 있다.

  1. 로딩 = V: 방향 벡터로서 각 변수의 기여도를 본다.
  2. 로딩 = V · sqrt(Λ): 변수와 성분 사이의 상관 또는 기여 강도를 해석하기에 유리하며, 표준화한 경우 상관계수에 근접한다.

로딩 절대값이 큰 변수를 보면 주성분의 의미를 추론할 수 있다. 부호는 방향성만 뜻한다. 어느 정의를 쓸지 보고서와 분석 파이프라인에 명시하고, 같은 기준을 계속 적용해야 한다.

부트스트랩이나 재표본화로 로딩의 안정성을 확인하고, 이상치에는 강건 스케일링을 적용해 민감도를 관리한다. 운영 환경에서는 피처 드리프트를 감지하고 재학습 주기를 정하며, 스케일러와 로딩 버전을 고정한다. 스트리밍 환경에서는 Incremental PCA를 적용할 수 있다.

데이터 성격에 따른 분해 방식

항목 공분산 고유값 분해 SVD 기반 PCA
성능 d가 소·중간일 때 빠름 n<<d, 희소/대규모에서 유리
확장성 메모리 O(d²) 요구 스트리밍/랜덤화 SVD로 확장 용이
안정성 조건수 나쁠 때 불안정 수치 안정성 우수
일관성 결정적 결과 산출 용이 랜덤화 시 시드 고정 필요
운영 편의 구현 단순, 해석 직관 라이브러리 의존, 튜닝 필요

압축과 모니터링에 연결하는 방식

제조와 공정 모니터링에서는 수백 개 센서 신호를 표준화한 뒤 PCA를 적용하고, T²/Q 통계로 이상을 탐지할 수 있다. 공정 변수를 줄여 SPC 차트를 단순화하면서 원인 변수도 역추적한다.

고객 분석에서는 행동·거래 지표를 주성분 점수로 축약해 세그먼트 클러스터링에 사용한다. 다중공선성을 해소하면 회귀와 요인 해석도 안정화된다.

이미지와 신호는 패치 또는 프레임을 PCA 공간에 투영해 저장·전송 비용을 줄이고, 노이즈를 억제해 다운스트림 모델의 성능을 개선할 수 있다. 금융 리스크 분석에서는 수익률 공분산 구조에서 금리·신용 스프레드 요인을 추출하고 민감도와 헤지 전략을 단순화한다.

IoT 예측정비에서는 주성분 기반 건강 지표를 잔여수명 예측의 입력으로 삼을 수 있다. 이때 드리프트 감시와 증분 업데이트를 함께 운영한다.

피처 수는 5090% 축소하면서 누적 설명분산비 9099%를 달성할 수 있으며, 이는 도메인에 의존한다. 모델 학습 시간과 메모리는 30~80% 절감할 수 있고, 다중공선성을 제거해 회귀 조건수 개선에도 연결된다. 저장·전송 비용과 재구성 오차를 함께 관리할 수 있다는 점도 PCA를 쓰는 이유다.

분석에서 운영까지 이어지는 흐름

변수 단위 상이단위 동일/의미 동일아니오이상치 영향 큼: 강건스케일링/윈저라이즈수치오차로 λ<0 소량 발생:0으로 절단입력: 원시 데이터 X (n×d)결측치 처리: 삭제/대치스케일링 필요?표준화 (z-score)평균 중심화만행렬 선택: 공분산 또는 상관n < d 또는 수치 불안정?SVD로 PCA공분산 고유값 분해주성분 개수 선택 (k)점수 계산: Z·V_k로딩 계산: V_k 또는V_k·sqrt(λ)출력: 차원 축소 데이터,설명분산비, 로딩

Python으로 확인하는 공분산 기반 PCA

전제 조건은 다음과 같다.

  • Python 3.10+, numpy 1.26+, scikit-learn 1.4+
  • 데이터 X: numpy 배열 (n×d), 결측치 사전 처리 완료 가정
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

# 샘플 데이터
rng = np.random.default_rng(42)
n, d = 500, 50
X = rng.normal(size=(n, d))
X[:, :5] += X[:, 5:10] * 0.8  # 상관 유도

# 1) 표준화
scaler = StandardScaler(with_mean=True, with_std=True)
Z = scaler.fit_transform(X)  # 표준화 데이터

# 2) 공분산(=상관) 행렬 계산
C = np.cov(Z, rowvar=False, bias=False)  # d×d

# 3) 고유값 분해
eigvals, eigvecs = np.linalg.eigh(C)  # 대칭 행렬 전용, 오름차순
idx = np.argsort(eigvals)[::-1]
eigvals, eigvecs = eigvals[idx], eigvecs[:, idx]

# 4) 설명분산비 및 누적
explained_var_ratio = eigvals / eigvals.sum()
cum_ratio = np.cumsum(explained_var_ratio)

# 5) k 선택(누적 95%)
k = int(np.searchsorted(cum_ratio, 0.95) + 1)

# 6) 점수(주성분 좌표) 및 로딩
V_k = eigvecs[:, :k]                        # 방향
scores = Z @ V_k                            # 점수
loadings_dir = V_k                          # 정의1: 방향 벡터
loadings_corr = V_k * np.sqrt(eigvals[:k])  # 정의2: V * sqrt(λ)

print(f"k={k}, 누적설명분산비={cum_ratio[k-1]:.3f}")
print("첫 주성분 상위 로딩(정의2):", np.argsort(-np.abs(loadings_corr[:, 0]))[:5])

# 7) sklearn PCA와 일치성 확인
pca = PCA(n_components=k, svd_solver="auto", random_state=0)
pca_scores = pca.fit_transform(Z)           # 점수
# sklearn components_: 각 행이 성분 방향(=Vᵀ) → 전치로 V
sk_V_k = pca.components_.T
sk_expl_var = pca.explained_variance_
sk_loadings_corr = sk_V_k * np.sqrt(sk_expl_var)

# 방향 부호는 임의 → 성분별 부호 정렬
sign = np.sign((sk_V_k * V_k).sum(axis=0))
sk_V_k *= sign
pca_scores *= sign

print("스코어 코사인 유사도(평균):",
      np.mean([np.corrcoef(scores[:, i], pca_scores[:, i])[0,1] for i in range(k)]))

수치 오차로 매우 작은 음수 고유값이 생길 수 있으므로 0으로 절단하거나 최소값 클리핑을 적용한다. 비표준화 데이터를 사용하면 로딩 해석이 단위에 의존하므로, 보고서에 로딩 정의를 명시해야 한다. 대규모 또는 스트리밍 데이터에는 IncrementalPCA나 randomized SVD를 적용한다.

주성분 분석차원 축소고유값 분해공분산 행렬SVD