#!/usr/bin/env python3


Grid Search에 pipeline 적용


Grid Search에 pipeline을 사용하는 방식

parameter를 정의하고, 이 parameter 그리드와 pipeline으로 GridSearchCV의 객체를 만듭니다.

객체를 만들 때는 각 parameter가 pipeline의 어떤 단계에 속한 것인지 알려줘야 합니다.

C와 gamma parameter는 두 번째 단계인 SVC의 parameter입니다. 만약 이 단계의 이름이 'svc'라면 pipeline용 parameter 그리드는 단계 이름과 parameter 이름을 '__'로 연결합니다.

그래서 SVC의 parameter C를 Grid Search로 탐색하려면 parameter Grid dictionary의 키를 'svc__C'로 해야합니다.

import numpy as np

from sklearn.pipeline import Pipeline

from sklearn.datasets import load_breast_cancer

from sklearn.model_selection import train_test_split, GridSearchCV

from sklearn.preprocessing import MinMaxScaler

from sklearn.svm import SVC


cancer = load_breast_cancer()

x_train, x_test, y_train, y_test = train_test_split(cancer.data, cancer.target,

                                                    stratify=cancer.target, random_state=0)


scaler_tuple = ('scaler', MinMaxScaler())

model_tuple = ('svc', SVC())


pipe = Pipeline([scaler_tuple, model_tuple])

pipe.fit(x_train, y_train)


values = np.array([0.001, 0.01, 0.1, 1, 10, 100])

params = {'svc__C':values, 'svc__gamma':values}


grid = GridSearchCV(pipe, param_grid=params, cv=5)

grid.fit(x_train, y_train)


print('optimal train score: {:.3f}'.format(grid.best_score_))

# optimal train score: 0.984


print('test score: {:.3f}'.format(grid.score(x_test, y_test)))

# test score: 0.951


print('optimal parameter: {}'.format(grid.best_params_))

# optimal parameter: {'svc__C': 10.0, 'svc__gamma': 0.1}



이전 포스팅 참조하기

Grid Search: [1]pipeline


이전 포스팅에서 본 그리드 서치와 다른 점은 교차 검증의 각 분할에 MinMaxScaler가 훈련 폴드에 매번 적용되어, 매개변수 검색 과정에서 검증 폴더의 정보가 누설되지 않은 것입니다.

import matplotlib.pyplot as plt

from mglearn.plots import plot_proper_processing


plot_proper_processing()

plt.show()

교차 검증 반복 안에서 전처리가 될 때 데이터 사용 형태




교차 검증에서 정보 누설에 의한 영향은 전처리 종류에 따라 다릅니다. 검증 폴드를 사용해 데이터의 스케일을 조정하는 경우엔 심각한 문제가 생기지 않지만, 검증 폴드를 이용해 특성을 추출하거나 선택하면 결과가 확연히 달라집니다. 다음 코드로 이 것을 확인할 수 있습니다.

rnd = np.random.RandomState(seed=0)

x = rnd.normal(size=(100, 10000))

y = rnd.normal(size=(100,))


데이터셋을 무작위로 생성해서 데이터x와 타깃 y사이에는 아무런 관계가 없습니다(즉 독립입니다.) 그러므로 이 데이터셋으로 무언가를 학습하기는 불가능합니다. SelectPercentile로 10,000개 중 가장 유용한 특성을 선택하고 교차 검증을 사용하여 Ridge회귀를 평가 하겠습니다.

from sklearn.feature_selection import SelectPercentile, f_regression


select = SelectPercentile(score_func=f_regression, percentile=5)

select.fit(x, y)


x_selected = select.transform(x)

print('{}'.format(x_selected.shape))

# (100, 500)


from sklearn.model_selection import cross_val_score

from sklearn.linear_model import Ridge


cv_score = cross_val_score(Ridge(), x_selected, y, cv=5)

print('ridge score: {:.3f}'.format(np.mean(cv_score)))

# ridge score: 0.915

