머신 러닝 교과서/ 좋은 훈련 세트 만들기: 데이터 전처리

누락된 데이터 다루기

  • 실제 애플리케이션에서는 여러 이유로 누락된 샘플이 있는 경우가 많다.

테이블 형태 데이터에서 누락된 값 식별

  • (CSV 파일에서 누락된 항목 체크하는 부분 생략. 데이터가 null 이거나 비어 있는 string인지를 체크를 하면 된다.)

누락된 값이 있는 샘플이나 특성 제외

  • (누락된 데이터가 있는 row를 drop 할 수도 있지만, 그런 데이터가 많은 경우 문제가 될 수 있다는 내용)

누락된 값 대체

  • 데이터를 제거하기 어려우면 보간 기법을 사용하여 데이터셋에 있는 다른 훈련 샘플로부터 누락된 값을 추정할 수 있다.
  • 가장 흔한 기법 중 하나는 평균으로 대체하는 것이다.
    • 사이킷런의 Imputer 클래스를 사용하면 간단히 처리할 수 있다.
    • (코드 생략)

사이킷런 추정기 API 익히기

  • Imputer 클래스는 데이터 변환에 사용되는 사이킷런의 변환기(transfomer) 클래스이다. 이런 추정기의 주요 메서드 두 개는 fit과 transform이다.
    • fit 메서드를 사용하여 훈련데이터에서 모델 파라미터를 학습한다.
    • transform 메서드를 사용하여 학습한 파라미터로 데이터를 변환한다.
    • 변환하려는 데이터 배열은 모델 학습에 사용한 데이터의 특성 개수와 같아야 한다.

  • 3장에서 사용한 분류기는 변환기 클래스와 개념상 매우 유사한 API를 가진 사이킷런의 추정기(estimator)이다.
    • 추정기는 predict 메서드가 있지만 transform 메서드도 가질 수 있다.
    • 추정기를 훈련할 때는 fit 메서드를 사용해서 모델의 파라미터를 학습했다.
    • 그 후 predict 메서드를 사용하여 새로운 데이터 샘플에 대한 예측을 만든다.

범주형 데이터 다루기

순서가 있는 특성과 순서가 없는 특성

  • 범주형 데이터에 관해 이야기할 때 순서가 있는 것과 없는 것을 구분해야 한다.
    • 순서가 있는 특성은 정렬하거나 차례대로 놓을 수 있는 범주형 특성으로 생각할 수 있다.
    • 예컨대 티셔츠 사이즈는 XL > L > M 과 같이 순서를 정할 수 있으므로 순서가 있는 특성이다.
    • 반면 티셔츠의 컬러는 순서가 없는 특성이다.

순서 특성 매핑

  • 학습 알고리즘이 순서 특성을 올바르게 인식하려면 문자열을 정수로 바꾸어야 한다.
    • 티셔츠의 size 특성(XL, L, M)은 숫자가 아니라서 이를 바꾸어주는 매핑 함수를 만들어야 한다.
  • (매핑하는 코드 예시 생략)

클래스 레이블 인코딩

  • 많은 머신 러닝 라이브러리는 클래스 레이블이 정수로 인코딩 되었을 것으로 기대한다.
  • (매핑 예시 코드 생략)

