0701 실습 파일 Feature Engineering
변수 스케일링(Feature Scaling)
트리 기반의 모델은 피처 스케일링이 필요없다. 트리 기반의 모델은 절대적인 크기보다 상대적인 크기의 영향을 받기 때문이다.
피처 스케일링이란, 피처의 범위를 조정하여 정규화하는 것을 의미한다. 일반적으로 분산과 표준편차를 조정하여 정규분포 형태를 띄게하는 것이 목표이다. (빨간 선이 표준 정규 분포, 위키백과)
Q. 왜 Feature Scaling을 해야할까?
A. 1. 피처의 범위가 다르면 피처끼리 비교가 어려우며 일부 머신러닝모델에서 제대로 작동하지 않는다.
ex. 주식데이터의 등락폭을 한 그래프 안에서 비교할 수 있다.(1주 10만원 주식 vs. 1주 1000원 주식)
2. 피처 스케일링이 잘 되어 있으면, 경사하강법(Gradient Descent)과 KNN, Clustering 등의 거리 기반 알고리즘에 속도와 성능을 올려준다.
피처 스케일링 기법(3가지+a)
참고: 어떤 스케일러를 쓸 것인가?
- 표준화(Z-score scaling, Normalization-Standardization)
- 정의: 평균을 제거하고 데이터를 단위 분산으로 조정한다
- 공식:
z = (X - X.mean) / std
- 특징: 평균을 0, 표준편차를 1로 만들어준다. 그러나 이상치가 있다면 평균과 표준편차에 영향을 미쳐 변환된 데이터의 확산은 매우 달라지게 되므로 이상치가 있는 경우 균형 잡힌 척도를 보장할 수 없다.
- 코드
from sklearn.preprocessing import StandardScaler ss = StandardScaler() print(ss.fit(train_data)) train_data_ss= ss.transform(train_data)
- Min-Max Scaling
- 정의: 모든 feature 값이 0~1사이에 있도록 데이터를 재조정한다
- 공식:
X_scaled = (X - X.min) / (X.max - X.min)
- 특징: 다만 이상치가 있는 경우 변환된 값이 매우 좁은 범위로 압축될 수 있어 이상치의 존재메 매우 민감하다
from sklearn.preprocessing import MinMaxScaler mm = MinMaxScaler() print(mm.fit(train_data)) train_data_mm= mm.transform(train_data)
- Robust Scaling
- 정의: 아웃라이어의 영향을 최소화한 기법이다. 중앙값(median)과 IQR(interquartile range)을 사용한다.
- 공식:
X_scaled = (X - X.median) / IQR*
* IQR: 상위 75%의 값-하위25%의 값 - 특징: 중앙값이 0, 이상치 영향이 덜 받는다(Robust, 강건하다). StandardScaler와 비교하면 표준화 후 동일한 값을 더 넓게 분포시키고 있다.
from sklearn.preprocessing import RobustScaler rs = RobustScaler() print(rs.fit(train_data)) train_data_rs= rs.transform(train_data)
=> 스케일링을 하면 분포가 변하지 않는다
정규분포와 트랜스포메이션
Q. 왜 고르게 분포된 값이 예측에 유리할까?
(히스토그램에서는) 값의 길이에 따라 나눈다. 절대평가 vs. 상대평가, cut 기반(Equal width binning)
Q. 정규화랑 트렌스포메이션 중 무엇부터 해야할까?
A. 표준 정규 분포(평균 0)에 가까운 것은 SalePrice_log1p_ss. 즉 log를 취한다음 Standardizationd Scaling을 실행하는 것이 표준 정규 분포에 가장 가깝게 된다.
cf. SalePrice_log1p는 정규분포에 가까운 형태다. 스케일 값이 영향을 미치는 모델에서는 정규분포보다 표준정규분포로 만들어주면 더 나은 성능을 낼 수도 있다. 다만 표준정규분포는 값이 왜곡될 수 있으므로 성능의 향상을 담보할 수는 없다. 진리의 케바케
로그함수와 지수함수
log transformation을 적용할 때,로그 함수는 x가 0으로 수렴할 때, y가 -∞로 발산하는 것을 주의해야 한다. 로그의 진수 조건에 따라 x>0을 만족해야 한다. 이에 주어진 값에 1을 더해서 적용해주는 것이 더 안전하다.
한편 로그함수와 지수함수는 역함수 관계이므로 label 값에 로그를 취했을 경우, 지수함수(np.exp1m)을 이용하여 원래의 데이터로 돌아갈 수 있다.
- 서로 역함수 관계
이산화
- equal with binning
- 정의: 범위를 n개의 bins로 나누어 bins의 길이가 동일하다(절대평가).
- 특징: 편향된 분포에 민감하다
- 코드:
pd.cut(train['SalePrice'], bins=4, labels=[1, 2, 3, 4])
- equal frequency binning
- 정의: 분위를 기준으로 n개의 bins로 나누어 bins의 데이터의 개수가 동일하다(상대평가).
- 특징: 알고리즘 성능 향상에 도움이 될 수 있으나 임의의 binning이 대상과의 관계를 방해할 수 있다.
- 코드:
pd.qcut(train['SalePrice'], q=4, labels=[1, 2, 3, 4])
* q: Number of quantiles
인코딩(Encoding)
인코딩은 Categorical Feature를 Numerical Feature로 변환하는 과정이다. 최근 *부스팅3대장 알고리즘 중에는 범주형 데이터를 알아서 처리해 주는 알고리즘도 있지만 사이킷런에서는 범주형 데이터를 피처로 사용하기 위해서는 별도의 변환작업이 필요하다.
Q. 왜 인코딩을 해야할까?
A. 1. 데이터 시각화에 유리하다(lineplot, scatterplot 등)
2. 머신러닝 모델은 Categorical Feature를 처리하지 못한다. 처리하더라도 인코딩 프로세스가 내재된 모델일 가능성이 높다.
* 부스팅3대장: Xgboost, LightGBM, CatBoost
- Ordinal Encoding
- 정의: Categorical Feature를 Numerical Feature 중 Ordinal Feature로 변환한다.
- 특징:
1) 숫자를 지정하지 않으면 0부터 1씩 증가하는 임의의 정수가 지정된다.
2) 원래 있던 값이 일대일 대응하여 숫자로만 바꿨을 뿐, 정보 자체는 동일하다.
3) 데이터의 값의 차이(숫자의 크기 차이)가 의미가 있으면 상관 없으나 순서가 없는 데이터에 적용하면 잘못된 해석을 줄 수 있다. - 코드:
train['MSZoning'].astype('category').cat.codes
- 사이킷런 예제
from sklearn.preprocessing import OrdinalEncoder enc = OrdinalEncoder() X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']] enc.fit(X) print(enc.transform([['female', 'from US', 'uses Safari']])) print(enc.categories_)
cf.Label Encoding은 뭐죠?
라벨과 오디널 인코딩은 거의 비슷해보이지만(사실 거의 같음), 라벨 인코더는 기본적으로 딱히 순서에 의미가 없는 데이터에 사용되게 된다.(예: 지역코드), 오디널 인코딩은 이름처럼 기본적으로 각 카테고리에 고유한 숫자값을 지정해주는 것은 같지만 순서에 의미가 있는 데각 숫자에 의미가 필요할 경우 사용하게 된다. (예: 우수: 3 중간: 2 나쁨: 1)
과거에는 라벨인코딩을 꼭 써야했지만, 최근에는 오디널 인코딩으로 대체되는 영역이 늘어나면서 코딩이 단순해지고 있다. 다만 딥러닝이나 몇몇 분야에서는 라벨 인코딩을 필수적으로 요구된다.
- 사이킷런 공식문서도 참고하세요!
- Label Encoding
- 사이킷런 예제
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() le.fit(["paris", "paris", "tokyo", "amsterdam"]) print(list(le.classes_)) print(["tokyo", "tokyo", "paris"]) print(le.transform(["tokyo", "tokyo", "paris"])) list(le.inverse_transform([2, 2, 1]))
- One-Hot-Encoding
- 정의: Categorical Feature의 고유값을 피처로 만들어 bool 변수(0 또는 1)로 변환한다.
- 특징:
1) 해당 피처의 모든 정보를 유지한다.
2) 그러나 해당 피처가 너무 많은 고유값을 가지고 있을 땐 지나치게 많은 Feature가 생성된다.
=> 계산에 시간이 오래 걸리고 과대적합(overfitting) 우려가 있다. - 코드:
pd.get_dummies(train['MSZoning'])
- 사이킷런 예제
from sklearn.preprocessing import OneHotEncoder enc = OneHotEncoder() X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']] enc.fit(X) enc_out = enc.transform([['female', 'from US', 'uses Safari'], ['male', 'from Europe', 'uses Safari']]).toarray() print(enc_out) print(enc.get_feature_names_out())
cf. enc_out.get_feature_names_out()
은 틀린코드다. enc_out
이 인코딩이 결과된(array)므로 적용할 수 없다.
Q. 왜 사이킷런의 원핫인코딩 방법을 알아야 할까?
A. 판다스로 인코딩했을 때 문제점은 테스트 데이터에 대한 인코딩에서 발생합니다. 사이킷런의 원핫인코딩은 train 데이터로만 학습해서 진행하기에 test에만 존재하는 데이터를 인코딩하지 않습니다. 그러나 판다스는 이 기능이 없어서 test셋과 train셋에 get_dummies하게 될 경우 피처의 수, 종류가 다를 수 있습니다. 그러나 학습과 예측을 하기 위해서는 피처가 동일해야 합니다. 이를 해결하기 위해선 concat으로 전처리를 하거나 set을 이용해서 고유값을 비교해야 하는데 너무 번거롭습니다. 또한 경진대회마다 test셋을 피처로 사용하지 말라는 정책이 있는 경우도 있고, 현실에서 test 데이터는 어떤 것인지 모르기 때문에 판다스를 이용한 원핫인코딩을 권장하지 않습니다.
파생변수
변수 생성(Feature Generation)은 이미 존재하는 변수로부터 여러가지 방법을 이용해 새로운 변수를 만들어내는 것이다.
데이터 분석가가 필요에 따라 자유롭게 생성하면 되지만 파생변수의 생성이 항상 데이터 해석을 편리하게 하고 머신러닝 성능을 올리는 것이 아니다.
다항식 전개(Polynomial Expansion)
주어진 다항식의 차수 값에 기반하여 파생변수를 생성할 수 있다. 예를 들어 [a, b]가 Feature로 주어질 때 다항식의 최대 차수(degree)를 2로 지정한다면, 이 경우 새로 생성될 수 있는 Polynomial Feature는 [1, a, b, a^2, ab, b^2]이다(완전제곱식). 이러한 파생변수 생성은 EDA보다는 머신러닝에 도움이 될 때가 있다. 머신러닝 모델은 여러가지 feature에 기반할 때 성능이 뛰어나므로, 이를 소위 '뻥튀기'해주는 것이다. 소수의 feature에만 기반한 label 값은 과대적합이 일어날 확률이 높으므로, 해당 방법을 사용하면 모델의 안정성을 향상시킬 수도 있다.
아래와 같이 uniform한 데이터에는 Polynomial Expansion이 도움이 될 수 있다.
아래와 같이 균등 분포된 그래프를 히스토그램을 그렸을 때 어딘가는 많고 적은 데이터가 있다면 그것도 특징이 될 수 있는데, 특징이 잘 구분되지 않는다면 power transform 등을 통해 값을 제곱을 해주거나 하면 특징이 좀 더 구분되어 보일 수 있다.
- 사이킷런 예제
from sklearn.preprocessing import PolynomialFeatures
X = np.arange(6).reshape(3, 2)
print(X)
poly = PolynomialFeatures(degree=2)
X_poly = poly.fit_transform(X)
pd.DataFrame(X_poly, columns=poly.get_feature_names_out())
![Untitled](https://media.discordapp.net/attachments/1019429418755694632/1039809791347937280/image.png)
'멋쟁이사자처럼 AIS7 > 오늘코드' 카테고리의 다른 글
[1115] Feature Engineering(3) - Benz (0) | 2022.11.15 |
---|---|
[1114] Feature Engineering(2) (0) | 2022.11.14 |
[1108] log를 이용한 데이터 전처리, 하이퍼 파라미터 튜닝, Feature Engineering (0) | 2022.11.08 |
[1107] 피처 선별 등의 머신러닝 접근법과 RMSLE (0) | 2022.11.07 |
[1101]지니 불순도, 엔트로피와 타이타닉 데이터 전처리 (1) | 2022.11.01 |
댓글