인공지능

[빅데이터 직무연구회] 8회차 모임 정리

Chipmunks 2018. 6. 26.
728x90

[빅데이터 직무연구회] 8회차 모임 정리

모임 요일 : 6월 25일 월요일 오후 4시


2장, 3장, 5장 복습

2.3.7 SVM (P. 125 ~ P. 137)

  1. SVM은 데이터를 여러 그룹으로 분류하는 데에 쓰일 수 있다. (P.129 3차원)
    1. 학습방법 : 클래스 사이의 경계에 위치한 데이터 포인트 (서포트 벡터) 와의 거리를 계산해 새로운 데이터 포인트 클래스를 예측 (P. 131)
  2. 매개변수 C와 gamma의 역할 (그림 2-42)
  3. 데이터 전처리 (= 표준화)

3.3 데이터 전처리와 스케일 조정 (P.169 ~ P.178)

3.3.2 데이터 변환 적용하기

  1. 전처리 MinMaxScaler
  2. 훈련세트와 테스트세트로 나누기(In[4])
  3. 객체생성(In[5])
  4. fit 메서드로 훈련 데이터 적용(In[6]) - 최솟값과 최댓값 계산
  5. transform 메서드로 사용해 훈련 데이터스케일 조정(In[7])
  6. transform 메서드로 테스트 세트도 변환(In[8])



5. 모델 평가와 성능향상 (P. 305)

5.1 교차검증

모델의 일반화 성능을 측정하는 방법 (P.306 ~ P.307)

5.2 그리드서치

매개변수를 튜닝하여 일반화 성능을 개선하는데 사용 (P.316) - https://en.wikipedia.org/wiki/Hyperparameter_optimization


각 파라미터를 적당한 범위 내에서 변경하면서 가장 성능이 좋은 파라미터 값을 검토해주는 방법으로 (ex. C & gamma) 튜닝하여 일반화 성능을 개선시킬 수 있다.


5.2.2 매개변수 과대적합과 검증 세트 (그림 5-5)


Chapter 6. 알고리즘 체인과 파이프라인

주제 : 데이터 변환 과정과 머신러닝 모델을 쉽게 연결해주는 Pipeline

1. Pipe line 이란?

https://www.slideshare.net/jeykottalam/pipelines-ampcamp



2. 모델 체인의 예시

cancer 데이터셋을 MinMaxScaler로 전처리를 해서 커널 SVM의 성능을 향상시킴

6.1 데이터 전처리와 매개변수 선택

그리드 서치를 사용해 매개변수를 찾으려고 함. 코드에 결함이 있음. 그림 6-1 설명.

데이터의 최솟값과 최댓값을 계산할 때 학습을 위해 훈련 세트에 있는 모든 데이터를 사용했음.
그런 다음 스케일이 조정된 훈련 데이터에서 교차 검증을 사용해 그리드 서치를 수행했음.
교차 검증의 각 분할에서 훈련 데이터에서 교차 검증을 사용해 그리드 서치를 수행함.

교차 검증의 각 분할에서 원본 훈련 세트 데이터의 어떤 부분은 훈련 폴드가 되고 어떤 부분은 검증 폴드가 됨. 검증 폴드는 훈련 폴드로 학습된 모델이 새로운 데이터에 적용될 때의 성능을 측정하는 데 사용함.

그러나 데이터 스케일을 조정할 때 검증 폴드에 들어 있는 정보까지 이미 사용함.

1. 교차 검증 시
SVC fit : 훈련 폴드
SVC predict : 검증 폴드
scaler fit : 훈련폴드 + 검증 폴드


2. 테스트 세트 예측 시

SVC fit : 훈련 폴드 + 검증 폴드

SVC predict : 테스트 세트

scaler fit : 훈련 폴드 + 검증 폴드


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


이 문제를 해결하기 위해, 교차 검증의 분할이 모든 전처리 과정보다 앞서 이뤄져야 한다. 따라서 Pipeline 기능을 사용하면 된다.


6.2 파이프라인 구축하기

각 단계를 리스트로 전달하여 파이프라인 객체를 만든다. 각 단계는 추정기의 객체와 임의의 이름(아무 문자열이나 가능, 이중 밑줄 문자(__) 제외) 으로 구성된 튜플(tuple)이다.

fit 메서드는 첫 번째 단계(scaler)의 fit 메서드를 호출해 훈련 데이터를 변환하고, 마지막으로 변환된 데이터에 SVM 모델을 훈련시킨다. 테스트 세트로 평가하려면 pipe.score를 호출한다.

파이프라인을 사용하면 "전처리 + 분류" 과정을 위해 작성해야 할 코드가 줄어든다. 가장 큰 장점은 cross_val_score 나 GridSearchCV에 파이프라인을 하나의 추정기처럼 사용할 수 있다는 것이다.

