반응형

 

차원 축소

    • 머신러닝 문제는 훈련 샘플 각각이 수천 수백개의 특성을 가지는 경우가 존재
    • 많은 특성은 훈련을 느리게 할수 있고 좋은 솔루션을 찾기 어렵게 만듬
    •  

이런 문제를 차원의 저주 라고함

  •  
  • 차원을 축소하면 일부 정보를 소실하지만 잃는 정보가 많지 않음
  • 차원을 축소하여 훈련 속도 증가, 데이터 시각화 에도 유용함
  • 차원 축소에서 사용되는 두 가지 접근 방법 (투영, 매니폴드 학습) 존재

 

차원의 저주

  • 현실은 3차원 세계임으로 고차원 공간을 직관적으로 파악하기 어려움
    • 훈련 세트의 차원이 클수록 과대적합의 위험성이 커짐
  • 차원의 저주를 해결하는 방법중 하나는 훈련 샘플의 밀도가 충분히 높아질 때까지 훈련 세트를 키우는 방법
    • 이 방법은 실제로 엄청난 수의 데이터 샘플을 추가 수집해야 함으로 불가능

 

차원 축소를 위한 접근 방법

  • 차원을 감소시키는 두 가지 주요한 접근법인 투영, 매니폴드 학습이 존재

 

투영

  • 모든 훈련 샘플이 고차원 안의 저차원 부분공간에 놓여 있음
  • 균일하게 펴져있지 않음
  • 투영은 높은 차원의 데이터를 낮은 차원의 데이터로 옮길때 균일하게 펼치는 것

 

매니폴드 학습

  • 많은 차원 축소 알고리즘이 훈련 샘플이 놓여 있는 매니폴드를 모델링하는 식으로 작동
    • 대부분 실제 고차원 데이터셋이 더 낮은 저차원 매니폴드에 가깝게 놓여 있다는 매니폴드 가정 또는 매니폴드 가설에 근거

 

모델을 훈련하기 전에 훈련 세트의 차원을 감소시키면 훈련 속도는 증가하지만 항상 더 좋은 솔루션은 아님, 데이터셋에 달려있음

 

PCA

  • 데이터에 가장 가까운 초평면을 정의한 다음, 데이터를 평면에 투영시키는 방법

 

분산 보존

  • 저 차원의 초평면에 훈련 세트를 투영하기 전에 먼저 적절한 초평면을 선택
  • 분산이 최대로 보존되는 축을 선택하는 것이 정보가 가장 적게 손실되므로 합리적인 방법

 

주성분

  • PCA는 훈련 세트에서 분산이 최대인 축을 찾음, 첫 번째 축에 직교하고 남은 분산을 최대한 보존하는 두 번째 축을 찾음
  • 위와 같은 방법으로 세 번째, 네 번째, .... , i 번째 축을 찾게 되는데 이때 i번째 축을 i번째 주성분이라고 함
  • 훈련 세트의 주성분을 찾는 방법으로는 특이값 분해(SVD)라는 표준 행렬 분해 기술을 사용
  • 훈련 세트 X를 세 개 행렬의 행렬곱 표현식
  • SVD 함수를 사용해 훈련 세트의 모든 주성분을 구한 후 두 개의 PC를 구하는 예제
    • PCA는 데이터셋의 평균이 0이라고 가정
    • 그러므로 데이터를 원점에 맞추고 사용
    X_centered = X - X.mean(axis=0)
    U, s, Vt = np.linalg.svd(X_centered)
    c1 = Vt.T[:, 0]
    c2 = Vt.T[:, 1]

 

d차원으로 투영하기

  • 주성분을 모두 추출시 처음 d개의 주성분으로 정의한 초평면에 투영하여 데이터셋의 차원을 d차원으로 축소시킬 수 있음
    • 해당 초평면은 분산을 가능한 한 최대로 보존하는 투영을 보장
  • 첫 두 개의 주성분으로 정희된 평면에 훈련 세트를 투영하는 예제
  • W2 = Vt.T[:, :2] X2D = X_centered.dot(W2)

 

사이킷런 사용하기

  • 앞서 설명한 SVD 분해 방법을 사용하여 구현
  • 사이킷런을 이용한 PCA 예제
  • from sklearn.decomposition import PCA pca = PCA(n_components=2) X2D = pca.fit_transform(X)

 

