● 다양체 학습

  • 높은 차원의 데이터를 저차원으로 축소하는 방법

https://scikit-learn.org/0.23/auto_examples/manifold/plot_compare_methods.html

  • 고차원 데이터를 2차원 또는 3차원으로 축소해 시각화에 활용할 수 있음
  • 차원 축소 과정에서 중요하지 않은 정보는 버려지고 중요한 정보만 남기 대문에 데이터 정제에 활용 가능

 

  • 데이터 생성 및 시각화 함수
# 필요 라이브러리
import numpy as np
import matplotlib.pyplot as plt
from sklearn import manifold
from sklearn import random_projection
from sklearn import datasets

# s_curve 데이터셋 생성
s_curve, color = datasets.make_s_curve(1000, random_state = 0)

# 손글씨 데이터셋 생성
digits, labels = datasets.load_digits(return_X_y = True)
# projection으로 만들기
rand_proj = random_projection.SparseRandomProjection(n_components = 3, random_state = 0)
projected_digits =rand_proj.fit_transform(digits)

# min_max_scale 하는 함수
def min_max_scale(x):
    min_value, max_value = np.min(x, 0), np.max(x, 0)
    x = (x - min_value) / (max_value - min_value)
    return x

# s_curve 시각화 함수
def plot_s_curve(s_curve, color, position, projection):
    s_curve = min_max_scale(s_curve)
    if projection == '3d':
        ax = plt.subplot(position, projection = projection)
        ax.scatter(s_curve[:, 0], s_curve[:, 1], s_curve[:, 2], c = color, cmap = plt.cm.Spectral)
        ax.view_init(4, -72)
    elif projection == '2d':
        ax = plt.subplot(position)
        ax.scatter(s_curve[:, 0], s_curve[:, 1], c = color, cmap = plt.cm.Spectral)

# 손글씨 시각화 함수
def plot_digits(digits, labels, position, projection):
    digits = min_max_scale(digits)
    if projection == '3d':
        ax = plt.subplot(position, projection = projection)
        for i in range(digits.shape[0]):
            ax.text(digits[i, 0], digits[i, 1], digits[i, 2], str(labels[i]),
                    color = plt.cm.Set1(labels[i] / 10.), fontdict = {'weight': 'bold', 'size': 9})
        ax.view_init(4, -72)
    elif projection == '2d':
        ax = plt.subplot(position)
        for i in range(digits.shape[0]):
            ax.text(digits[i, 0], digits[i, 1], str(labels[i]),
                    color = plt.cm.Set1(labels[i] / 10.), fontdict = {'weight': 'bold', 'size': 9})

 

  - 데이터 시각화

# 작성한 함수를 사용하여 s_curve와 손글씨 데이터를 각각 시각화(3차원으로)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve, color, 121, '3d')
plot_digits(projected_digits, labels, 122, '3d')

# 작성한 함수를 사용하여 s_curve와 손글씨 데이터를 각각 시각화(2차원으로)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve, color, 121, '2d')
plot_digits(projected_digits, labels, 122, '2d')

 

 

1. Locally Linear Embedding(LLE)

  • 국소 이웃 거리를 보존하는 저차원 임베딩을 찾음
# s_curve는 2차원으로
s_curve_lle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                              method = 'standard', random_state = 0).fit_transform(s_curve)

# 손글씨는 3차원으로
digits_lle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'standard', random_state = 0).fit_transform(s_curve)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_lle, color, 121, '2d')
plot_digits(digits_lle, labels, 122, '3d')

 

 

2. Local Tangent Space Alignment(LTSA)

  • 탄젠트 공간을 통해 각 이웃의 국소 성질을 특성화
  • 국소 탄젠트 공간을 정렬
# LLE에서 method만 'ltsa'로 바꾸면 ltsa 사용 가능

s_curve_ltsa = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                               method = 'ltsa', random_state = 0).fit_transform(s_curve)

digits_ltsa = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'ltsa', random_state = 0).fit_transform(digits)

 

# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_ltsa, color, 121, '2d')
plot_digits(digits_ltsa, labels, 122, '3d')

 

 

3. Hessian Eigenmapping

  • LLE의 문제를 해결한 다른 방법
    • LLE는 단순히 이웃간의 거리를 기반으로만 유지를 한다는 제한점이 있음
  • 국소 선형 구조를 복원하기 위해 각 이웃에서 hessian 기반의 이차 형태를 중심으로 회전
# LLE에서 method만 'hessian'으로 바꾸면 hessian 사용 가능

s_curve_hlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                               method = 'hessian', random_state = 0).fit_transform(s_curve)

digits_hlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'hessian', random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_hlle, color, 121, '2d')
plot_digits(digits_hlle, labels, 122, '3d')

s_curve데이터는 ltsa와 색깔이 반대로 나옴, 손글씨 데이터는 별 차이 없음

 

4. Modfied Locally Linear Embedding

  • 각 이웃에 여러 가중치 벡터를 사용
  • n_neighbors > n_components를 만족해야 함
# LLE에서 method만 'modified'로 바꾸면 modified 사용 가능

s_curve_mlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                               method = 'modified', random_state = 0).fit_transform(s_curve)

digits_mlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'modified', random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_mlle, color, 121, '2d')
plot_digits(digits_mlle, labels, 122, '3d')

 

5. Isomap

  • 초기의 다양체 학습 알고리즘
  • MDS와 커널 PCA의 확장으로 볼 수 있음
  • 모든 점들 사이의 측지 거리를 유지하는 저차원 임베딩을 찾음
s_curve_isomap = manifold.Isomap(n_neighbors = 30, n_components = 2).fit_transform(s_curve)
digits_isomap = manifold.Isomap(n_neighbors = 30, n_components = 3).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_isomap, color, 121, '2d')
plot_digits(digits_isomap, labels, 122, '3d')

손글씨 데이터에서 위의 다른 분석에서는 한쪽에 몰려있던 것에 비해 측지거리를 유지한 모습을 확인할 수 있음

 

6. Multi Dimensional Scaling(MDS)

  • 고차원 공간에서의 거리를 고려하는 저차원 공간을 찾음
  • neighbors 개념이 없고 차원(components)만 지정
  • 거리를 각각 고려하며 동작하여 분석 속도가 느림
s_curve_mds = manifold.MDS(n_components = 2, random_state = 0).fit_transform(s_curve)
digits_mds = manifold.MDS(n_components = 3, random_state = 0).fit_transform(digits)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_mds, color, 121, '2d')
plot_digits(digits_mds, labels, 122, '3d')

s_curve 데이터에서는 s모양 유지, 손글씨 데이터에서는 거리를 유지하면서 골고루 퍼진 형태로 embedding

 

7. Spectral Embedding(SE)

  • 스펙트럼 분해를 통해 데이터의 저차원 표현을 찾음
  • 데이터의 점이 저차원 공간에서도 서로 가깝게 유지되도록 함
s_curve_se = manifold.SpectralEmbedding(n_components = 2, random_state = 0).fit_transform(s_curve)
digits_se = manifold.SpectralEmbedding(n_components = 3, random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_se, color, 121, '2d')
plot_digits(digits_se, labels, 122, '3d')

모양이 펼쳐져 있지만 서로 가까이 유지되는 형태가 유지됨

 

 

8. t-distributed Stochastic Neighbor Embedding(t-SNE)

  • 데이터 포인트의 유사성을 확률로 변환
  • 국소(local) 구조에 민감
  • 국소 구조를 기반으로 샘플 그룹을 추출하는데 강함
  • 항상 KL발산의 국소 최소값에서 끝남
    • 쿨백-라이블러 발산(Kullback–Leibler divergence, KLD)은 두 확률분포의 차이를 계산하는 데에 사용하는 함수로, 어떤 이상적인 분포에 대해, 그 분포를 근사하는 다른 분포를 사용해 샘플링을 한다면 발생할 수 있는 정보 엔트로피 차이를 계산, 상대 엔트로피(relative entropy), 정보 획득량(information gain), 인포메이션 다이버전스(information divergence)라고도 한다.
  • 계산 비용이 많이 듦(시간이 오래 걸림)
  • 전역 구조를 보존하지 않음