6.3 그리드 서치에 파이프라인 적용하기

"svm" 문자열 단계의 매개변수 이름을 설정하려면 "__" 로 연결해 만들어야 한다. SVC의 매개변수 C를 그리드 서치로 탐색하려면 매개변수 그리드 딕셔너리의 키를 "svm__C"로 해야 하고 gamma도 똑같다.

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

1. 교차 검증 시
SVC fit : 훈련 폴드
SVC predict : 검증 폴드
scaler fit : 훈련 폴드

2. 테스트 세트 예측 시
SVC fit : 훈련 폴드 + 검증 폴드
SVC predict : 테스트 세트
scaler fit : 훈련 폴드 + 검증 폴드

교차 검증에서 정보 누설에 의한 영향은 전처리 종류에 따라 다르다. 검증 폴드를 사용해 데이터의 스케일을 조정하는 경우 심각한 문제가 생기진 않는다. 그러나 검증 폴드를 이용해 특성을 추출하거나 선택하면 결과가 확연히 달라진다.

6.4 파이프라인 인터페이스

파이프라인에 들어갈 추정기는 마지막 단계를 제외하고는 모두 transform 메서드를 가지고 있어야 한다.


내부적으로 Pipeline.fit 메서드가 실행되는 동안, 파이프라인은 각 단계에서 이전 단계의 transform의 출력을 입력으로 받아 fittransform 메서드를 차례로 호출한다. (또는 fit_transform ) 그리고 마지막 단계는 fit 메서드만 호출한다.


6.4.1 make_pipeline 을 사용한 파이프라인 생성

각 단계의 이름을 지정하지 않으면, 자동으로 만들어준다. 파이썬 클래스 이름의 소문자 버전이다. 같은 파이썬 클래스를 사용하면 이름 뒤에 숫자가 추가로 붙는다.


의미 있는 이름을 붙이려면 Pipeline으로 객체를 직접 만드는 것이 좋다.


6.4.2 단계 속성에 접근하기

named_steps 속성을 사용하면 파이프라인의 각 단계에 쉽게 접근할 수 있다.

6.4.3 그리드 서치 안의 파이프라인 속서에 접근하기

GridSearchCV로 최적의 모델을 찾은 후 전체 훈련 데이터로 학습한 모델이 grid.best_estimator_ 에 저장되어 있다. 한 단계의 best_estimator_ 에 접근하려면, 파이프라인의 named_steps 속성을 사용한다.


6.5 전처리와 모델의 매개변수를 위한 그리드 서치

파이프라인으로 만든 교차 검증의 결과를 히트맵으로 사용해 나타낸다. 따라서 매개변수들에 따른 결정 계수 값을 한 눈에 확인할 수 있다.

모델의 매개변수와 함께 전처리 과정의 매개변수를 찾는 것은 매우 강력한 전략이다. 다만 시간이 오래 걸리 다는 단점이 있다.

6.6 모델 전략을 위한 그리드 서치

파이프라인을 구성하는 단계도 탐색 대상으로 삼을 수 있다. StandardScaler와 MinMaxScaler 중 어떤 것을 사용할지.


%load_ext watermark
%watermark -v -p sklearn,numpy,scipy,matplotlib
CPython 3.5.4
IPython 6.2.1

sklearn 0.19.1
numpy 1.13.3
scipy 1.0.0
matplotlib 2.1.0
In [2]:
%matplotlib inline
from preamble import *

알고리즘 체인과 파이프라인

In [3]:
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()
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=0)

# 훈련 데이터의 최솟값, 최댓값을 계산합니다
scaler = MinMaxScaler().fit(X_train)
In [4]:
# 훈련 데이터의 스케일을 조정합니다
X_train_scaled = scaler.transform(X_train)

svm = SVC()
# 스케일 조정된 훈련데이터에 SVM을 학습시킵니다
svm.fit(X_train_scaled, y_train)
# 테스트 데이터의 스케일을 조정하고 점수를 계산합니다
X_test_scaled = scaler.transform(X_test)
print("테스트 점수: {:.2f}".format(svm.score(X_test_scaled, y_test)))
테스트 점수: 0.95

데이터 전처리와 매개변수 선택

In [5]:
from sklearn.model_selection import GridSearchCV
# 이 코드는 예를 위한 것입니다. 실제로 사용하지 마세요.
param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 100],
              'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
grid = GridSearchCV(SVC(), param_grid=param_grid, cv=5)
grid.fit(X_train_scaled, y_train)
print("최상의 교차 검증 정확도: {:.2f}".format(grid.best_score_))
print("테스트 점수: {:.2f}".format(grid.score(X_test_scaled, y_test)))
print("최적의 매개변수: ", grid.best_params_)
최상의 교차 검증 정확도: 0.98
테스트 점수: 0.97
최적의 매개변수:  {'gamma': 1, 'C': 1}
In [6]:
mglearn.plots.plot_improper_processing()

