TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 머신러닝>

[머신러닝] 회귀 - 자전거 대여 수요 예측

by 기록자_Recordian 2024. 10. 23.
728x90
반응형
회귀 관련 내용

[머신러닝] 회귀(Regression)

[머신러닝]경사 하강법(GD, gradient descent)

[머신러닝] 회귀 - LinearRegression 클래스

[머신러닝] 회귀 - 다항 회귀와 과대(과소) 적합

[머신러닝] 회귀 - 규제 선형 모델: 릿지, 라쏘, 엘라스틱넷

[머신러닝] 로지스틱 회귀

[머신러닝] 회귀 트리


자전거 대여 수요 예측 실습

 

캐글의 자전거 대여 수요 예측 경연에서 사용된 학습 데이터 세트를 이용해 선형 회귀와 트리 기반 회귀 비교

 

[Bike Sharing Demand] 자전거 수요 예측 데이터 분석

Explore and run machine learning code with Kaggle Notebooks | Using data from Bike Sharing Demand

www.kaggle.com

데이터 세트는 상단 링크에서 train.csv 파일을 내려받으면 된다. (캐글 로그인 > 경쟁 규칙 동의 후)

출처: 캐글

 

[데이터 세트 주요 칼럼]

출처: 캐글


데이터 클렌징 및 가공과 데이터 시각화

 

- 내려받은 csv (필자의 경우 파일명은: 05.bike_train.csv) 데이터 세트를 이용해 모델을 학습한 후 대여 횟수(count) 예측

  • csv 파일을 새로운 디렉터리로 이동
  • 데이터 세트를 DataFrame으로 로드해 대략적으로 데이터 확인
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)

bike_df = pd.read_csv("C:/Users/niceq/Documents/DataScience/Python ML Guide/Data/05. train_bike.csv")
print(bike_df.shape)
bike_df.head()

 

- 데이터 칼럼의 타입 살펴보기

bike_df.info()

 

▶ 10886개의 로우(row) 중 Null 데이터는 없으며, 대부분의 칼럼이 int 또는 float 숫자형

Datetime 칼럼의 경우, 년-월-일 시:분:초 형태로 되어 있으므로 이에 대한 가공이 필요하다.

문자열을 pandas의 datetime 타입으로 변경하여 년, 월, 일, 시간 4개의 속성으로 분리.

 

- apply(pd.to_datatime) 메서드를 사용하여 년, 월, 일, 시간 칼럼 추출

# 문자열을 datatime 타입으로 변경
bike_df['datetime'] = bike_df.datetime.apply(pd.to_datetime)

# datetime 타입에서 년, 월, 일, 시간 추출
bike_df['year'] = bike_df.datetime.apply(lambda x: x.year)
bike_df['month'] = bike_df.datetime.apply(lambda x: x.month)
bike_df['day'] = bike_df.datetime.apply(lambda x: x.day)
bike_df['hour'] = bike_df.datetime.apply(lambda x: x.hour)
bike_df.head()

 

- 필요없는 칼럼 삭제

  • 년, 월, 일, 시간 분리하였으므로 datatime 칼럼 삭제
  • casual 칼럼: 사전에 등록하지 않은 사용자의 자전거 대여 횟수, registered 칼럼: 사전에 등록한 사용자의 자전거 대여 횟수 ▶ casual + registered = count 이므로, 삭제
# 필요없는 칼럼 삭제
drop_columns = ['datetime', 'casual', 'registered']
bike_df.drop(drop_columns, axis=1, inplace=True)
bike_df.head()

 

- 주요 칼럼별로 Target 값인 count가 어떻게 분포되어있는지 시각화

  • 칼럼별 값에 따른 count의 합을 표현하기 위해서 시본의 barplot 적용
  • 모든 칼럼을 한 번에 시각화 하기 위해서 matplotlib의 subplots() 를 기반으로 barplot 표현
  • 총 8개의 barplot을 그리기 위해 plt.subplots()의 인자로 ncols=4, nrows=2를 입력하여 2개의 행과 4개의 열을 가진 그래프로 표현
# 주요 칼럼별로 Target 값인 count가 어떻게 분포되어있는지 시각화
fig, axs = plt.subplots(figsize=(16, 8), ncols=4, nrows=2)
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=bike_df, ax=axs[row][col])

