[ 핸즈 온 머신러닝 2 ] 차원 축소란? (Dimension Reduction)
어제보다 나은 사람이 되기

걱정보단 실행을, 그러나 계획적으로

Box World 자세히보기

AI/Hands-On Machine Learning 2판

[ 핸즈 온 머신러닝 2 ] 차원 축소란? (Dimension Reduction)

Box형 2021. 2. 6. 02:56
반응형

사람은 어려움 속에서 성장한다

- 제임스 캐시 페니 (J.C 페니 백화점 창립자) -


시작하며

 특성(feature)은 얼핏 보면 데이터의 다양한 측면을 보여주기 때문에 많을 수록 좋은 것 같습니다. 실제로 우리가 마주하는 머신러닝 문제에서는 Training set의 feature가 수백만 개까지 가지고 있는 경우가 있습니다.

 그러나 이런 많은 특성은 훈련을 느리게 하기도 하고, 때론 좋은 솔루션을 찾는데 방해하기도 합니다. 우리는 이를 차원의 저주(curse of dimensionality)라고 합니다.


8.1 차원의 저주

 우리가 살고 있는 세계는 3차원으로, 4차원 이상으로만 가도 직관적으로 상상하기 어렵습니다. 게다가 고차원 공간에선 우리의 상식과 다르게 작동하는 것들이 많습니다.

 예를 들어 1*1 사각형(2차원)이 있을 때, 이 안에 있는 임의의 점을 선택할 건데 이 점이 사각형의 경계선과 0.001 이내로 가까울 확률은 0.4%밖에 되지 않습니다.

 그런데 만약에 10000차원의 단위 면적을 가지는 초입방체에서는 이러한 가능성이 99.99999%로 커집니다.  간단하게 생각해보면 차원이 하나 늘어날 때마다 뭔가 경계선들이 늘어나니까 10000차원으로 가면 경계선들이 엄청나게 많기 때문에 어떤 점을 선택하든 경계선과 매우 가까울 확률이 높은 것입니다. 

 또 다른 예시로 2차원 단위 면적에서 임의의 두 점을 선택했을 때 두 점 사이의 거리는 평균적으로 0.52인데, 1000000차원에서는 이것이 408.25까지 늘어납니다. 고차원은 훨씬 더 많은 공간을 가지고 있기 때문에 그럴거라는 짐작은 들지만 역시나 직관적이진 않습니다. 

 어쨌든 이로 인해 고차원의 데이터셋은 보통 데이터들끼리 멀리 떨어져 가능성이 높다는 걸 유추해볼 수 있습니다. 이러한 경우 예측을 위한 훨씬 더 많은 외삽(extrapolation : 관찰이 어려운 데이터에 대해 추측하는 것)을 요구하기 때문에 불안정해집니다. 우리는 보통 이것을 고차원일수록 Overfitting 위험이 크다고 합니다.

 이를 해결하기 위한 이론적인 해결법은 고차원에서도 데이터 끼리의 거리가 가까울 수 있도록 즉 밀도가 높아질 때까지 dataset의 크기를 키우는 것입니다. 그러나 일정한 밀도에 도달하기까지 필요한 데이터 수는 차원 수가 커짐에 따라 기하급수적으로 늘어나기 때문에 현실적으로 어렵습니다.


8.2 차원 축소를 위한 접근 방법

 Dimension Reduction 알고리즘을 이해하기 위해서는 투영(projection)매니폴드 학습 두가지 접근법을 이해해야 합니다.


8.2.1 투영

 대부분 우리가 마주할 dataset은 모든 차원에 대해 균일하게 퍼져있지 않습니다. 즉 많은 feature들 중 특정 feature들끼리 강한 연관을 가지는 경우가 많습니다.모든 data들이 고차원 공간 안에서(많은 feature들이 있지만) 저차원 subspace에 놓여있습니다(특정 feature들끼리 강한 연관을 가지는 경우가 많다).

 예를 들어 위와같은 3차원 데이터셋을 살펴보겠습니다. 보시다시피 모든 data들이 거의 평면 형태로 놓여있는데 이것이 바로 3차원 공간에 있는 저차원(2차원) subspace입니다.

 여기서 모든 data를 2차원 subspace에 투영하면 다음과 같은 2차원 데이터셋을 얻었는데, 이것으로 우린 데이터셋의 차원을 3에서 2차원으로 줄였다고 할 수 있습니다. 그리고 이에 따라 데이터는 새로운 feature인 $z_1$과 $z_2$에 대응됩니다.

 그러나 다음 그림에서 표현된 스위스 롤 데이터셋에선 투영이 그다지 좋은 방법은 아닙니다.

 만약 이것을 feature $x_3$를 버리고 평면에 투영시키면, 다음 그림의 왼쪽처럼 스위스 롤의 층이 뭉개집니다. 우리가 원하는 것은 오른쪽처럼 스위스 롤을 펼친 형태의 2차원 데이터를 얻고싶은데 말이죠.