파이프라인 구축하기

In [7]:
from sklearn.pipeline import Pipeline
pipe = Pipeline([("scaler", MinMaxScaler()), ("svm", SVC())])
In [8]:
pipe.fit(X_train, y_train)
Out[8]:
Pipeline(memory=None,
     steps=[('scaler', MinMaxScaler(copy=True, feature_range=(0, 1))), ('svm', SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False))])
In [9]:
print("테스트 점수: {:.2f}".format(pipe.score(X_test, y_test)))
테스트 점수: 0.95

그리드 서치에 파이프라인 적용하기

In [10]:
param_grid = {'svm__C': [0.001, 0.01, 0.1, 1, 10, 100],
              'svm__gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
In [11]:
grid = GridSearchCV(pipe, param_grid=param_grid, cv=5)
grid.fit(X_train, y_train)
print("최상의 교차 검증 정확도: {:.2f}".format(grid.best_score_))
print("테스트 세트 점수: {:.2f}".format(grid.score(X_test, y_test)))
print("최적의 매개변수: {}".format(grid.best_params_))
최상의 교차 검증 정확도: 0.98
테스트 세트 점수: 0.97
최적의 매개변수: {'svm__gamma': 1, 'svm__C': 1}
In [12]:
mglearn.plots.plot_proper_processing()
In [13]:
rnd = np.random.RandomState(seed=0)
X = rnd.normal(size=(100, 10000))
y = rnd.normal(size=(100,))
In [14]:
from sklearn.feature_selection import SelectPercentile, f_regression

select = SelectPercentile(score_func=f_regression, percentile=5).fit(X, y)
X_selected = select.transform(X)
print("X_selected.shape: {}".format(X_selected.shape))
X_selected.shape: (100, 500)
In [15]:
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import Ridge
print("교차 검증 점수 (릿지): {:.2f}".format(
      np.mean(cross_val_score(Ridge(), X_selected, y, cv=5))))
교차 검증 점수 (릿지): 0.91
In [16]:
pipe = Pipeline([("select", SelectPercentile(score_func=f_regression,
                                             percentile=5)),
                 ("ridge", Ridge())])
print("교차 검증 점수 (파이프라인): {:.2f}".format(
      np.mean(cross_val_score(pipe, X, y, cv=5))))
교차 검증 점수 (파이프라인): -0.25

파이프라인 인터페이스

In [17]:
def fit(self, X, y):
    X_transformed = X
    for name, estimator in self.steps[:-1]:
        # 마지막 단계를 빼고 fit과 transform을 반복합니다
        X_transformed = estimator.fit_transform(X_transformed, y)
    # 마지막 단계 fit을 호출합니다
    self.steps[-1][1].fit(X_transformed, y)
    return self
In [18]:
def predict(self, X):
    X_transformed = X
    for step in self.steps[:-1]:
        # 마지막 단계를 빼고 transform을 반복합니다
        X_transformed = step[1].transform(X_transformed)
    # 마지막 단계 predict을 호출합니다
    return self.steps[-1][1].predict(X_transformed)

make_pipleline을 사용한 파이프라인 생성

In [19]:
from sklearn.pipeline import make_pipeline
# 표준적인 방법
pipe_long = Pipeline([("scaler", MinMaxScaler()), ("svm", SVC(C=100))])
# 간소화된 방법
pipe_short = make_pipeline(MinMaxScaler(), SVC(C=100))
In [20]:
print("파이프라인 단계:\n{}".format(pipe_short.steps))
파이프라인 단계:
[('minmaxscaler', MinMaxScaler(copy=True, feature_range=(0, 1))), ('svc', SVC(C=100, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False))]
In [21]:
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

pipe = make_pipeline(StandardScaler(), PCA(n_components=2), StandardScaler())
print("파이프라인 단계:\n{}".format(pipe.steps))
파이프라인 단계:
[('standardscaler-1', StandardScaler(copy=True, with_mean=True, with_std=True)), ('pca', PCA(copy=True, iterated_power='auto', n_components=2, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)), ('standardscaler-2', StandardScaler(copy=True, with_mean=True, with_std=True))]

단계 속성에 접근하기

In [22]:
# cancer 데이터셋에 앞서 만든 파이프라인을 적용합니다
pipe.fit(cancer.data)
# "pca" 단계의 두 개 주성분을 추출합니다
components = pipe.named_steps["pca"].components_
print("components.shape: {}".format(components.shape))
components.shape: (2, 30)

그리드 서치 안의 파이프라인의 속성에 접근하기

In [23]:
from sklearn.linear_model import LogisticRegression

pipe = make_pipeline(StandardScaler(), LogisticRegression())
In [24]:
param_grid = {'logisticregression__C': [0.01, 0.1, 1, 10, 100]}
In [25]:
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=4)
grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)
Out[25]:
GridSearchCV(cv=5, error_score='raise',
       estimator=Pipeline(memory=None,
     steps=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('logisticregression', LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False))]),
       fit_params=None, iid=True, n_jobs=1,
       param_grid={'logisticregression__C': [0.01, 0.1, 1, 10, 100]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)
