본문 바로가기
Data Science/Project

[홀로서기 #04] 회귀 모델링 하기 전에 꼭 확인하기 (1) - 범주형 특성 변수(Categorical Features)

by 루크 Luke 2021. 12. 28.
반응형

홀로서기 기획 연재물은 최근 개인 프로젝트를 진행하면서 겪은 어려움들을 기록한 지극히 개인적인 콘텐츠입니다.


SQL로 최종 데이터 가져오기

  지난 시간에 이어서 서울권 공유자전거(따릉이) 이용 데이터를 살펴본다. 시각화를 통해 데이터의 전체적인 모양과 흐름 파악을 마쳤으니, 예측 모델링을 위한 최종 전처리를 진행하고 실제로 ML(Machine Learning) 모델을 적용해봤다. 지금까지(홀로서기 #03)는 서울 강남역 근처의 5개소 대여소만을 기준으로 데이터를 봤다면, 최종 모델링은 전체 데이터를 활용해서 진행했다.  아래 Python 코드는 AWS의 RDS(데이터베이스)에 저장된 데이터 중 최종 모델링에 활용할 데이터를 SQL Query로 불러오는 예시를 나타낸 것이다.

conn_aws = mysql.connector.connect(
    host = "[Host Address]",
    port = [Port Number],
    user = "[Username]",
    password = "[Passward]", 
    database = "bicycle_spring_2021"
)

cur = conn_aws.cursor(buffered=True)
sql = """(
    SELECT s.count
        , s.month
        , s.day
        , s.dayofweek
        , stopnum
        , temperature
        , rain
        , windspeed
        , humidity
        , pressure
        , sunshine
        , snow
        , cloud
    FROM (
        SELECT stop_index1 as stopnum
            , MONTH(time_start) as month
            , DAY(time_start) as day
            , DAYOFWEEK(time_start) as dayofweek
            , COUNT(*) as count
        FROM User
        WHERE (time_start >= '2021-03-01')
        GROUP BY month, day, stopnum, dayofweek
    ) AS s
    LEFT JOIN Weather_v AS w
    ON s.month = w.month
    AND s.day = w.day
    AND s.dayofweek = w.dayofweek
)"""
cur.execute(sql)
result = cur.fetchall()

final = pd.DataFrame(result, columns=['count', 'month', 'day', 'dayofweek', 'stop', 'temperature', 'rain', 'windspeed', 'humidity', 'pressure', 'sunshine', 'snow', 'cloud'])
final.head()

위 SQL Query와 Python 코드를 바탕으로 다음과 같은 final 데이터를 확인할 수 있다.

(참고) 이번 회귀 모델링은 시계열에 대한 부분은 제외한다.

- 일별 수요량 Target으로, 대여소, 각 날씨 특성, 요일만을 Feature로 두고 프로젝트를 진행했다.

 

범주형 특성 변수(Categorical Feature) -> OneHotEncoder

  ML 모델링을 진행하기 앞서, Feature를 다듬어주어야 할 부분이 바로 범주형 특성 변수이다. 이를 이르는 말은 사람마다 다른 것 같은데, 나의 경우에는 '범주형'이면서 모델의 '특성(Feature)'로 쓰이는 '변수(Variable)'라는 뜻으로 범주형 특성 변수라는 말을 채택했다. 아무튼, 분류 모델에서도 이 범주형 특성 변수에 대해서는 LabelEncoder 등을 통해서 모델에 적절한 형태로 변경해주는 작업을 수행하는데, 회귀 모델의 경우에는 더욱 각별히 주의할 필요가 있다.

 

  회귀 모델의 변수가 된다는 것은, 모델의 결과물이라고 할 수 있는 '수리적인 식'의 하나의 변수로서 '계산되는 값'이 된다는 말이다. 쉽게 말하면, <<수요량 = 3 * (정류소 번호)>>라는 모델 식이 나온다면, 정류소 번호가 하나의 계산되는 값이 된다는 말이다. 2500번 정류소와 1200번 정류소는 수치적으로는 동일하지만 정류장 종류가 다르다는 부분만 처리가 이루어져야 하는데, 2500과 1200으로 처리되서 수요량에 수치 자체가 다르게 영향을 미치게 된다는 것이다.

  → 이런 범주형 특성 변수를 회귀 모델링에서는 보통 '더미(Dummy) 변수'로 처리한다. (더미 변수로 처리되는 것이 무엇인지는 뒷 부분의 인코딩 결과에서 더욱 상세히 볼 수 있을 것이다.)

 

  Python에서 '더미 변수화'를 진행할 때, 이러한 Categorical Feature를 처리하는 방식은 크게 3가지가 있다.

  1) 'Pandas DataFrame'에서 get_dummies()를 활용하는 방식
  2) 'Scikit-learn'에서 OneHotEncoder()를 활용하는 방식
  3) 'tensorflow'에서 to_categorical을 활용하는 방식

 

  필자는 직관적으로 'OneHotEncoding을 한다'라는 표현을 사용하는 것을 좋아하는데, 그래서인지 사이킷런의 원 핫 인코더를 많이 사용한다. 이번 전처리에서도 OneHotEncoder를 사용했다. 데이터에 범주형 특성변수는 크게 2가지가 있었다. '대여소(stop)'와 '요일(dayofweek)'이다. OneHotEncoding를 수행하는 코드는 아래와 같다.

