Machine Learning/캐글스터디(파이썬 머신러닝)

[5장-4] 자전거 대여 수요 예측

뉴욕킴 2023. 5. 14. 23:10

# 필요 라이브러리 불러오기 

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning)

# 구글 드라이브 연결 및 파일 불러오기

from google.colab import drive
drive.mount("/content/drive")

Mounted at /content/drive

df = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/자전거 대여 수요예측/train.csv')
df.head()

 

▶ 주요 컬럼 설명

  • datetime: hourly date + timestamp
  • season: 1 = 봄, 2 = 여름, 3 = 가을, 4 = 겨울
  • holiday: 1 = 토, 일요일의 주말을 제외한 국경일 등의 휴일, 0 = 휴일이 아닌 날
  • workingday: 1 = 토, 일요일의 주말 및 휴일이 아닌 주중, 0 = 주말 및 휴일
  • weather: 1 = 맑음, 약간 구름 낀 흐림 2 = 안개, 안개 + 흐림 3 = 가벼운 눈, 가벼운 비 + 천둥 4 = 심한 눈/비, 천둥/번개
  • temp: 온도(섭씨)
  • atemp: 체감온도(섭씨)
  • humidity: 상대습도
  • windspeed: 풍속
  • casual: 사전에 등록되지 않는 사용자가 대여한 횟수
  • registered: 사전에 등록된 사용자가 대여한 횟수
  • count: 대여 횟수
df.info()

# 데이터세트: 10,886개, 칼럼 개수: 12개 확인, datetime만 object 칼럼 인 것 확인 → 추후 가공 필요한 것 확인
# datatime 타입 변환: object → apply(pd.to_datetime)
df['datetime']=df.datetime.apply(pd.to_datetime)
# datetime 타입에서 년,월,일,시간 추출
df['year']=df.datetime.apply(lambda x: x.year)
df['month']=df.datetime.apply(lambda x: x.month)
df['day']=df.datetime.apply(lambda x: x.day)
df['hour']=df.datetime.apply(lambda x: x.hour)
df.head(3)

# → year, month, day, hour 칼럼 추가 확인
# year, month, day, hour 칼럼 추가 확인
# 기존 datatime 칼럼은 삭제
drop_columns=['datetime','casual','registered']
df.drop(drop_columns,axis=1,inplace=True)
# 대여횟수(count)가 어떻게 분포되어 있는지 시각화 진행
# 2개의 행, 4개이 열 표현 → nrows=2, ncols=4
fig, axs = plt.subplots(figsize=(16, 8), nrows=2, ncols=4)
cat_features = ['year', 'month','season','weather','day', 'hour', 'holiday','workingday']
# cat_features에 있는 모든 칼럼별로 개별 칼럼값에 따른 count의 합을 barplot으로 시각화
for i, feature in enumerate(cat_features):
    row = int(i/4)
    col = i%4
    # 시본의 barplot을 이용해 칼럼값에 따른 count의 평균값을 표현
    sns.barplot(x=feature, y='count', data=df, ax=axs[row][col])   #줄 위치 중요!

# season: 1 = 봄, 2 = 여름, 3 = 가을, 4 = 겨울
# weather: 1 = 맑음, 약간 구름 낀 흐림 2 = 안개, 안개 + 흐림 3 = 가벼운 눈, 가벼운 비 + 천둥 4 = 심한 눈/비, 천둥/번개
# holiday: 1 = 토, 일요일의 주말을 제외한 국경일 등의 휴일, 0 = 휴일이 아닌 날
# workingday: 1 = 토, 일요일의 주말 및 휴일이 아닌 주중, 0 = 주말 및 휴일

 

# 다양한 회귀모델을 데이터 세트에 적용해 예측 성능 측정: RMSLE(root mean square log error)
from sklearn.metrics import mean_squared_error, mean_absolute_error

# log 값 변환 시 NaN등의 이슈로 log() 가 아닌 log1p() 를 이용하여 RMSLE 계산
def rmsle(y, pred):
    log_y = np.log1p(y)
    log_pred = np.log1p(pred)
    squared_error = (log_y - log_pred) ** 2
    rmsle = np.sqrt(np.mean(squared_error))
    return rmsle

