노트1/과제

[데이콘] 서울시 자전거 이용 예측 AI모델 (캐글 Bike Sharing Demand) RandomForestRegressor, RMSE

Paige09 2021. 12. 20. 17:49

 

 

https://dacon.io/competitions/open/235698/overview/description

 

#오늘의 파이썬 #1일1오파 #파이썬 # python - DACON

좋아요는 1분 내에 한 번만 클릭 할 수 있습니다.

dacon.io

 

 

[데이터]

2017년 4월 1일부터 - 5월 31일까지 시간별 따릉이 대여수와 기상상황

 

id : 날짜와 시간별 id

hour_bef_temperature : 1시간 전 기온

hour_bef_precipitation : 1시간 전 비 정보, 비가 오지 않았으면 0, 비가 오면 1

hour_bef_windspeed : 1시간 전 풍속(평균)

hour_bef_humidity : 1시간 전 습도

hour_bef_visibility : 1시간 전 시정(視程), 시계(視界)(특정 기상 상태에 따른 가시성을 의미)

hour_bef_ozone : 1시간 전 오존

hour_bef_pm10 : 1시간 전 미세먼지(머리카락 굵기의 1/5에서 1/7 크기의 미세먼지)

hour_bef_pm2.5 : 1시간 전 미세먼지(머리카락 굵기의 1/20에서 1/30 크기의 미세먼지)

count : 시간에 따른 따릉이 대여 수

 

[목표]

각 날짜의 1시간 전의 기상상황으로 1시간 후의 따릉이 대여수를 예측하기

 

[평가지표]

RMSE

 

[과정 요약]

1. EDA

2. 전처리 : 보간법 or 시간대별 평균치

3. 모델링 : 의사결정회귀나무. 변수 중요도가 낮은 컬럼을 제외해가며 RMSE 체크. 하이퍼파라미터 값을 수정해가며 튜닝하기

 

 


 

 

1. 파일 불러오기 ~ EDA

train.head()

train.shape

train.info()

train.discribe()

train.groupby('hour').mean()['count'].plot()

히트맵 등

 

import pandas as pd
from sklearn.ensemble import RandomForestRegressor

train = pd.read_csv(r'./data/train.csv')
test = pd.read_csv(r'./data/test.csv')
submission = pd.read_csv(r'./data/submission.csv') # 답안지

train.info() # 결측치 확인

# 변수 간 상관관계 확인
import seaborn as sns
plt.figure(figsize=(10,10))
sns.heatmap(train.corr(), annot=True, fmt='.2f')  # annot_kws={'size':10, }, cmap = 'rainbow'

train.isna().sum()
train[train['hour_bef_temperature'].isna()] # 결측치 위치 확인
# 0시, 18시인데 두 시간대의 온도는 매우 다를 것이므로 온도 컬럼 전체의 중앙값/평균값 같은 걸로 대체하는건 무리가 있다고 판단.

 

 

2. 전처리

결측치를 대체하기 위해 여기서 사용한 방법은

1) 보간법

2) 시간별 평균값을 이용해서 결측치를 대체 - 컬럼마다 시간별 평균값을 체크해야한다는 점에서 보간법보다 시간이 더 걸림

 

# 보간법
train.interpolate(inplace=True)
test.interpolate(inplace=True)
# 시간별 평균값 확인해서 결측값을 메꾸기
train.groupby('hour')['hour_bef_temperature'].mean() 
train['hour_bef_temperature'].fillna({934:14.788136, 1035:20.926667}, inplace=True)
# 저장하기 위해서 inplace 옵션 True

# 마찬가지로 test 결측치 처리. 이 때 결측값을 대체할 값은 train에서 가져온다는 점 주의

 

 

3. 모델링

 

from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor(criterion = 'mse')
# 따릉이 대회의 평가지표는 RMSE. RMSE는 MSE 평가지표에 루트를 씌운 것이므로 MSE를 평가척도로 정함.

X_train = train.drop(['count'], axis=1)
Y_train = train['count']

model.fit(X_train, Y_train) # 모델 학습

 

변수의 중요도를 파악해서, 낮은 변수부터 하나씩 제거해가며 여러 모델을 테스트 해본다.

 

model.feature_importances_ # 변수의 중요도 파악

# 첫번째 모델 = id, count 컬럼 제외
X_train_1 = train.drop(['id','count'], axis=1)
test_1 = test.drop(['id'], axis=1)