설명된 분산의 비율

  • explained_variance_ratio_ 변수에 저장된 주성분의 설명된 분산의 비율도 유용한 정보
  • 이 비율은 각 주성분의 축을 따라 있는 데이터셋의 분산 비율을 의미
  • 분산 비율 예제
    • 위 정보로 데이터셋 분산의 84.2%가 첫 번째 PC를 따라 놓여 있고 나머지 14.6%가 두 번째 PC를 따라 놓여 있다는 것을 의미 이를 통해 세 번째 PC에는 1.2% 미만이 남아있음
  • pca.explained_variance_ratio_ >> array([0.84248607, 0.14631839])

 

적절한 차원 수 선택하기

  • 차원을 축소하지 않고 PCA를 계산한 뒤 훈련 세트의 분산을 95%유지하는 예제
    • 적절한 차원수를 찾으면 해당 d를 components로 설정해 PCA 다시 실행
    • 유지하려는 주성분의 수를 지정하는 것 보다 보존하려는 분산의 비율을 n_components에 0.0 ~ 1.0 사이로 설정하는 것이 좋음
    • pca = PCA(n_components=0.95) X_reduced = pca.fit_transform(X_train) pca.n_components_ >> 154
  • from sklearn.datasets import fetch_openml mnist = fetch_openml('mnist_784', version=1, as_frame=False) mnist.target = mnist.target.astype(np.uint8) from sklearn.model_selection import train_test_split X = mnist["data"] y = mnist["target"] X_train, X_test, y_train, y_test = train_test_split(X, y) pca = PCA() pca.fit(X_train) cumsum = np.cumsum(pca.explained_variance_ratio_) d = np.argmax(cumsum >= 0.95) + 1 d >> 154

 

압축을 위한 PCA

  • 차원을 축소하고 난 후에는 훈련 세트의 크기가 줄어듦
  • 이렇게 축소된 데이터셋은 다양한 알고리즘의 학습 속도를 높일수 있음
  • 한번 축소된 데이터를 원본 데이터로 복구시켜도 손실된 데이터가 존재함으로 완전 같지는 않지만 유사함
    • 원본 데이터와 재구성된 데이터 사이의 평균 제곱 거리를 재구성 오차라고 함
  • inverse_transform()을 통해 차원축소시킨 데이터 재구성 데이터로 변환 예제
  • pca = PCA(n_components=154) X_reduced = pca.fit_transform(X_train) X_recovered = pca.inverse_transform(X_reduced)
  • 원본 데이터와 재구성된 데이터 비교

 

랜덤 PCA

  • svd_solver 매개변수를 "randomized"로 지정하면 sklaern은 랜덤 PCA라고 부르는 확률적 알고리즘을 사용해 처음 d개의 주성분에 대한 근삿값을 빠르게 찾음
    • 해당 알고리즘의 계산 복잡도는 완전한 SVD 방식
      • 수식
        이 아닌
      • d가 n보다 많이 작으면 작을수록 완전한 SVD보다 빠름
  • 랜덤 PCA 예제
    • svd_solver의 기본값은 'auto'
    • m이나 n이 500보다 크고 d가 m이나 n의 80%보다 작으면 sklearn은 자동으로 랜덤 PCA 알고리즘 사용
      • 반대의 경우에는 완전한 SVD 사용
    • sklearn에서 완전한 SVD 방식을 강제적으로 사용할때는 svd_solver 에 "full" 지정
  • rnd_pca = PCA(n_components=154, svd_solver="randomized", random_state=42) X_reduced = rnd_pca.fit_transform(X_train)

 

점진적 PCA (IPCA)

  • PCA 구현의 문제는 SVD 알고리즘을 실행하기 위해 전체 훈련 세트를 메모리에 적재 해야하는 점
  • 점진적 PCA는 훈련 세트를 미니배치로 나눈 뒤 IPCA 알고리즘에 한 번에 하나씩 적용
  • IPCA 예제
  • 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)
  • memmap을 통한 IPCA 예제
  • X_mm = np.memmap(filename, dtype="float32", mode="readonly", shape=(m, n)) batch_size = m // n_batches inc_pca = IncrementalPCA(n_components=154, batch_size=batch_size) inc_pca.fit(X_mm)

 

