티스토리 뷰

Study/AI

[ML][DS] ColumnTransformer를 활용한 Column Align

생각많은 소심남 2019. 5. 30. 11:40

 이전에 다뤘던 포스트 중에 ANN을 사용해서 Churn prediction을 했던 내용이 있다. 간단히 말해 개인에 대한 정보를 바탕으로 이 사람이 credit이 있는지 없는지 여부를 판단해주는 예제였다. 그때 사용했던 데이터를 보통 데이터 필드가 numerical variable도 있고, categorical variable도 있었다. 물론 categorical variable을 ML이나 Deep Learning에서 다루기 위해서는 뭔가 의미있는 정보로 변화시켜주는 일련의 Encoding 과정이 필요했고, 그 때 기억으로는 Scikit-learn에서 제공하는 LabelEncoder와 OneHotEncoder를 사용해서 데이터를 Binary 처리를 하고, 학습에 반영했다. 아마 이렇게 처리하는 방식이 Categorical Variable을 학습에 적용하는 아주 일반적이고, 쉬운 방법이다. (물론 OneHotEncoding 외에도 성능을 개선시킨 Entity embedding이라던지 encoding 기법이 여러가지 있다)

 그래서 회사에서 일을 하려다보니, HR 정보를 활용해서 퇴직자를 예측하는 일을 잠깐 시도해보게 되었고, 이런 케이스에서 위와 같은 방법을 적용하려고 했다.

  • 데이터는 4개년도 인사 데이터를 활용하되, 이전 3개년 데이터는 학습 데이터로, 이후 1개년 데이터는 검증 데이터로 사용함.
  • 물론 개인의 성과와 직급, 직종, 연차 등의 정보가 들어있어서, numerical data, categorical data가 섞여있는 상태임.

 그런데 데이터를 눈으로 보다보니까 문제가 좀 있었다. 여러가지 자잘자잘한 문제가 있었지만, 가장 큰 문제는 data field별 category가 연도별로 제각각이었다는 것이었다. 예를 들어 학습된 3개년 데이터에는 있었던 category가 갑자기 검증 데이터에서는 없어진 경우도 있었고, 없던 category가 갑자기 검증 데이터에서 나오는 경우도 있었다. 

 물론 어떤 사람은 "에이 그냥 전체 4개년 데이터를 merge해서 Label/OneHotEncode하면 category가 다 반영되서 해결되는거 아닌가?" 할 수 있는데, 이게 된다고 치면 만약 미래의 데이터에 또 새로운 category가 나오면 또 merge시키고 전체를 학습시켜야 하는 케이스가 발생하게 된다. 이런 걸 막기 위해서, 원래 접근 방법대로 했는데, 없는 데이터 필드에 대한 처리를 해결하려 나는 scikit-learn에서 제공하는 ColumnTransformer라는 걸 좀 활용했다. (엄밀히 말하면 make_column_transfomer를 사용함)

# Scikit-learn
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer, make_column_transformer

...
# job_list : unique label of job
# rank_list : unique label of rank
# path_list : unique label of path
# school_list : unique label of school
# degree_list : unique label of degree
# dept_list : unique label of department

category_onehot = OneHotEncoder(categories=[job_list, rank_list, path_list, \
                             school_list, degree_list, ['Y', 'N'], \
                             dept_list, ['Y', 'N'], ['S','A','B','C','D']])

preprocess = make_column_transformer(
    (StandardScaler(), ['age', 'year', 'grade_point']),
    (category_onehot, 
     ['job', 'rank', 'path', \
      'school', 'degree', 'enter', 'dept',\
      'high_grade', 'grade']),
    ('passthrough', ['retirement'])
)

...

training = preprocess.fit_transform(training_df).toarray()

 사용법은 간단하다. 데이터 중에 numerical variable로 처리할 데이터와 categorical variable로 처리할 데이터를 분리해서 각각에 맞는 preprocessing기법을 넣어주면 된다. 위의 예시에서는 numerical variable인 'age' 'year', 'grade_point'같은 경우는 처리에 용이하도록 StandardScaler()를 통해 normalize를 시켜줬고, categorical variable은 기존의 onehotencoder를 사용했다. 문제는 아까와 같이 없어진 category와 새로운 category에 대한 처리인데, 나같은 경우는 전체 4개년 데이터에 대해서 unique한 category만 list로 추출해서 onehotencoder의 category로 넣어주었다.

 (참고로 원래 기존에 주로 활용했던 onehotencoder 사용법은 attribute 중 categorical_feature라는 인자에 category화할 dataFrame의 column index만 따로 masking해서 처리하는 방식이었는데, scikit-learn이 업데이트되면서, 해당 방법이 deprecation되고 사용을 지양하도록 가이드되고 있다.)

 아무튼 이렇게 하니까 학습에 사용할 3개년 데이터 encoding시에도 검증 데이터에만 있는 category를 받을 수 있도록 onehotencoder가 동작해서 결론적으로는 전체 데이터 data field가 align이 되었고, 이를 바탕으로 신경망을 설계할 수 있었다. 뭔가 정리가 안된거 같긴 한데, 그냥 기억을 남기는 차원에서 글을 남겨본다.

댓글