교차 검증으로 계산한 평균 R-square는 91.5%로 매우 좋은 모델입니다. 데이터셋을 완전히 무작위로 만들었으니 있을 수 없는 일입니다. 교차 검증 밖에서 특성을 선택했기 때문에 훈련과 테스트 폴드 양쪽에 연관된 특성이 찾아 질 수 있습니다.

테스트 폴드에서 유출된 정보는 매우 중요한 역할을 하기 때문에 비현실적으로 높은 결과가 나왔습니다.



select_tuple = ('select', SelectPercentile(score_func=f_regression, percentile=5))

model_tuple = ('ridge', Ridge())


pipe = Pipeline([select_tuple, model_tuple])

cv_score_pipe = cross_val_score(pipe, x, y, cv=5)

print('ridge score(pipe): {:.3f}'.format(np.mean(cv_score_pipe)))

# ridge score(pipe): -0.086

이번에는 R-square가 음수라 성능이 매우 낮은 모델임을 나타냅니다. 파이프라인을 사용했기 때문에 특성 선택이 교차 검증 반복 안으로 들어갔습니다. 이 말은 훈련 폴드를 사용해서만 특성이 선택되었고 테스트 폴드는 사용하지 않았다는 뜻입니다. 특성 선택 단계에서 훈련 폴드의 타깃값과 연관된 특성을 찾았지만, 전체 테이터가 무작위로 만들어졌으니 테스트 폴드의 타깃과는 연관성이 없습니다. 이 예는 특성 선택 단계에서 일어나는 정보 누설을 막는 것이 모델의 성능을 평가하는데 큰 차이를 만든다는 것을 보여줍니다.




참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'algorithm chain & pipeline > pipeline' 카테고리의 다른 글

pipeline  (0) 2018.04.20

#!/usr/bin/env python3


pipeline


입력 데이터의 표현 형태에 매우 민감한 머신러닝 algorithm이 많습니다.

직접 데이터의 스케일을 조정하고 특성을 연결하거나, 비지도 학습으로 특성을 만들기도 합니다.

따라서 대부분의 머신러닝은 하나의 algorithm으로 이뤄져 있지 않고, 여러 단계의 처리 과정과 머신러닝 모델이 연결되어 있습니다.


다음은 데이터를 분할하고 최솟값, 최댓값을 찾아 데이터의 스케일을 바꾸고 SVM훈련시키는 코드입니다.

SVC는 이전 포스팅을 참고하세요

[1]Kernelized Support Vector Machines


from sklearn.svm import SVC

from sklearn.datasets import load_breast_cancer

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import MinMaxScaler


cancer = load_breast_cancer()

print('{}'.format(cancer.keys()))

# dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])


x_train, x_test, y_train, y_test = train_test_split(cancer.data, cancer.target,

                                                    stratify=cancer.target, random_state=0)


scaler = MinMaxScaler()

scaler.fit(x_train)


x_train_scaled = scaler.transform(x_train)

x_test_scaled = scaler.transform(x_test)


svc = SVC(C=1, kernel='rbf', gamma='auto', degree=3)

svc.fit(x_train_scaled, y_train)


pre_train_score = svc.score(x_train_scaled, y_train)

print('{:.3f}'.format(pre_train_score))

# 0.955


pre_test_score = svc.score(x_test_scaled, y_test)

print('{:.3f}'.format(pre_test_score))

# 0.951



단순한 GridSearchCV를 사용해서 더 좋은 SVC parameter를 찾는 방법은 다음과 같습니다.

from sklearn.model_selection import GridSearchCV

import numpy as np


values = np.array([0.001, 0.01, 0.1, 1, 10, 100])

params = {'C':values, 'gamma':values}


grid = GridSearchCV(SVC(), param_grid=params, cv=5)

grid.fit(x_train_scaled, y_train)


print('optimal parameter {}'.format(grid.best_params_))

# optimal parameter {'C': 1.0, 'gamma': 1.0}


print('best CV score {:.3f}'.format(grid.best_score_))

# best CV score 0.981


print('best test score {:.3f}'.format(grid.score(x_test_scaled, y_test)))

# best test score 0.944

