
대부분의 경우 머신러닝 문제는 훈련 샘플이 수천~수백만 개의 특성을 가지고 있다.
이러한 특성은 훈련을 느리게 할 뿐 아니라 좋은 솔루션을 찾기 어렵게 만든다.
이를 해결하기 위해 특성수를 크게 줄여서 문제를 해결하기도 한다. (차원축소)
오늘은 차원 축소에 사용되는 두가지 접근과, 차원 축소 기법에 대해 알아보도록 하겠다.
우리는 3차원 세계에 살고 있기 때문에 고차원 공간을 직관적으로 이해하기 어렵다. 또한, 고차원은 많은 공간을 가지고 있기 대문에 두 점이 같은 단위 초입방체에 놓여있더라도 멀리 떨어져 있을 수 있다. 따라서, 저차원일 때보다 예측이 더 불안정하다.
즉, 과대적합될 위험이 크다.
실전의 문제는 훈련 샘플이 모든 차원에 걸쳐 균일하게 퍼져 있지 않다. 즉, 고차원 공간 안의 저차원 부분 공간에 대부분 놓여있다.
2차원에 가깝게 배치된 3차원 데이터셋
이를 투영하면 2D 데이터셋을 얻을 수 있다.


왼쪽) 투영결과, 오른쪽) 스위스 롤을 펼친 모습
고차원 공간 속에 존재하지만 국소적으로는 d차원 평면처럼 보이는 구조
데이터가 있는 매니폴드를 학습해 차원을 줄이는 알고리즘
차원을 줄이면 훈련 속도가 향상된다.
하지만 이 원칙은 항상 성립하는 것은 아니다. 최종 성능은 데이터 구조에 따라 달라진다.
그렇다면 올바른 초평면을 고르는 것은 매우 중요한 일이다.

import numpy as np
x= [...] # 3D 데이터셋이라고 가정
X_centered = X - X.mean(axis=0)
U, s, Vt= np.linalg.svd(X_centered)
c1 = Vt[0]
c2 = Vt[1]
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X2D = pca.fit_transform(X)
» pca.explained_variance_ratio_
array([0.7578477, 0.15186921])
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', as_frame=False)
X_train, y_train = mnist.data[:60_000], mnist.target[:60_000]
X_test, y_test = mnist.data[60_000:], mnist.target[60_000:]
pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ration_)
d = np.argmax(cumsum >= 0.95) + 1 # 차원
# 보존하려는 비율로 n_components를 설정
pca = PCA (n_components=0.95)
X_reduced = pca.fit_transform(X_train)
pca.n_components_ # 훈련 중 주성분 개수는 이 속성에 저장
X_recovered = pca.inverse_transform(X_reduced)
왼쪽) 원본, 오른쪽) 압축 후 복원
rnd_pca = PCA(n_components=154, svd_solver= ”randomized", random_state=42)
X_reduced = rnd_pca.fit_transfonn(X_train)
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):
inc_pca.partial_fit( X_batch )
X_reduced = inc_pca.transform(X_train)
filename = ”my_mnist.mmap ”
X_mmap = np.memmap (filename, dtype='float32’, mode='write’, shape=X_train.shape)
X_mmap[:] = X_train # 대신 반복을 사용해 데이터를 한 정 크씩 저장할 수 있습니다 .
X_mmap.flush ()
X_mmap = n.memmap (filename, dtype='float32', mode='readonly').reshape(-1,784)
batch_size=X_mmap.shape[0] // n_batches
inc_pca = IncrementalPCA(n_components=154, batch_size=batch_size)
inc_pca .fit (X_mmap)
랜덤한 투영은 실제로 거리를 상당히 잘 보존할 가능성이 매우 높고, 따라서 투영 후에도 비슷한 두개의 샘플은 비슷한 채로 남고 매우 다른 두개의 샘플은 매우 다른 채로 남는다.
johnson_lindenstrauss_min_dim()에 구현되어 있다.from sklearn.random_projection import johnson_lindenstrauss_min_dim
m, ε = 5_000, 0.1
d = johnson_lindenstrauss_min_dim(m, eps=ε)
>>> d
7300
n = 20_000
np.random.seed(42)
P = np.random.randn(d,n) / np.sqrt(d) # 표준편차
X = np.random.randn(m, n)
X_reduced = X @ P.T
LocallyLinearEmbedding을 사용해 펼쳐본다.from sklearn.datasets import make_swiss_roll
from skleanr.manifold import LocallyLinearEmbedding
X_swiss, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)
llle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)
X_unrolled = lle.fit_transform(X_swiss)
LLE를 사용해 펼친 스위스롤
scikitlearn에서 제공하는 LLE 구현의 계산복잡도는샘플 간의 거리를 보존하면서 차원을 축소한다.
MDS를 활용해 스위스롤을 2D로 축소
각 샘플을 가장 가까운 이웃과 연결하는 식의 그래프를 만들고, 샘플 간 지오데식 거리를 유지하면서 차원을 축소한다.
두 노드 사이의 지오데식 거리는 두노드 사이의 최단 경로를 이루는 노드의 수이다.
스위스롤의 곡률을 완전히 없앤다. (클러스터를 강조하기 위해 롤을 분해한다.)
Isomap를 활용해 스위스롤을 2D로 축소
t-SNE 활용해 스위스롤을 2D로 축소