8.2.2 매니폴드 학습

 스위스 롤은 2D 매니폴드의 한 가지 예시였습니다. 즉 스위스 롤을 펴게 되면 평면이기 때문에 3차원에서 휘어지고 뒤틀려있는 스위스 롤은 2D 매니폴드로 보는 것이죠.

 예를 들어 위 데이터 공간에 개미가 한마리 산다고 하겠습니다. 우리는 이 롤을 그래프 밖에서 보기때문에 점과 점사이 거리를 구할 때 유클리디안 방식으로 구하게 됩니다. 그러나 개미 입장에선 점프할 수 없기 때문에 롤을 따라 바깥으로 도달하여 점과 점 사이의 거리를 구하게 됩니다.

 이를 일반화하면 d차원 매니폴드는 개미 입장에서 d차원 초평면으로 보일 수 있는 n차원 공간의 일부입니다. ($d < n$)

 많은 차원 축소 알고리즘은 이러한 꼬여있는 매니폴드를 풀어헤친 형태를 모델링하는 식으로 작동하는데, 이를 매니폴드 학습(manifold learning)이라고 합니다. 

 매니폴드 학습이 많이 활용되는 가장 큰 이유는 Classification이나 Regression같은 작업 시 저차원 매니폴드 형태로 데이터를 표현하면 훨씬 더 간단해질거라고 가정하기 때문입니다. 그러나 다음 그림을 보면 매번 간단해지는 것은 아니라는 걸 알 수 있습니다.

 

 

 

 


8.3 PCA

 주성분 분석(PCA)는 가장 보편적인 차원 축소 알고리즘 중 하나인데, 데이터의 분포를 최대한 유지하려 하면서 저차원에 투영시킵니다.


8.3.1 분산 보존

 저차원의 초평면에 dataset을 투영시키기 위해 가장 중요한 것은 데이터 분포를 유지하는 것 즉 분산을 보존해야합니다. 예를 들어 다음 그래프는 2차원 데이터셋을 1차원 축에 투영한 결과입니다. 딱 봐도 첫번재 실선이 분산을 가장 잘 보존하고, 세번째 점선이 분산을 매우 적게 보존한다는 걸 알 수 있습니다.

 분산이 최대로 보존되는 1차원 축을 선택해야 정보가 가장 적게 손실되기 때문에 합리적인건 당연한데, 이를 다르게 말하면 투영 되기 전 데이터와 투영된 데이터 간 평균 제곱 거리를 최소화하는 축을 선택해야한다는 말과 동일합니다.


8.3.2 주성분

 앞으로 남은 PCA에 대해 구체적으로 설명하기 전에 결론적으로 PCA가 하는 작업에 대해 설명 드리겠습니다.

  • 주어진 원본 데이터에 대해서 분산을 가장 많이 보존하는 축부터 찾으면서 임의의 n개의 축을 찾습니다.
  • 내가 투영할 저차원이 d차원이라면 분산을 가장 많이 보존하는 축부터 d개의 축을 선택한 후 이 축들로 이뤄진 d차원 공간에 원본 데이터를 투영시키면 끝입니다.

만약 그림을 통해 이 과정을 쉽게 이해하고 싶다면 다음 포스팅을 참고해주세요.

[AI/Coursera ( Machine Learning )] - [머신러닝 순한맛] PCA(Principal Component Analysis) 알고리즘이란?

 

[머신러닝 순한맛] PCA(Principal Component Analysis) 알고리즘이란?

″성공의 핵심 요소는 인내심이다.” -Bill Gates- 시작하며  우리 저번 포스팅에서 Data Compression 또는 Dimentianality Reduction의 기본적인 원리를 알아보았습니다. 이번 시간에는 이러한 원리를 바탕으