# 사이킷런의 mean_square_error() 를 이용하여 RMSE 계산
def rmse(y,pred):
    return np.sqrt(mean_squared_error(y,pred))

# MSE, RMSE, RMSLE 를 모두 계산 
def evaluate_regr(y,pred):
    rmsle_val = rmsle(y,pred)
    rmse_val = rmse(y,pred)
    # MAE 는 scikit learn의 mean_absolute_error() 로 계산
    mae_val = mean_absolute_error(y,pred)
    print('RMSLE: {0:.3f}, RMSE: {1:.3F}, MAE: {2:.3F}'.format(rmsle_val, rmse_val, mae_val))

 

# 회귀 모델을 이용해 자전거 대여 횟수 예측
# 순서: 결과값이 정규 분포로 되어 있는지 확인 → 카테고리형 회귀 모델의 경우 원-핫 인코딩으로 피처를 인코딩

#1. 사이킷런의 LinearRegression 객체를 이용해 회귀 예측
from sklearn.model_selection import train_test_split , GridSearchCV
from sklearn.linear_model import LinearRegression , Ridge , Lasso

y_target = df['count']
X_features = df.drop(['count'],axis=1,inplace=False)

X_train, X_test, y_train, y_test = train_test_split(X_features, y_target, test_size=0.3, random_state=0)

lr_reg = LinearRegression()
lr_reg.fit(X_train, y_train)
pred = lr_reg.predict(X_test)

evaluate_regr(y_test ,pred)

RMSLE: 1.165, RMSE: 140.900, MAE: 105.924

def get_top_error_data(y_test, pred, n_tops = 5):
    # DataFrame에 컬럼들로 실제 대여횟수(count)와 예측 값을 서로 비교 할 수 있도록 생성. 
    result_df = pd.DataFrame(y_test.values, columns=['real_count'])
    result_df['predicted_count']= np.round(pred)
    result_df['diff'] = np.abs(result_df['real_count'] - result_df['predicted_count'])
    # 예측값과 실제값이 가장 큰 데이터 순으로 출력. 
    print(result_df.sort_values('diff', ascending=False)[:n_tops])
    
get_top_error_data(y_test,pred,n_tops=5)

# 상위 5개 오류 값이 546~568로 예측 오류가 큼
# 회귀에서 큰 예측 오류 발생시 → target 값의 분포가 왜곡된 형태 이루고 있는지 확인하기 → count 칼럼이 정규 분포를 이루는지 확인
y_target.hist()

# count 칼럼 값이 정규 분포가 아닌 0~200 사이에 몰려있는 것 확인
# 이를 '로그'를 적용해 정규 분포 형태로 바꿔줌 → log1p() 사용
y_log_transform = np.log1p(y_target)
y_log_transform.hist()

# 타깃 칼럼인 count 값을 log1p로 로그 변환
y_target_log = np.log1p(y_target)

# 로그 변환된 y_target_log를 반영하여 학습/테스트 데이터 셋 분할
X_train, X_test, y_train, y_test = train_test_split(X_features, y_target_log, test_size=0.3, random_state=0)
lr_reg = LinearRegression()
lr_reg.fit(X_train, y_train)
pred = lr_reg.predict(X_test)

# 테스트 데이터 셋의 Target 값은 Log 변환되었으므로 다시 expm1를 이용하여 원래 scale로 변환
y_test_exp = np.expm1(y_test)

# 예측 값 역시 Log 변환된 타깃 기반으로 학습되어 예측되었으므로 다시 exmpl으로 scale변환
pred_exp = np.expm1(pred)

evaluate_regr(y_test_exp ,pred_exp)

RMSLE: 1.017, RMSE: 162.594, MAE: 109.286

 

# 전: RMSLE: 1.165, RMSE: 140.900, MAE: 105.924
# 후: RMSLE: 1.017, RMSE: 162.594, MAE: 109.286
→ RMSLE 오류는 줄었지만, RMSE는 증가 → 개별 피처들의 인코딩을 적용

 

# 각 피처의 회귀 계수값 시각화 진행
coef = pd.Series(lr_reg.coef_, index=X_features.columns)
coef_sort = coef.sort_values(ascending=False)
sns.barplot(x=coef_sort.values, y=coef_sort.index)