순서가 없는 특성에 원-핫 인코딩 적용

  • color와 같이 순서가 없는 특성을 숫자로 바꾸는 경우 학습 알고리즘이 blue가 red 보다 크다는 식의 순서를 부여할 수 있다.
  • 이 문제를 해결하기 위한 통상적인 방법은 원-핫 인코딩(one-hot encoding) 기법이다.
    • 이 방식의 아이디어는 순서 없는 특성에 들어 있는 고유한 값마다 새로운 더미(dummy) 특성을 만드는 것이다.
    • 예컨대 blue에 대한 샘플은 blue=1, red=0, green=0 과 같이 변환하여 사용하는 것이다.
  • 사이킷런의 preprocessing 모듈에 구현된 OneHotEncoder를 사용하면 이런 변환을 수행할 수 있다.
  • 원-핫 인코딩으로 더미 변수를 만드는 더 편리한 방법은 판다스의 get_dummies 메서드를 사용하는 것이다.
  • 원-핫 인코딩된 데이터셋을 사용할 때 다중 공선성(multicollinearity) 문제를 주의해야 한다.
    • 어떤 알고리즘에서는 이슈가 될 수 있다 (예컨대 역행렬을 구해야 하는 경우)
    • 특성 간의 상관관계가 높으면 역행렬을 계산하기 어려워 수치적으로 불안정해진다.
    • 변수 간의 상관관계를 감소하려면 원-핫 인코딩된 배열에서 특성 열 하나를 삭제한다. 이렇게 특성을 삭제해도 잃는 정보는 없는데, 예컨대 blue 열을 삭제해도, green = 0, red=0이면 blue임을 유추할 수 있다.

데이터셋을 훈련 세트와 테스트 세트로 나누기

  • (UCI 머신러닝 저장소에서 Wine 데이터셋을 받는 내용 생략)
  • 사이킷런의 model_selection 모듈에 있는 train_test_split 함수를 사용하면 데이터셋을 훈련 세트와 테스트 세트로 편리하게 나눌 수 있다.

특성 스케일 맞추기

  • 특성 스케일 조정은 전처리 파이프라인에서 아주 중요한 단계이다.
    • 결정 트리와 랜덤 포레스트는 특성 스케일 조정에 대해 걱정할 필요가 없는 몇 안 되는 알고리즘 중 하나이다.
    • 그러나 경사 하강법 같은 대부분의 머신 러닝과 최적화 알고리즘은 특성의 스케일이 같을 때 훨씬 성능이 좋다.
  • 두 개의 특성에서 첫 번째 특성이 1-10 사이의 scale을 갖고 있고 두 번째 특성이 1-100000 사이의 스케일을 갖는다고 가정하면, k-최근접 이웃 같은 알고리즘에서는 샘플 간의 거리가 두 번째 특성 축에 의해 좌우될 것이다.
  • 스케일이 다른 특성을 맞추는 대표적인 방법은 정규화(normalization)와 표준화(standardization)이 있다.
  • 정규화는 특성의 스케일을 [0, 1] 범위에 맞추는 것을 의미한다.
    • 최소-최대 스케일 변환(min-max scaling)의 특별한 경우이다.
    • 데이터를 정규화 하기 위해 다음과 같이 특성의 열마다 최소-최대 변환을 적용하여 샘플 x^{(i)} 에서 새로운 값 x_{norm}^{(i)} 을 계산한다.

x_{norm}^{(i)} = {x^{(i)} - x_{min} \over x_{max} - x_{min}}

  • 최소-최대 스케일 변환을 통한 정규화는 범위가 정해진 값이 필요할 때 유용하게 사용할 수 있는 일반적인 기법이다.
  • 표준화는 많은 머신러닝 알고리즘 특히 경사하강법 같은 최적화 알고리즘에서 널리 사용된다.
    • 표준화를 사용하면 특성의 평균을 0에 맞추고 표준편차를 1로 만들어 정규분포와 같은 특징을 가지도록 만든다. 이는 가중치를 더 쉽게 학습할 수 있도록 만든다.
    • 또 표준화는 이상치 정보가 유지되기 때문에 제한된 범위로 데이터를 조정하는 최소-최대 스케일 변환에 비해 알고리즘이 이상치에 덜 민감하다.

x_{sid}^{(i)} = {x^{(i)} - \mu_{x} \over \sigma_{x}}

  • 여기서 \mu_{x} 는 어떤 특성의 샘플 평균이고 \sigma_{x} 는 그에 해당하는 표준 편차이다.