<파악 내용>
- year: 2012년이 2011년보다 상대적으로 값이 높다: 시간이 지날수록 자전거 대여 횟수가 지속적으로 증가
- month: 6, 7, 8, 9월이 높다
- season: 봄(1), 겨울(4)이 낮고 여름(2), 가을(3)이 높다
- weather: 눈(3) 또는 비(4)가 오는 날이 낮고, 맑거나(1) 약간 안개가 있는(2) 경우 높다
- hour: 오전 출근 시간(8)과 오후 퇴근 시간(17, 18)이 상대적으로 높다
- day: 차이가 크지 않다
- holiday(0) & workingday(1):주중(workingday, 1)일 경우가 상대적으로 높다 

 

- 다양한 회귀 모델을 데이터 세트에 적용해 예측 성능 측정

캐글에서는 RMSLE(Root Mean Square Log Error)를 성능 평가 지표로 요구하였으나, 사이킷런에서는 해당 지표를 제공하지 않아 직접 만들어야 한다. (추가로, MAE, RMSE 까지 평가하는) 함수를 생성한다.

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_squared_error()를 이용해 RMSE 계산
def rmse(y, pred):
    return np.sqrt(mean_squared_error(y, pred))

# MAE, RMSE, RMSLE 모두 계산
def evaluate_regr(y, pred):
    rmsle_val = rmsle(y, pred)
    rmse_val = rmse(y, pred)
    # MAE는 사이킷런의 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))

로그 변환, 피처 인코딩과 모델 학습/예측/평가

 

- 회귀 모델을 이용해 자전거 대여 횟수 예측

회귀 모델 적용 전에 데이터 세트에 대해 처리해야 할 사항

  • 결괏값이 정규 분포로 돼 있는지 확인
  • 카테고리형 회귀 모델의 경우 원-핫 인코딩으로 피처를 인코딩
# 회귀 예측
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.linear_model import LinearRegression, Ridge, Lasso

y_target = bike_df['count']
X_features = bike_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)

 

- 실제 값과 예측값이 어느 정도 차이가 나는지 DataFrame의 칼럼으로 만들어서 오류 값이 가장 큰 순으로 5개만 확인

def get_top_error_data(y_tst, 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 값의 분포가 왜곡된 형태를 이루고 있는지를 먼저 확인해 본다. Target 값의 분포는 정규 분포 형태가 가장 좋다.

 

- 판다스 DataFrame의 hist()를 이용해 count 칼럼이 정규 분포를 이루는지 확인

y_target.hist()

▶ 칼럼 값이 정규 분포가 아닌 0~200 사이에 왜곡돼 있는 것을 확인할 수 있다.

이러한 경우 왜곡된 값을 정규 분포 형태로 바꾸는 가장 일반적인 방법은 로그를 적용해 변환하는 것이다.

 

- log1p()를 적용한 'count' 값의 분포 확인

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 값은 로그 변환됐으므로 다시 expm1을 이용해 원래 스케일로 변환
y_test_exp = np.expm1(y_test)

# 예측값 역시 스케일 변환
pred_exp = np.expm1(pred)

evaluate_regr(y_test_exp, pred_exp)

▶ RMSLE 는 줄어들었으나, RMSE와 MAE는 증가하였다.

 

- 개별 피처들의 인코딩 적용

  • 각 피처의 회귀 계숫값 시각화
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)

▶ 사이킷런은 카테고리만을 위한 데이터 타입이 없어 모두 숫자로 변환해야 한다.

그러나 숫자형 카테고리 값을 선형 회귀에 사용할 경우 회귀 계수를 연산할 때 이 숫자형 값에 크게 영향을 받는 경우가 발생할 수 있어 션형 회귀에서는 이러한 피처 인코딩에 원-핫 인코딩을 적용해 변환해야 한다.

year, hour, month, season, holiday, workingday 피처 - 계수 영향도가 상대적으로 높은 것은 이들이 숫자형 카테고리 이기 때문이며, 개별 숫자값의 크기가 의미가 있는 것이 아니다.

 

<데이터 전처리 관련 글>

 

[파이썬] 데이터 전처리

시작에 앞서해당 내용은 ' 권철민 지음. 위키북스' 를 토대로 작성되었습니다. 보다 자세한 내용은 해당 서적에 상세히 나와있으니 서적을 참고해 주시기 바랍니다. 네이버 도서책으로 만나는

puppy-foot-it.tistory.com

 

