아래와 같은 순서로 책을 보시면 파이썬 => 판다스 => 머신러닝, 딥러닝을 공부할 수 있습니다. 제가 정리한 글에 있습니다.
https://steemit.com/kr/@papasmf1/73cj22
핸즈온 머신러닝 책의 소스는 아래의 깃허브에 있습니다. 참고하실 수 있습니다.
https://github.com/rickiepark/handson-ml
제가 테스트하고 실습하는 환경은 맥에 아나콘다 최근 패키지를 설치해서 주피터랩으로 실행하고 있습니다.
8장 차원 축소를 정리해 보았습니다.^^
많은 경우 머신러닝 문제는 훈련 샘플 각각이 수천 심지어 수백만 개의 특성을 가지고 있습니다. 이는 훈련을 느리게 할 뿐만 아니라, 앞으로 보게 되겠지만 앞으로 보게 되겠지만 좋은 솔루션을 찾기 어렵게 만듭니다. 이런 문제를 종종 차원의 저주 corse of dimensionality라고 합니다.
다행히도 실전문제에서는 특성 수를 크게 줄여서 불가능한 문제를 가능한 범위로 변경할 수 있는 경우가 많습니다.
차원을 축소시키면 일부 정보가 유실됩니다. 그래서 훈련 속도가 빨라질 수는 있지만 시스템의 성능이 조금 나빠질 수 있습니다. 그러므로 차원 축소를 고려하기 전에 훈련이 너무 느린지 먼저 원본 데이터로 시스템을 훈련시켜봐야 합니다.
8.1 차원의 저주
우리는 3차원 세계에서 살고 있어서 고차원 공간을 직관적으로 상상하기 어렵습니다.
이론적으로 차원의 저주를 해결하는 해결책 하나는 훈련 샘플의 밀도가 충분히 높아질 때까지 훈련 세트의 크기를 키우는 것입니다. 불행하게도 실제로는 일정 밀도에 도달하기 위해 필요한 훈련 샘플 수는 차원 수가 커짐에 따라 기하급수적으로 늘어납니다. 특성 수가 단 100개라고 생각해도, 모든 차원에 걸쳐 균일하게 퍼져 있다고 가정하고 훈련 샘플을 서로 평균 0.1 이내에 위치시키려면 관측 가능한 우주에 있는 원자 수 모두를 합친 것보다 더 많은 훈련 샘플을 모아야 합니다.
8.2 차원 축소를 위한 접근 방법
8.2.1 투영
대부분의 실전문제는 훈련 샘플이 모든 차원에 걸쳐 균일하게 퍼져 있지 않습니다. 결과적으로 모든 훈련 샘플이 사실 고차원 공간 안의 저차원 부분 공간에 놓여 있습니다.
(그림 참조)
8.2.2 매니폴드 학습
스위스 롤은 2D 매니폴드의 한 예입니다. 간단히 말해 2D 매니폴드는 고차원 공간에서 휘어지거나 뒤틀린 2D모양입니다.
많은 차원 축소 알고리즘이 훈련 샘플이 놓여 있는 매니폴드를 모델링하는 식으로 작동합니다. 이를 매니폴드 학습이라고 합니다. 이는 대부분 시제 고차원 데이터셋이 더 낮은 저차원 매니폴드에 가깝게 놓여 있다는 매니폴드 가정 또는 매니폴드 가설에 근거합니다.
요약하면 모델을 훈련시키기 전에 훈련 세트의 차원을 감소시키면 훈련 속도는 빨라지지만 항상 더 낫거나 간단한 솔루션이 되는 것은 아닙니다. 이는 전적으로 데이터셋에 달렸습니다.
8.3 PCA
주성분 분석 Principal Component Analysis는 가장 인기 있는 차원 축소 알고리즘입니다. 먼저 데이터에 가장 가까운 초평면을 정의한 다음, 데이터를 이 평면에 투영시킵니다.
8.3.1 분산 보존
저차원의 평면에 훈련 세트를 투영하기 전에 먼저 올바른 초평면을 선택해야 합니다.
8.3.2 주성분
PCA는 훈련 세트에서 분산이 최대인 축을 찾습니다.
from future import division, print_function, unicode_literals
import numpy as np
import os
np.random.seed(42)
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
plt.rcParams['font.family'] = 'NanumBarunGothic'
plt.rcParams['axes.unicode_minus'] = False
PROJECT_ROOT_DIR = "."
CHAPTER_ID = "dim_reduction"
def save_fig(fig_id, tight_layout=True):
path = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID, fig_id + ".png")
if tight_layout:
plt.tight_layout()
plt.savefig(path, format='png', dpi=300)
np.random.seed(4)
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1
angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
X = np.empty((m, 3))
X[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2
X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)
X_centered = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(X_centered)
c1 = Vt.T[:, 0]
c2 = Vt.T[:, 1]
8.3.3 d차원으로 투영하기
주성분을 모두 추출해냈다면 처음 d개의 주성분으로정의한 초평면에 투영하여 데이터셋의 차원을 d차원으로 축소시킬 수 있습니다.
다음 파이썬 코드는 첫 두개의 주성분으로 정의된 평면에 훈련 세트를 투영합니다.
W2 = Vt.T[:, :2]
X2D = X_centered.dot(W2)
PCA변환이 되었습니다! 지금까지 분산을 가능한 한 최대로 유지하면서 어떻게 데이터셋의 차원을 특정 차원으로 축소하는지 보았습니다.
8.3.4 사이킷런 사용하기
사이킷런의 PCA모델은 앞서 한 것처럼 SVD분해 방법을 사용하여 구현합니다. 다음은 PCA모델을 사용해 데이터셋의 차원을 2로 줄이는 코드입니다.
from sklearn.decomposition import PCA
pca = PCA(n_components = 2)
X2D = pca.fit_transform(X)
8.3.5 설명된 분산의 비율
explained_variance_ratio_변수에 저장된 주성분의 설명된 분산의 비율도 유용한 정보 중 하나입니다. 이 값은 각 주성분의 축을 따라 있는 데이터셋의 분산 비율을 나타냅니다.
pca.explained_variance_ratio_
array([0.84248607, 0.14631839])
8.3.6 적절한 차원 수 선택하기
축소할 차원 수를 임의로 정하기보다는 충분한 분산(에를 들면 95%)이 될 때까지 더해야 할 차원 수를 선택하는 쪽을 더 선호합니다.
다음 코드는 차원을 축소하지 않고 PCA를 계산한 뒤 훈련 세트의 분산을 95%로 유지하는 데 필요한 최소한의 차원 수를 계산합니다.
#3D 데이터셋, 평면 그리고 이 평면으로의 투영을 그립니다.
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(6, 3.8))
ax = fig.add_subplot(111, projection='3d')
X3D_above = X[X[:, 2] > X3D_inv[:, 2]]
X3D_below = X[X[:, 2] <= X3D_inv[:, 2]]
ax.plot(X3D_below[:, 0], X3D_below[:, 1], X3D_below[:, 2], "bo", alpha=0.5)
ax.plot_surface(x1, x2, z, alpha=0.2, color="k")
np.linalg.norm(C, axis=0)
ax.add_artist(Arrow3D([0, C[0, 0]],[0, C[0, 1]],[0, C[0, 2]], mutation_scale=15, lw=1, arrowstyle="-|>", color="k"))
ax.add_artist(Arrow3D([0, C[1, 0]],[0, C[1, 1]],[0, C[1, 2]], mutation_scale=15, lw=1, arrowstyle="-|>", color="k"))
ax.plot([0], [0], [0], "k.")
for i in range(m):
if X[i, 2] > X3D_inv[i, 2]:
ax.plot([X[i][0], X3D_inv[i][0]], [X[i][1], X3D_inv[i][1]], [X[i][2], X3D_inv[i][2]], "k-")
else:
ax.plot([X[i][0], X3D_inv[i][0]], [X[i][1], X3D_inv[i][1]], [X[i][2], X3D_inv[i][2]], "k-", color="#505050")
ax.plot(X3D_inv[:, 0], X3D_inv[:, 1], X3D_inv[:, 2], "k+")
ax.plot(X3D_inv[:, 0], X3D_inv[:, 1], X3D_inv[:, 2], "k.")
ax.plot(X3D_above[:, 0], X3D_above[:, 1], X3D_above[:, 2], "bo")
ax.set_xlabel("$x_1$", fontsize=18, labelpad=7)
ax.set_ylabel("$x_2$", fontsize=18, labelpad=7)
ax.set_zlabel("$x_3$", fontsize=18, labelpad=4)
ax.set_xlim(axes[0:2])
ax.set_ylim(axes[2:4])
ax.set_zlim(axes[4:6])
save_fig("dataset_3d_plot")
plt.show()
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
ax.plot(X2D[:, 0], X2D[:, 1], "k+")
ax.plot(X2D[:, 0], X2D[:, 1], "k.")
ax.plot([0], [0], "ko")
ax.arrow(0, 0, 0, 1, head_width=0.05, length_includes_head=True, head_length=0.1, fc='k', ec='k')
ax.arrow(0, 0, 1, 0, head_width=0.05, length_includes_head=True, head_length=0.1, fc='k', ec='k')
ax.set_xlabel("$z_1$", fontsize=18)
ax.set_ylabel("$z_2$", fontsize=18, rotation=0)
ax.axis([-1.5, 1.3, -1.2, 1.2])
ax.grid(True)
save_fig("dataset_2d_plot")