커널 PCA (kPCA)

  • 커널 PCA는 차원 축소를 위한 복잡한 비선형 투형을 수행하는 것
    • kPCA는 투영된 후에 샘플의 군집을 유지, 꼬인 매니폴드에 가까운 데이터셋 펼칠때 유용
  • KernelPCA 예제
    • 선형 커널, RBF 커널, 시그모이드 커널 비교
  • from sklearn.decomposition import KernelPCA rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.04) X_reduced = rbf_pca.fit_transform(X)

 

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

  • kPCA는 비지도 학습이기 때문에 좋은 커널과 하이퍼파라미터를 선택하기 위한 명확한 성능 측정 기준이 없음
  • 그리드 탐색을 사용해 주어진 문제에서 성능이 가장 좋은 커널과 하이퍼파라미터를 선택할 수 있음
  • kPCA를 사용해 2차원 축소후 로지스틱 회귀 적용 예제
  • 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'}
  • 완전한 비지도 방법으로 가장 낮은 재구성 오차를 만드는 커널과 하이퍼파라미터를 선택하는 방식도 존재 하지만 재구성이 선형 PCA 만큼 쉽지않음
  • 축소된 공간에 있는 샘플에 대해 선형 PCA를 역전시키면 재구성된 데이터 포인트는 원본 공간이 아닌 특성 공간에 놓임
    • 특성 곤간은 무한 차원 그래서 재구성된 포인트 계산 X
    • 재구성에 따른 실제 에러 계산 X
    • 대신 재구성된 포인트에 가깝게 매핑된 원본 공간의 포인트를 찾을 수 있음, 이를 재구성 원상이라고 함
  • 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) from sklearn.metrics import mean_squared_error mean_squared_error(X, X_preimage) >>32.78630879576612

 

LLE

  • 지역 선형 임베딩(LLE)는 또 다른 강력한 비선형 차원 축소(NLDR)기법
    • 투영에 의존하지 않는 매니폴드 학습법
    • LLE는 각 훈련 샘플이 가장 가까운 이웃에 얼마나 선형적으로 연관되어 있는지 측정
    • 그 후에 국부적인 관계가 가장 잘 보존되는 훈련 세트의 차원 표현을 찾음
  • sklearn LLE 예제
    • 결과 시각화
      • 지역적으로 샘플 간 거리가 잘 보존됨, 크게보면 샘플 간 거리가 잘 유지되어 있지는 않음
      • LLE는 매니폴드를 모델링 하는데 잘 작동함
  • from sklearn.manifold import LocallyLinearEmbedding lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42) X_reduced = lle.fit_transform(X)

 

다른 차원 축소 기법

랜덤 투영

  • 랜덤한 선형 투영을 사용해 데이터를 저차원 공간으로 투영
  • 랜덤 투영이 실제로 거리를 잘 보존

 

다차원 스케일링 (MDS)

  • 샘플 간의 거리를 보존하면서 차원을 축소
  • 예제
  • from sklearn.manifold import MDS mds = MDS(n_components=2, random_state=42) X_reduced_mds = mds.fit_transform(X)

 

lsomap

  • 각 샘플을 가장 가까운 이웃과 연결하는 식으로 그래프를 만듬
  • 샘플 간의 지오데식 거리를 유지하면서 차원 축소
  • 예제
  • from sklearn.manifold import Isomap isomap = Isomap(n_components=2) X_reduced_isomap = isomap.fit_transform(X)

 

t-SNE

  • 비슷한 샘플은 가까이, 비슷하지 않은 샘플은 멀리 떨어지도록 하면서 차원 축소
  • 고차원 공간에 있는 샘플의 군집을 시각화할 때 사용
  • 예제
  • from sklearn.manifold import TSNE tsne = TSNE(n_components=2, random_state=42) X_reduced_tsne = tsne.fit_transform(X)

 