model_1 = RandomForestRegressor(criterion = 'mse')
model_1.fit(X_train_1, Y_train)
ypread_1 = model_1.predict(test_1)

submission_1 = pd.read_csv(r'./data/submission.csv') # 답안지
submission_1['count'] = ypread_1
submission_1.to_csv(r'./submission_1.csv', index=False)


# 두번째 모델 = id, count, hour_bef_precipitation 제외

X_train_2 = train.drop(['id','count','hour_bef_precipitation'], axis=1)
test_2 = test.drop(['id','hour_bef_precipitation'], axis=1)

model_2 = RandomForestRegressor(criterion = 'mse')
model_2.fit(X_train_2, Y_train)
ypread_2 = model_2.predict(test_2)

submission_2 = pd.read_csv(r'./data/submission.csv') # 답안지
submission_2['count'] = ypread_2
submission_2.to_csv(r'./submission_2.csv', index=False)

 

이런 식으로 네가지 모델을 돌려봤는데,

id, count, hour_bef_precipitation, hour_bef_pm2.5 변수를 제거한 모델3이 RMSE 46.87 정도로 가장 낮게 나왔다.

 

 

다음으로 하이퍼파라미터 튜닝.

이를 통해 모델의 과적합을 방지하고 스코어를 올린다.

 

Scikit-learn 패키지의 GridSearchCV 이용

GridSearch는 완전 탐색(Exhaustive Search)을 사용해서 가능한 모든 조합 중 가장 우수한 조합을 찾아줌(시간이 오래 걸린다는 단점)

 

 

[참고] GridSearchCV,  RandomizedSearchCV

더보기

1. Scikit-learn 패키지의 GridSearchCV

교차검증(Cross Validation, CV)을 사용해서 가능한 모든 조합 중 가장 우수한 조합을 찾아줌

=> 완전 탐색(Exhaustive Search). 시간이 오래 걸린다는 단점.

 

cv : 교차검증 시 fold 개수

scoring : 평가 지표 (ex. 'accuracy')

n_jobs=None  사용하고자 하는 CPU 개수. 많이 쓸수록 빠름. 모두 활용하려면 -1

 

 

2. Scikit-learn 패키지의 RandomizedSearchCV

모든 조합을 훑는 대신, 각 반복마다 랜덤한 값을 선택해서 조합을 평가.

GridSearchCV보다 시간면에서 효율적.

 

n_iter : 파라미터 랜덤 탐색 횟수 (기본값 10)

cv : 교차검증 시 fold 개수

scoring : 평가 지표 (ex. 'accuracy')

n_jobs=None  사용하고자 하는 CPU 개수. 많이 쓸수록 빠름. 모두 활용하려면 -1

 

 

1. n_estimators=100  학습시킬 의사결정나무 개수(이를 종합해서 최종값 산출하게 됨)
2. max_depth=None   뿌리 노드로부터 최대로 내려갈 수 있는 나무의 깊이. 과대적합 방지용.
3. n_jobs=None  사용하고자 하는 CPU 개수. 많이 쓸수록 빠름. 모두 활용하려면 -1

4. min_samples_leaf  leaf 노드에서 필요한 최소한의 샘플 수. 너무 적으면 과적합이 일어날 수 있다

5. criterion  노드 분리기준. gini( / entropy(데이터 혼잡도)

6. splitter  노드 분리 방법. random / best

 

 

from sklearn.model_selection import GridSearchCV

model = RandomForestRegressor(criterion = 'mse', random_state=0)

parameters = {'n_estimators': [200, 300, 500],
              'max_features': [5, 6, 7, 8],
              'min_samples_leaf': [1, 3, 5]}

greedy_CV = GridSearchCV(model,
                         param_grid=parameters,
                         cv = 3, n_jobs = -1)
greedy_CV.fit(X_train, Y_train)

greedy_CV.best_params_

greedy_CV_pred = greedy_CV.predict(test)
submission['count'] = greedy_CV_pred
submission.to_csv(r'./submission_greedyCV.csv', index=False)

 

max_features : 8, min_samples_leaf : 3, n_estimators : 300 일 때

앞서 테스트했던 모델3보다 더 낮은 RMSE 기록.

 

 

 

 


 

 

 

[참고]  평균 제곱근 오차 RMSE  https://hjryu09.tistory.com/33