유용한 특성 선택

  • 모델이 테스트 세트보다 훈련 세트에서 성능이 높다면 과대적합(overfitting)에 대한 강력한 신호이다.
    • 새로운 데이터에는 잘 인반화하지 못하기 때문에 모델 분산이 크다고 말한다.
    • 과대적합의 이유는 주어진 훈련 데이터에 비해 모델이 너무 복잡하기 때문이다.
  • 일반화 오차를 감소시키기 위해 많이 사용하는 방법은 다음과 같다.
    • 더 많은 훈련 데이터를 모은다.
    • 규제를 통해 복잡도를 제한한다.
    • 파라미터 개수가 적은 간단한 모델을 선택한다.
    • 데이터 차원을 줄인다.

모델 복잡도 제한을 위한 L1 규제와 L2 규제

  • 3장에서 L2 규제(L2 gerularization)는 개별 가중치 값을 제한하여 모델 복잡도를 줄이는 한 방법이라고 설명했다.
    • 가중치 벡터 w 의 L2 규제는 다음과 같이 정의한다.

L2:\|w\|_{2}^{2} = \sum_{j=1}^{m} w_{j}^{2}

  • 모델 복잡도를 줄이는 또 다른 방법은 L1 규제(L1 gerularization)이다.

L1:\|w\|_{2} = \sum_{j=1}^{m} |w_{j}|

  • 이는 가중치 제곱을 그냥 가중치 절댓값으로 바꾼 것이다.
    • L2 규제와 대조적으로 L1 규제는 보통 희소한 특성 벡터를 만든다.
    • 대부분의 특성 가중치가 0이 된다.
    • 실제로 관련 없는 특성이 많은 고차원 데이터셋일 경우 이런 희소성이 도움이 될 수 있다.
    • 특히 샘플보다 관련 없는 특성이 더 많은 경우이다.
    • 이런 맥락으로 보면 L1 규제는 특성 선택의 기법이 될 수 있다.

L2 규제의 기하학적 해석

  • L2 규제는 비용 함수에 페널티 항(penalty term)을 추가한다.
    • 규제가 없는 비용 함수로 훈련한 모델에 비해 가중치 값을 아주 작게 만드는 효과를 낸다.
  • L1 규제가 어떻게 희소성을 만드는지 이해하기 위해 규제의 기하학적 해석에 대해 고찰해 보자
    • 두 개의 가중치 값 w_{1} w_{2} 에 대한 볼록한 비용 함수의 등고선을 그려보면 다음과 같다.

  • 2장 아달린에서 사용했던 제곱 오차합(SSE) 비용 함수가 구 모양이어서 로지스틱 회귀의 비용 함수보다 그리기 쉽다.
    • 여기서 얻은 개념은 로지스틱 회귀에도 동일하게 적용 가능하다.
    • 그림 4-4와 같이 우리의 목표는 훈련 데이터에서 비용 함수를 최소화하는 가중치 값의 조합을 찾는 것임을 기억하라 (타원의 중심 포인트)
  • 규제를 더 작은 가중치를 얻기 위해 비용 함수에 추가하는 페널티 항으로 생각할 수 있다. 다른 말로 하면 큰 가중치를 제한한다.
    • 규제 파라미터 \lambda 로 규제의 강도를 크게 하면 가중치가 0에 가까워지고 훈련 데이터에 대한 모델 의존성은 줄어든다.
  • L2 페널티 항에 대한 이 개념을 아래 그림에 표현해 보자.

  • 위 그림에서 이차식인 L2 규제 항은 회색 공으로 표현되어 있다.
    • 가중치 값은 규제 예산을 초과할 수 없다. 즉, 가중치 값의 조합이 회색공 바깥에 놓일 수 없다.
    • 반면 우리는 여전히 비용 함수를 최소화해야 한다.
    • 페널티 제약이 있는 상황에서 최선은 L2 회색 공과 규제가 없는 비용 함수의 등고선이 만나는 지점이다.
    • 규제 파라미터 \lambda 가 커질수록 페널티 비용이 빠르게 증가하여 L2 공을 작게 만든다.
    • 예컨대 규제 파라미터를무한대로 증가하면 가중치 값이 L2 공의 중심인 0이 될 것이다.
  • 이 예시에서 중요한 핵심을 정리하면, 우리의 목표는 규제가 없는 비용과 페널티 항의 합을 최소화 하는 것이다.
    • 이는 모델을 학습할만한 충분한 훈련 데이터가 없을 때 편향을 추가하여 모델을 간단하게 만듦으로써 분산을 줄이는 것으로 해석할 수 있다.

