[데이콘] 서울시 자전거 이용 예측 AI모델 (캐글 Bike Sharing Demand) RandomForestRegressor, RMSE
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