box-world.tistory.com


 다시 돌아와서 방금 PCA는 분산을 가장 많이 보존하는 축부터 n개의 축을 찾는다고 했는데, 이때 이 축들을 주성분(PC : principal componet)라고 부릅니다.

 앞에서 본 예시에선 첫번째 실선이 첫번째 PC이고, 두번째 선이 두번째 PC가 됩니다. 그리고 이 두 PC가 만들어낸 평면과 수직한 축이 세번째 PC가 됩니다.

 그러면 이렇게 PC는 어떻게 찾을까요? 우리는 SVD(singular value decomposition)이라는 표준 행렬 분해 기술을 이용합니다. 쉽게 말해서 이것은 행렬 A를 다음과 같이 분해할 수 있는데 이때 분해된 $V^T$를 전치시켜 $V$를 구하면 여기에 우리가 찾고자하는 모든 PC의 단위 벡터가 담겨있습니다.


8.3.3 d차원으로 투영하기

 PC를 모두 추출했다면, 이제 처음 d개의 PC로 정의한 초평면에 투영하여 데이터셋의 차원을 d차원으로 축소해야합니다. 이때의 초평면은 데이터의 분산을 최대한 보존하게 투영시킴을 보장합니다.

 예를 들어 앞서 봤던 다음 그림은 데이터의 분포를 잘 유지하면서 투영된 결과입니다.

 이렇게 d차원으로 축소된 데이터셋 $X^{d-proj}$을 얻고 싶다면 다음 식과 같이 기존 데이터로 이뤄진 행렬 $X$와 분산을 가장 높게 보존하는 d개의 PC의 단위벡터를 가지는 $W_d$를 행렬곱하여 얻을 수 있습니다.

 다음은 sklearn에서 PCA를 적용하여 데이터셋을 2차원으로 줄이는 코드입니다.

from sklearn.decomposition import PCA

pca = PCA(n_components = 2)
X2D = pca.fit_transform(X)

8.3.5 expained_variance_ratio_ 변수

 expained_variance_ratio_ 변수에는 원본 데이터셋에 대해 PC가 보존하는 분산의 비율이 들어있습니다. 다음은 가장 높게 보존하는 순으로 두가지 PC의 expained_variance_ratio_ 를 살펴보는 코드입니다.

pca.explained_variance_ratio_

>>
array([0.84248607, 0.14631839])

 이것은 데이터셋 분산의 84.2%가 첫번째 PC에 놓이고, 14.6%의 데이터가 두 번째 PC를 따라 놓임을 의미합니다. $100 - (84.2 + 14.6) = 1.2$이므로 3번째 PC부터는 데이터셋 분산의 1.2% 미만의 데이터가 놓일 것입니다.


8.3.6 적절한 차원 수 선택하기

 축소할 차원 수는 임의로 정하기 보다는 각 PC별로 표현하는 데이터 분산의 합이 충분할 때까지(ex. 95% 이상) 필요한 PC의 개수로 차원 수를 선택하는 것이 좋습니다. 물론 데이터 시각화를 위해 차원을 축소하는 경우는 보통 2, 3차원을 씁니다.

 다음 코드에선 PCA를 계산한 후 원본 데이터셋의 분산을 95%로 유지하는데 필요한 최소한의 PC 개수 즉 차원 수를 계산합니다.

pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1

 그 후 n_components를 설정하여 PCA를 다시 실행하는 인자로 보존할 분산의 비율을 넣어주면 됩니다.

pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)

 두 번째 방법은 보존되는 분산의 비율을 차원 수에 대한 함수로 그리는 것입니다. 이 그래프에는 보존되는 분산의 비율이 빠르게 성장하다 멈추는 변곡점이 있는데, 이걸로 축소할 차원 수를 결정할 수 있습니다.


8.3.7 압축을 위한 PCA

 앞서 봤듯이 차원 축소는 dataset의 크기를 줄입니다. 이러한 압축은 SVM과 같은 Classification 알고리즘의 속도를 크게 높입니다.

 반대로 압축된 데이터셋에 PCA 투영의 변환을 반대로 적용하여 다시 원래의 차원으로 되돌릴 수 있습니다. 다만 축소에서 일부 정보를 잃어버렸기 때문에 완벽한 원본 데이터셋을 얻을 순 없지만 매우 비슷합니다.

 원본 데이터와 축소 후 다시 복원된 데이터 사이의 평균 제곱 거리를 재구성 오차(reconstruction error)라고 합니다. 다음은 차원 축소 후 다시 복원하는 코드입니다.