선형 판별 분석 (LDA)

  • 실제로는 분류 알고리즘
  • 훈련 과정에서 클래스 사이를 가장 잘 구분하는 축을 학습
    • 해당 축을 초평면 정의에 사용
  • 투영을 통해 가능한 한 클래스를 멀리 떨어지게 유지시키므로 SVM 분류기 같은 다른 분류 알고리즘을 적용하기 전에 차원을 축소시키는 데 좋음
  • 예제
  • from sklearn.discriminant_analysis import LinearDiscriminantAnalysis lda = LinearDiscriminantAnalysis(n_components=2) X_mnist = mnist["data"] y_mnist = mnist["target"] lda.fit(X_mnist, y_mnist) X_reduced_lda = lda.transform(X_mnist)

 

연습문제

1. 데이터셋의 차원을 축소하는 주요 목적은 무엇인가요? 대표적인 단점은 무엇인가요?

- 주 목적 : 알고리즘 속도 향상, 데이터 시각화를 통한 통찰, 메모리 공간 절약
- 단점 : 정보 손실로 인한 알고리즘 성능 저하, 계산 비용 높음, 파이프라인 복잡도 증가, 변환된 데이터 이해 어려움

 

2. 차원의 저주란 무엇인가요?

저차원 공간에서는 없는 문제가 고차원에서는 발생한다는 것. 무작위로 선택한 고차원 벡터는 매우 희소해서 과대적합의 위험이 크다. 많은 양의 데이터를 필요로한다.

 

3. 데이터셋의 차원을 축소시키고 나서 이 작업을 원복할 수 있나요? 할 수 있다면 어떻게 가능할까요? 가능하지 않다면 왜일까요?

- 완벽하게 되돌리는 것은 불가능. 재구성정도는 가능하지만 완전히는 X
- 차원 축소시 어느정도 정보의 손실이 발생

 

4. 매우 비선형적인 데이터셋의 차원을 축소하는 데 PCA를 사용할 수 있을까요?

가능. 불필요한 차원이 없다면 PCA의 차원 축소는 많은 정보를 잃게 한다.

 

5. 설명된 분산율 95%로 지정한 PCA를 1,000개의 차원을 가진 데이터셋에 적용한다고 가정한다. 결과 데이터셋의 차원은 얼마나 될까?

데이터셋마다 다름. 1 ~ 950 차원 사이를 가짐 ( 극단적인 데이터 형태로, 완전 일직선, 완전 무작위 )

 

6. 기본 PCA, 점진적 PCA, 랜덤 PCA, 커널PCA는 어느 경우에 사용될까요?

기본은 메모리에 맞을 때, 점진적은 메모리에 담을 수 없는 대용량 일 때 사용(온라인에도 유용). 랜덤은 데이터셋이 메모리 크기에 맞고 차원을 크게 축소시킬 때 사용. 커널은 비선형 데이터셋에 유용하다.

 

7. 어떤 데이터셋에 적용한 차원 축소 알고리즘의 성능을 어떻게 평가할 수 있을까요?

정보를 많이 잃지 않았다면 잘한 것. 한 가지 방법은 재구성 오차를 측정하는 것.
전처리 단계로 사용하면 성능 측정 가능. 원본 데이터셋과 유사한 성능을 내야함

 

8. 두 개의 차원 축소 알고리즘을 연결할 수 있을까요?

 가능. PCA로 불필요 차원 줄이고, LLE 처럼 느린 알고리즘을 적용.

 

9. (3장에서 소개한) MNIST 데이터셋을 로드하고 훈련 세트와 테스트 세트로 분할합니다(처음 60,000개는 훈련을 위한 샘플이고 나머지 10,000개는 테스트용입니다).

  • 데이터 로드
X_train = mnist['data'][:60000]
y_train = mnist['target'][:60000]

X_test = mnist['data'][60000:]
y_test = mnist['target'][60000:]
  • 데이터셋에 랜덤 포레스트 분류기를 훈련시키고 얼마나 오래 걸리는지 시간을 잰 다음, 테스트 세트로 만들어진 모델을 평가
from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)

import time

t0 = time.time()
rnd_clf.fit(X_train, y_train)
t1 = time.time()

print("훈련 시간: {:.2f}s".format(t1 - t0))
>> 훈련 시간: 54.82s

from sklearn.metrics import accuracy_score

y_pred = rnd_clf.predict(X_test)
accuracy_score(y_test, y_pred)
>> 0.9705
  • PCA를 사용해 설명된 분산이 95%가 되도록 차원을 축소
