728x90
1. Bike Sharing Demand
- Data 클렌징 및 가공
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('./bike_train.csv')
print(bike_df.shape)
bike_df.head(10)
# (10886, 12)
- 주요 Column 설명
### 주요 컬럼 설명
* 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: 대여 횟수
bike_df.info()
# 문자열을 datetime 타입으로 변경.
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(10)
bike_df.info()
drop_columns = ['datetime','casual','registered']
bike_df.drop(drop_columns, axis=1,inplace=True)
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])
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))
회귀 평가 지표
log1p와 expm1을 사용하는 이유
- 너무 작은 값은 수치 변환시 0으로 변환된 log0은 -inf가 되므로 오류 발생
- 때문에 변환전 값에 1을 더한 값을 로그 변환할 필요가 있으며, 이를 위해 np.log1p()를 이용
- np.log1p()로 로그 변환된 값은 np.expm1()으로 다시 원본 변환될 수 있음
import numpy as np
print(1e-1000 == 0.0)
print(np.log(1e-1000))
print(np.log(1e-1000 + 1))
print(np.log1p(1e-1000))
# True
# -inf
# 0.0
# 0.0
var_1 = np.log1p(100)
var_2 = np.expm1(var_1)
print(var_1, var_2)
# 4.61512051684126 100.00000000000003
로그 변환, 피처 인코딩, 모델 학습 / 예측 / 평가
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)
# 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)
# real_count predicted_count diff
# 1618 890 322.0 568.0
# 3151 798 241.0 557.0
# 966 884 327.0 557.0
# 412 745 194.0 551.0
# 2817 856 310.0 546.0
y_target.hist()
y_log_transform = np.log1p(y_target)
y_log_transform.hist()
Target Value를 로그 변환(log1p)했을 경우 예측값(y_preds)이나 실제값(y_test)은 다시 scale 변환(expm1)해줘야 함
# 타깃 칼럼인 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
coef = pd.Series(lr_reg.coef_, index=X_features.columns)
coef.sort_values(ascending=False)
# year 0.418513
# hour 0.098348
# month 0.076839
# atemp 0.030857
# temp 0.011712
# windspeed 0.003961
# day 0.003353
# humidity -0.014512
# weather -0.014733
# season -0.044525
# holiday -0.046010
# workingday -0.066383
# dtype: float64
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)
출력 변수(종속 변수)가 연속-수치형 변수 & 입력 변수가 범주형 변수: 가변수(dummy variable) 이용
- K개의 범주를 갖는 변수에 대해서 K-1 개의 가변수가 필요 (ALL 0인 경우를 하나의 가변수로 취급하기 때문에 -1)
# 'year', month', 'day', hour'등의 피처들을 One Hot Encoding
X_features_ohe = pd.get_dummies(
X_features,
columns=[
"year",
"month",
"day",
"hour",
"holiday",
"workingday",
"season",
"weather",
],
)
X_features_ohe.head(10)
# 원-핫 인코딩이 적용된 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.689, MAE: 63.382
# ### Ridge ###
# RMSLE: 0.590, RMSE: 98.529, MAE: 63.893
# ### Lasso ###
# RMSLE: 0.635, RMSE: 113.219, MAE: 72.803
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.355, RMSE: 50.442, MAE: 31.170
# ### GradientBoostingRegressor ###
# RMSLE: 0.330, RMSE: 53.315, MAE: 32.730
# ### XGBRegressor ###
# RMSLE: 0.342, RMSE: 51.732, MAE: 31.251
# ### LGBMRegressor ###
# RMSLE: 0.319, RMSE: 47.215, MAE: 29.029
728x90