● 다양체 학습
- 높은 차원의 데이터를 저차원으로 축소하는 방법

- 고차원 데이터를 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')

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')

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로 정제된 데이터를 썼을 때 큰 향상이 이루어짐
'Python > Machine Learning' 카테고리의 다른 글
[머신러닝 알고리즘] 추천 시스템 (1) | 2023.03.03 |
---|---|
[머신러닝 알고리즘] 분해 (0) | 2023.03.03 |
[머신러닝 알고리즘] 군집화 (1) | 2023.03.02 |
[머신러닝 알고리즘] XGBoost, LightGBM (0) | 2023.02.23 |
[머신러닝 알고리즘] 앙상블 (0) | 2023.02.18 |