s_curve_tsne = manifold.TSNE(n_components = 2, random_state = 0).fit_transform(s_curve)
digits_tsne = manifold.TSNE(n_components = 3, random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_tsne, color, 121, '2d')
plot_digits(digits_tsne, labels, 122, '3d')

  • 단순히 s모양으로 된 데이터의 국소 구조를 유지한 채 임베딩이 된 모습
  • 포인트들 간의 유사성을 확률로 변환하는 알고리즘 동작 때문에 나온 분포
  • 손글끼는 데이터 포인트의 유사성을 확률로 변환하였기 때문에 잘 정리되어 임베딩된 모습

 

 

9. 정제된 표현을 이용한 학습

  • 다양체 학습의 결과를 정제된 데이터로 생각할 수 있음
  • 저차원 변환을 한 데이터를 다른 모델의 학습을 위한 데이터로 사용 가능
  • 정제된 표현이기 때문에 분석 비교적 용이함
  • 기계학습 모델의 입력으로 사용했을때 성능향상을 기대할 수 있음

 

  - 다른 모델에 활용 예시

# 다른 모델 라이브러리 불러오기
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

knn = KNeighborsClassifier()
svm = SVC()
decision_tree = DecisionTreeClassifier()
random_forest = RandomForestClassifier()


# 손글씨 데이터 새로 불러오기
raw_digits, target = datasets.load_digits(return_X_y = True)

 

  - 원본 데이터 사용 시

# KNN
score = cross_val_score(
    estimator = knn,
    X = raw_digits, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.94722222 0.95555556 0.96657382 0.98050139 0.9637883 ]
mean cross val score: 0.9627282575054161 (+/- 0.011168537355954218)


# SVM
score = cross_val_score(
    estimator = svm,
    X = raw_digits, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.96111111 0.94444444 0.98328691 0.98885794 0.93871866]
mean cross val score: 0.9632838130609718 (+/- 0.02008605863225686)


# Decision Tree
score = cross_val_score(
    estimator = decision_tree,
    X = raw_digits, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.77222222 0.73333333 0.79387187 0.82451253 0.78272981]
mean cross val score: 0.7813339523367379 (+/- 0.029700574831777498)


# Random Forest
score = cross_val_score(
    estimator = random_forest,
    X = raw_digits, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.94166667 0.90833333 0.96100279 0.96657382 0.92479109]
mean cross val score: 0.9404735376044568 (+/- 0.021831308367203855)

 

  - 정데된 데이터 사용 시

# KNN
score = cross_val_score(
    estimator = knn,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.96388889 0.96388889 0.98328691 0.99164345 0.96935933]
mean cross val score: 0.9744134942742185 (+/- 0.011159643225931395)


# SVM
score = cross_val_score(
    estimator = svm,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.95       0.93333333 0.98328691 0.98885794 0.96100279]
mean cross val score: 0.9632961931290621 (+/- 0.02065358544710109)


# Decision Tree
score = cross_val_score(
    estimator = decision_tree,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.96944444 0.91388889 0.98050139 0.9637883  0.93314763]
mean cross val score: 0.9521541318477251 (+/- 0.024752179064191634)


# Random Forest
score = cross_val_score(
    estimator = random_forest,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.95833333 0.91944444 0.99164345 0.99164345 0.97214485]
mean cross val score: 0.966641906530486 (+/- 0.02674722386771949)

 

  - 원본 데이터를 썼을 때보다 tsne로 정제된 데이터를 썼을 때 score가 0.1 내외의 차이로 상승되어 나옴

  - 특히 Decision Tree 알고리즘에서 tsne로 정제된 데이터를 썼을 때 큰 향상이 이루어짐

+ Recent posts