# 사이킷런은 카테고리만을 위한 데이터 타입이 없고, 모두 숫자로 변환 해야됨
→ 피처 인코딩에 원-핫 인코딩을 적용해 변환해야됨 → get_dummies() 이용

# 'year', month', 'day', hour'등의 피처들을 One Hot Encoding
X_features_ohe = pd.get_dummies(X_features, columns=['year','month','day','hour','holiday','workingday','season','weather'])
# 사이킷런의 선형회귀 모델인 LinearRegression, Ridge, Lasso 모두 학습해 예측 성능을 확인
# → 모델과 학습, 테스트 데이터 세트 입력/ 성능 평가 수치를 반환하는 get_model_predict() 함수 만들기
# 원-핫 인코딩이 적용된 feature 데이터 세트 기반으로 학습/예측 데이터 분할. 
X_train, X_test, y_train, y_test = train_test_split(X_features_ohe, y_target_log,test_size=0.3, random_state=0)

# 모델과 학습/테스트 데이터 셋을 입력하면 성능 평가 수치를 반환
def get_model_predict(model, X_train, X_test, y_train, y_test, is_expm1=False):
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    if is_expm1 :
        y_test = np.expm1(y_test)
        pred = np.expm1(pred)
    print('###',model.__class__.__name__,'###')
    evaluate_regr(y_test, pred)
# end of function get_model_predict    

# model 별로 평가 수행
lr_reg = LinearRegression()
ridge_reg = Ridge(alpha=10)
lasso_reg = Lasso(alpha=0.01)

for model in [lr_reg, ridge_reg, lasso_reg]:
    get_model_predict(model,X_train, X_test, y_train, y_test,is_expm1=True)

### LinearRegression ###

RMSLE: 0.590, RMSE: 97.688, MAE: 63.382

### Ridge ###

RMSLE: 0.590, RMSE: 98.529, MAE: 63.893

### Lasso ###

RMSLE: 0.635, RMSE: 113.219, MAE: 72.803

 

# 전: RMSLE: 1.165, RMSE: 140.900, MAE: 105.924
# 후: RMSLE: 1.017, RMSE: 162.594, MAE: 109.286

원 핫 인코딩 적용 후 ↓ 선형 회귀 예측 성능 향상된 부분 확인 가능
### LinearRegression ###
# RMSLE: 0.590, RMSE: 97.688, MAE: 63.382
### Ridge ###
# RMSLE: 0.590, RMSE: 98.529, MAE: 63.893
### Lasso ###
# RMSLE: 0.635, RMSE: 113.219, MAE: 72.803

 

# 회귀 계수 상위 25개 피처를 추출
coef = pd.Series(lr_reg.coef_ , index=X_features_ohe.columns)
coef_sort = coef.sort_values(ascending=False)[:20]
sns.barplot(x=coef_sort.values, y=coef_sort.index)

# 회귀 트리를 이용해 회귀 예측 수행
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor

# 랜덤 포레스트, GBM, XGBoost, LightGBM model 별로 평가 수행
rf_reg = RandomForestRegressor(n_estimators=500)
gbm_reg = GradientBoostingRegressor(n_estimators=500)
xgb_reg = XGBRegressor(n_estimators=500)
lgbm_reg = LGBMRegressor(n_estimators=500)

for model in [rf_reg, gbm_reg, xgb_reg, lgbm_reg]:
    # XGBoost의 경우 DataFrame이 입력 될 경우 버전에 따라 오류 발생 가능. ndarray로 변환.
    get_model_predict(model,X_train.values, X_test.values, y_train.values, y_test.values,is_expm1=True)

### RandomForestRegressor ###

RMSLE: 0.354, RMSE: 50.424, MAE: 31.213

### GradientBoostingRegressor ###

RMSLE: 0.330, RMSE: 53.337, MAE: 32.742

### XGBRegressor ###

RMSLE: 0.342, RMSE: 51.732, MAE: 31.251

### LGBMRegressor ###

RMSLE: 0.319, RMSE: 47.215, MAE: 29.029

 

▶ 코드 공유

자전거 대여 수요 예측_230514 - Colaboratory (google.com)

 

Google Colaboratory Notebook

Run, share, and edit Python notebooks

colab.research.google.com