์ด๋ฒ ์ธ์ ์์๋ ์์ฐจ ํน์ฑ ์ ํ์ ํ๋ ๋ฐฉ๋ฒ๊ณผ ๋๋ค ํฌ๋ ์คํธ์์ ํน์ฑ ์ค์๋๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค. ๊ฐ๋ณผ๊น์?
1. ์์ฐจ ํน์ฑ ์ ํ ์๊ณ ๋ฆฌ์ฆ
๋ชจ๋ธ ๋ณต์ก๋๋ฅผ ์ค์ด๋ ๋ฐฉ๋ฒ์ Session 11์์ ์๊ฐํ์๋๋ฐ์, ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ํน์ฑ ์ ํ์ ํตํ ์ฐจ์ ์ถ์(dimensionality reduction)๊ฐ ์์ต๋๋ค. ๊ท์ ๊ฐ ์๋ ๋ชจ๋ธ์์ ์ ์ฉํ์ฃ . ์ฐจ์ ์ถ์์๋ ์ฃผ์ ์นดํ ๊ณ ๋ฆฌ์ธ ํน์ฑ ์ ํ(feature selection)๊ณผ ํน์ฑ ์ถ์ถ(feature extraction)์ด ์์ต๋๋ค.
ํน์ฑ ์ ํ์ ํน์ฑ ์ค์์ ์ ํํ๋ ๊ฒ์ด๊ณ , ์ถ์ถ์ ํน์ฑ์์ ์ป์ ์ ๋ณด๋ค๋ก ์ ํน์ฑ์ ๋ง๋๋ ๊ฒ์ ๋๋ค. ํน์ฑ ์ ํ์ ์์ด์ ์ค์ํ ๊ฒ์ ๋ฌธ์ ์ ๊ฐ์ฅ ๊ด๋ จ์ด ๋์ ํน์ฑ ๋ถ๋ถ์งํฉ์ ์๋์ ํํ๋ ๊ฒ์ ๋๋ค. ์ด๋ฒ์๋ ํน์ฑ ์ ํ ์๊ณ ๋ฆฌ์ฆ์ธ ์์ฐจ ํน์ฑ ์ ํ(sequential feature selection) ์๊ณ ๋ฆฌ์ฆ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์์ฐจ ํน์ฑ ์ ํ ์๊ณ ๋ฆฌ์ฆ์ ํ์ ์๊ณ ๋ฆฌ์ฆ(greedy search algorithm)์ผ๋ก d ์ฐจ์์ด์๋ ํน์ฑ๊ณต๊ฐ์ d๋ณด๋ค ์์ k ์ฐจ์์ผ๋ก ์ถ์์ํต๋๋ค.
์์ฐจ ํน์ฑ ์๊ณ ๋ฆฌ์ฆ ์ค ์ ํต์ ์ธ ๊ฒ์ ์์ฐจ ํ์ง ์ ํ(sequential backward selection, SBS)์ ๋๋ค. SBS๋ ์ด๊ธฐ ํน์ฑ์ ๋ถ๋ถ๊ณต๊ฐ์ผ๋ก ์ฐจ์์ ์ถ์์ํต๋๋ค.
SBS ์๊ณ ๋ฆฌ์ฆ์ ์ ํน์ฑ์ ๋ถ๋ถ๊ณต๊ฐ์ด ๋ชฉํํ ํน์ฑ ๊ฐ์๊ฐ ๋ ๋๊น์ง ์ ์ฒด ํน์ฑ์์ ์์ฐจ์ ์ผ๋ก ํน์ฑ์ ์ ๊ฑฐํฉ๋๋ค. ์ด๋ ํน์ฑ์ ์ ๊ฑฐ ๊ธฐ์ค์ ์ํด ์ต์ํํ ๊ธฐ์ค ํจ์๊ฐ ํ์ํฉ๋๋ค. ๊ธฐ์คํจ์์์ ๊ณ์ฐํ ๊ฐ์ ์ ๊ฑฐ ์ ํ์ ๋ชจ๋ธ์ ์ฑ๋ฅ ์ฐจ์ด์ ๋๋ค. ๊ฐ์ฅ ๊ธฐ์ค ๊ฐ์ด ํฐ ํน์ฑ์ ์ ๊ฑฐํ๊ฒ ๋๊ฒ ์ฃ . ๊ฐ๋จํ ๋ค ๋จ๊ณ๋ก ์ ๋ฆฌํด๋ณด๊ฒ ์ต๋๋ค.
- ์๊ณ ๋ฆฌ์ฆ์ k=d (d๋ ์ ์ฒด ํน์ฑ๊ณต๊ฐ์ ์ฐจ์)๋ก ์ด๊ธฐํํฉ๋๋ค.
- ์กฐ๊ฑด x = argmax J(Xk - x)๋ฅผ ์ต๋ํํ๋ ํน์ฑ x'๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
- ํน์ฑ ์งํฉ์์ ํน์ฑ x'๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
- k๊ฐ ๋ชฉํํ ๊ฐ์๊ฐ ๋๋ฉด ์ข ๋ฃํ๊ฑฐ๋ 2๋ก ๋์๊ฐ๋๋ค.
SBS ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ดํท๋ฐ์ ๊ตฌํ๋์ด์์ง ์์ต๋๋ค. ํ์ด์ฌ์ผ๋ก ์ง์ ๊ตฌํํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
from sklearn.base import clone
from itertools import combinations
import numpy as np
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
class SBS():
def __init__(self, estimator, k_features, scoring=accuracy_score,
test_size=0.25, random_state=1):
self.scoring = scoring
self.estimator = clone(estimator)
self.k_features = k_features
self.test_size = test_size
self.random_state = random_state
def fit(self, X, y):
X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=self.test_size,
random_state=self.random_state)
dim = X_train.shape[1]
self.indices_ = tuple(range(dim))
self.subsets_ = [self.indices_]
score = self._calc_score(X_train, y_train,
X_test, y_test, self.indices_)
self.scores_ = [score]
while dim > self.k_features:
scores = []
subsets = []
for p in combinations(self.indices_, r=dim - 1):
score = self._calc_score(X_train, y_train,
X_test, y_test, p)
scores.append(score)
subsets.append(p)
best = np.argmax(scores)
self.indices_ = subsets[best]
self.subsets_.append(self.indices_)
dim -= 1
self.scores_.append(scores[best])
self.k_score_ = self.scores_[-1]
return self
def transform(self, X):
return X[:, self.indices_]
def _calc_score(self, X_train, y_train, X_test, y_test, indices):
self.estimator.fit(X_train[:, indices], y_train)
y_pred = self.estimator.predict(X_test[:, indices])
score = self.scoring(y_test, y_pred)
return score
์ฌ๊ธฐ์ ๋ชฉํํ ํน์ฑ ๊ฐ์ k๋ k_feature ๋งค๊ฐ๋ณ์์ ๋๋ค. accuracy_score ํจ์๋ฅผ ์ฌ์ฉํด ๋ชจ๋ธ์ ์ฑ๋ฅ์ ํ๊ฐํ๊ณ , fit ๋ฉ์๋์ ๋ฐ๋ณต๋ฌธ ์์์ itertools.combination ํจ์์ ์ํด ์์ฑ๋ ํน์ฑ ์กฐํฉ์ ํ๊ฐํ๊ณ ์ค์ ๋๋ค. ๊ทธ๋ฆฌ๊ณ X_test ์ ๊ธฐ์ดํ ์กฐํฉ์ ์ ํ๋ ์ ์๋ฅผ self.scores_๋ฆฌ์คํธ์ ๋ชจ์๋๋ค. ์ด ์ ์๋ก ๋์ค์ ๊ฒฐ๊ณผ๋ฅผ ํ๊ฐํฉ๋๋ค. ์ต์ข ์ผ๋ก ๋ง๋ค์ด์ง ํน์ฑ์ ์ด ์ธ๋ฑ์ค๋ self.indices_์ ํ ๋น๋ฉ๋๋ค. ์ด๊ฒ์ transform์์ ์ ํ๋ ํน์ฑ์ผ๋ก ๊ตฌ์ฑ๋ ์๋ก์ด ๋ฐฐ์ด์ ๋ฐํํ ๋ ์ฐ์ ๋๋ค.
์ด์ ์ฌ์ดํท๋ฐ์ KNN ๋ถ๋ฅ๊ธฐ๋ฅผ ํตํด์ ํ์ธํด๋ณผ๊น์?
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5)
# ํน์ฑ์ ์ ํํฉ๋๋ค.
sbs = SBS(knn, k_features=1)
sbs.fit(X_train_std, y_train)
# ํน์ฑ ์กฐํฉ์ ์ฑ๋ฅ ๊ทธ๋ํ๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
k_feat = [len(k) for k in sbs.subsets_]
plt.plot(k_feat, sbs.scores_, marker='o')
plt.ylim([0.7, 1.02])
plt.ylabel('Accuracy')
plt.xlabel('Number of features')
plt.grid()
plt.tight_layout()
plt.show()
fit ์์์ SBS๊ฐ ๋ฐ์ดํฐ์ ์ ํ๋ จ๊ณผ ํ ์คํธ๋ก ๋๋๊ธฐ๋ ํ์ง๋ง ์ฌ์ ํ ์ด ์ฝ๋์์๋ X_train ๋ฐ์ดํฐ๋ง ์ฃผ์ ํฉ๋๋ค. ์ด๋ SBS์ fit ๋ฉ์๋๊ฐ ๋๋๋ ๋ฐ์ดํฐ์ ์ค ํ ์คํธ ์ธํธ๋ฅผ ๊ฒ์ฆ์ธํธ(validation set)์ด๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ์๋ ํ๋ จ ๋ฐ์ดํฐ์ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ถ๋ฆฌํด๋์์ผํฉ๋๋ค.
SBS๋ก ๊ฐ ๋จ๊ณ์์ ๊ฐ์ฅ ์ข์ ํน์ฑ์กฐํฉ์ ์ ์๋ฅผ ๋ชจ์๋์์ผ๋ฏ๋ก ์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ๊ฒ์ฆ ์ธํธ๋ก ๊ณ์ฐํ KNN ๋ถ๋ฅ๊ธฐ์ ์ ํ๋๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์ ๊ทธ๋ํ์์ ํ์ธํ ์ ์๋ฏ์ด KNN ๋ถ๋ฅ๊ธฐ์ ์ ํ๋๋ ํน์ฑ ๊ฐ์๊ฐ ์ค์์ ๋ ํฅ์๋ฉ๋๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด์ ์๋ ํ ์คํธ ์ธํธ์์์ KNN ๋ถ๋ฅ๊ธฐ ์ฑ๋ฅ์ ํ๊ฐํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
knn.fit(X_train_std, y_train)
print('ํ๋ จ ์ ํ๋:', knn.score(X_train_std, y_train))
print('ํ
์คํธ ์ ํ๋:', knn.score(X_test_std, y_test)
ํ๋ จ ์ธํธ์์๋ 97% ์ ๋์ ์ ํ๋๋ฅผ, ํ ์คํธ ์ธํธ์์๋ 96% ์ ๋์ ์ ํ๋๋ฅผ ๋ณด์ฌ์ฃผ๋ค์. ๊ทธ๋ผ ์ ํ๋ ์ธ ๊ฐ์ ํน์ฑ์์์ ์ฑ๋ฅ๋ ํ์ธํด๋ณผ๊น์?
knn.fit(X_train_std[:, k3], y_train)
print('ํ๋ จ ์ ํ๋:', knn.score(X_train_std[:, k3], y_train))
print('ํ
์คํธ ์ ํ๋:', knn.score(X_test_std[:, k3], y_test))
์ ์ฒด ํน์ฑ์ 1/4๋ ์๋๋ ํน์ฑ์ ์ฌ์ฉํ์ง๋ง ํ ์คํธ ์ธํธ์ ์ ํ๋๋ ํฌ๊ฒ ๋จ์ด์ก๋ค๊ณ ํ๊ธด ํ๋ค์ด๋ณด์ ๋๋ค. ์ด ์ธ ๊ฐ์ ํน์ฑ์ ํ๋ณ์ ๋ณด๊ฐ ์๋ ๋ฐ์ดํฐ์ ๋ณด๋ค ๊ทธ๋ฆฌ ์์ง ์๋ค๋ ๋ป์ ๋๋ค.
Wine ๋ฐ์ดํฐ์ ์ ์๋๋ ๊ทธ๋ฆฌ ํฌ์ง ์์ ๋ฐ์ดํฐ์ ์ด๋ผ ๋ฐ์ดํฐ์ ์ ํ๋ จ๊ณผ ํ ์คํธ๋ก ๋๋ ๊ฒ๊ณผ ๋ค์ ํ๋ จ๊ณผ ๊ฒ์ฆ์ผ๋ก ๋๋ ๊ฒ์ ์ํฅ์ ๋ง์ด ๋ฐ์ต๋๋ค.
์ฌ๊ธฐ์ ์ ์ ์๋ ์ ์ ํน์ฑ ๊ฐ์๋ฅผ ์ค์ด๋ ๊ฒ์ด KNN ๋ชจ๋ธ์ ์ฑ๋ฅ์ ๋์ด์ง ์์ง๋ง ํ ์ดํฐ ํฌ๊ธฐ๋ฅผ ์ค์ผ ์ ์์๋ค๋ ์ ์ด๊ณ , ๊ทธ ๋๋ฌธ์ ๋ ๊ฐ๋จํ ๋ชจ๋ธ์ ์ป์ ์ ์์๋ค๋ ์ ์ ๋๋ค.
2. ๋๋ค ํฌ๋ ์คํธ์ ํน์ฑ ์ค์๋ ์ฌ์ฉ
์ด์ ์ธ์ ์์ ์์๋ธ์ ์๊ฐํ ๋ ์ ๊น ๋ฑ์ฅํ๋ ๋๋ค ํฌ๋ ์คํธ๋ฅผ ๊ธฐ์ตํ์๋์? ๋๋ค ํฌ๋ ์คํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฒฐ์ ํธ๋ฆฌ์์ ๊ณ์ฐํ ํ๊ท ๋ถ์๋๋ฅผ ๊ฐ์์ํด์ผ๋ก์จ ํน์ฑ์ ์ค์๋๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ์ฌ์ดํท๋ฐ์์ RandomForestClassifier ๋ชจ๋ธ์ ํ๋ จํ๊ณ feature_importances_์์ฑ์์ ํน์ฑ ์ค์๋ ๊ฐ์ ํ์ธํ ์ ์์ต๋๋ค.
from sklearn.ensemble import RandomForestClassifier
feat_labels = df_wine.columns[1:]
forest = RandomForestClassifier(n_estimators=500,
random_state=1)
forest.fit(X_train, y_train)
importances = forest.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(X_train.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30,
feat_labels[indices[f]],
importances[indices[f]]))
plt.title('Feature Importance')
plt.bar(range(X_train.shape[1]),
importances[indices],
align='center')
plt.xticks(range(X_train.shape[1]),
feat_labels[indices], rotation=90)
plt.xlim([-1, X_train.shape[1]])
plt.tight_layout()
plt.show()
์ ์ฝ๋๋ฅผ ์คํํ๋ฉด ๊ฐ ํน์ฑ์ ์๋์ ์ค์๋์ ๋ฐ๋ฅธ ์์๋ฅผ ํ๋ก ๋ณด์ฌ์ค๋๋ค. ์ด ์ค์๋๋ ํฉ์ด 1์ด ๋๋๋ก ์ ๊ทํ ๋์ด์์ต๋๋ค. 500๊ฐ ๊ฒฐ์ ํธ๋ฆฌ์์ ๊ฐ์ฅ ํ๋ณ๋ ฅ์ด ์ข์ ํน์ฑ์ proline๋ถํฐ alcohol๊น์ง์ ๋๋ค. ์ด ๊ทธ๋ํ์์ ์์ ํน์ฑ ์ค ๋ ๊ฐ๋ ์์์ ๊ตฌํํ SBS ์๊ณ ๋ฆฌ์ฆ์์ ์ ํํ 3๊ฐ์ ํน์ฑ์ ๋ค์ด์์ต๋๋ค.
๋๋ค ํฌ๋ ์คํธ์์ ๋ ๊ฐ ์ด์์ ํน์ฑ์ด ์๋ก ์๊ด๊ด๊ณ๊ฐ ๊น๋ค๋ฉด, ํ๋๋ ์์ฃผ ์ ์ก์๋ด์ง๋ง ๋ค๋ฅธ ์ ๋ณด๋ ์ ์ฐพ์๋ด์ง ๋ชปํ ์ ์์ต๋๋ค. ๋ง์ฝ ํน์ฑ ์ค์๋ ๊ฐ๋ณด๋ค ๋ชจ๋ธ์ ์ฑ๋ฅ์๋ง ๊ด์ฌ์ด ์๋ค๋ฉด ๋ฌด์ํ์ ๋ ์ข์ต๋๋ค๋ง, ์์๋์ ๋ ์ข์ต๋๋ค.
์ฌ์ดํท๋ฐ์ SelectFromModel์ ๋ชจ๋ธ ํ๋ จ์ด ๋๋ ๋ค์์ ์ฌ์ฉ์๊ฐ ์ ํ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ํน์ฑ์ ์ ํํฉ๋๋ค. ๋์ค์ ๋ฑ์ฅํ Pipeline์ ๋จ๊ณ์์ RondomForestClassifier๋ฅผ ํน์ฑ ์ ํ๊ธฐ๋ก ์ฌ์ฉํ ๋ ์ ์ฉํฉ๋๋ค. ์๋ ์ฝ๋๋ ์ฌ์ฉ์ ์ง์ ๊ฐ, ์ฆ ์๊ณ๊ฐ์ 0.1๋ก ํด ํน์ฑ์ ์ค์ํ 5๊ฐ๋ก ์ค์ฌ์ค๋๋ค.
from sklearn.feature_selection import SelectFromModel
sfm = SelectFromModel(forest, threshold=0.1, prefit=True)
X_selected = sfm.transform(X_train)
print('์ด ์๊ณ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ํ์ ์:', X_selected.shape[1])
# ์ด ์๊ณ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ํ์ ์: 5
for f in range(X_selected.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30,
feat_labels[indices[f]],
importances[indices[f]]))
์ฌ๊ธฐ๊น์ง ์์ฐจ ํน์ฑ ์ ํ ์๊ณ ๋ฆฌ์ฆ๊ณผ ๋๋ค ํฌ๋ ์คํธ์ ๋ํด์ ์์๋ณด์์ต๋๋ค. ์ฃผ๋ก ์ฐจ์ ์ถ์๋ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๋ฅผ ์ค์ด๋ ๊ธฐ๋ฒ๋ค์ด์๋๋ฐ์, ๋ค์์ผ๋ก ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ ์ธ ๊ฐ์ ์ธ์ ๋ค์์๋ ์ฐจ์ ์ถ์๋ฅผ ์ฌ์ฉํ ๋ฐ์ดํฐ ์์ถ์ ๋ค๋ฃฐ ์์ ์ ๋๋ค. ๊ทธ๋ผ ์ ๋ ๋ค์ ์ธ์ ์์ PCA๋ฅผ ๋ค๊ณ ๋์์ค๋๋ก ํ๊ฒ ์ต๋๋ค. ๋ค์ ์ธ์ ์์ ๋ดฌ์!