3. Modeling

  • 자전거의 일별 대여량을 예측하는 문제이므로 회귀(Regression) 모델을 사용합니다.
  • 이 대회는 회귀 평가 지표중 하나인 RMSLE(Root Mean Squared Logarithmic Error) Score 로 평가합니다.

3.1. RMSLE

  • RMSLE 를 구하는 공식입니다.

  • Python 에서 RMSLE를 구해주는 라이브러리가 없으니 직접 함수로 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# score 제작 준비
from sklearn.metrics import make_scorer
 
# RMSLE 함수 정의
def RMSLE(predicted_values, actual_values):
    
    # 예측값과 실제 값을 numpy 배열 형태로 변환
    predicted_values = np.array(predicted_values)
    actual_values = np.array(actual_values)
    
    # 예측값과 실제 값에 1을 더하고 로그 변환 
    log_predict = np.log(predicted_values + 1)
    log_actual = np.log(actual_values + 1)
    
    # 로그 변환한 예측값에서 로그 변환한 실제 값을 빼고 제곱
    difference = log_predict - log_actual
    difference = np.square(difference)
    
    # 위에서 계산한 값의 평균
    mean_difference = difference.mean()
    
    # 위에서 계산한 값에 루트
    score = np.sqrt(mean_difference)
    
    return score
 
rmsle_score = make_scorer(RMSLE)


3.2. Model 선택

  • 앙상블(Ensemble) 회귀 모델로 예측을 진행합니다.

    1. Random Foreset
    2. Adaboost
    3. Bagging
  • 사용할 모델을 불러옵니다.

1
2
3
from sklearn.ensemble import RandomForestRegressor # Random Forest
from sklearn.ensemble import AdaBoostRegressor # AdaBoost
from sklearn.ensemble import BaggingRegressor # Bagging


3.3. K-Fold Cross Validation (교차검증)

  • Train set 의 불균형을 방지하기 위해 K-Fold 교차검증을 이용해 모델의 성능을 테스트 합니다.
1
2
3
4
5
# kfold 준비
from sklearn.model_selection import KFold, cross_val_score
 
# kfold 설정
kfold = KFold(n_splits=10, random_state=1230, shuffle=True)

  • 3가지 모델 성능을 Kfold 교차검증을 사용해 테스트 해봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 사용할 모델 설정
models = [RandomForestRegressor(), AdaBoostRegressor(), BaggingRegressor()]
regressors = ['RandomForest''AdaBoost''Bagging']
score_result = []
 
# Kfold 교차 검증
for x in models:
    Rgr = x
    score = cross_val_score(Rgr, X_train, target, cv=kfold, scoring=rmsle_score)
    score_result.append(score.mean())
 
# 결과를 dataframe 으로 변경
Kfold_score_result = pd.DataFrame({'Score' : score_result}, index=regressors)
Kfold_score_result

  • Score는 낮을 수록 성능이 좋다는 지표입니다.

  • RandomForest 을 선택하는 것이 좋아보입니다.

  • 음… 그런데 이대로 끝내버리기에는 뭔가 아쉬운거 같습니다.

  • windspeed 의 값이 0인 부분을 결측값이라고 생각하고 처리 후 비교해 봅시다.


3.4. ‘windspeed’ Feature Engineering

  • windspeed 의 0 값을 0이 아닌 값으로 예측하여 넣어보도록 하겠습니다.

  • 모델은 Random Forest 로 진행합니다.

  • 풍속과 관계가 있을 것 같은 Feature 로는 season, weather, humidity, temp, month, hour 정도로 추릴 수 있을 것 같습니다.

  • windspeed 의 0의 값을 처리하는 파이프라인을 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
train = pd.read_csv('train.csv')
 