L1 규제를 사용한 희소성

  • L1 규제 이면에 있는 주요 개념은 L2의 것과 유사하다.
    • L1 페널티는 가중치 절댓값의 합이기 때문에 아래 그림과 같이 다이아몬드 모양의 제한범위를 그릴 수 있다. (L2 항은 이차식)

  • 그림 4-6에서 w_{1} = 0 일 때 비용 함수의 등고선이 L1 다이아몬드와 만나는 것을 볼 수 있다.
    • L1 규제의 등고선은 날카롭기 때문에 비용 함수의 포물선과 L1 다이아몬드의 경계가 만나는 최적점은 축에 가까이 위치할 가능성이 높다.
    • 이것이 희소성이 나타나는 이유이다.
  • 규제의 강도를 달리하여 그래프를 그리면 아래와 같다.

순서 특성 선택 알고리즘

  • 모델 복잡도를 줄이고 과대적합을 피하는 다른 방법은 특성 선택을 통한 차원 축소(dimensionality reduction)이다. 이는 규제가 없는 모델에서 특히 유용하다.
  • 차원 축소 기버벵는 두 개의 주요 카테고리인 특성 선택(feature selection)과 특성 추출(feature extraction)이 있다.
    • 특성 선택은 원본 특성에서 일부를 선택하고 특성 추출은 일련의 특성에서 얻은 정보로 새로운 특성을 만든다.
    • 여기서는 특성 선택을 살펴보고 5장에서 특성 추출 기법에 대해 배워보겠다.
  • 순차 특성 선택(sequential feature selection) 알고리즘은 탐욕적 알고리즘(greedy search algorithm)으로 초기 d 차원의 특성 공간을 k < d k s=2 차원의 특성 부분 공간으로 축소한다.
    • 특성 선택 알고리즘은 주어진 문제에 가장 관련이 높은 특성 부분 집합을 자동으로 선택하는 것이 목적으로 관계 없는 특성이나 잡음을 제거하여 계산 효율성을 높이고 모델의 일반화 오차를 줄인다.
    • 규제를 제공하지 않는 알고리즘을 사용할 때 유용하다.
  • 전통적인 순차 특성 알고리즘은 순차 후진 선택 (Sequential Backward Selection, SBS)이다.
    • 계산 효율성을 향상하기 위해 모델 성능을 가능한 적게 희생하면서 초기 특성의 부분 공간으로 차원을 축소한다.
    • 과대적합의 문제를 안고 있는 모델이라면 SBS가 예측 성능을 높일 수도 있다.
  • SBS 알고리즘 이면의 아이디어는 매우 간단하다.
    • SBS는 새로운 특성의 부분 공간이 목표하는 특성 개수가 될 때까지 전체 특성에서 순찾거으로 특성을 제거한다.
    • 각 단계에서 어떤 특성을 제거할지 판단하기 위해 최소화할 기준 함수를 정의한다.
    • 기준 함수에서 계산한 값은 어떤 특성을 제거하기 전후의 모델 성능 차이이다.
    • 각 단계에서 제거할 특성은 기준 값이 가장 큰 특성으로 정의할 수 있다.
    • 이해하기 쉽게 말하면 각 단계에서 제거했을 때 성능 손실이 최대가 되는 특성을 제거한다.
  • SBS 정의에 따라 이 알고리즘을 간단히 네 단계로 정의할 수 있다.
    1. 알고리즘을 k = d 로 초기화한다. d 는 전체 특성 공간 X_{d} 의 차원이다.
    2. 조건 x^{-} = arg max J(X_{k} - x) 를 최대화하는 특성 x^{-} 를 결정한다. 여기서 x \in X_{k} 이다.
    3. 특성 집합에서 특성 x^{-} 를 제거한다. 즉 X_{k-1}:=X_{k} - x^{-}; k:=k-1 이다.
    4. k 가 목표하는 특성 개수가 되면 종료한다. 아니면 2단계로 돌아간다.
  • SBS 알고리즘은 아직 사이킷런에 구현되어 있지 않다. 간단한 알고리즘이므로 다음과 같이 직접 구현할 수 있다.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None)

