[머신러닝] Bike Sharing Demand 데이터를 사용한 회귀: Feature Engineering(Binning, 로그변환) + 회귀모델평가지표(MAE, MSE, RMSE, RMSLE) + 하이퍼파라미터튜닝(RandomizedSearchCV)
https://www.kaggle.com/competitions/bike-sharing-demand
Bike Sharing Demand | Kaggle
www.kaggle.com
train = pd.read_csv('data/bike/train.csv')
print(train.shape)
display(train.head(3)) #(10886, 12)
test = pd.read_csv('data/bike/test.csv')
print(test.shape)
display(test.head(3)) #(6493, 9)
# 예측해야 할 값
set(train.columns) - set(test.columns) # 우리가 예측해야 할 값은 count이다
{'casual', 'count', 'registered'}
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10886 entries, 0 to 10885
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 datetime 10886 non-null object
1 season 10886 non-null int64
2 holiday 10886 non-null int64
3 workingday 10886 non-null int64
4 weather 10886 non-null int64
5 temp 10886 non-null float64
6 atemp 10886 non-null float64
7 humidity 10886 non-null int64
8 windspeed 10886 non-null float64
9 casual 10886 non-null int64
10 registered 10886 non-null int64
11 count 10886 non-null int64
dtypes: float64(3), int64(8), object(1)
memory usage: 1020.7+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6493 entries, 0 to 6492
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 datetime 6493 non-null object
1 season 6493 non-null int64
2 holiday 6493 non-null int64
3 workingday 6493 non-null int64
4 weather 6493 non-null int64
5 temp 6493 non-null float64
6 atemp 6493 non-null float64
7 humidity 6493 non-null int64
8 windspeed 6493 non-null float64
dtypes: float64(3), int64(5), object(1)
memory usage: 456.7+ KB
datetime 컬럼을 연,월,일,시,분,초로 만들어보자
# datetime의 데이터타입이 object이므로 datetime으로 타입을 바꿔주자
train["datetime"] = pd.to_datetime(train["datetime"])
# 파생변수 만들기
train["year"] = train["datetime"].dt.year
train["month"] = train["datetime"].dt.month
train["day"] = train["datetime"].dt.day
train["hour"] = train["datetime"].dt.hour
train["minute"] = train["datetime"].dt.minute
train["second"] = train["datetime"].dt.second
#test도 마찬가지로 바꿔준다.
test["datetime"] = pd.to_datetime(test["datetime"])
test["year"] = test["datetime"].dt.year
test["month"] = test["datetime"].dt.month
test["day"] = test["datetime"].dt.day
test["hour"] = test["datetime"].dt.hour
test["minute"] = test["datetime"].dt.minute
test["second"] = test["datetime"].dt.second
수치변수의 히스토그램을 그려보는 이유❓
👉 이걸 왜하느냐❓ 수치형 변수의 경우 종속변수와 혹은 관련이 있다고 보이는 scatterplot을 그려보면서 이상치를 확인해볼 수 있다.
현실세계에서 자전거 대여와 풍속이 관계가 있을까?
히스토그램을 그려봤을 때 풍속이 0인 값들이 많았다.
train[train["windspeed"] == 0].shape → (1313, 18)
sns.scatterplot(data = train, x="windspeed", y="count")
현실세계에서 풍속과 대여수가 관련이 있을까?
sns.scatterplot(data = train, x="humidity", y="count")
sns.scatterplot(data = train, x="temp", y="atemp")
train[train["weather"] == 4]
값이 1개밖에 없으므로 weather별 barplot을 그렸을 때 값이 현실세계와는 다르게 나타나게 된다.
💡 데이터만 보지말고 현실세도 연관시켜서 생각해보자! 결국 현실세계 문제를 해결하는 것이다.
year와 month는 모두 수치형변수이지만 year는 2011, 2012 데이터가 전부이기때문이다.
# datetime 컬럼에서 년-월만 가져오기
train["year_date"] = train["datetime"].astype(str).str[:7]
test["year_date"] = test["datetime"].astype(str).str[:7]
# 인코딩
train["year_month_code"] = train["datetime"].astype("category").cat.codes
test["year_month_code"] = test["datetime"].astype("category").cat.codes
📍.cat.codes
# label 및 feature_names
label_name = "count"
feature_names = train.columns.tolist()
feature_names.remove(label_name)
feature_names.remove("datetime")
feature_names.remove("casual")
feature_names.remove("registered")
# feature_names.remove("year")
feature_names.remove("month")
feature_names.remove("day")
feature_names.remove("minute")
feature_names.remove("second")
feature_names.remove("year-month")
feature_names
# 학습 및 예측 데이터셋
X_train = train[feature_names]
X_test = test[feature_names]
y_train = train[label_name]
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor(random_state=42, n_jobs=-1)
model
RandomForestRegressor(n_jobs=-1, random_state=42)
train data를 k개로 나눠서 학습과 검증을 하는 과정이며, test data를 학습시켜 예측하기 전 best estimator와 best score를 알 수 있다.
이는 모의고사를 풀어서 답을 구하는 과정과 유사하다.
from sklearn.model_selection import cross_val_predict
y_valid_predict = cross_val_predict(model, X_train, y_train,
cv = 5, n_jobs=-1, random_state=42)
# 판다스로 구하기
mae = abs(y_train - y_val_predict).mean()
# 사이킷런
from sklearn.metrics import mean_absolure_error
mean_absolure_error(y_train, y_valid_predict)
48.45686583248034
# 판다스로 구하기
mse = ((y_train - y_val_predict) ** 2).mean()
mse = np.square(y_train, y_predict).mean()
# 사이킷런
from sklearn.metrics import mean_squared_error
mean_absolure_error(y_train, y_valid_predict)
5255.510393476759
# 판다스
rmse = (((y_train = y_val_predict) ** 2).mean()) ** 0.5
rmse = np.sqrt(((y_train = y_val_predict) ** 2).mean())
rmse = np.sqrt(np.square(y_train, y_predict).mean())
rmse = mse ** 0.5
rmse = np.sqrt(mse)
72.4948990859133
# 판다스
rmsle = np.sqrt(np.square(np.log(1+y_train) - np.log(1+y_val_predict)).mean())
rmsle = np.sqrt(np.square(np.log1p(y_train) - np.log1p(y_val_predict)).mean())
# 사이킷런
from sklearn.metrics import mean_squared_log_error
mean_squared_log_error(y_train, y_valid_pred) ** 0.5
0.5103498006704638
# 학습
model.fit(X_train, y_train)
# 시각화
sns.barplot(x=model.feature_importances_, y=model.feature_names_in_)
# 예측
y_predict = model.predict(X_test)
이 대회의 평가 지표가 RMSLE이다. 따라서 종속변수 혹은 예측값에 로그를 먼저 취해 로그값으로 예측하고 예측값을 복원하기 위해서이다.
로그변환은 주로 수치형변수의 분포가 정규분포에 가깝지 않을 경우 이를 보정하기 위해 사용해주는 Feature engineering기법 중 하나이다.
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(12,3))
sns.kdeplot(train["count"], ax = axes[0])
sns.kdeplot(train["count_log1p"], ax = axes[1])
sns.kdeplot(train["count_expm1"], ax = axes[2])
train[["count", "count_log1p", "count_expm1"]].describe()
# 독립 및 종속변수에 사용할 미쳐 지정
label_name = "count_log1p"
feature_names = ['holiday', 'workingday', 'weather', 'temp',
'atemp', 'humidity', 'windspeed', 'year', 'hour','dayofweek']
feature_names
# 학습 및 예측 데이터셋 나누기
X_train = train[feature_names]
X_test = test[feature_names]
y_train = train[label_name]
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor(random_state=42, n_jobs=-1)
model
# 파라미터 값을 랜덤으로 지정해준다.
from sklearn.model_selection import RandomizedSearchCV
params_distribution = {'max_depth' : np.random.randint(3, 20, 10),
'max_features': np.random.uniform(0.7, 1, 10)}
reg = RandomizedSearchCV(model,
params_distribution = params_distribution,
scoring = 'neg_root_mean_squared_error',
n_iter = 10,
cv = 5,
n_jobs=-1,
verbose=2,
random_state = 42)
reg.fit(X_train, y_train)
Fitting 5 folds for each of 10 candidates, totalling 50 fits
reg.best_estimator_
>>> RandomForestRegressor(max_depth=23, max_features=0.8636521987264846, n_jobs=-1, random_state=42)
rmsle = abs(reg.best_score_)
rmsle
>>> 0.48870366876532
pd.DataFrame(reg.cv_results_).sort_values("rank_test_score")
# 모델불러오기 - 학습 및 예측
best_model = reg.best_estimator_
y_predict = best_model.fit(X_train, y_train).predict(X_test)
[KMOOC-실습으로배우는머신러닝]3. Classification (0) | 2022.11.21 |
---|---|
[머신러닝] Houde Price 데이터를 사용한 데이터 전처리 + EDA+ Feature Engineering + 회귀모델 (0) | 2022.11.17 |
[머신러닝] House Price데이처를 사용한 데이터 탐색 + 데이터 전처리+ Feature Engineering (0) | 2022.11.17 |