# month 와 hour feature
train['datetime'= pd.to_datetime(train['datetime'])
train['month'= train['datetime'].dt.month
train['hour'= train['datetime'].dt.hour
 
# 범주형 변수들 category 타입으로 변경
train['season'= train['season'].astype('category')
train['weather'= train['weather'].astype('category')
train['month'= train['month'].astype('category')
train['hour'= train['hour'].astype('category')
 
# 사용하지 않는 feature 제거
drop_feature = ['datetime''casual''registered''atemp']
train.drop(drop_feature, inplace=True, axis=1)
 
# windspeed 0과 0이 아닌 데이터 분리
train_wind0 = train[train['windspeed'== 0]
train_notwind0 = train[train['windspeed'!= 0]
 
# modeling
wind_col = ['season''weather''temp''humidity''month''hour']
model_wind = RandomForestRegressor()
model_wind.fit(train_notwind0[wind_col], train_notwind0['windspeed'])
 
# 예측 결과 추가
wind0_values = model_wind.predict(train_wind0[wind_col])
train_wind0['windspeed'= wind0_values
 
# 데이터 병합
new_train_data = train_notwind0.append(train_wind0).reset_index()
new_train_data.drop('index', inplace=True, axis=1)
 
new_train_data

  • 새로운 train set 을 만들었으니 windspeed 가 얼마나 얼마나 영향을 줄지 테스트 해봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# One-Hot encoding
new_train_data = pd.get_dummies(new_train_data, columns=['season','weather'])
 
# train set 과 target 설정
X_new_train_data = new_train_data.drop('count', axis=1)
new_target = new_train_data['count']
 
# 사용할 모델 설정
models = [RandomForestRegressor(), AdaBoostRegressor(), BaggingRegressor()]
regressors = ['RandomForest''AdaBoost''Bagging']
new_score_result = []
 
# Kfold 교차 검증
for x in models:
    new_Rgr = x
    new_score = cross_val_score(new_Rgr, X_new_train_data, new_target, cv=kfold, scoring=rmsle_score)
    new_score_result.append(new_score.mean())
 
# 결과를 dataframe 으로 변경
new_Kfold_score_result = pd.DataFrame({'new Score' : new_score_result}, index=regressors)
new_Kfold_score_result

  • 유의미한 변화를 기대했지만… 거의 변화가 없는 것을 볼 수 있습니다.(뻘짓)

  • windspeed 는 자전거 대여량을 예측하는데 큰 영향을 주지 않는 것 같습니다.

  • 그냥 windspeed 가 0인 것을 사용합니다.

  • 이번엔 Random Forest의 Parameter 를 수정해 봅니다.


3.5. Parameter Tunning

  • RandomForestRegressor 에는 많은 Parameter 들이 존재합니다.
  • 공식문서 에서 더 많은 내용을 찾을 수 있습니다.
  • 이 중에서 나무의 수를 결정하는 n_estimators 를 수정해 봅니다. (default=100)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 랜덤포레스트 모델 사용준비
from sklearn.ensemble import RandomForestRegressor
 
val = [102030405070100150200]
result = []
 
for i in val:
    # 랜덤포레스트 모델
    model = RandomForestRegressor(n_estimators=i)
 
    # kfold 교차 검증
    score = cross_val_score(model, X_train, target, cv=kfold, scoring=rmsle_score)
 
    # score 평균
    mean = score.mean()
    result.append(mean)

  • 10부터 200까지의 숫자를 적절히 넣어주고 결과를 그래프화 합니다.
1
2
3
4
5
6
fig, ax = plt.subplots(figsize=(12,5))
 
# x축은 n_estimators 값, y축은 score 의 평균
plt.plot(val,result)
 
plt.show()

  • 값의 그래프를 보니 n_estimators 의 default 값인 100보다 150에서 아주 미세하게 더 성능이 좋은 것을 확인할 수 있습니다.


3.6. Submission

  • Random Forest Regressor 를 사용하고 n_estimators 의 값은 150으로 사용합니다.
  • 이제 submission 파일을 만들어 봅니다.
1
2
3
4
5
6
7
8
9
10
# 학습 및 예측
final_model = RandomForestRegressor(n_estimators=150)
final_model.fit(X_train, target)
prediction = final_model.predict(test_data)
 
# 예측 결과를 submission에 입력
submission['count'= prediction
 
# 제출 파일
submission.to_csv('bike submission2.csv', index=False)

  • submission 파일을 Kaggle에 제출 하고 score를 확인합니다.

  • Score는 0.51131 로 테스트했던 성능보다 낮게 나왔습니다.(오버피팅의 문제가 있을지도…)


3.7. 마무리

  • 회귀 문제를 연습해 보기에 괜찮은 문제인것 같습니다.

  • 성능이 만족할 만큼 높게 나오지 않았기 때문에 개선이 많이 필요 할 것 같습니다.

  • 분류 문제에서도 느꼈지만 데이터의 전처리가 매우매우매우 중요한 것 같습니다.

  • Category type 과 One-Hot encoding 에 대해서 좀 더 공부를 해봐야겠습니다. (이 부분은 나중에 실험 해볼 예정)


Kaggle 에 있는 수 많은 notebook 을 참고하며 공부했습니다.