X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y)

sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

fig = plt.figure()
ax = plt.subplot(111)

colors = ['blue', 'green', 'red', 'cyan', 'magenta', 'yellow', 'black', 'pink', 'lightgreen', 'lightblue', 'gray', 'indigo', 'orange']

weights, params = [], []

for c in np.arange(-4, 6.):
   lr = LogisticRegression(solver='liblinear', multi_class='auto', penalty='l1', C=10.**c, random_state=0)
    lr.fit(X_train_std, y_train)
    weights.append(lr.coef_[1])
    params.append(10**c)

weights = np.array(weights)

for column, color in zip(range(weights.shape[1]), colors):
    plt.plot(params, weights[:, column], label=df_wine.columns[column+1], color=color)

plt.axhline(0, color='black', linestyle='--', linewidth=3)
plt.xlim([10**(-5), 10**5])
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.xscale('log')
plt.legend(loc='upper left')
ax.legend(loc='upper center', bbox_to_anchor=(1.38, 1.03), ncol=1, fancybox=True)
plt.show()
  • 위 SBS 구현을 이용해서 KNN 분류기의 정확도를 그리면 다음과 같다.
    • 아래 그림에서 볼 수 있듯 특성 개수가 줄어들었을 때 검증 세트에서 KNN 분류기의 정확도가 향상되었다. 이는 3장의 KNN 알고리즘에서 설명했던 차원의 저주가 감소하기 때문이다.

랜덤 포레스트의 특성 중요도 사용

  • 데이터셋에 유용한 특성을 선택하는 또 다른 방법은 랜덤 포레스트를 사용하는 것이다.
    • 랜덤 포레스트를 사용하면 앙상블에 참여한 모든 결정 트리에서 계산한 평균적인 불순도 감소로 특성 중요도를 측정할 수 있다.
    • 데이터셋이 선형적으로 구분 가능한지 여부를 가정할 필요가 없다.
  • 편리하게도 사이킷런의 랜덤 포레스트 구현은 특성 중요도 값을 이미 수집하고 있다.
    • RandomForestClassifier 모델을 훈련한 후 feature_importances_ 속성에서 확인할 수 있다.
  • 다음은 Wine 데이터셋에서 500개의 트리를 가진 랜덤 포레스트를 훈련하고 각각의 중요도에 따라 13개의 특성에 순위를 매긴 결과이다.
    • 트리 기반 모델은 표준화나 정규화를 할 필요가 없다.

  • 500개의 결정 트리에서 평균적인 불순도 감소를 기반으로 이 데이터셋에서 가장 판별력이 좋은 특성은 Proline, Flavanoids, Color intensity, OD280/OD315 of diluted wines, Alcohol이다.
  • 랜덤 포레스트에서 두 개 이상의 특성이 매우 상관관계가 높다면 하나의 특성은 매우 높은 순위를 갖지만 다른 특성 정보는 완전히 잡아내지 못할 수 있다.
    • 특성 중요도 값을 해석하는 것보다 모델의 예측 성능에만 관심이 있다면 이 문제를 신경 쓸 필요는 없다.
[ssba]

The author

지성을 추구하는 사람/ suyeongpark@abyne.com

댓글 남기기

This site uses Akismet to reduce spam. Learn how your comment data is processed.