pca = PCA(n_components = 154)
X_reduced = pca.fit_transform(X_train)
X_recovered = pca.inverse_transform(X_reduced)

 다음은 MNIST 데이터셋에 대하여 원본 데이터셋과 압축 후 복원된 결과를 비교한 그림입니다. 이미지의 품질이 손상되긴 했지만 숫자의 모양은 온전한 것을 확인할 수 있습니다.

 데이터를 복원하는 공식은 다음과 같습니다.

 

 

 

 


8.3.8 랜덤 PCA

 svd_solver = "randomized"로 지정하면 sklearn은 랜덤 PCA라는 확률적 알고리즘을 이용하여 축소할 d차원에 대한 d개의 PC를 '근삿값'으로 빠르게 찾습니다.

rnd_pca = PCA(n_components=154, svd_solver="randomized", random_state=42)
X_reduced = rnd_pca.fit_transform(X_train)

svd_solver의 기본값은 "auto"인데, 원본 데이터의 크기나 차원 수가 500보다 크고, 축소할 차원이 이것들의 80%보다 작으면 sklearn은 자동으로 랜덤 PCA 알고리즘을 사용합니다. 만약 이것을 방지하고 싶다면 "full"을 사용하면 됩니다.


8.3.9 점진적 PCA

 PCA 구현의 문제는 SVD 알고리즘 실행을 위해 전체 데이터셋을 메모리에 올려야 한다는 점입니다. 점진적 PCA(IPCA : incremental PCA)는 dataset을 mini-batch로 나눈 뒤 하나 씩 주입하여 적용하여 이를 보완합니다.

 다음 코드는 MNIST 데이터셋을 100개의 mini-batch로 나눠 차원을 축소하는 코드입니다.

from sklearn.decomposition import IncrementalPCA

n_batches = 100
inc_pca = IncrementalPCA(n_components=154)
for X_batch in np.array_split(X_train, n_batches):
    print(".", end="") # 책에는 없음
    inc_pca.partial_fit(X_batch)

X_reduced = inc_pca.transform(X_train)

8.4 커널 PCA

 이전 포스팅에서 고차원 공간을 암묵적으로 매핑하여 SVM의 Classification과 Regression을 가능하게 하는 수학적 기법인 커널 트릭에 대해 이야기했습니다.

[AI/Hands-On Machine Learning 2판] - [ 핸즈온 머신러닝 2판 ] SVM이란?

 

[ 핸즈온 머신러닝 2판 ] SVM이란?

인내는 어떤 실력보다 강하다 - 벤 호건 (프로 골퍼) - 시작하며  SVM은 Classification, Regression 등 다양한 곳에서 활용됩니다. 특히 복잡한 데이터셋을 학습하기에 용이하다는 점도 SVM의 인기를 높이

box-world.tistory.com

 우리는 같은 기법을 PCA에 적용하여 차원 축소를 위한 복잡한 비선형 투영을 할 수 있고 이를 커널 PCA(kPCA)라고 부릅니다.

from sklearn.decomposition import KernelPCA

rbf_pca = KernelPCA(n_components = 2, kernel="rbf", gamma=0.04)
X_reduced = rbf_pca.fit_transform(X)

 다음은 서로 다른 커널을 사용하여 2차원으로 축소시킨 스위스 롤의 모습입니다.


8.4.1 커널 선택과 하이퍼파라미터 튜닝

 kPCA는 Unsupervised Learning이므로 어떤 커널과 하이퍼파라미터를 선택해야 좋은 성능을 내는지 명확하게 알 수 있는 기준이 없습니다. 그러나 그리드 탐색을 이용하면 이를 보완할 수 있습니다.

 다음 코드에서는 kPCA를 이용하여 2차원으로 축소하고 Logistic Regression으로 Classification을 진행합니다. 그 다음 가장 높은 accuracy를 얻기 위해 GridSearchCV를사용하여 kPCA에 가장 좋은 커널과 gamma 파라미터를 사용합니다.

from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

clf = Pipeline([
        ("kpca", KernelPCA(n_components=2)),
        ("log_reg", LogisticRegression(solver="lbfgs"))
    ])

param_grid = [{
        "kpca__gamma": np.linspace(0.03, 0.05, 10),
        "kpca__kernel": ["rbf", "sigmoid"]
    }]

grid_search = GridSearchCV(clf, param_grid, cv=3)
grid_search.fit(X, y)

 그리고 이러한 커널과 파라미터는 다음과 같이 확인합니다.

print(grid_search.best_params_)

