๋ณธ์ ์๋๊ฒ ์ค๋๋ง์ ๋ผ์ดํธ ๋จธ์ ๋ฌ๋ ๊ธ์ ์ฌ๋ฆฌ๊ฒ ๋์๋ค์! ์ด๋ฒ ์ธ์ ์์๋ ์ ๋ฒ์ ์๊ณ ํ๋ค์ถ์ด ์์๋ธ ํ์ต์ ๋ํด์ ์์๋ณด๋ ค๊ณ ํ๋๋ฐ์, ์ฌ๋ฌ๋ถ์ ์์๋ธํ๋ฉด ๋ฌด์์ด ๋ ์ค๋ฅด์๋์? ์์งํ ์ ๋ ์์ ์ด ๊ฐ์ฅ ๋จผ์ ๋ ์ฌ๋๋๋ฐ์, ์ฌ์ค ๋น์ทํ ๊ฐ๋ ์ด๊ธฐ๋ ํฉ๋๋ค.
์ด๋ฒ ์ธ์ ์์๋ ์ฌ๋ฌ ๊ฐ ๋ถ๋ฅ๊ธฐ๋ฅผ ํฉ์ณ ์ข์ ์ฑ๋ฅ์ ๋ด๋ ์์๋ธ ํ์ต์ ์ ์์ ์ข ๋ฅ ์ค ํ๋์ธ ๋ค์๊ฒฐ ํฌํ์ ๋ํด์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!
A. ์์๋ธ ํ์ต
์์๋ธ ํ์ต(ensemble learning)์ ์ฌ๋ฌ ๊ฐ์ ๋ถ๋ฅ๊ธฐ๋ฅผ ํ๋์ ๋ฉํ ๋ถ๋ฅ๊ธฐ๋ก ์ฐ๊ฒฐํด์ ๋ ์ข์ ์ฑ๋ฅ์ ์ด๋์ด๋ด๋ ๊ธฐ๋ฒ์ ๋๋ค. ์์๋ธ ํ์ต์ ์ํ ๋ถ๋ฅ๊ธฐ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ต๋๋ค. ์ผ๋จ ๋จผ์ , ์์๋ธ์ ์๋ ์๋ฆฌ์ ์ ๋ ์ฑ๋ฅ์ด ์ข์์ง์ ๋ํด์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋จผ์ , ๊ฐ์ฅ ์ธ๊ธฐ์๋ ์์๋ธ ๋ฐฉ๋ฒ์ธ ๊ณผ๋ฐ์ ํฌํ(majority voting) ๋ฐฉ์์ ๋ํด์ ์ด์ผ๊ธฐํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด ๋ถ๋ฅ๊ธฐ์ ๊ณผ๋ฐ์๊ฐ ์์ธกํ ํด๋์ค ๋ ์ด๋ธ์ ์ ํํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
๊ณผ๋ฐ์ ํฌํ๋ ์ด์ค ํด๋์ค ๋ถ๋ฅ์ ํด๋นํ์ง๋ง ๋ค์ค ํด๋์ค ๋ฌธ์ ์์๋ ์ผ๋ฐํ๊ฐ ๊ฐ๋ฅํฉ๋๋ค. ์ด๋ฅผ ๋ค์๊ฒฐ ํฌํ(plurality voting)์ด๋ผ๊ณ ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ์๋ ์ต๋น๊ฐ์ ์ ํํฉ๋๋ค. ์๋ ๊ทธ๋ฆผ์์ ๊ณผ๋ฐ์ ํฌํ์ ๋ค์๊ฒฐ ํฌํ์ ๊ฐ๋ ์ ํ์ธํ์ค ์ ์์ต๋๋ค. ๊ฐ๊ฐ์ ๋ชจ์์ ํด๋์ค ๋ ์ด๋ธ์ ๋ํ๋ ๋๋ค.
ํ๋ จ ์ธํธ๋ฅผ ์ฌ์ฉํด m๊ฐ์ ๋ค๋ฅธ ๋ถ๋ฅ๊ธฐ๋ฅผ ํ๋ จ์ํต๋๋ค. ์ด๊ฒ์ C๋ผ๊ณ ํ ๊ฒ์. ๊ทธ๋ฆฌ๊ณ ์์๋ธ ๋ฐฉ๋ฒ์ ๋ฐ๋ผ ์ฌ๋ฌ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํด ๊ตฌ์ถ์ ํ๊ฑฐ๋ ๋ถ๋ฅ ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ๊ณ ํ๋ จ ์ธํธ์ ๋ถ๋ถ ์งํฉ์ ๋ฌ๋ฆฌํด ํ์ตํ ์๋ ์์ต๋๋ค. ์ ๋ช ํ ์์๋ธ ๋ฐฉ๋ฒ ์ค ํ๋๋ ๋๋ค ํฌ๋ ์คํธ(random forest)์ ๋๋ค.
๊ณผ๋ฐ์ ํฌํ๋ ๋ค์๊ฒฐ ํฌํ๋ก ์์ธกํ๋ ค๋ฉด ๊ฐ๋ณ ๋ถ๋ฅ๊ธฐ์ ์์ธก ๋ ์ด๋ธ์ ๋ชจ์์ ๊ฐ์ฅ ๋ง์ ํ๋ฅผ ๋ฐ์ ๋ ์ด๋ธ y hat์ ์ ํํฉ๋๋ค. ์์ผ๋ก ๋ํ๋ด๋ฉด ๋๊ฐ ์๋์ ๊ฐ์ต๋๋ค.
์๋ฅผ ๋ค์ด ํด๋์ค 1์ด -1์ด๊ณ ํด๋์ค 2๊ฐ +1์ด๋ฉด ๊ณผ๋ฐ์ ํฌํ ์์ธก์ ์๋์ ๊ฐ์ต๋๋ค.
์, ๊ทธ๋ผ ์์๋ธ ํ์ต์ด ์ ๋ ์ฑ๋ฅ์ด ์ข์์ง๋ฅผ ์ค๋ช ํ๊ธฐ ์ํด ์กฐํฉ ์ด๋ก ์ ์ ์ฉํด๋ณด๊ฒ ์ต๋๋ค. ์๋ฅผ ๋ค์ด์, ์ด์ง ๋ถ๋ฅ ์์ ์ ํ ๋ ๋์ผํ ์๋ฌ์จ e๋ฅผ ๊ฐ์ง n๊ฐ์ ๋ถ๋ฅ๊ธฐ๋ฅผ ์๊ฐํ๋ณด๊ฒ ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ๋ถ๋ฅ๊ธฐ๋ ์๋ก์๊ฒ ์ํฅ์ ์ฃผ์ง ์๊ณ ์๊ด๊ด๊ณ ์์ด ๋ ๋ฆฝ์ ์ด๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด ์ด ๋ถ๋ฅ๊ธฐ๋ค์ ์์๋ธ์ด ๋ง๋๋ ์ค์ฐจ ํ๋ฅ ์ ์ดํญ ๋ถํฌ์ ํ๋ฅ ์ง๋ ํจ์๋ก ๋ํ๋ผ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ ๋ ์ดํญ ๊ณ์๋ก n๊ฐ ์์์์ k๊ฐ๋ฅผ ๋ฝ๋ ์กฐํฉ์ ๋๋ค. ์ด ์์ ์์๋ธ์ด ํ๋ฆด ํ๋ฅ ์ ๊ณ์ฐํฉ๋๋ค. ์ข ๋ ๊ตฌ์ฒด์ ์ธ ์๋ก, ์๋ฌ์จ e๊ฐ 0.25์ธ ๋ถ๋ฅ๊ธฐ 11๊ฐ๋ก ๊ตฌ์ฑ๋ ์์๋ธ์ ์๋ฌ์จ์ ์๋์ ๊ฐ์ต๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด ๊ฐ๊ฐ์ ๋ถ๋ฅ๊ธฐ์ ์๋ฌ์จ 0.25๋ณด๋ค ์์๋ธ ๋ถ๋ฅ๊ธฐ์ ์๋ฌ์จ 0.034์ ์๋ฌ์จ์ด ํ์ฐํ ๋ฎ์ ๊ฒ์ ํ์ธํ์ค ์ ์์ต๋๋ค! ๋ง์ผ ์๋ฌ์จ์ด 0.5์ธ ๋ถ๋ฅ๊ธฐ๊ฐ ์ง์ ๊ฐ๋ผ ์์ธก์ด ๋ฐ๋ฐ์ผ๋ก ๋๋๋ฉด ์๋ฌ๋ก ์ทจ๊ธ๋ฉ๋๋ค. ์ผ๋จ, ์ด์์ ์์๋ธ ๋ถ๋ฅ๊ธฐ์ ๋ค์ํ ๋ฒ์์ ๋ถ๋ฅ๊ธฐ๋ฅผ ๊ฐ์ง ๊ฒฝ์ฐ๋ฅผ ๋น๊ตํ๊ธฐ ์ํด ํ๋ฅ ์ง๋ ํจ์๋ฅผ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
from scipy.special import comb
import math
def ensemble_error(n_classifier, error):
k_start = int(math.ceil(n_classifier / 2.))
probs = [comb(n_classifier, k) * error**k * (1-error)**(n_classifier - k)
for k in range(k_start, n_classifier + 1)]
return sum(probs)
ensemble_error(n_classifier=11, error=0.25)
ensemble_error ํจ์๋ฅผ ๊ตฌํํ ํ์ ๋ถ๋ฅ๊ธฐ ์๋ฌ๊ฐ 0.0๋ถํฐ 1.0 ์ฌ์ด์ ์์ ๋ ์์๋ธ ์๋ฌ์จ์ ๊ณ์ฐํ๊ฒ ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์์๋ธ๊ณผ ๊ฐ๋ณ ๋ถ๋ฅ๊ธฐ ์๋ฌ ์ฌ์ด ๊ด๊ณ๋ฅผ ์๊ฐํ ํด๋ณด๊ฒ ์ต๋๋ค.
import numpy as np
error_range = np.arange(0.0, 1.01, 0.01)
ens_errors = [ensemble_error(n_classifier=11, error=error)
for error in error_range]
import matplotlib.pyplot as plt
plt.plot(error_range,
ens_errors,
label='Ensemble error',
linewidth=2)
plt.plot(error_range,
error_range,
linestyle='--',
label='Base error',
linewidth=2)
plt.xlabel('Base error')
plt.ylabel('Base/Ensemble error')
plt.legend(loc='upper left')
plt.grid(alpha=0.5)
plt.show()
์์ ๊ฒฐ๊ณผ ๊ทธ๋ํ์์ ๋ณผ ์ ์๋ฏ ์์๋ธ์ ์๋ฌ ํ๋ฅ ์ ๊ฐ๋ณ ๋ถ๋ฅ๊ธฐ๋ณด๋ค ํญ์ ์ข์ต๋๋ค. ํ์ง๋ง, ๊ฐ๋ณ ๋ถ๋ฅ๊ธฐ์ ์ฑ๋ฅ, ์๋ฌ์จ์ด 0.5๋ณด๋ค ๋ฎ์์ผํ๋ค๋ ์กฐ๊ฑด์ด ์์ต๋๋ค.
B. ๋ค์๊ฒฐ ํฌํ๋ฅผ ์ฌ์ฉํ ๋ถ๋ฅ ์์๋ธ
1. ๊ฐ๋จํ ๋ค์๊ฒฐ ํฌํ ๋ถ๋ฅ๊ธฐ ๊ตฌํ
๋ค์๊ฒฐ ํฌํ์์๋ ๋ถ๋ฅ ๋ชจ๋ธ์ ์ ๋ขฐ๋์ ๊ฐ์ค์น๋ฅผ ๋ถ์ฌํด ์ฐ๊ฒฐํฉ๋๋ค. ์ผ๋จ ์ํ์ ์ผ๋ก ํํํ ๊ฐ์ค์น๊ฐ ์ ์ฉ๋ ๋ค์๊ฒฐ ํฌํ๋ ์๋์ ๊ฐ์ด ์ธ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ w๋ c๋ผ๋ ๋ถ๋ฅ๊ธฐ์ ์ฐ๊ด๋ ๊ฐ์ค์น์ด๊ณ , y hat์ ํญ์ ๊ทธ๋ซ๋ฏ์ด ์์๋ธ์ด ์์ธกํ ํด๋์ค ๋ ์ด๋ธ์ ๋๋ค. ๋ค๋ฌธ์ X ์ฒ๋ผ ์๊ธด ์นด์ด๋ ํน์ฑํจ์๋ฅผ ์๋ฏธํฉ๋๋ค. ํน์ฑํจ์๋ ์๋์ ๊ฐ์ต๋๋ค.
์ด ํน์ฑํจ์๋ฅผ ์ด์ฉํด์ ๊ฐ์ค์น๊ฐ ๋์ผํ๋ค๋ ๊ฐ์ ํ์ ์์ ๋์๋ ๋ค์๊ฒฐ ํฌํ์ ์์ ๊ฐ๋จํ๊ฒ ์ธ ์ ์์ต๋๋ค.
argmax์ bincountํจ์๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ์ค์น๊ฐ ์ ์ฉ๋ ๋ค์๊ฒฐ ํฌํ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค. bincountํจ์๋ 0 ์ด์์ ์ ์๋ก ๋ ๋ฐฐ์ด์ ์ ๋ ฅ๋ฐ์ ๊ฐ ์ ์๊ฐ ๋ฑ์ฅํ๋ ํ์๋ฅผ ์นด์ดํธํฉ๋๋ค. ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
import numpy as np
np.argmax(np.bincount([0,0,1], weights=[0.2,0.2,0.6]))
์ด์ ์ธ์ ์์ ์ด์ผ๊ธฐ ํ๋ ์ ์ด ์๋๋ฐ, ์ฌ์ดํท๋ฐ์ ์ผ๋ถ ๋ถ๋ฅ๊ธฐ๋ predict_proba ๋ฉ์๋๋ก ์์ธก ํด๋์ค ๋ ์ด๋ธ ํ๋ฅ ์ ๋ฐํํ ์ ์์ต๋๋ค. ์์๋ธ ๋ถ๋ฅ๊ธฐ๊ฐ ๋ณด์ ์ด ์ ๋์ด์๋ค๋ฉด ํด๋์ค ๋ ์ด๋ธ ๋์ ์์ธก ํด๋์ค ํ๋ฅ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค๊ณ ํฉ๋๋ค. ํ๋ฅ ์ ์ด์ฉํ ๋ค์๊ฒฐ ํฌํ๋ ์๋ ์๊ณผ ๊ฐ์ต๋๋ค.
์ฌ๊ธฐ์ p๋ ํด๋์ค๋ ์ด๋ธ i์ ๋ํ j๋ฒ์งธ ๋ถ๋ฅ๊ธฐ์ ์์ธก ํ๋ฅ ์ ๋๋ค. ๋ํ์ด์ average์ argmax๋ฅผ ์ด์ฉํด์ ํด๋์ค ํ๋ฅ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ์ค์น ์ ์ฉ ๋ค์๊ฒฐ ํฌํ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
ex = np.array([0.9,0.1], [0.8,0.2], [0.4,0.6])
p = np.average(ex, axis=0, weights=[0.2, 0.2, 0.6])
print(p)
np.argmax(p)
์์ ์น๊ตฌ๋ค์ ํจ์ณ์ MajorityVoteClassifier ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
from sklearn.base import BaseEstimator
from sklearn.base import ClassifierMixin
from sklearn.preprocessing import LabelEncoder
from sklearn.externals import six
from sklearn.base import clone
from sklearn.pipeline import _name_estimators
import numpy as np
import operator
class MajorityVoteClassifier(BaseEstimator, ClassifierMixin):
def __init__(self, classifiers,
vote='classlabel', weights=None):
#vote์ ๊ธฐ๋ณธ๊ฐ์ classlabel. classlabel์ด๋ฉด ๋ค์์ธ ํด๋์ค ๋ ์ด๋ธ ์ธ๋ฑ์ค, 'probability'์ด๋ฉด ํ๋ฅ ํฉ์ด ๊ฐ์ฅ ํฐ ์ธ๋ฑ์ค๋ก ์์ธก
#weight๋ ๋ฐฐ์ดํ์
. ๊ธฐ๋ณธ๊ฐ None
#Classifier์ ์์๋ธ์ ์ฌ์ฉํ ๋ถ๋ฅ๊ธฐ
self.classifiers = classifiers
self.named_classifiers = {key: value for key, value in _name_estimators(classifiers)}
self.vote = vote
self.weights = weights
def fit(self, X, y):
#X = ํ๋ จ ์ํ ํ๋ ฌ.
#y =
self.lablenc_ = LabelEncoder()
self.lablenc_.fit(y)
self.classes_ = self.lablenc_.classes_
self.classifiers_ = []
for clf in self.classifiers:
fitted_clf = clone(clf).fit(X, self.lablenc_.transform(y))
self.classifiers_.append(fitted_clf)
return self
์ ํด๋์ค๋ sklearn.base์ BaseEstimator ์ ClassifierMixin ํด๋์ค๋ฅผ ์์ํ์ฌ ๊ธฐ๋ณธ์ ๊ธฐ๋ฅ์ ๊ฐ์ถ๊ณ , ์ฌ๊ธฐ์๋ ๋ถ๋ฅ๊ธฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ค์ ๋ฐ ๋ฐํํ๋ get_params์ set_params ๋ฉ์๋์ ์์ธก ์ ํ๋๋ฅผ ๊ณ์ฐํ๋ score ๋ฉ์๋๊ฐ ํฌํจ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ predict ๋ฉ์๋๋ฅผ ๋ง๋ญ๋๋ค. ์ด ๋ฉ์๋๋ vote='classlabel'๋ก MajorityVoteClassifier ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ง๋ฉด ํด๋์ค ๋ ์ด๋ธ ๊ธฐ๋ฐ ๋ค์๊ฒฐ ํฌํ๋ฅผ ์ฌ์ฉํด ์์ธกํฉ๋๋ค. vote='probability'๋ก ๋ง๋ค์ด์ง๋ฉด ํ๋ฅ ๊ธฐ๋ฐ ํด๋์ค ๋ ์ด๋ธ์ ์์ธกํฉ๋๋ค.
๊ทธ๋ผ ๊ณ์ํด์ predict ๋ฉ์๋์ predict_proba ๋ฉ์๋๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
def predict(self, X):
"""
X : ์ํ ๋ฐ์ดํฐ ํ๋ ฌ
maj_vote : ์์ธก๋ ํด๋์ค ๋ ์ด๋ธ
"""
if self.vote == 'probability':
maj_vote = np.argmax(self.predict_proba(X),axis=1)
else: # 'classlabel' ํฌํ
# clf.predict ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์๋๋ค.
predictions = np.asarray([clf.predict(X) for clf in self.classifiers_]).T
maj_vote = np.apply_along_axis(
lambda x:
np.argmax(np.bincount(x, weights=self.weights)),
axis=1,
arr=predictions)
maj_vote = self.lablenc_.inverse_transform(maj_vote)
return maj_vote
def predict_proba(self, X):
"""
X : ์ํ ๋ฐ์ดํฐ ํ๋ ฌ
avg_proba : ์ํ๋ง๋ค ๊ฐ์ค์น๊ฐ ์ ์ฉ๋ ํด๋์ค์ ํ๊ท ํ๋ฅ
"""
probas = np.asarray([clf.predict_proba(X)
for clf in self.classifiers_])
avg_proba = np.average(probas, axis=0, weights=self.weights)
return avg_proba
def get_params(self, deep=True):
""" GridSearch๋ฅผ ์ํด ๋ถ๋ฅ๊ธฐ ๋งค๊ฐ๋ณ์ ์ด๋ฆ์ ๋ฐํ """
if not deep:
return super(MajorityVoteClassifier,
self).get_params(deep=False)
else:
out = self.named_classifiers.copy()
for name, step in\
six.iteritems(self.named_classifiers):
for key, value in six.iteritems(
step.get_params(deep=True)):
out['%s__%s' % (name, key)] = value
return out
์์๋ธ์ ์๋ ๋ถ๋ฅ๊ธฐ์ ๋งค๊ฐ๋ณ์๋ค์ ๊ฐ๊ฐ ์ ๊ทผํ๊ธฐ ์ํด _name_estimators ํจ์๋ฅผ ์ฌ์ฉํ๊ณ , get_params ๋ฉ์๋๋ฅผ ์ ์ํด์ฃผ์์ต๋๋ค.
2. ๋ค์๊ฒฐ ํฌํ ๋ฐฉ์์ ์ฌ์ฉํด ์์ธก ๋ง๋ค๊ธฐ
์ด์ ์์์ ๋ง๋ ํด๋์ค๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค. ์ผ๋จ์ ๋จผ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์์ผํ๋๋ฐ์, datasets๋ชจ๋์ ์ด์ฉํด ๊ฐํธํ๊ฒ ๋ถ๊ฝ ๋ฐ์ดํฐ์ ์ ์ฝ์ด์ค๊ฒ ์ต๋๋ค. ๊ฝ์ Iris-versicolor์ Iris-verginica ๋ ๊ฐ์ง๋ง ๋ถ๋ฅํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
iris = datasets.load_iris()
X, y = iris.data[50:, [1, 2]], iris.target[50:]
le = LabelEncoder()
y = le.fit_transform(y)
X_train, X_test, y_train, y_test =\
train_test_split(X, y,
test_size=0.5,
random_state=1,
stratify=y)
์ด ๊ฒฝ์ฐ ๋ฐ์ ํ๋ จ ๋ฐ์ดํฐ๋ก, ๋ฐ์ ํ ์คํธ ๋ฐ์ดํฐ๋ก ์ค์ ํด์ฃผ์์ต๋๋ค. ์ด์ ํ๋ จ ์ธํธ๋ฅผ ์ฌ์ฉํด์ ์ธ ๊ฐ์ ๋ถ๋ฅ๊ธฐ๋ฅผ ํ๋ จํด๋ณด๋๋ก ํ์ฃ . ๋ก์ง์คํฑ ํ๊ท, ๊ฒฐ์ ํธ๋ฆฌ, k-์ต๊ทผ์ ์ด์ ๋ถ๋ฅ๊ธฐ๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
์ผ๋จ ์์๋ธ๋ก ๋ฌถ๊ธฐ ์ ์, 10๊ฒน ๊ต์ฐจ๊ฒ์ฆ์ผ๋ก ์ฑ๋ฅ์ ํ๊ฐํ๊ณ ์์ํด๋ณด๋๋ก ํ ๊ฒ์. ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
clf1 = LogisticRegression(solver='liblinear',
penalty='l2',
C=0.001,
random_state=1)
clf2 = DecisionTreeClassifier(max_depth=1,
criterion='entropy',
random_state=0)
clf3 = KNeighborsClassifier(n_neighbors=1,
p=2,
metric='minkowski')
pipe1 = Pipeline([['sc', StandardScaler()],
['clf', clf1]])
pipe3 = Pipeline([['sc', StandardScaler()],
['clf', clf3]])
clf_labels = ['Logistic regression', 'Decision tree', 'KNN']
print('10-๊ฒน ๊ต์ฐจ ๊ฒ์ฆ:\n')
for clf, label in zip([pipe1, clf2, pipe3], clf_labels):
scores = cross_val_score(estimator=clf,
X=X_train,
y=y_train,
cv=10,
scoring='roc_auc')
print("ROC AUC: %0.2f (+/- %0.2f) [%s]"
% (scores.mean(), scores.std(), label))
๊ฒฐ๊ณผ์์ ํ์ธํ์ค ์ ์๋ฏ์ด ์ฑ๋ฅ์ ๋ค๋ค ๋น์ทํ๋ค์. ๋ก์ง์คํฑ ํ๊ท์ k-์ต๊ทผ์ ์ด์ ๋ถ๋ฅ๊ธฐ๋ ์ ํ์ดํ ๋ผ์ธ์ผ๋ก ํ๋ จํ๋๋ฉด, ์ด ๋ ๊ฐ์ง๋ ๊ฒฐ์ ํธ๋ฆฌ์ ๋ฌ๋ฆฌ ์ค์ผ์ผ์ ๋ฏผ๊ฐํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ํ์คํ ์ ์ฒ๋ฆฌ๊ฐ ์ค์ํ ์น๊ตฌ๋ค์ด์ฃ . ๋ฌผ๋ก ๋ถ๊ฝ ๋ฐ์ดํฐ์ ํน์ฑ์ด ๋ชจ๋ ๊ฐ์ ์ค์ผ์ผ๋ก ์ธก์ ๋์์ง๋ง ํ์คํ ์ ์ฒ๋ฆฌ๋ฅผ ์ต๊ดํํด๋ด ์๋ค!
์ด์ ์ฐ๋ฆฌ๊ฐ ์ง์ ๋ง๋ค์ด๋ ๋ง๋ค์ด๋ ํด๋์ค๋ก ์์๋ธ์ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค!
# ๋ค์๊ฒฐ ํฌํ (ํด๋์ค ๋ ์ด๋ธ ์นด์ดํธ)
mv_clf = MajorityVoteClassifier(classifiers=[pipe1, clf2, pipe3])
clf_labels += ['Majority voting']
all_clf = [pipe1, clf2, pipe3, mv_clf]
for clf, label in zip(all_clf, clf_labels):
scores = cross_val_score(estimator=clf,
X=X_train,
y=y_train,
cv=10,
scoring='roc_auc')
print("ROC AUC: %0.2f (+/- %0.2f) [%s]"
% (scores.mean(), scores.std(), label))
๋ง์ง๋ง ์ค์ด ์์๋ธ ์ ๋๋ค. ์ฑ๋ฅ์ด ํ์ฐํ ๋ฐ์ด๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค!
3. ์์๋ธ ๋ถ๋ฅ๊ธฐ ํ๊ฐ์ ํ๋
์ผ๋จ ์์ ์ธ์ ์์ ๊ณต๋ถํ๋ ROC๋ฅผ ํตํด์ MajorityVoteClassifier์ ์ผ๋ฐํ ์ฑ๋ฅ์ ํ์ธํด๋ณผ๊ฒ์. ํ ์คํธ ์ธํธ๋ฅผ ์ด์ฉํ๊ฒ ์ต๋๋ค.
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
colors = ['black', 'orange', 'blue', 'green']
linestyles = [':', '--', '-.', '-']
for clf, label, clr, ls \
in zip(all_clf,
clf_labels, colors, linestyles):
# assuming the label of the positive class is 1
y_pred = clf.fit(X_train,
y_train).predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_true=y_test,
y_score=y_pred)
roc_auc = auc(x=fpr, y=tpr)
plt.plot(fpr, tpr,
color=clr,
linestyle=ls,
label='%s (auc = %0.2f)' % (label, roc_auc))
plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1],
linestyle='--',
color='gray',
linewidth=2)
plt.xlim([-0.1, 1.1])
plt.ylim([-0.1, 1.1])
plt.grid(alpha=0.5)
plt.xlabel('False positive rate (FPR)')
plt.ylabel('True positive rate (TPR)')
plt.show()
ROC๊ณก์ ์์ ํ์ธํ์ค ์ ์๋ฏ์ด ์์๋ธ ๋ถ๋ฅ๊ธฐ๊ฐ ํ ์คํธ์ธํธ ์์๋ ๋งค์ฐ ์ข์ ์ฑ๋ฅ์ ๋ด๊ณ ์๋ค์. ๋ก์ง์คํฑ ํ๊ท๋ ๋น์ทํ๊ฒ ์ข์ ์ฑ์ ์ ๋ด๊ณ ์๋๋ฐ, ์ด๋ ์์ ๋ฐ์ดํฐ ์ ์์ ์๊ธฐ๋ ๋์ ๋ถ์ฐ ๋๋ฌธ์ผ๋ก ์ถ์ ๋ฉ๋๋ค. ์ด ์ด์ผ๊ธฐ๊ฐ ๊ถ๊ธํ์ ๋ถ๋ค์ ๋ก์ง์คํฑ ํ๊ท ์ธ์ ์ ๋ณด๊ณ ์ค์ธ์!
์ฌ๊ธฐ์๋ ํน์ฑ์ด ๋ ๊ฐ๋ง ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ๊ฒฐ์ ๊ฒฝ๊ณ๋ฅผ ํ์ธํด๋ณผ ์ ์์ต๋๋ค. ๋ก์ง์คํฑ ํ๊ท์ k-์ต๊ทผ์ ์ด์ ํ์ดํ๋ผ์ธ์๋ ์ ์ฒ๋ฆฌ๊ฐ ์๊ธฐ ๋๋ฌธ์ ํน์ฑ์ ํ์คํํ๋ ๊ฑด ์๋ตํด๋ ๋ฉ๋๋ค. ์๋ ์ฝ๋์์๋ ๊ฒฐ์ ํธ๋ฆฌ ๊ฒฐ์ ๊ฒฝ๊ณ๋ฅผ ๋ค๋ฅธ ๋ชจ๋ธ๊ณผ ์ค์ผ์ผ์ ๋ง์ถ๊ธฐ ์ํด์ ์ฌ์ฉํ์ต๋๋ค.
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
from itertools import product
all_clf = [pipe1, clf2, pipe3, mv_clf]
x_min = X_train_std[:, 0].min() - 1
x_max = X_train_std[:, 0].max() + 1
y_min = X_train_std[:, 1].min() - 1
y_max = X_train_std[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
np.arange(y_min, y_max, 0.1))
f, axarr = plt.subplots(nrows=2, ncols=2,
sharex='col',
sharey='row',
figsize=(7, 5))
for idx, clf, tt in zip(product([0, 1], [0, 1]),
all_clf, clf_labels):
clf.fit(X_train_std, y_train)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
axarr[idx[0], idx[1]].contourf(xx, yy, Z, alpha=0.3)
axarr[idx[0], idx[1]].scatter(X_train_std[y_train==0, 0],
X_train_std[y_train==0, 1],
c='blue',
marker='^',
s=50)
axarr[idx[0], idx[1]].scatter(X_train_std[y_train==1, 0],
X_train_std[y_train==1, 1],
c='green',
marker='o',
s=50)
axarr[idx[0], idx[1]].set_title(tt)
plt.text(-3.5, -5.,
s='Sepal width [standardized]',
ha='center', va='center', fontsize=12)
plt.text(-12.5, 4.5,
s='Petal length [standardized]',
ha='center', va='center',
fontsize=12, rotation=90)
plt.show()
์์๋ธ ํ๋์ ์ํด์ ๊ฐ๋ณ ๋ถ๋ฅ๊ธฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ํ๋ํ๊ธฐ ์ ์ GridSearchCV ๊ฐ์ฒด ์์ ๋งค๊ฐ๋ณ์์ ์ ๊ทผํ๋ ๋ฒ์ ์ฐพ๊ธฐ ์ํด์ get_params ๋ฉ์๋๋ฅผ ํธ์ถํด๋ณผ๊ฒ์.
mv_clf.get_params()
์์ฒญ๋๊ฒ ๊ธฐ๋ค์. ์ด ๊ธธ๊ณ ๊ธด ๋ฐํ ๊ฐ์ ์ ์ดํด๋ณด๋ฉด ๊ฐ๋ณ๋ถ๋ฅ๊ธฐ ์์ฑ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์ฝ์ด๋ผ ์ ์์ต๋๋ค. ์์๋ฅผ ์ํด ๊ทธ๋ฆฌ๋ ์์น๋ก ๋ก์ง์คํฑ ํ๊ท์ ๋งค๊ฐ๋ณ์ C์ ๊ฒฐ์ ํธ๋ฆฌ ๊น์ด๋ฅผ ํ๋ํด๋ณผ๊ฒ์.
from sklearn.model_selection import GridSearchCV
params = {'decisiontreeclassifier__max_depth': [1, 2],
'pipeline-1__clf__C': [0.001, 0.1, 100.0]}
grid = GridSearchCV(estimator=mv_clf,
param_grid=params,
cv=10,
scoring='roc_auc',
iid=False)
grid.fit(X_train, y_train)
๊ทธ๋ฆฌ๋ ์์น ์คํ์ด ์๋ฃ๋๋ฉด ROC AUC ์ ์๋ฅผ ์ถ๋ ฅํ ์ ์์ต๋๋ค.
for params, mean_score, scores in grid.grid_scores_:
print("%0.3f+/-%0.2f %r"
% (mean_score, scores.std()/2, params))
print('์ต์ ์ ๋งค๊ฐ๋ณ์: %s' %grid.best_params_)
print('์ ํ๋: %.2f' %grid.best_score_)
์ฌ๊ธฐ์ ํ์ธํ์ค ์ ์๋ฏ์ด ๊ท์ ๊ฐ๋๊ฐ ๊ฐ์ฅ ๋ฎ์ ๋ ๊ฐ์ฅ ๊ฒฐ๊ณผ๊ฐ ์ข์๋ฐ, ํธ๋ฆฌ์ ๊น์ด๋ ๊ทธ๋ฆฌ ์ํฅ์ ์ฃผ์ง ์๋ ๊ฒ ๊ฐ๋ค์. ์ด ๋ฐ์ดํฐ๋ฅผ ๋ถํ ํ๋๋ฐ์๋ ๊น์ด 1๋ก๋ ์ถฉ๋ถํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ฌ๊ธฐ๊น์ง ์์๋ธ ํ์ต๊ณผ ์์๋ธ ํ์ต์ ๋ํ ์ฃผ์, ๋ค์๊ฒฐ ํฌํ์ ๋ํด์ ์์๋ณด์์ต๋๋ค. ์์๋ธ ํ์ต์ ๋จธ์ ๋ฌ๋์ ์์ด์๋ ๊ผญ๊ผญ๊ผญ ์์์ผํ๋ ๊ฐ๋ ์ด๋ผ๊ณ ์ ๋ ์๊ฐํฉ๋๋ค. ์์ผ๋ก ํ ์ธ์ ๋ ์์๋ธ์ ๋ํ ๊ฒ์ ์์๋ณด๊ฒ ๋ ํ ๋ฐ์, ์ด๋ฒ ์ธ์ ์ด๋ ํจ๊ป ์ฝ์ด์ฃผ์๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ผ ๋ค์ ์ธ์ ๋ดฌ์!