- 판다스의 get_dummies()를 이용해 위의 숫자형 카테고리 칼럼을 모두 원-핫 인코딩 후 예측 성능 확인

get_model_predict() 함수를 생성하여 사이킷런의 선형 회귀 모델 LinearRegression, Ridge, Lasso 모두 학습해 예측 성능 확인

# 숫자형 카테고리 피처('year', 'month' 등) 원-핫 인코딩
X_features_ohe = pd.get_dummies(X_features, columns=['year', 'month', 'day', 'hour', 'holiday', 'workingday', 'season', 'weather'])

# 원-핫 인코딩이 적용된 피처 데이터 세트 기반으로 학습/예측 데이터 분할
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

# 모델 별로 평가 수행
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)

▶  원-핫 인코딩을 적용 후 선형 회귀의 예측 성능이 많이 향상되었다.

 

- 원-핫 인코딩된 데이터 세트에서 회귀 계수가 높은 피처 시각화

원-핫 인코딩으로 피처가 늘어났으므로 회귀 계수 상위 20개 피처 추출

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)

  원-핫 인코딩을 통해서 피처들의 영향도가 달라졌고, 모델의 성능도 향상되었다.

 

- 회귀 트리를 이용해 회귀 예측 수행

앞서 적용한 Target 값의 로그 변환된 값과 원-핫 인코딩된 피처 데이터 세트를 그대로 이용해 랜덤 포레스트, GBM, XGBoost, LightGBM을 순차적으로 성능 평가

※ XGBoost의 경우 DataFrame이 학습/테스트 데이터로 입력될 경우 오류가 발생할 수 있으므로, values 속성을 이용해 넘파이 ndarray로 변환

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의 경우 ndarray로 변환
    get_model_predict(model, X_train.values, X_test.values, y_train.values, y_test.values, is_expm1=True)

[LightGBM] [Info] Auto-choosing row-wise multi-threading, the overhead of testing was 0.001390 seconds.
You can set `force_row_wise=true` to remove the overhead.
And if memory is not enough, you can set `force_col_wise=true`.
[LightGBM] [Info] Total Bins 348
[LightGBM] [Info] Number of data points in the train set: 7620, number of used features: 72
[LightGBM] [Info] Start training from score 4.582043

 

위의 로그는 특히 대규모 데이터 세트의 경우 기계 학습에 사용되는 고성능 그래디언트 부스팅 프레임워크인 LightGBM에서 가져온 것인데, 해석을 하자면

1. 행별 멀티스레딩 자동 선택:
LightGBM은 자동으로 행별 멀티스레딩을 선택하여 여러 CPU 스레드를 사용하여 데이터를 병렬로 처리함으로써 계산 속도를 높인다. 오버헤드는 최적의 스레딩 방법을 결정하는 데 추가로 필요한 시간으로, 이 경우에는 0.001390초로 매우 짧다.

 

2. force_row_wise=true:
이는 LightGBM이 행별 처리를 사용하도록 하는 매개변수이다. 즉, 각 행이 스레드에 의해 개별적으로 처리되므로 오버헤드 테스트가 필요하지 않다.

 

3. force_col_wise=true:
시스템에 메모리가 충분하지 않은 경우 LightGBM이 데이터를 열별로 처리하는 열별 처리를 선택할 수 있으며, 이는 속도를 희생하면서 메모리 소비를 줄인다.

 

4. 총 빈 수 348:
빈은 기본적으로 LightGBM이 연속 기능을 분할하는 데 사용하는 이산 간격의 수이다. 이 경우 모든 기능에 걸쳐 348개의 저장소가 사용되었다.

 

5. train 세트의 데이터 포인트 수: 7620, 사용된 기능 수: 72:
이는 학습에 사용된 7620개의 행(데이터 포인트)과 72개의 특성이 있음을 나타낸다.

 

6.점수 4.582043에서 훈련 시작:
이는 반복을 수행하기 전에 LightGBM이 시작하는 초기 예측 점수이며, 이 점수는 초기 데이터 세트를 기반으로 한 평균 또는 사전 예측 값일 수 있다.

▶ LightGBM은 이러한 설정과 로그를 사용하여 모델 훈련 중에 성능과 리소스 활용도를 최적화한다.


다음 내용

 


[출처]

파이썬 머신러닝 완벽 가이드

캐글

728x90
반응형