๐ตโ๐ซ ์ฐจ์์ ์ ์ฃผ, ๋ฐ์ดํฐ๊ฐ ๋๋ฌด ๋ง์๋ ๋ฌธ์ ?
๋ฐ์ดํฐ์ ์ธ๊ณ๋ ์ข ์ข ๋๋ฌด ๋ง์ ์ ๋ณด๋ก ๊ฐ๋ ์ฐฌ ๊ฑฐ๋ํ ๋์๊ด๊ณผ ๊ฐ๋ค. ๋ชจ๋ ์ฑ (ํน์ฑ)์ ๋ค ์ฝ์ผ๋ ค๋ค ๊ธธ์ ์๊ธฐ ์ญ์์ด๋ค. ๋ชจ๋ธ ํ์ต๋ ๋ง์ฐฌ๊ฐ์ง๋ค. ํน์ฑ(์ฐจ์)์ด ๋๋ฌด ๋ง์์ง๋ฉด ์คํ๋ ค ์ฑ๋ฅ์ด ๋จ์ด์ง๋ โ์ฐจ์์ ์ ์ฃผโ ์ ๊ฑธ๋ฆฌ๊ฒ ๋๋ค.
๐ก โ์ฐจ์์ด ๋์ด๋ ์๋ก ๋ฐ์ดํฐ ๊ณต๊ฐ์ ๊ธ๊ฒฉํ ๋์ด์ง๊ณ , ๋ฐ์ดํฐ๋ ํฌ์(sparse)ํด์ง๋ค.โ
๋ชจ๋ธ์ด ํ์ตํ ๋ฐ์ดํฐ๋ ํ์ ์ ์ธ๋ฐ, ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ ๊ณต๊ฐ๋ง ๋์ด์ง๋ ํ์ต์ด ์ ๋๋ก ๋ ๋ฆฌ๊ฐ ์๋ค. ์ฐ์ฐ๋ ์ฆ๊ฐ๋ ๋ค์ด๋ค.
์ด ์ ์ฃผ๋ฅผ ํ๊ธฐ ์ํ ๋ง๋ฒ์ด ๋ฐ๋ก ์ฐจ์ ์ถ์์ด๊ณ , ์ค๋ ์ฐ๋ฆฌ๋ ๊ทธ์ค ๊ฐ์ฅ ๊ฐ๋ ฅํ ๋ง๋ฒ์ธ ์ฃผ์ฑ๋ถ ๋ถ์(PCA) ๊ณผ ์๊ฐํ์ ๋ฌ์ธ t-SNE๋ฅผ ๋ฐฐ์ ๋ค.
๐งโโ๏ธ ์ฃผ์ฑ๋ถ ๋ถ์(PCA), ๋ฐ์ดํฐ์ ๋ณธ์ง์ ์ฐพ์์
PCA๋ ๋ฐ์ดํฐ์ ํฉ์ด์ ธ ์๋ ์ฌ๋ฌ ํน์ฑ๋ค์ ์๊ด๊ด๊ณ๋ฅผ ํ์ ํ๊ณ , ์ด๋ค์ ๊ฐ์ฅ ์ ์ค๋ช ํ๋ ์๋ก์ด ์ถ, ์ฆ ์ฃผ์ฑ๋ถ(Principal Component) ์ ์ฐพ์๋ด๋ ๊ธฐ๋ฒ์ด๋ค. ๋จ์ํ ๋ช๋ช ํน์ฑ์ ๋ฒ๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ, ๊ธฐ์กด ํน์ฑ๋ค์ ์กฐํฉํ์ฌ ๋ฐ์ดํฐ์ ๋ถ์ฐ์ ๊ฐ์ฅ ์ ๋ณด์กดํ๋ ์๋ก์ด ํน์ฑ์ ๋ง๋ค์ด๋ด๋ ๊ฒ์ด๋ค.
์ ํ๋์ํ์ ๊ด์ ์์ PCA๋ ๋ฐ์ดํฐ์ ๊ณต๋ถ์ฐ ํ๋ ฌ์ ๊ณ ์ ๊ฐ ๋ถํดํ๋ ๊ฒ๊ณผ ๊ฐ๋ค. ์ด๋ ์ป์ด์ง๋ ๊ณ ์ ๋ฒกํฐ๊ฐ ๋ฐ์ดํฐ์ ๋ถ์ฐ์ด ๊ฐ์ฅ ํฐ ๋ฐฉํฅ์ ๋ํ๋ด๋ ์ฃผ์ฑ๋ถ์ด ๋๋ค. ์ด๋ ต๊ฒ ๋ค๋ฆฌ์ง๋ง, โ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฅ ์ ์ค๋ช ํ๋ ์๋ก์ด ์ถ์ ์ฐพ๋๋คโ ๋ ๊ฐ๋ ๋ง ๊ธฐ์ตํด๋ ์ถฉ๋ถํ๋ค!
๐ ๋ถ๊ฝ ๋ฐ์ดํฐ์ ์ผ๋ก PCA ์ค์ตํ๊ธฐ
๋ฐฑ๋ฌธ์ด ๋ถ์ฌ์ผ๊ฒฌ! Scikit-learn
์ ๋ถ๊ฝ ๋ฐ์ดํฐ์
์ผ๋ก PCA์ ์๋ ฅ์ ์ง์ ํ์ธํด๋ดค๋ค. 4๊ฐ์ ํน์ฑ์ 2๊ฐ๋ก ์ค์ฌ ์๋ณธ๊ณผ ์ฑ๋ฅ์ ๋น๊ตํ๋ค.
1. ๋ฐ์ดํฐ ์ค๋น ๋ฐ ์ค์ผ์ผ๋ง
๋จผ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ ํ์คํ๋ฅผ ์งํํ๋ค. PCA๋ ๋ฐ์ดํฐ์ ์ค์ผ์ผ์ ์ํฅ์ ๋ฐ๊ธฐ ๋๋ฌธ์ ์ค์ผ์ผ๋ง์ ํ์์ ์ธ ์ ์ฒ๋ฆฌ ๊ณผ์ ์ด๋ค.
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# ๋ฐ์ดํฐ ๋ก๋ ๋ฐ ์ค์ผ์ผ๋ง
iris = load_iris()
scaler = StandardScaler()
iris_scaled = scaler.fit_transform(iris.data)
2. PCA ์ ์ฉ ๋ฐ ์ฑ๋ฅ ๋น๊ต
4๊ฐ์ ํน์ฑ์ ๋จ 2๊ฐ์ ์ฃผ์ฑ๋ถ์ผ๋ก ์ค์ฌ๋ดค๋ค.
# 2์ฐจ์์ผ๋ก PCA ๋ณํ
pca = PCA(n_components=2)
iris_pca = pca.fit_transform(iris_scaled)
# ๋๋ค ํฌ๋ ์คํธ ๋ถ๋ฅ๊ธฐ๋ก ์ฑ๋ฅ ๋น๊ต
rcf = RandomForestClassifier(random_state=156)
# ์๋ณธ ๋ฐ์ดํฐ ๊ต์ฐจ ๊ฒ์ฆ
scores = cross_val_score(rcf, iris.data, iris.target, scoring='accuracy', cv=3)
print(f'์๋ณธ ๋ฐ์ดํฐ ํ๊ท ์ ํ๋: {np.mean(scores):.2f}')
# PCA ๋ฐ์ดํฐ ๊ต์ฐจ ๊ฒ์ฆ
scores_pca = cross_val_score(rcf, iris_pca, iris.target, scoring='accuracy', cv=3)
print(f'PCA ๋ณํ ๋ฐ์ดํฐ ํ๊ท ์ ํ๋: {np.mean(scores_pca):.2f}')
๊ฒฐ๊ณผ: ์๋ณธ ๋ฐ์ดํฐ ํ๊ท ์ ํ๋๋
0.96
, PCA ๋ณํ ๋ฐ์ดํฐ ํ๊ท ์ ํ๋๋0.89
๊ฐ ๋์๋ค.
4๊ฐ์ ํน์ฑ์ ๋จ 2๊ฐ๋ก ์ค์๋๋ฐ๋ ์ฑ๋ฅ ์ ํ๊ฐ ํฌ์ง ์์๋ค. PCA๊ฐ ์ ๋ณด ์์ค์ ์ต์ํํ๋ฉฐ ์ฐจ์์ ํจ๊ณผ์ ์ผ๋ก ์ถ์ํ์์ ๋ณด์ฌ์ค๋ค. ์ฑ๊ณต! ๐
๐จ ๋งค๋ํด๋ ํ์ต๊ณผ t-SNE๋ก ์๊ฐํํ๊ธฐ
PCA๊ฐ ๋ฐ์ดํฐ์ ์ ์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ๋ณด์กดํ๋ ์ ํ์ ์ธ ๋ฐฉ๋ฒ์ด๋ผ๋ฉด, t-SNE๋ ์ด์ ๋ฐ์ดํฐ ๊ฐ์ ๊ด๊ณ๋ฅผ ๋ณด์กดํ๋ฉฐ ๊ณ ์ฐจ์ ๋ฐ์ดํฐ๋ฅผ 2์ฐจ์์ด๋ 3์ฐจ์์ผ๋ก ์๊ฐํํ๋ ๋ฐ ํ์ํ ๋น์ ํ ๊ธฐ๋ฒ์ด๋ค.
Scikit-learn
์ ์ซ์ ๋ฐ์ดํฐ์
(64์ฐจ์)์ t-SNE๋ก ์๊ฐํํด๋ดค๋ค.
from sklearn.datasets import load_digits
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects
# ๋ฐ์ดํฐ ๋ก๋ ๋ฐ t-SNE ๋ณํ
digits = load_digits()
tsne = TSNE(n_components=2, init='pca', random_state=123)
X_digits_tsne = tsne.fit_transform(digits.data)
# t-SNE ๊ฒฐ๊ณผ ์๊ฐํ
def plot_projection(x, colors):
f = plt.figure(figsize=(8,8))
ax = plt.subplot(aspect='equal')
for i in range(10):
plt.scatter(x[colors==i, 0], x[colors==i, 1])
for i in range(10):
xtext, ytext = np.median(x[colors==i, :], axis=0)
txt = ax.text(xtext, ytext, str(i), fontsize=24)
txt.set_path_effects([PathEffects.Stroke(linewidth=5, foreground="w"), PathEffects.Normal()])
plot_projection(X_digits_tsne, digits.target)
plt.show()
๊ฒฐ๊ณผ๋ฅผ ๋ณด๋, ๊ฐ์ ์ซ์๋ผ๋ฆฌ ์น๊ธฐ์ข ๊ธฐ ๋ชจ์ฌ ๊ตฐ์ง์ ์ด๋ฃจ๋ ๋ชจ์ต์ด ์ ๋ง ์ ๊ธฐํ๋ค. ๋ฐ์ดํฐ์ ์จ๊ฒจ์ง ๊ตฌ์กฐ๋ฅผ ํ๋์ ํ์ ํ ์ ์์๋ค.
๐ ๏ธ ํ์ดํ๋ผ์ธ์ผ๋ก PCA์ ๋ชจ๋ธ๋ง์ ํ๋ฒ์!
์์ค์ฝ์ ์ ๋ฐฉ์ ๋ฐ์ดํฐ์
(ํน์ฑ 30๊ฐ)์ make_pipeline
์ผ๋ก ์ ์ฒ๋ฆฌ, ์ฐจ์ ์ถ์, ๋ชจ๋ธ ํ์ต์ ํ๋ฒ์ ์ฒ๋ฆฌํ๋ค.
from sklearn.datasets import load_breast_cancer
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# ๋ฐ์ดํฐ ๋ก๋ ๋ฐ ๋ถํ
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, test_size=0.2, random_state=42
)
# ํ์ดํ๋ผ์ธ ์์ฑ ๋ฐ ํ์ต
pipe = make_pipeline(StandardScaler(), PCA(n_components=2), LogisticRegression())
pipe.fit(X_train, y_train)
print(f"Train Score: {pipe.score(X_train, y_train):.4f}")
print(f"Test Score: {pipe.score(X_test, y_test):.4f}")
๊ฒฐ๊ณผ: 30๊ฐ์ ํน์ฑ์ ๋จ 2๊ฐ๋ก ์ค์๋๋ฐ๋ ํ ์คํธ ์ ํ๋๊ฐ 97.37% ๋ผ๋ ๋๋ผ์ด ์ฑ๋ฅ์ ๋ณด์ฌ์คฌ๋ค. ํ์ดํ๋ผ์ธ ๋๋ถ์ ์ฝ๋๊ฐ ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ์ฒด๊ณ์ ์ผ๋ก ๋ณํ๋ค.
โจ ์ค๋์ ํ๊ณ
์ค๋์ ๋น์ง๋ ํ์ต์ ๋ํ์ ์ธ ์ฐจ์ ์ถ์ ๊ธฐ๋ฒ์ธ PCA์ t-SNE๋ฅผ ๊น์ด ์๊ฒ ๋ฐฐ์ ๋ค. ๋จ์ํ ํน์ฑ ๊ฐ์๋ฅผ ์ค์ด๋ ๊ฒ์ ๋์ด, ๋ฐ์ดํฐ์ ๋ถ์ฐ์ ์ต๋ํ ๋ณด์กดํ๋ โ์ฃผ์ฑ๋ถโ์ ์ฐพ๋ PCA์ ์๋ฆฌ์, ๋ฐ์ดํฐ์ ๊ตญ์์ ๊ตฌ์กฐ๋ฅผ ๋ณด์กดํ๋ฉฐ ์๊ฐํํ๋ t-SNE์ ๊ฐ๋ ฅํจ์ ์ง์ ํ์ธํ๋ค.
๋ถ๊ฝ, MNIST, ์ ๋ฐฉ์ ๋ฐ์ดํฐ์ ๋ฑ ๋ค์ํ ์ค์ต์ ํตํด ์ฐจ์์ ํฌ๊ฒ ์ค์ฌ๋ ๋ชจ๋ธ ์ฑ๋ฅ์ด ํฌ๊ฒ ์ ํ๋์ง ์๋ ๊ฒ์ ๋ณด๊ณ ์ฐจ์ ์ถ์์ ์๋ ฅ์ ์ค๊ฐํ๋ค. ํนํ ํ์ดํ๋ผ์ธ์ ์ฌ์ฉํ๋ ๋ณต์กํ ๋จธ์ ๋ฌ๋ ์ํฌํ๋ก์ฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์๋ค.
์์ผ๋ก ๋ณต์กํ ๋ฐ์ดํฐ์ ์ ๋ง์ฃผํ์ ๋, ๋ฌด์์ ๋ชจ๋ธ์ ๋ฃ๊ธฐ ์ ์ PCA์ t-SNE๋ฅผ ํตํด ๋ฐ์ดํฐ์ ๋ณธ์ง์ ํ์ ํ๊ณ ํจ์จ์ ์ผ๋ก ๋ถ์ํ๋ ์ต๊ด์ ๋ค์ฌ์ผ๊ฒ ๋ค. ์ฐจ์์ ์ ์ฃผ, ์ด์ ๋๋ ต์ง ์๋ค! ๐