신군의 역학사전

[ML] 비지도학습 - 주성분분석 (Principal Component Analysis, PCA) 본문

Machine Learning/Unsupervised Learning

[ML] 비지도학습 - 주성분분석 (Principal Component Analysis, PCA)

긔눈 2025. 5. 26. 12:00
반응형

1. 개요(Overview)

주성분분석(Principal Component Analysis, PCA)는 고차원 데이터를 저차원 공간에 효과적으로 투영하여 데이터의 주요 변동을 최대한 보존하는 투영 기반 차원 축소 기법으로, 데이터에 가장 가까운 초평면(Hyperplane)을 정의한 다음, 데이터를 해당 평면에 투영시킨다. 이와같은 PCA의 활용을 통해 고차원 데이터 분석에서 발생할 수 있는 문제점들을 효과적으로 해결할 수 있다.

 

2. 주성분(Principal Component)

PCA의 목적은 Raw Data의 데이터의 특성(분포)을 최대한 유지하며 차원을 낮추는 것으로, 저차원의 부분공간에 투영하기 전에 최적의 초평면을 결정해야한다. 여기서 최적의 초평면은 투영한 저차원의 데이터의 분산이 최대가 되도록하는 평면이 되는데, 이를 그림으로 정리해보면 아래와 같다.

Reference[1]

분산이 클수록 투영시킨 데이터가 원래의 퍼짐을 잘 반영하고, 그렇지 못할수록 투영 결과는 점에 가까워진다. 점에 가깝게 투영된 데이터는 본래 Raw Data가 갖고있던 분포, 즉 데이터의 특성을 대부분 잃어버렸다고 판단할 수 있다. 따라서 Raw Data가 갖고있는 본래의 분포를 저차원에서 최대한 보존하기 위해서는 투영시킨 저차원 데이터의 분산이 최대가 되어야한다(최대한 넓게 퍼져있어야 한다).

 

X1, X2, ... Xp를 변수, 즉 단위벡터의 개념으로 생각하면 첫 주성분은 우선 아래와 같이 정의할 수 있다. 여기서 별도의 연산을 통해, 이 Z1이 분산이 최대가 되도록 해야한다.

X1, X2, ... Xp 앞에 붙는 계수들은 Loadings라 불리며, 데이터가 특정 방향으로 치우치는 것을 방지하기 위해 아래와 같이 정규화해야한다.

여기서 i번째 관측치를 (xi1, xi2, ... , xip)와 같이 정의할 수 있으며, 해당 관측치를 Z1이란 첫번째 주성분에 투영(Projection)시키면 아래의 zi1으로 정리할 수 있다. 투영이므로 간단하게 벡터 내적 연산을 수행해주면 된다.

우리가 관심있는 대상은 오로지 분산이기 때문에, 부가적인 연산을 줄이기 위해 X내의 변수가 모두 평균이 0임을 가정하면, 주성분 Zm의 분산은 아래와 같이 정의되며

이를 앞서 내적으로 구했던 수식을 대입하면 아래와 같다.

고차원 공간에서의 직관적인 이해가 어려운 경우 3차원->2차원으로의 차원축소 상황을 예시로 생각해보면 쉬운데, X1,X2,X3축으로 이루어진 3차원 공간에서 첫번재 주성분 Z1은 X1,X2,X3에 대한 좌표로 표현할 수 있고 그때의 좌표를 (Pi11, Pi21, Pi31)이라 해보자. 그럼 해당 주성분은 아래와 같은 벡터형태로도 표현이 가능하다.

이때의 i번째 관측치의 좌표를 (xi1, xi2, xi3)라 한다면, X1, X2, X3축에서 마찬가지로 아래와 같이 벡터형태로 나타낼 수 있고

마찬가지로 Z1과 Xi의 내적을 통해 관측치에 대한 분산 값을 계산할 수 있다. 어쨌든 분산을 최대화하는 최적화과정은 특이값 분해(Singular Value Decomposition, SVD)로 해결이 가능하며, 이과정을 통해 도출된 첫번째 주성분은 아래의 하늘색 선과 같이 나타납니다.

Reference[2]

 

이후 두번째 혹은 그 이후의 주성분을 구하기 위해서는 이전 단계에서 구한 주성분과 직교하며 분산이 최대가 되는 주성분을 같은 방법으로 찾아가며 1,2,3, ... n번째 주성분을 찾아나가게 된다. "직교"라는 제약조건을 두는 이유는 첫번째 주성분으로 설명이 가능한 정보를 굳이 두번째 주성분에 담을 필요가 없기 때문이지 않을까라고 개인적으로 정리해보긴 했는데.. 맞는 설명일지는 모르겠다 ㅋㅋ..

 

추가로 PCA에서 얻을 수 있는 주성분의 최대 개수는 관측치의 수와 변수(특징)의 수에 의해 결정이 되는데, 둘중 작은 값을 기준으로 최대 주성분의 수가 결정된다. 여기서 주의할점은 최대 주성분의 수는 min(n,p)가 아닌, min(n-1, p)개라는 점. 이는 각 변수의 평균을 0으로 가정하였기 때문에 자유도(D.O.F)가 1 감소하기 때문이다(n:관측치의 개수, p:변수(특징)의 개수).

 

3. 최적 주성분 수 결정 - 설명된 분산의 비율(Proportion of Variance Explained, PVE)

앞서 정리한 바와 마찬가지로, 주성분은 최대 min(n-1,p)개까지 정의할 수 있다. 다만 주성분의 수가 늘어날수록 계산비용과 시간은 당연히 증가하기 때문에, 적정선에서 최적 주성분 수를 결정해야한다. 이는 K-means Clustering과 마찬가지로 엘보우 메소드를 통해 결정할 수 있다.

 

