ML) Kaggle Competition 해보는 중 NaN 처리

Wonjun Lee·2024년 6월 22일

Machine learning 프로젝트의 주제로 Kaggle에서 진행중인 Store sales - time series forecasting Competition을 선택하였다.

가게의 판매량을 예측하는 모델을 만드는 과제였고, Model Ensemble 기법을 활용하여 해결하고자 하였다.

Base model을 선정하고 학습 시키려는 과정에서 NaN에 의한 문제가 발생하였다.

XGBoost만을 이용해 학습할 때는 괜찮았는데 이유를 찾아보니 XGBoost는 NaN의 처리를 자동으로 해주는 기능이 탑재되어 있기 때문이었다.

때문에 다른 모델들은 Training data를 입력받을때 문제가 발생했고 이 문제를 해결하고자 하였다.

문제가 발생한 곳은 Oil price 컬럼이었다. 해당 판매기록 날짜 당일의 유가를 매핑시켜 높은 것으로 함께 제공되는 데이터셋을 이용했었다.

우선 유가데이터 자체의 결측값을 제거하기 위하여 선형보간법을 적용했다.
그리고 다시 매핑 프로그램을 수행해도 여전히 같은 문제가 발생했다.

# 유가를 선형보간
oil_price['dcoilwtico'] = oil_price['dcoilwtico'].interpolate()
oil_price.iloc[0,1] = 93.055
#  유가 옵션  : NaN이라도 붙이기
#
oil_prise_dict = dict(zip(oil_price['date'], oil_price['dcoilwtico']))

df['oil_price'] = df['date'].map(lambda x: oil_prise_dict.get(x))
print(df)

유가 날짜와 매핑되지 않는 날짜가 training set에 있었기 때문에 해당 인스턴스들은 NaN이 입력되어 있는 상태로 남았다.

단순히 최근 유가와 다음에 처음으로 나오는 유가의 평균을 적용해도 되었으나 NaN이 연속으로 매우 많이 나오는 경우 프로그램으로 인한 오버헤드와 모델 성능 저하가 우려되었다.

가장 적합한 유가를 출력해내기 위해서 Random forest를 이용해 유가 예측을 수행하기로 하였다.

# model 학습을 위한 Feature construction
# 연 월 일을 기반으로 유가를 예측함
print(oil_price.head())
oil_price['year'] = 0
oil_price['month'] = 0
oil_price['day'] = 0
for i in range(len(oil_price)) :
  time_unit = getTimeUnits(oil_price.loc[i, 'date'])
  oil_price.loc[i, 'year'] = time_unit[0]
  oil_price.loc[i, 'month'] = time_unit[1]
  oil_price.loc[i, 'day'] = time_unit[2]
oil_price.drop(columns=['date'], inplace=True)
print(oil_price.head())


# 모델 학습
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

oil_price_predictor = RandomForestRegressor()
X_oil = oil_price.drop('dcoilwtico', axis=1)
y_oil = oil_price['dcoilwtico']
oil_X_train, oil_X_test, oil_y_train, oil_y_test = train_test_split(X_oil, y_oil, test_size=0.2, random_state=42) # Tr, Te 분할

oil_price_predictor.fit(oil_price.drop('dcoilwtico', axis=1), oil_price['dcoilwtico'])
print(mean_squared_error(oil_y_test, oil_price_predictor.predict(oil_X_test)))

그리고 같은 날짜의 인스턴스들은 동일한 유가가 부여되어야 하므로 다음 반복문을 수행하여 모델의 출력을 훈련용 데이터 셋에 부여하였다.

# 유가 예측 모델을 이용하여 예측 유가 적용하기
import pandas as pd
import numpy as np
while i < len(df):
  if np.isnan(df.loc[i, 'oil_price']) :
    print('NaN :', i)
    input_data = {'year': [df.loc[i, 'year']], 'month': [df.loc[i, 'month']], 'day': [df.loc[i, 'day']]}
    predicted_value = oil_price_predictor.predict(pd.DataFrame(data=input_data))
    df.loc[i, 'oil_price'] = predicted_value[0]
    print(predicted_value)
    j = i + 1
    while j < len(df) and df.loc[j, 'date'] == df.loc[i, 'date']:
      df.loc[j, 'oil_price'] = predicted_value[0]
      j += 1
  i = i + 1

초반엔 모든 튜플에 대해 수행하게 되어서 수행시간이 매우 오래 걸렸으나 수정하여 약 2분 만에 300만개 이상의 인스턴스를 수정할 수 있었다.

이렇게 하여 최종적으로 앙상블 모델 입력으로 사용할 수 있게 되었고, 다른 모델들과 paired t-test 결과 최대 99% 신뢰도로 귀무가설을 기각하며 성능 차이의 유의미함이 검증되었다.

0개의 댓글