from sklearn.preprocessing import OneHotEncoder
 
# stop
stop_onehot = OneHotEncoder()
new_stop = stop_onehot.fit_transform(final['stop'].values.reshape(-1,1)).toarray()
new_stop_df = pd.DataFrame(new_stop, columns=["stop_"+str(int(i)) for i in range(new_stop.shape[1])])

# dayofweek
dayofweek_onehot = OneHotEncoder()
new_dayofweek = dayofweek_onehot.fit_transform(final['dayofweek'].values.reshape(-1,1)).toarray()
new_dayofweek_df = pd.DataFrame(new_dayofweek, columns=["dayofweek_"+str(int(i)) for i in range(new_dayofweek.shape[1])])

# for MemoryError
new_stop_df = new_stop_df.astype('int8')
new_dayofweek_df = new_dayofweek_df.astype('int8')

# concat
final = pd.concat([final, new_stop_df, new_dayofweek_df], axis=1)
final.info()

- 필자는 메모리 에러 때문에 굉장히 난항을 거쳤다. 데이터 크기가 꽤 큰 편인데 반해, 노트북 메모리가 얼마 남지 않아서 변수 사용이 쉽지 않았다. 그래서 찾은 개선책은 데이터의 타입을 바꿔준 것이다! 기존 int64 데이터타입에 비해 int8 데이터타입은 최대 10배의 메모리 절약을 수행했다. Memory Error로 인코딩이 원활하지 않으면, 변수에서 타입을 줄일 수 있는 것이 무엇이 있을지 고민해보자.

 

OneHotEncoding 결과물이다.

 

 

  위 사진을 보면 OneHotEncoding 결과를 볼 수 있다. 가져온 사진만으로는 무슨 결과물이라는 거지? 할 수 있다. 이해한다. 나도 그랬었다. 원 핫 인코딩이 뭐냐면! 각각의 범주에만 1을 부여하고, 나머지는 0을 부여하는 것이라고 이해하면 쉽다. 더 이해하기 쉽도록 아래 그림을 참조해보자.

 

왼쪽이 기존의 범주형 특성 변수의 데이터들, 오른쪽이 원핫인코딩 이후의 변화된 데이터들이다.

- 2409번 대여소의 월요일 데이터는 s_2409에 1, 월요일에 1을 제외하고 모든 곳에 0이 찍힌다. 다른 정류소나 요일에 대해서는 0으로 처리된다는 말이다. 회귀 모델링이 진행되면 2410번 정류장에 대한 영향력이 있더라도, 2409번 데이터에서는 2410번 특성이 0으로 처리되기 때문에, 문제가 되지 않게 된다. 이렇게 처리되는 것을 '더미' 처리 된다고 말한다.

- 2413번 대여소도 마찬가지로 이해하면 된다.

 

모델링 최종 데이터

전처리까지 완료한 데이터에 대한 정보는 아래와 같다.

 

1. 총 208,477개 데이터

2. Y(Target) : 일별 대여소별 대여 수(count)

3. X(Feature):

  - 주형(Categorical) - 대여소, 요일 (→ OneHotEncoding)

  - 연속형(Continuous) : 날씨 (기온, 강수량, 풍속, 습도, 기압, 일조량, 적설, 구름)

 

 


Photo : Unsplash, "Data Science"

 

분류 모델을 접할 일이 더욱 많기 때문에, 이번 회귀 프로젝트가 필자에게는 매우 소중하다.

수학, 통계적으로 검토해볼 내용들도 있어서, 다음 모델링 파트에서는 더욱 Deep한 내용을 바탕으로 콘텐츠를 작성해보려고 한다.

하나하나 채워가는 것들이 데이터 사이언스 분야에서의 자산이라고 느낀다.

곧 프로젝트가 마무리될 것 같다. 화이팅. 홀로서기 #4 끝.

 


반응형

댓글