from sklearn.decomposition import PCA

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

rnd_clf2 = RandomForestClassifier(n_estimators=100, random_state=42)
t0 = time.time()
rnd_clf2.fit(X_train_reduced, y_train)
t1 = time.time()

print("훈련 시간: {:.2f}s".format(t1 - t0))
>> 훈련 시간: 133.43s # 오히려 느려짐

PCA는 속도를 아주 빠르게 만들어 주지만 항상 그런 것은 아님

원본데이터의 일부 손실이 발생함으로 성능이 떨어짐

 

10. t-SNE 알고리즘을 사용해 MNIST 데이터셋을 2차원으로 축소시키고 맷플롯립으로 그래프를 그려보세요.

  • 데이터 로드
np.random.seed(42)

m = 10000
idx = np.random.permutation(60000)[:m]

X = mnist['data'][idx]
y = mnist['target'][idx]
  • t-sne 차원 축소
from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, random_state=42)
X_reduced = tsne.fit_transform(X)

plt.figure(figsize=(13,10))
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, cmap="jet")
plt.axis('off')
plt.colorbar()
plt.show()

  • 위 그림에서 많이 겹쳐진 2,3,5만 따로 수행
    • 변경전
    • plt.figure(figsize=(9,9)) cmap = mpl.cm.get_cmap("jet") for digit in (2, 3, 5): plt.scatter(X_reduced[y == digit, 0], X_reduced[y == digit, 1], c=[cmap(digit / 9)]) plt.axis('off') plt.show()
    • 변경후
    • idx = (y == 2) | (y == 3) | (y == 5) X_subset = X[idx] y_subset = y[idx] tsne_subset = TSNE(n_components=2, random_state=42) X_subset_reduced = tsne_subset.fit_transform(X_subset) plt.figure(figsize=(9,9)) for digit in (2, 3, 5): plt.scatter(X_subset_reduced[y_subset == digit, 0], X_subset_reduced[y_subset == digit, 1], c=[cmap(digit / 9)]) plt.axis('off') plt.show()
  • 최종 시각화
from sklearn.preprocessing import MinMaxScaler
from matplotlib.offsetbox import AnnotationBbox, OffsetImage

def plot_digits(X, y, min_distance=0.05, images=None, figsize=(13, 10)):
    # 입력 특성의 스케일을 0에서 1 사이로 만듭니다.
    X_normalized = MinMaxScaler().fit_transform(X)
    # 그릴 숫자의 좌표 목록을 만듭니다.
    # 반복문 아래에서 `if` 문장을 쓰지 않기 위해 시작할 때 이미 그래프가 그려져 있다고 가정합니다.
    neighbors = np.array([[10., 10.]])
    # 나머지는 이해하기 쉽습니다.
    plt.figure(figsize=figsize)
    cmap = mpl.cm.get_cmap("jet")
    digits = np.unique(y)
    for digit in digits:
        plt.scatter(X_normalized[y == digit, 0], X_normalized[y == digit, 1], c=[cmap(digit / 9)])
    plt.axis("off")
    ax = plt.gcf().gca()  # 현재 그래프의 축을 가져옵니다.
    for index, image_coord in enumerate(X_normalized):
        closest_distance = np.linalg.norm(neighbors - image_coord, axis=1).min()
        if closest_distance > min_distance:
            neighbors = np.r_[neighbors, [image_coord]]
            if images is None:
                plt.text(image_coord[0], image_coord[1], str(int(y[index])),
                         color=cmap(y[index] / 9), fontdict={"weight": "bold", "size": 16})
            else:
                image = images[index].reshape(28, 28)
                imagebox = AnnotationBbox(OffsetImage(image, cmap="binary"), image_coord)
                ax.add_artist(imagebox)

plot_digits(X_reduced, y, images=X, figsize=(35, 25))

반응형

'Study > Self Education' 카테고리의 다른 글

핸즈온 머신러닝 - 10  (0) 2024.06.19
핸즈온 머신러닝 - 9  (0) 2024.06.18
핸즈온 머신러닝 - 7  (0) 2024.06.18
핸즈온 머신러닝 - 6  (0) 2024.06.18
핸즈온 머신러닝 - 5  (0) 2024.06.17