[Day 44] feature engineering(2)

임종우·2022년 11월 14일
0

ai_school_TIL

목록 보기
28/34

22.11.14.
지난 주에 이어 feature engineering에 대해 배우고 있다.



0701

변수 선택(Feature selection)

  • 데이터에 존재하는 feature들 중 가치가 높은 feature를 선택하고 그렇지 않은 feature를 버리는 과정.

분산 기반 선택

  • feature가 대부분 다 같은 값으로 이루어져 있거나 모두 다른 값이라면, 해당 feature는 예측에 도움이 되지 않을 확률이 높다.
  • 따라서 머신러닝 모델에서 해당 feature를 제거한다.
non_col_list = []

for col in train.select_dtypes(include = 'O').columns:
    if train[col].value_counts(1)[0] * 100 >= 90:
        non_col_list.append(col)
        
print(non_col_list)

범주형 변수 중, 특정 범주가 90퍼센트 이상을 차지하는 변수들을 확인하였다.
해당 변수들은 예측에 도움이 되지 않을 확률이 높으므로 피쳐에서 제거하면 될 것 같다.

상관관계 기반 선택

  • 어떤 feature 들 사이에 지나치게 높은 상관관계가 있다면, 둘 중 하나만 채택하고, 하나는 버린다. 이를 통해 과적합을 막을 수 있다.

feauture importance

  • model을 학습한 후 .feature_importances를 이용해 피쳐 중요도를 볼 수 있다. 이는 각 feature가 해당 모델에서 결과 도출에 얼마나 기여했는지를 나타낸다.
  • 그러나, 이는 모델과 난수마다 다르며, 절대적인 지표가 아니다.

0702

왜도와 첨도 살펴보기

  • 왜도(skew)
    .skew()로 살펴본다. 음수면 분포가 오른쪽으로, 양수면 왼쪽으로 치우쳐져 있다.
  • 첨도(kurt, kurtosis)
    .kurt()로 살펴본다. 0이면 정규분포, 0보다 크면 정규분포보다 뾰족한 분포이다.(Fisher의 정의)

왜도와 첨도를 살펴보고, 분포의 그래프도 그려본다. 이를 토대로 어떻게 데이터를 사용할지 EDA를 진행한다.

왜도가 큰 값의 경우 로그 변환을 해주기도 한다.

결측치

  • .isnull()을 사용한다. sum() 혹은 mean()으로 비율을 살펴볼 수 있다.
df = df.drop(columns = isna_mean[isna_mean >= 0.80].index)

결측치의 비율이 80퍼센트 이상인 피쳐들을 삭제하였다.

  • 결측치를 채워주기 위해서는 fillna를 사용한다.
    다양한 값으로 결측치를 채울 수 있고, 선택은 개인의 몫이다.
    수치형변수냐, 범주형 변수냐에 따라서도 달라질 수 있다.

실습에서 최빈값으로 채워보기도 진행했다.

df[fill_mode] = df[fill_mode].fillna(df[fill_mode].describe().loc['top'])
# 또는
df[fill_mode].fillna(df[fill_mode].mode().loc[0])

최빈값은 .mode로 판다스에서 구할 수 있다.

df[feature_num] = df[feature_num].fillna(df[feature_num].median())
df[feature_num].isnull().sum().sum()

이런 식으로 fillna를 사용하기도 했다.
fillna의 인자에 str, 수치 뿐 아니라 dictionary나 Series를 넣을 수 있었다. (함수는 X)

데이터 타입 바꾸기

  • 데이터의 타입이 수치형 변수더라도, 범주형 변수와 같이 동작하는 변수들이 있다.
    nunique가 적은 수치형변수들이 그러하다. 사실상 ordinal encoding 되어있다고 생각할 수 있다.
    해당 변수들은 범주형 변수로 바꾸어 one hot encoding 해주는 것이 더 효율적일 수 있다.
num_col = df.select_dtypes(include = 'number').columns
df[num_col].nunique()[df[num_col].nunique() < 15].index.tolist()

와 같은 코드로 수치형 변수 중 범주형 변수로 변환시킬 변수들을 골랐다.
그 후 .astype(str)을 통해 범주형 변수로 변경해주었다.


