Search
Duplicate

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

누락된 데이터 다루기

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

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

(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^{(i)}에서 새로운 값 xnorm(i)x_{norm}^{(i)}을 계산한다.
xnorm(i)=x(i)xminxmaxxminx_{norm}^{(i)} = {x^{(i)} - x_{min} \over x_{max} - x_{min}}
최소-최대 스케일 변환을 통한 정규화는 범위가 정해진 값이 필요할 때 유용하게 사용할 수 있는 일반적인 기법이다.
표준화는 많은 머신러닝 알고리즘 특히 경사하강법 같은 최적화 알고리즘에서 널리 사용된다.
표준화를 사용하면 특성의 평균을 0에 맞추고 표준편차를 1로 만들어 정규분포와 같은 특징을 가지도록 만든다. 이는 가중치를 더 쉽게 학습할 수 있도록 만든다.
또 표준화는 이상치 정보가 유지되기 때문에 제한된 범위로 데이터를 조정하는 최소-최대 스케일 변환에 비해 알고리즘이 이상치에 덜 민감하다.
xsid(i)=x(i)μxσxx_{sid}^{(i)} = {x^{(i)} - \mu_{x} \over \sigma_{x}}
여기서 μx\mu_{x}는 어떤 특성의 샘플 평균이고 σx\sigma_{x}는 그에 해당하는 표준 편차이다.

유용한 특성 선택

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

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

3장에서 L2 규제(L2 gerularization)는 개별 가중치 값을 제한하여 모델 복잡도를 줄이는 한 방법이라고 설명했다.
가중치 벡터 ww의 L2 규제는 다음과 같이 정의한다.
L2:w22=j=1mwj2L2:\|w\|_{2}^{2} = \sum_{j=1}^{m} w_{j}^{2}
모델 복잡도를 줄이는 또 다른 방법은 L1 규제(L1 gerularization)이다.
L1:w2=j=1mwjL1:\|w\|_{2} = \sum_{j=1}^{m} |w_{j}|
이는 가중치 제곱을 그냥 가중치 절댓값으로 바꾼 것이다.
L2 규제와 대조적으로 L1 규제는 보통 희소한 특성 벡터를 만든다.
대부분의 특성 가중치가 0이 된다.
실제로 관련 없는 특성이 많은 고차원 데이터셋일 경우 이런 희소성이 도움이 될 수 있다.
특히 샘플보다 관련 없는 특성이 더 많은 경우이다.
이런 맥락으로 보면 L1 규제는 특성 선택의 기법이 될 수 있다.

L2 규제의 기하학적 해석

L2 규제는 비용 함수에 페널티 항(penalty term)을 추가한다.
규제가 없는 비용 함수로 훈련한 모델에 비해 가중치 값을 아주 작게 만드는 효과를 낸다.
L1 규제가 어떻게 희소성을 만드는지 이해하기 위해 규제의 기하학적 해석에 대해 고찰해 보자
두 개의 가중치 값 w1w_{1}과 w2w_{2}에 대한 볼록한 비용 함수의 등고선을 그려보면 다음과 같다.
2장 아달린에서 사용했던 제곱 오차합(SSE) 비용 함수가 구 모양이어서 로지스틱 회귀의 비용 함수보다 그리기 쉽다.
여기서 얻은 개념은 로지스틱 회귀에도 동일하게 적용 가능하다.
그림 4-4와 같이 우리의 목표는 훈련 데이터에서 비용 함수를 최소화하는 가중치 값의 조합을 찾는 것임을 기억하라 (타원의 중심 포인트)
규제를 더 작은 가중치를 얻기 위해 비용 함수에 추가하는 페널티 항으로 생각할 수 있다. 다른 말로 하면 큰 가중치를 제한한다.
규제 파라미터 λ\lambda로 규제의 강도를 크게 하면 가중치가 0에 가까워지고 훈련 데이터에 대한 모델 의존성은 줄어든다.
L2 페널티 항에 대한 이 개념을 아래 그림에 표현해 보자.
위 그림에서 이차식인 L2 규제 항은 회색 공으로 표현되어 있다.
가중치 값은 규제 예산을 초과할 수 없다. 즉, 가중치 값의 조합이 회색공 바깥에 놓일 수 없다.
반면 우리는 여전히 비용 함수를 최소화해야 한다.
페널티 제약이 있는 상황에서 최선은 L2 회색 공과 규제가 없는 비용 함수의 등고선이 만나는 지점이다.
규제 파라미터 λ\lambda가 커질수록 페널티 비용이 빠르게 증가하여 L2 공을 작게 만든다.
예컨대 규제 파라미터를무한대로 증가하면 가중치 값이 L2 공의 중심인 0이 될 것이다.
이 예시에서 중요한 핵심을 정리하면, 우리의 목표는 규제가 없는 비용과 페널티 항의 합을 최소화 하는 것이다.
이는 모델을 학습할만한 충분한 훈련 데이터가 없을 때 편향을 추가하여 모델을 간단하게 만듦으로써 분산을 줄이는 것으로 해석할 수 있다.

L1 규제를 사용한 희소성

L1 규제 이면에 있는 주요 개념은 L2의 것과 유사하다.
L1 페널티는 가중치 절댓값의 합이기 때문에 아래 그림과 같이 다이아몬드 모양의 제한범위를 그릴 수 있다. (L2 항은 이차식)
그림 4-6에서 w1=0w_{1} = 0 일 때 비용 함수의 등고선이 L1 다이아몬드와 만나는 것을 볼 수 있다.
L1 규제의 등고선은 날카롭기 때문에 비용 함수의 포물선과 L1 다이아몬드의 경계가 만나는 최적점은 축에 가까이 위치할 가능성이 높다.
이것이 희소성이 나타나는 이유이다.
규제의 강도를 달리하여 그래프를 그리면 아래와 같다.

순서 특성 선택 알고리즘

모델 복잡도를 줄이고 과대적합을 피하는 다른 방법은 특성 선택을 통한 차원 축소(dimensionality reduction)이다. 이는 규제가 없는 모델에서 특히 유용하다.
차원 축소 기버벵는 두 개의 주요 카테고리인 특성 선택(feature selection)과 특성 추출(feature extraction)이 있다.
특성 선택은 원본 특성에서 일부를 선택하고 특성 추출은 일련의 특성에서 얻은 정보로 새로운 특성을 만든다.
여기서는 특성 선택을 살펴보고 5장에서 특성 추출 기법에 대해 배워보겠다.
순차 특성 선택(sequential feature selection) 알고리즘은 탐욕적 알고리즘(greedy search algorithm)으로 초기 dd차원의 특성 공간을 k<dk < d인  ks=2ks = 2차원의 특성 부분 공간으로 축소한다.
특성 선택 알고리즘은 주어진 문제에 가장 관련이 높은 특성 부분 집합을 자동으로 선택하는 것이 목적으로 관계 없는 특성이나 잡음을 제거하여 계산 효율성을 높이고 모델의 일반화 오차를 줄인다.
규제를 제공하지 않는 알고리즘을 사용할 때 유용하다.
전통적인 순차 특성 알고리즘은 순차 후진 선택 (Sequential Backward Selection, SBS)이다.
계산 효율성을 향상하기 위해 모델 성능을 가능한 적게 희생하면서 초기 특성의 부분 공간으로 차원을 축소한다.
과대적합의 문제를 안고 있는 모델이라면 SBS가 예측 성능을 높일 수도 있다.
SBS 알고리즘 이면의 아이디어는 매우 간단하다.
SBS는 새로운 특성의 부분 공간이 목표하는 특성 개수가 될 때까지 전체 특성에서 순찾거으로 특성을 제거한다.
각 단계에서 어떤 특성을 제거할지 판단하기 위해 최소화할 기준 함수를 정의한다.
기준 함수에서 계산한 값은 어떤 특성을 제거하기 전후의 모델 성능 차이이다.
각 단계에서 제거할 특성은 기준 값이 가장 큰 특성으로 정의할 수 있다.
이해하기 쉽게 말하면 각 단계에서 제거했을 때 성능 손실이 최대가 되는 특성을 제거한다.
SBS 정의에 따라 이 알고리즘을 간단히 네 단계로 정의할 수 있다.
1.
알고리즘을 k=dk = d로 초기화한다. dd는 전체 특성 공간 XdX_{d}의 차원이다.
2.
조건 x=argmaxJ(Xkx)x^{-} = arg \max J(X_{k} - x)를 최대화하는 특성 xx^{-}를 결정한다. 여기서  xXkx \in X_{k}이다.
3.
특성 집합에서 특성 xx^{-}를 제거한다. 즉  Xk1:=Xkx;k:=k1X_{k-1}:=X_{k} - x^{-}; k:=k-1이다.
4.
kk가 목표하는 특성 개수가 되면 종료한다. 아니면 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()
Python
위 SBS 구현을 이용해서 KNN 분류기의 정확도를 그리면 다음과 같다.
아래 그림에서 볼 수 있듯 특성 개수가 줄어들었을 때 검증 세트에서 KNN 분류기의 정확도가 향상되었다. 이는 3장의 KNN 알고리즘에서 설명했던 차원의 저주가 감소하기 때문이다.

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

데이터셋에 유용한 특성을 선택하는 또 다른 방법은 랜덤 포레스트를 사용하는 것이다.
랜덤 포레스트를 사용하면 앙상블에 참여한 모든 결정 트리에서 계산한 평균적인 불순도 감소로 특성 중요도를 측정할 수 있다.
데이터셋이 선형적으로 구분 가능한지 여부를 가정할 필요가 없다.
편리하게도 사이킷런의 랜덤 포레스트 구현은 특성 중요도 값을 이미 수집하고 있다.
RandomForestClassifier 모델을 훈련한 후 feature_importances_ 속성에서 확인할 수 있다.
다음은 Wine 데이터셋에서 500개의 트리를 가진 랜덤 포레스트를 훈련하고 각각의 중요도에 따라 13개의 특성에 순위를 매긴 결과이다.
트리 기반 모델은 표준화나 정규화를 할 필요가 없다.
500개의 결정 트리에서 평균적인 불순도 감소를 기반으로 이 데이터셋에서 가장 판별력이 좋은 특성은 Proline, Flavanoids, Color intensity, OD280/OD315 of diluted wines, Alcohol이다.
랜덤 포레스트에서 두 개 이상의 특성이 매우 상관관계가 높다면 하나의 특성은 매우 높은 순위를 갖지만 다른 특성 정보는 완전히 잡아내지 못할 수 있다.
특성 중요도 값을 해석하는 것보다 모델의 예측 성능에만 관심이 있다면 이 문제를 신경 쓸 필요는 없다.