>>
{'kpca__gamma': 0.043333333333333335, 'kpca__kernel': 'rbf'}

 완전한 Unsupervised learning으로 가장 낮은 재구성 오차를 만드는 커널과 하이퍼파라미터를 선택하는 방법도 있습니다.

 다만 재구성은 선형 PCA만큼 쉽지 않습니다. 다행히 커널 트릭을 이용하면 이러한 문제를 해결할 수 있습니다.

 차원 축소된 데이터를 다시 복원하면 이때의 데이터는 원래 차원의 공간이 아닌 위 그림에서 x로 표시된 feature space에 놓이게 됩니다. 이것은 무한 차원이기 때문에 재구성 오차 등의 계산이 불가능합니다. 다행히도 재구성된 데이터의 위치와 가깝게 매핑된 원본 공간의 위치는 찾을 수 있는데 이를 재구성 원상(pre-image)라고 합니다.

 원상을 얻으면 원본 데이터와의 제곱 거리를 측정하여 오차를 구할 수 있게 되고 이에 따른 최적의 커널과 하이퍼파라미터를 찾는 것도 가능해집니다.

 이쯤되면 재구성을 어떻게 하는지 궁금해지는데요. 한 가지 방법은 축소된 데이터를 훈련 세트로, 원본 데이터를 타깃으로 하여 Supervised learning model training을 진행하는 것입니다. sklearn에서는 fit_inverse_transform = True로 지정하면 이를 자동으로 수행합니다.

rbf_pca = KernelPCA(n_components = 2, kernel="rbf", gamma=0.0433,
                    fit_inverse_transform=True)
X_reduced = rbf_pca.fit_transform(X)
X_preimage = rbf_pca.inverse_transform(X_reduced)

 그 후 다음과 같이 pre-image error를 계산할 수 있습니다.

from sklearn.metrics import mean_squared_error

mean_squared_error(X, X_preimage)

 8.5 LLE

 지역 선형 임베딩이라 부르는 LLE(locally linear embedding)는 강력한 non-linear dimensionally reduction 기술로 투영이 아닌 매니폴드 학습입니다.

 LLE는 각 data가 가장 가까운 이웃에 얼마나 선형적으로 연관되어 있는지 측정합니다. 그 후 앞서 언급한 개미의 시선으로 바라본 매니폴드가 가장 잘 보존될 원본 데이터의 저차원 표현을 찾습니다. 이 방법은 Noise가 많지 않다면, 꼬인 매니폴드를 펼치는데 잘 작동합니다.

from sklearn.manifold import LocallyLinearEmbedding

lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)
X_reduced = lle.fit_transform(X)

 결과는 다음 그림에서 볼 수 있습니다. 보다시피 스위스 롤이 잘 펴졌고, 샘플 간 거리도 잘 보존되어있는 듯 하지만, 거시적으로 보면 샘플 간 거리가 잘 유지도히지 않습니다. 그렇지만 LLE는 매니폴드를 펼치는데 잘 동작합니다.

 

 

 

 


8.6 기타 차원 축소 알고리즘

1) 랜덤 투영

 말 그대로 linear한 투영을 랜덤하게 하여 데이터를 저차원으로 투영합니다. 놀랍게도 이러한 랜덤 투영이 실제 거리를 잘 보존한다는 것이 수학적으로 증명되었습니다.

2) 다차원 스케일링(MDS)

 데이터 간 거리를 보존하면서, 차원을 축소합니다.

3) lsomap

 각 데이터를 가장 가까운 이웃과 연결하는 식으로 그래프를 만듭니다. 그 후 데이터 간 geodesic distance를 유지하면 차원을 축소합니다.

4) t-SNE

 비슷한 데이터는 가까이, 그렇지 않다면 멀리 떨어지도록 하여 차원을 축소합니다. 주로 고차원 데이터를 시각화할 때 많이 쓰입니다. (ex. MNIST )

5) 선형 판별 분석(LDA)

 사실 분류 알고리즘입니다. 이것은 학습 시 클래스들을 가장 잘 구분하는 축을 학습하여 이 축으로 데이터가 투영되는 초평면을 정의하는데 사용합니다. 이것의 장점은 투영을 통해 가능한 한 클래스를 멀리 떨어지게 유지 시키므로 SVM과 같은 알고리즘을 적용하기 전 차원을 축소하는데 용이합니다.


 다음 포스팅에서는 Unsupervised Learning에 대해 다뤄보겠습니다. 긴 글 읽어주셔서 감사합니다. 행복한 하루 보내세요 :)

반응형