기타

  • 만약 기술통계값을 살펴봤는데, 수치형 데이터의 범위가 0~3의 정수 처럼 개수가 적다면 이는 수치형데이터보다는 범주형 데이터에 가깝다. 이럴때에는 nunique 값으로 추가적인 확인이 필요하다.

  • 히스토그램은 수치 데이터의 분포를 확인하기 위해 그려본다. 막대가 이산적이라면 수치데이터가 아니라 범주형 데이터임도 확인할 수 있다. 수치데이터의 경우 분포 확인, 왜도와 첨도의 대략적인 파악을 통해 데이터가 너무 한쪽에 치우쳐져 있지는 않은지 확인한다.
    -> 전처리를 한다면 학습과 예측에 도움이 될만한 피쳐엔지니어링 기법이 무엇이 있을지 고민

  • 이상치를 어떻게 처리해줘야할지?
    -> 이상치를 평군이나 중앙값 등으로 대체하면 데이터에 왜곡이 될 수 있으니 주의가 필요하다. 차라리 상황에 따라서는 최댓값이나 최솟값 등으로 대체하는게 낫다.
    -> 현실 세계에서 풀어야할 문제 중에는 이상치를 탐지하는 문제도 있다. 이상치가 특별한 의미를 가지고 있다거나.

  • 희소값 : 범주형 데이터 중에서 빈도수가 적은 것.
    One hot encoding시 희소값이 너무 많다면 연산에 시간이 오래걸릴 수도 있고, 과대적합이 발생할 수 있다. 따라서 희소한 값이라면 결측치 처리하거나 ‘기타’와 같이 묶어준다.

  • 스케일링
    트리계열모델에서는 스케일링이 필요없다. 그런데 로그를 취해 정규분포 형태를 띄게 해주는 것은 더 나은 성능을 내기도 한다.
    스케일링은 회귀에 있어 아주 중요하다.

  • 로그 트랜스포메이션
    로그 트랜스포메이션을 먼저 한 후 스케일링해야 표준정규분포와 같은 형태가 된다.
    왜 데이터를 정규분포 형태로 만들어주면 머신러닝이나 딥러닝에서 더 나은 성능을 낼까?
    분포의 중심(평균) 부분에 일반적으로 더 많은 데이터가 존재하기 때문에라고 생각함
    데이터가 너무 한쪽에 몰려있거나 치우쳐져 있을 때보다 고르게 분포되어 있다면 데이터의 특성을 더 고르게 학습할 수 있습니다. (강사님 답)
    정의역에 음수가 있다면, 최솟값의 절댓값 + 1 만큼을 더한 후 로그를 씌워준다.
    다시 돌리기 위해서는 지수함수를 씌운 후 해당 만큼을 빼준다.
    그러니까 log1p(x – 최솟값) 한 후 expm1(결과값) + 최솟값 하면 된다.

  • 이산화(범주화)
    Cut과 qcut. Cut은 절대평가 qcut은 상대평가.
    RFM 분석 기법에서도 종종 사용되는 방법으로 비즈니스 분석에서 다룰 예정.
    Recency, frequency, monetary => 고객이 얼마나 최근에, 자주, 많이 구매했는지를 분석할 때 사용
    연속된 수치 데이터를 구간화. => 머신러닝 알고리즘에 힌트를 줄 수도 있다
    트리모델의 경우 너무 잘게 데이터를 나누지 않아 일반화에 도움이 될 수도 있다
    나누는 기준이 중요한데, EDA를 통해 어떻게 나누는 것이 예측에 도움이 될지 확인, 나누는 기준에 따라 모델의 성능에 영향을 주게 된다
    오히려 잘못나누면 모델의 성능이 떨어질 수도 있다

  • 인코딩
    pandas에서는 .cat.codes, ordianl, get_dummies, one-hot
    범주형 데이터 => 수치형 데이터(0~N숫자로 바꿔준다면 Ordinal, 해당되는 것만 1로 만들어준다면 One-Hot)
    LabelEncoder, OrdinalEncoder 의 입력값의 차이?
    LabelEncoder 입력이 1차원 y 값, OrdinalEncoder 입력이 2차원 X값

  • OndHotEncoder
    handle_unknown = 'ignore' 를 넣어주면, 변환 중에 알 수 없는 범주가 발생하면 이 기능에 대한 결과 원-핫 인코딩 열은 모두 0이 됩니다. 역변환에서 알 수 없는 범주는 없음으로 표시됩니다
    그러니까 test를 인코딩하려는데, train에서 fit 할때에는 없던 범주가 있다면 오류가 나고, 우리의 목표는 이런건 다 결측치로 처리해버리는 거고, 그러려면 handle_unknown = ‘ignore’로 해주어야 한다!
    업데이트된 버전의 infrequent_if_exist는 fit에(train에) 없던 범주에 대해 자동으로 기타처리를 해준다!
    원핫인코딩 하고 난 후에는 train, test 피처의 수가 같은지 꼭! 확인

  • 전처리, EDA, 시각화
    까먹지 않게 계속 실습하려고 노력하자. 노력!

profile
ai school 기간 동안의 TIL!

0개의 댓글