본문 바로가기
Programming/Project

[NYC 택시 수요 예측 PJT] 10. Result Analysis and Feature Analysis

by 최성현 2021. 8. 19.
320x100

앞서 XGBoost, LightGBM, Random Forest를 통해 예측한 결과를 분석을 해보려고 한다. 모델의 결과는 MAE, MAPE, MSE를 통해 나타내었으며, parsing을 통해 각 파라미터와 오차는 experinments 폴더에 저장이 되었다. MAE, MAPE, MSE와 같은 지표는 예측 모형의 효과를 검증하고 성능을 평가하기 위해 사용된다.

 

1. 오차의 종류

 

1-1. MAE (평균 절대 오차)

MAE는 Mean Absolute Error의 약자로 실제 값과 예측 값의 차이를 절댓값의 평균으로 나타낸 값이다. MAE는 오차에 절댓값을 취하므로 오차의 크기가 그대로 반영되어 직관적인 특성을 가진 지표이나 절댓값을 취하기 때문에 실제보다 낮은 값인지 높은 값인지는 알 수 없다. 

 

1-2. MAPE (평균 절대 퍼센트 오차)

MAPE는 MAE에 백분율을 취해 퍼센트 값으로 변환한 지표이다. MAPE는 퍼센트 오차값을 가지며 0에 가까울수록 좋은 성능임을 확인할 수 있다. 

 

1-3. MSE (평균 제곱 오차)

MSE는 Mean Squared Error의 약자로 잔차의 제곱에 대해 평균을 취한 값으로 개별 관측값들이 중심에서 얼마나 멀리 떨어져 있는지의 척도를 나타낸다. 값이 작을수록 추정의 정확성이 높아지며 MSE는 다음과 같이 표현될 수 있다.

 

1-4. Conclusion

Acroynm Full Name Residual Operation? Robust To Ouliers?
MAE Mean Absolute Error Absolute Value Yes
MAPE Mean Absolute Percentage Error Absolute Value Yes
MSE Mean Squared Error Square No

Robust란 outlier에 덜 민감한지를 의미한다.

 

출처 : https://www.dataquest.io/blog/understanding-regression-error-metrics
 

Tutorial: Understanding Regression Error Metrics in Python

Error metrics are short and useful summaries of the quality of our data. We dive into four common regression metrics and discuss their use cases.

www.dataquest.io

 

 

2. 결과 분석

 

Model Name XGBoost LightGBM Random Forest
MAE 57.678736 48.241491 34.952141
MAPE 538.520156 421.685697 182.127233
MSE 16512.330237 13755.68489 12153.455921

MAE, MAPE, MSE 모두 XGBoost -> LightGBM -> Random Forest 순으로 줄고있다.

결과에 대해 특정 feature에 대해 영향을 받는지 확인해보고자 한다. 

 

test_df.head()

2-1. Data 복구

예측 모델을 위해 변환시켰던 데이터(Label Encoding, 삭제한 데이터, Datetime )를 다시 reverse encoding 한다.

먼저 예측값과 실제값 그리고 년도를 테이블에 추가한다. 예측값은 Random Forest모델을 통해 예측한 값이다.

test_df['y_true'] = y_test_raw
test_df['y_pred'] = rf_pred
test_df['year'] = 2015
test_df.tail()

 

그리고 datetime이라는 열을 추가하고 연도-월-일-시간을 표기하게 한다.

test_df['datetime'] = pd.to_datetime(test_df[['year', 'month', 'day', 'hour']])
test_df.tail()

다음은 Label Encoding 했던 zipcode를 다시 zipcode 형식으로 변환한다.

test_df['zip_code'] = le.inverse_transform(test_df['zip_code_le'])
test_df.tail()

test_df = test_df.set_index('datetime')

인덱스로 datetime을 다시 추가해서 data를 보려고 한다.

 

2-2. Datetime별 차이

test_df.groupby('datetime').sum()[['y_true', 'y_pred']].plot();

예측값(y_pred)과 실제값(y_ture)을 비교해보면 전반적으로 예측값이 실제값보다 높은 경향을 보이고 있음을 확인할 수 있다.

 

2-2. Zipcode별 차이

from ipywidgets import interact
def visualize_output_by_zipcode(df):
    def view_images(zip_code):
        data = df.loc[df['zip_code'] == str(zip_code)][['y_true', 'y_pred']]
        try:
            ax = data.plot();
            ax.set_title(f'zip_code : {zip_code}')
        except:
            pass
        
    interact(view_images, zip_code=(10001, 10200))
visualize_output_by_zipcode(test_df)

zipcode를 바꿔가며 결과를 확인할 수 있다. 각 수요 패턴 및 예측 경향을 보며 추가적으로 고려해봐야 할 부분을 확인할 수 있다.

2-3. Hour별 차이

test_df[['hour','y_true', 'y_pred']].groupby('hour').sum()[['y_true', 'y_pred']].plot();

모든 시간대에 높게 예측을 하고 있다.

아래는 시간대별 예측값을 출력한 것이며, 그래프로 봤을 때 보다 정확하게 확인이 가능하다.

test_df[['hour','y_true', 'y_pred']].groupby('hour').sum()[['y_true', 'y_pred']]

 

2-4. Weekday별 차이

test_df[['weekday','y_true', 'y_pred']].groupby('weekday').sum()[['y_true', 'y_pred']].plot();

※ 0(월요일) - 6(토요일)

2-5. 평일/주말별 차이

test_df[['is_weekend','y_true', 'y_pred']].groupby('is_weekend').sum()[['y_true', 'y_pred']].plot(kind='bar');

주말에 비해 평일에 실제값과 예측값의 차이가 크나, 평일은 5일, 주말은 2일임을 생각해보면 샘플 수에 의해 차이가 날 수도 있다고 생각한다.

 

모델링을 통해 예측을 끝내고 각 모델의 성능을 확인해봤으며, Feature에 따라 예측 결과에 어떤 영향을 주는지 확인해봤다. 어떤 Feature를 쓰느냐에 따라 예측 결과에 큰 영향을 미칠 수 있으며 다양한 실험을 통해 최고의 결과를 낼 수 있는 Feature를 선택하는 것이 중요하다고 생각한다.

300x250