https://ymechanics.tistory.com/entry/%EC%97%98%EB%B3%B4%EC%9A%B0-%EB%A9%94%EC%86%8C%EB%93%9C-Elbow-method

 

[ML] 엘보우 메소드 (Elbow method)

엘보우 메소드(Elbow Method)위키피디아의 정의를 인용하자면, 엘보우 메소드는 군집 분석(Clustering)에서 데이터 셋의 클러스터 수를 결정하는 데 사용되는 휴리스틱(heuristic) 방법입니다. 휴리스틱(h

ymechanics.tistory.com

 

그전에 각 주성분의 영향력?(strength)을 알기 위해서, 적절한 변수를 정의할 필요가 있는데, 그게 바로 PVE이다. 한국어 번역이 굉장히 어색하긴하지만, 설명된 분산의 비율?정도로 번역되는 것 같다. 정의된 PVE는 아래와 같고, 식을 자세히 살펴보면 평균을 0으로 가정하였기 때문에, 분모에는 전체 분산의 총합이, 분자에는 m번째 주성분에 대한 분산값이 들어간다. 즉, 전체 분산에서 Zm이라는 주성분의 분산의 비율을 구함으로써 각 주성분의 영향력을 판단할 수 있는 것.

왼쪽 그래프는 각 주성분의 PVE값을, 오른쪽 그래프는 누적값을 나타낸 그래프인데, 엘보우 포인트를 최적 주성분 수로 결정하면 된다. 아래의 예시에서는 2개가 되겠다.

Reference[3]

 

4. 선형회귀와의 차이점

Reference[4]

어떻게보면 데이터의 경향성을 올바르게 반영할 수 있는 부분공간(Subspace)를 찾는 과정이라는 점에서, PCA는 선형회귀와 유사한 면이 있다. 다만 약간의 차이는 있는데, 이는 위의 그림으로 설명이 가능하다. PCA는 PC 평면의 수직방향 최소거리를, 선형회귀는 y-axis 방향 최소거리를 바탕으로 최적 부분공간을 찾아낸다는 차이점이 있다.

 

5. PCA 구현하기

기본적으로 사이킷런에서 PCA를 내장함수로 지원한다. 따라서 아래와 같이 필요한 모듈을 우선 임포트해와야 한다. sklearn.decomposition에서 PCA를 임포트해오면 된다. 나머지는 예시로 사용할 데이터셋 불러오거나 그외 필수적인 모듈 불러오는 내용.

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Times New Roman'

from sklearn.decomposition import PCA
from sklearn.datasets import load_iris

우선 필요한 IRIS 데이터셋을 로드해준다. 여기서 target_names는 해당 데이터셋의 클래스로 해당 예제에서 큰 의미는 없다. ['setosa', 'versicolor', 'virginica'] 총 3개의 클래스가 존재하며, 서로 다른 붓꽃 종류가 있다는 것을 의미한다.

iris = load_iris()
X = iris.data  
y = iris.target
target_names = iris.target_names

PCA라는 함수에는 주성분 수를 인자로 전달할 수 있으며, IRIS 데이터셋은 4D의 Feature Space를 갖기 때문에, 주성분 수를 4개로 전달해봤다. 그리고 pca.fit_transform을 통해 원본 데이터 X를 주성분 공간으로 투영하여 차원 축소된 결과 X_pca를 반환한다.

pca = PCA(n_components=4)
X_pca = pca.fit_transform(X)

pca.fit_transform을 통해 학습이 완료되면 pca라는 모델 내에 학습 정보가 기록이 되며, 모델에서 직접 주성분과 PVE값을 출력해서 볼 수 있다.

print("Principal Components (components_):")
print(pca.components_)
print("\nExplained Variance (explained_variance_):")
print(pca.explained_variance_)
print("\nExplained Variance Ratio (explained_variance_ratio_):")
print(pca.explained_variance_ratio_)

대충 결과를 뽑아보면 아래와 같고

plt.figure(figsize=(9, 6))
pc_nums = np.arange(1, len(pca.explained_variance_ratio_) + 1)
plt.plot(pc_nums, pca.explained_variance_ratio_, marker="o", linestyle="-")
plt.xlabel("# of PC", fontsize=16)
plt.ylabel("PVE", fontsize=16)
plt.title("Elbow Plot", fontsize=18)
plt.xticks(pc_nums)
plt.grid(True)
plt.show()

이를 그래프로 시각화해보면 아래의 Elbow Plot이 출력된다.

엘보우 메소드에 따르면 주성분 수는 2개일때가 최적이며, 아래와 같이 1,2번째 주성분에 대해 차원축소한 결과를 아래와 같이 가시화할 수 있다.

plt.figure(figsize=(9, 6))
colors = ["navy", "turquoise", "darkorange"]
for color, i, target_name in zip(colors, [0, 1, 2], target_names):
    plt.scatter(
        X_pca[y == i, 0],
        X_pca[y == i, 1],
        color=color,
        label=target_name,
        edgecolor="k",
        s=50,
    )
plt.xlabel("1st PC", fontsize=16)
plt.ylabel("2nd PC", fontsize=16)
plt.title("PCA of Iris Dataset", fontsize=18)
plt.legend(loc="best")
plt.show()

 

 

 

Reference

[1] https://youtu.be/FhQm2Tc8Kic?si=3-_0wr2UFWOHOSOL

[2] https://austingwalters.com/pca-principal-component-analysis/

[3] An Introduction to Statistical Learning with Applications in Python, Gareth James, Daniela Witten, Trevor Hastie, Robert Tibshirani, Jonathan Taylor(2023)

[4] https://www.scielo.org.mx/scielo.php?script=sci_arttext&pid=S1870-90442016000100031

반응형