In [26]:
print("최상의 모델:\n{}".format(grid.best_estimator_))
최상의 모델:
Pipeline(memory=None,
     steps=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('logisticregression', LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False))])
In [27]:
print("로지스틱 회귀 단계:\n{}".format(
      grid.best_estimator_.named_steps["logisticregression"]))
로지스틱 회귀 단계:
LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False)
In [28]:
print("로지스틱 회귀 계수:\n{}".format(
      grid.best_estimator_.named_steps["logisticregression"].coef_))
로지스틱 회귀 계수:
[[-0.389 -0.375 -0.376 -0.396 -0.115  0.017 -0.355 -0.39  -0.058  0.209
  -0.495 -0.004 -0.371 -0.383 -0.045  0.198  0.004 -0.049  0.21   0.224
  -0.547 -0.525 -0.499 -0.515 -0.393 -0.123 -0.388 -0.417 -0.325 -0.139]]

전처리와 모델의 매개변수를 위한 그리드 서치

In [29]:
from sklearn.datasets import load_boston
boston = load_boston()
X_train, X_test, y_train, y_test = train_test_split(boston.data, boston.target,
                                                    random_state=0)

from sklearn.preprocessing import PolynomialFeatures
pipe = make_pipeline(
    StandardScaler(),
    PolynomialFeatures(),
    Ridge())
In [30]:
param_grid = {'polynomialfeatures__degree': [1, 2, 3],
              'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}
In [31]:
grid = GridSearchCV(pipe, param_grid=param_grid, cv=5, n_jobs=-1)
grid.fit(X_train, y_train)
Out[31]:
GridSearchCV(cv=5, error_score='raise',
       estimator=Pipeline(memory=None,
     steps=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('polynomialfeatures', PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)), ('ridge', Ridge(alpha=1.0, copy_X=True, fit_intercept=True, max_iter=None,
   normalize=False, random_state=None, solver='auto', tol=0.001))]),
       fit_params=None, iid=True, n_jobs=-1,
       param_grid={'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100], 'polynomialfeatures__degree': [1, 2, 3]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)
In [32]:
mglearn.tools.heatmap(grid.cv_results_['mean_test_score'].reshape(3, -1),
                      xlabel="ridge__alpha", ylabel="polynomialfeatures__degree",
                      xticklabels=param_grid['ridge__alpha'],
                      yticklabels=param_grid['polynomialfeatures__degree'], vmin=0)
Out[32]:
<matplotlib.collections.PolyCollection at 0x1c1c764898>
In [33]:
print("최적의 매개변수: {}".format(grid.best_params_))
최적의 매개변수: {'ridge__alpha': 10, 'polynomialfeatures__degree': 2}
In [34]:
print("테스트 세트 점수: {:.2f}".format(grid.score(X_test, y_test)))
테스트 세트 점수: 0.77
In [35]:
param_grid = {'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}
pipe = make_pipeline(StandardScaler(), Ridge())
grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)
print("다항 특성이 없을 때 점수: {:.2f}".format(grid.score(X_test, y_test)))
다항 특성이 없을 때 점수: 0.63

모델 선택을 위한 그리드 서치

In [36]:
pipe = Pipeline([('preprocessing', StandardScaler()), ('classifier', SVC())])
In [37]:
from sklearn.ensemble import RandomForestClassifier

param_grid = [
    {'classifier': [SVC()], 'preprocessing': [StandardScaler()],
     'classifier__gamma': [0.001, 0.01, 0.1, 1, 10, 100],
     'classifier__C': [0.001, 0.01, 0.1, 1, 10, 100]},
    {'classifier': [RandomForestClassifier(n_estimators=100)],
     'preprocessing': [None], 'classifier__max_features': [1, 2, 3]}]
In [38]:
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=0)

grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)

print("최적의 매개변수:\n{}\n".format(grid.best_params_))
print("최상의 교차 검증 점수: {:.2f}".format(grid.best_score_))
print("테스트 세트 점수: {:.2f}".format(grid.score(X_test, y_test)))
최적의 매개변수:
{'classifier': SVC(C=10, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.01, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False), 'classifier__gamma': 0.01, 'preprocessing': StandardScaler(copy=True, with_mean=True, with_std=True), 'classifier__C': 10}

최상의 교차 검증 점수: 0.99
테스트 세트 점수: 0.98

요약 및 정리


댓글