여기서 스케일을 조정한 데이터를 사용해서 SVC의 parameter에 대해 grid search를 수행했습니다.

데이터의 최솟값과 최댓값을 계산할 때 학습을 위해 train set에 있는 모든 데이터를 사용했습니다. 후에 스케일이 조정된 train data에서 교차 검증을 사용해 grid search를 수행했습니다. 교차 검증의 각 분할에서 원본 훈련 세트 데이터의 어떤 부분은 훈련 폴드가 되고 어떤 부분은 검증 폴드가 됩니다. 검증 폴드는 훈련 폴드로 학습된 모델이 새로운 데이터에 적용될 때의 성능을 측정하는 데 사용합니다. 그러나 데이터 스케일을 조정할 때 검증 폴드에 있는 정보까지 이미 사용했습니다. 다시 말하면 교차 검증의 반복마다 선택된 검증 폴드는 전체 훈련 세트의 일부이며, 우리나는 데이터의 스케일을 조정하기 위해 전체 훈련 세트를 이용했습니다. 하지만 이는 새로운 데이터가 모델에 나타날 때와는 완전히 다릅니다. 새로운 데이터가 관측되면 이 데이터는 훈련 데이터의 스케일 조정에 사용되지 않은 것이라, 그 최솟값과 최댓값이 훈련 데이터와 다를 수 있습니다.


다음 코드는 이 과정을 시각화하여 보여줍니다.

from mglearn.plots import plot_improper_processing

import matplotlib.pyplot as plt


plot_improper_processing()

plt.show()

교차 검증 반복 밖에서 전처리가 될 때 데이터 사용 형태

이렇기 때문에 교차 검증의 분할 방식은 모델이 새 데이터를 만났을 때를 올바로 반영하지 못하고 있습니다. 검증 폴드 데이터의 정보가 모델 구축 과정에 이미 누설되었으므로 교차 검증에서 최적의 매개변수를 찾지 못하고 낙관적인 결과가 만들어지게 됩니다.


이 문제를 해결하려면 교차 검증의 분할이 모든 전처리 과정보다 앞서 이뤄저야 합니다. 데이터셋의 정보를 이용하는 모든 처리 과정은 데이터셋의 훈련 부분에만 적용되어야 하므로 교차 검증 반복 안에 있어야 합니다.


scikit-learn에서 cross_var_score 함수와 GridSearchCV로 이런 방식을 구현하려면 Pipeline을 사용하면 됩니다.


Pipeline은 여러 처리 단계를 하나의 scikit-learn 추정기 형태로 묶어주는 파이썬 클래스입니다. Pipeline은 fit, predict, score 메소드를 제공합니다.


Pipeline을 사용하는 가장 일반적인 경우는 분류기 같은 지도 학습 모델과 전처리 단계를 연결할 때입니다.



MinMaxScaler로 데이터의 스케일을 조정하고 SVM 모델을 훈련시키는 workflow를 Pipeline을 사용해 표현해보겠습니다.

from sklearn.pipeline import Pipeline


scaler_tuple = ('scaler', MinMaxScaler())

model_tuple = ('svm', SVC())


pipe = Pipeline([scaler_tuple, model_tuple])

pipe.fit(x_train, y_train)


pipe.fit은 첫 번째 단계(scaler)의 fit 매소드를 호출하여 훈련데이터를 변환하고,

마지막에는 변환된 데이터에 SVM모델을 학습시킵니다. 테스트 세트로 평가하려면 pipe.score를 호출합니다.

pipe_train_score = pipe.score(x_train, y_train)

pipe_test_score = pipe.score(x_test, y_test)


print('{:.3f}'.format(pipe_train_score))

# 0.955


print('{:.3f}'.format(pipe_test_score))

# 0.951

이 값은 전처리한 후 훈련점수와 테스트 점수가 같습니다.



참고 자료: 

[1]Introduction to Machine Learning with Python, Sarah Guido

'algorithm chain & pipeline > pipeline' 카테고리의 다른 글

Grid Search에 pipeline 적용  (0) 2018.04.20

+ Recent posts