이제 머신러닝 알고리즘을 위해 데이터를 준비할 차례입니다. 이 작업을 그냥 수동으로 하는 대신 함수를 만들어 자동화해야 하는 이유가 있습니다.
- 어떤 데이터셋에 대해서도 데이터 변환을 손쉽게 반복할 수 있습니다(예를 들어 다음번에 새로운 데이터셋을 사용할 때).
- 향후 프로젝트에 사용할 수 있는 변환 라이브러리를 점진적으로 구축하게 됩니다.
- 실제 시스템에서 알고리즘에 새 데이터를 주입하기 전에 변환시키는 데 이 함수를 사용할 수 있습니다.
- 여러 가지 데이터 변환을 쉽게 시도해볼 수 있고 어떤 조합이 가장 좋은지 확인하는데 편리합니다.
하지만 먼저 원래 훈련 세트로 복원하고(strat_train_set을 다시 한번 복사합니다), 예측 변수와 타깃 값에 같은 변형을 적용하지 않기 위해 예측 변수와 레이블을 분리하겠습니다(drop()은 데이터 복사본을 만들며 strat)train)set에는 여향을 주지 않습니다).
2.5.1 데이터 정제
대부분의 머신러닝 알고리즘은 누락된 특성을 다루지 못하므로 이를 처리할 수 있는 함수를 몇개 만들겠습니다. 앞서 tatal)bedrooms 특성에 값이 없는 경우를 보았는데 이를 고쳐보겠습니다. 방법은 세 가지 입니다.
데이터프레임의 dropna(), drop(), fillna() 메서드를 이용해 이런 작업을 간단하게 처리할 수 있습니다.
옵션 3을 선태갛면 훈련 세트에서 중간값을 계산하고 누락된 값을 이 값으로 채워 넣어야 합니다. 하지만 계산된 중간 값을 저장하는 것 또한 잊지 말아야 합니다. 나중에 시스템을 평가할 때 테스트 세트에 있는 누락된 값을 바꾸기위해 필요하고 시스템이 실제 운영될 때 새로운 데이터에서 누락된 값을 바꿔야 하기 때문입니다.
사이킷런의 imputer는 누락된 값을 손쉽게 다루도록 해줍니다. 어떻게 사용하는지 살펴보죠, 먼저 누락된 값을 특성의 중간값으로 대체한다고 지정하고 Imputer의 객체를 생성합니다.
중간값이 수치형 특성에서만 계산될 수 있기 때문에 텍스트 특성인 ocean_proximity를 제외한 데이터 복사본을 생성합니다.
이제 imputer 개체의 fit() 메서드를 사용해 훈련 데이터에 적용할 수 있습니다.
imputer.fit(housing_num)
imputer는 각 특성의 중간값을 계산해서 그 결과를 객체의 statistics_속성에 저장합니다.
total_bedrooms 특성에만 누락된 값이 있지만 나중에 시스템에 서비스될 때 새로운 데이터에서 어떤값이 누락될지 확신할 수 없으므로 모든 수치형 특성에 imputer를 적용하는 것이 바람직합니다.
사이킷런의 설계 철학
사이킷런의 API는 아주 잘 설계되어 있습니다. 주요 설계 원칙은 다음과 같습니다.
- 일관성. 모든 객체가 일관되고 단순한 인터페이스를 공유합니다.
- 추정기(estimator). 데이터셋을 기반으로 일련의 모델 파라미터들을 추정하는 객체를 추정기라고 합니다(예를 들어 imputer 객체는 추정기입니다). 추정 자체는 fit()메서드에 의해 수행되고 하나의 매개변수로 하나의 데이터셋만 전달합니다(지도 학습 알고리즘에서 매개변수가 두 개로, 두 번째 데이터셋은 레이블을 담고 있습니다). 추정 과정에서 필요한 다른 매개변수들은 모두 하이퍼ㄹ파라미터로 간주되고(예를 들면 imputer 객체의 strategy 매개변수), 인스턴스 변수로 저장됩니다(보통 생성자의 매개변수로 전달합니다).
- 변환기(transformer). (imputer 같이) 데이터셋을 변환하는 추정기를 변환기라고 합니다. 여기서도 API는 매우 단순합니다. 변환은 데이터셋을 매개변수로 전달받느 transform()메서드가 수행합니다. 그리고 변환된 데이터셋을 반환합니다. 이런 변환은 일반적으로 imputer의 경우와 같이 학습된 모델 파라미터에 의해 결정됩니다. 모든 변환기는 fit()과 transform()을 연달아 호출하는 것과 동일한 fit)transform()메서드도 가지고 있습니다(이 따금 fit_transform()이 최적화되어 있어서 더 빠릅니다).
- 예측기(predictor). 일부 추정기는 주어진 데이터셋에 대해 예측을 만들 수 있습니다. 예를 들어 앞 장에 나온 LinearRegression모델이 예측기입니다. 어떤 나라의 1인당 GDP가 주어질 때 삶의 만족도를 예측했습니다. 예측기의 PREDICT()메서드는 새로운 데이터셋을 받아 이에 상응하는 예측값을 반환합니다. 또한 테스트 세트(지도 학습 알고리즘이라면 레이블도 함께)를 사용해 예측의 품질을 측정하는 score()메서드를 가집니다.
- 검사 기능. 모든 측정기의 하이퍼파라미터는 공개(public) 인스턴스 변수로 직접 접근할 수 있고(예를 들면 imputer.strategy), 모든 추정기의 학습된 모델 파라미터도 접미사로 밑줄을 붙여서 공개 인스턴스 변수로 제공됩니다(예를 들면 imputer.statistics_).
- 클래스 남용 방지. 데이텃셋을 별도의 클래스가 아니라 넘파이 배열이나 사이파이 회소(sparse)행렬로 표현합니다. 하이퍼파라미터는 보통의 파이썬 문자열이나 숫자입니다.
- 조합성. 기존의 구성요소를 최대한 재사용합니다. 앞으로 보겠지만 예를 들어 여러 변환기를 연결한 다음 마지막에 추정기 하나를 배치한 Pipeline추정기를 쉽게 만들 수 있습니다.
- 합리적인 기본값. 사이킷런은 일단 돌아가는 기본 시스템을 빠르게 만들 수 있도록 대부분의 매개 변수에 합리적인 기본값을 지정해 두었습니다.
2.5.2 텍스트와 범주형 특성 다루기
앞서 범주형 특성 ocean_proximity가 텍스트라 중간값을 계산할 수 없어서 그냥 남겨뒸습니다.
대부분의 머신러닝 알고리즘은 숫자형을 다루므로 이 카테고리를 텍스트에서 숫자로 바꾸도록 하겠습니다. 이를 위해 각 카테고리를 다른 정숫값으로 매핑해주는 판다스 factorize()메서드를 사용합니다.
훨씬 나아져쎄요. 이제 housing_cat_encoded는 완전히 숫자입니다. factorize()메서드는 카테고리 리스트로 반환해줍니다.
이 표현 방식의 문제는 머신러닝 알고니즘이 가까이 있는 두 값이 떨어져 있는 두 값보다 더 비슷하다고 생각한다는 점입니다. 실제로는 그렇지 않습니다(예를 들어 카테고리 0과 1보다 카테고리 0과 4가 더 시슷합니다). 이 문제는 일반적으로 카테고리별 이진 특성을 만들어 해결합니다. 카테고리가 '<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
사이킷런은 숫자로 된 범주형 값을 원0핫 벡터로 바꿔주는 OneHotEncoder를 제공합니다. 카테고리들을 원-핫 벡터로 인코딩해보겠습니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">fit_transform()메서드는 2차원 배열을 넣어줘야 하는데 housing_cat_encoded는 1차원 배열이므로 구조를 바꿔야 합니다. 또한 출력을 보면 넘파이 배열이 아니고 사이파이SciPy 회소 행렬sparse matrix입니다. 이는 수천개의 카테고리가 있는 범주형 특성일 경우 매우 효울적입니다. 이런 특성을 원-핫 인크딩하면 열이 수천 개인 행렬로 변하고 각 행은 1이 하나뿐이고 그 외에는 모두 0으로 채워져 있을 것입니다.. 0을 모두 메모리에 저장하는 것은 낭비이므로 회소 행렬은 0이 아닌 원소의 위치만 저장합니다. 이 행렬의 거의 일반적인 2차원 배열처럼 사용할 수있지만 (밀집된)넘파이 배열로 바꾸려면 toarrray()메서드를 호출하면 됩니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">텍스트 카테고리를 숫자 카테고리로, 숫자 카테고리를 원-핫 벡터로 바꿔주는 이 두가지 변환을 CategoricalEncoder를 사용하여 한번에 처리할 수 있습니다. 이 파이썬 클래스는 사이킷런 0.19.0과 그 이전 버전에는 포함되어 있지 않습니다. 하지만 곧 추가될 예정이라 아마 이 책을 읽을 때쯤이면 이미 포함되어 있을 가증성이 많습니다. 그렇지 않다면 이 장의 주피터 노트북에서 가져와 사용할 수 있습니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">기본적으로 CategoricalEncoder는 회소 행렬을 축결하지만 밀집행렬을 원할 경우 encoding 매개변수를 "onehot-dense"로 지정할 수 있습니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">categories_ 인스턴스 변수를 사용해 카테고리 리스트를 얻을 수 있습니다. 이 리스트는 카테고리 특성마다 1D 카테고리 배열을 담고 있습니다(여기에서는 하나의 카테고리 특성만 있기 때문에 배열 하나만을 담고 있는 리스트입니다).1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">Tip 카테고리 특성이 담을 수 있는 카테고리 수가 많다면(예를 들면 국가 코드, 직업, 생물 종류 등) 원-핫 인코디은 많은 수의 입력 특성을 만듭니다. 이는 훈련을 느리게 하고 성능을 감소시킬 수 있습니다. 이런 경우에는 임베딩 이라고 하는 조금 더 조밀한 표현을 사용할 수 있지만 신경망에 대한 이해가 필요합니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">2.5.3 나만의 변환기1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">사이킷런이 유용한 변환기를 많이 제공하지만 특별한 정제 작업이나 어떤 특성을 조합하는 등의 작업을 위해 자신만의 변환기를 만들어야 할 때가 있습니다. 내가 만든 변환기를 (파이프라인과 같은) 사이킷런의 긴으과 매끄럽게 연동하고 싶을 것입니다. 사이킷런은 (상속이 아닌)덕 타이핑(duck typing)을 지원하므로 fit()(self를 반환), transform(), fit_transform()메서드를 구현한 파이썬 클래스를 만들면 됩니다. 마지막 메서드 TransformerMixin을 상속하면 자동으로 생성됩니다. 또한 BaseEstimator를 상속하면(그리고 생성자에 *args 나 **kargs를 사용하지 않으면) 하이퍼파라미터 튜닝에 필요한 두 메서드(get_params()와 set_params())를 추가로 얻게 됩니다. 옐르 들어 다음 앞서 이야기한 조합 특성을 추가하는 간단한 변환기입니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">이 경우에는 변환기가 add_bedrooms_per_room 하이퍼파라미터 하나를 가지고 있고 기본값을 True로 지정합니다(합리적인 기본값을 주는 것이 좋습니다). 이 특성을 추가하는 것이 머신러닝 알고리즘에 도움이 될지 안될지 이 하이퍼파라미터로 쉽게 확인해 볼 수 있습니다. 일반적으로 100% 확신이 없는 모든 데이터 준비 단계에 대해 하이퍼라라미터를 추가할 수 있습니다. 이런 데이터 준비 단곌르 자동화할수록 더 많은 조합을 자동으로 시도해 볼 수 있고 최상의 조합을 찾을 가능성을 매우 높여줍니다(그리고 시간도 많이 절약됩니다).1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">2.5.4 특성 스케일링1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">데이터에 적용할 가장 중요한 변환 중 하나가 특성 스케일링입니다. 몇 가지를 빼고는, 머신러닝 알고리즘은 입력 숫자 특성들의 스케일이 많이 다르면 잘 작동하지 않습니다. 주택가격 데이터도 이에 해당합니다. 즉, 전체 방 개수의 범위는 6에서 39,320인 반면 중간 소득의 범위는 0에서 15까지입니다. 타갓 값에 대한 스케일링을 일반적으로 불필요합니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">모든 특성의 범위를 같도록 만들어주는 방법으로 min-max 스케일링과 표준화standardization가 널리 사용됩니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">min-max 스케일링은 매우 간단합니다(많은 사람이 이를 정규화 라고 부릅니다. 0~1 범위에 들도록 값을 이동하고 스케일을 조정하면 됩니다. 데이터에서 최솟값을 뺀 후 최댓값과 최솟값의 차이로 나누면 이렇게 할 수 있습니다. 사이킷런에는 이에 해당하는 MinMaxScaler 변환기를 제공합니다. 0~1사이를 원하지 않는다면 feature_range 매개변수로 범위를 변경할 수 있습니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">표준화는 많이 다릅니다. 먼저 평균을 뺀 후(그래서 표준화를 하면 항상 평균이 0이 됩니다) 표준편차로 나누어 결과 분포의 분산이 1이 되도록 합니다. min-max스케일링과는 달리 표준화는 범위의 상한과 하한이 없어 어떤 알고리즘에는 문제가 될 수 있습니다.(예를 들어 신경망은 종종 입력값이 범위로 0에서 1사이르 기대합니다), 그러나 표준화는 이상치에 영향을 덜 받습니다. 예를 들어 중간 소득을(잘못해서) 100이라 입력한 구역을 가정해 봅시다. min-max스케일링은 0~15사이의 모든 다른 값으 0~0.15로 만들어버리겠지만, 표준화는 크게 영향받지 않습니다. 사이킷런에는 표준화를위한 StandardScaler 변환기가 있습니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">CAUTION_1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">모든 변환기에서 스케일링은(테스트 세트가 포함된) 전체 데이터가 아니고 훈련 데이터에 대해서만 fit()메서드를 적용해야 합니다. 그런 다음 훈련 세트와 테스트세터(그리고 새로운 데이터)에 대해 transform()메서드를 사용합니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">2.5.5 변환 파이프라인1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">앞서 보았듯이 변환 단계가 많으며 정확한 숫서대로 실행되어야 합니다. 다행이 사이킷런에는 연소된 변환을 순서대로 처리할 수 있도록 도와주는 Pipeline클래스가 있습니다. 다음은 숫자 특성을 처리하는 간단한 파이프라인입니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">Pipeline은 연속된 단계를 나타내는 이름/추정기 쌍의 목록을 입력으로 받습니다. 마지막 단계에는 변환기와 추정기를 모두 사용할 수 있고 그 외에는 모두 변환기여야 합니다(즉, fit_transform()메서드를 가지고 있어야 합니다). 이름은 무엇이든 상관없지만, 이중 밑줄 문자(__)는 포함하지 않아야 합니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">파이프라인의 fit()메서드를 호출하면 모든 변환기의 fit_transfor()메서드를 순서대로 호추하면서 한 단계의 출력을 다음 단계의 입력으로 전달합니다. 마지막 단계에서는 fit()메서드만 호출합니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">파이프라인 객체는 마지막 추정기와 동일한 메서드를 제공합니다. 이 예에서는 마지막 추정기가 변환기 StandardScaler이므로 파이프라인이 데이터에 대해 모든 변환을 순서대로 적용하는 transform() 메서드를 가지고 있습니다(또한 fit()과 transform()을 차례대로 호출하는 대신에 사용할 수 있는 fit_transform() 메서드도 가지고 있습니다).1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">수치형 컬럼을 넘파이 배열로 추출하는 대신 판다스의 데이터프레임을 파이프라인에 직접 주입할 수 있다면 좋을 것입니다. 사이킷런의 판다스의 데이터프레임을 다룰 수는 없지만 이를 처리하는 변환기를 직접 만들 수는 있습니다.1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
DataFrameSelector는 나머지는 버리고 필요한 특성을 선택하여 데이터프레임을 넘파이 배열로 바꾸는 식으로 데이터를 변환합니다. 이를 이용해 데이터프레임을 받아 수치형만 다루는 파이프라인을 손쉽게 만들 수 있습니다. 수치형 특성을 선택한 DataFrameSelector로 파이프라인을 시작해서 앞서 이야기한 다른 전처리 단계들을 나열합니다. 범주형 특성을 다루는 또 다른 파이프라인도 DataFrameSelector로 범주형 특성을 선택하고 CategoricalEncoder를 적용하면 간단히 만들 수 있습니다.
하지만 어떻게 이 두 파이프라인을 하나의 파이프라인으로 합칠 수 있을까요? 정답은 사이킷런의 ReatureUnion입니다. 변환기 목록(또는 모두 변환기로 이뤄진 파이프라인)을 전달하고 transform()메서드를 호출하면 각 변환기의 transform()메서드를 병렬로 실행합니다. 그런 다음 각 변환기의 결과를 합쳐 반환합니다(물론 fit() 메서드를 호춯하면 각 변환기의 fit()메서드를 실행합니다) 숫자형과 범주형 특성을 모두 다루는 전체 파이프라인은 다음과 같습니다.
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p=""> 1h>
<1h 0="" 1="" encoding="" ocean="" one-hot="" p="">
1h>
댓글 없음:
댓글 쓰기