랜덤포레스트 사용하기
Load Dataset
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
# 노트북 안에 그래프를 그리기 위해
%matplotlib inline
# 그래프에서 마이너스 폰트 깨지는 문제에 대한 대처
mpl.rcParams['axes.unicode_minus'] = False
import warnings
warnings.filterwarnings('ignore')
train = pd.read_csv("data/train.csv", parse_dates=["datetime"])
train.shape
(10886, 12)
test = pd.read_csv("data/test.csv", parse_dates=["datetime"])
test.shape
(6493, 9)
Feature Engineering
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
train["dayofweek"] = train["datetime"].dt.dayofweek
train.shape
(10886, 19)
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
test["dayofweek"] = test["datetime"].dt.dayofweek
test.shape
(6493, 16)
# widspeed 풍속에 0 값이 가장 많다. => 잘못 기록된 데이터를 고쳐 줄 필요가 있음
fig, axes = plt.subplots(nrows=2)
fig.set_size_inches(18,10)
plt.sca(axes[0])
plt.xticks(rotation=30, ha='right')
axes[0].set(ylabel='Count',title="train windspeed")
sns.countplot(data=train, x="windspeed", ax=axes[0])
plt.sca(axes[1])
plt.xticks(rotation=30, ha='right')
axes[1].set(ylabel='Count',title="test windspeed")
sns.countplot(data=test, x="windspeed", ax=axes[1])
# 풍속의 0 값에 특정 값을 넣어준다.
# 평균을 구해 일괄적으로 넣어줄 수도 있지만, 예측의 정확도를 높이는 데 도움이 될 것 같진 않다.
# train.loc[train["windspeed"] == 0, "windspeed"] = train["windspeed"].mean()
# test.loc[train["windspeed"] == 0, "windspeed"] = train["windspeed"].mean()
# 풍속이 0인 것과 아닌 것의 세트를 나누어 준다.
trainWind0 = train.loc[train['windspeed'] == 0]
trainWindNot0 = train.loc[train['windspeed'] != 0]
print(trainWind0.shape)
print(trainWindNot0.shape)
(1313, 19)
(9573, 19)
# 그래서 머신러닝으로 예측을 해서 풍속을 넣어주도록 한다.
from sklearn.ensemble import RandomForestClassifier
def predict_windspeed(data):
# 풍속이 0인것과 아닌 것을 나누어 준다.
dataWind0 = data.loc[data['windspeed'] == 0]
dataWindNot0 = data.loc[data['windspeed'] != 0]
# 풍속을 예측할 피처를 선택한다.
wCol = ["season", "weather", "humidity", "month", "temp", "year", "atemp"]
# 풍속이 0이 아닌 데이터들의 타입을 스트링으로 바꿔준다.
dataWindNot0["windspeed"] = dataWindNot0["windspeed"].astype("str")
# 랜덤포레스트 분류기를 사용한다.
rfModel_wind = RandomForestClassifier()
# wCol에 있는 피처의 값을 바탕으로 풍속을 학습시킨다.
rfModel_wind.fit(dataWindNot0[wCol], dataWindNot0["windspeed"])
# 학습한 값을 바탕으로 풍속이 0으로 기록된 데이터의 풍속을 예측한다.
wind0Values = rfModel_wind.predict(X = dataWind0[wCol])
# 값을 다 예측 후 비교해 보기 위해
# 예측한 값을 넣어 줄 데이터 프레임을 새로 만든다.
predictWind0 = dataWind0
predictWindNot0 = dataWindNot0
# 값이 0으로 기록된 풍속에 대해 예측한 값을 넣어준다.
predictWind0["windspeed"] = wind0Values
# dataWindNot0 0이 아닌 풍속이 있는 데이터프레임에 예측한 값이 있는 데이터프레임을 합쳐준다.
data = predictWindNot0.append(predictWind0)
# 풍속의 데이터 타입을 float으로 지정해 준다.
data["windspeed"] = data["windspeed"].astype("float")
data.reset_index(inplace=True)
data.drop('index', inplace=True, axis=1)
return data
# 0값을 조정한다.
train = predict_windspeed(train)
# test = predict_windspeed(test)
# widspeed 의 0 값을 조정한 데이터를 시각화
fig, ax1 = plt.subplots()
fig.set_size_inches(18,6)
plt.sca(ax1)
plt.xticks(rotation=30, ha='right')
ax1.set(ylabel='Count',title="train windspeed")
sns.countplot(data=train, x="windspeed", ax=ax1)
Feature Selection
- 신호와 잡음을 구분해야 한다.
- 피처가 많다고 해서 무조건 좋은 성능을 내지 않는다.
- 피처를 하나씩 추가하고 변경해 가면서 성능이 좋지 않은 피처는 제거하도록 한다.
# 연속형 feature와 범주형 feature
# 연속형 feature = ["temp","humidity","windspeed","atemp"]
# 범주형 feature의 type을 category로 변경해 준다.
categorical_feature_names = ["season","holiday","workingday","weather",
"dayofweek","month","year","hour"]
for var in categorical_feature_names:
train[var] = train[var].astype("category")
test[var] = test[var].astype("category")
feature_names = ["season", "weather", "temp", "atemp", "humidity", "windspeed",
"year", "hour", "dayofweek", "holiday", "workingday"]
feature_names
['season',
'weather',
'temp',
'atemp',
'humidity',
'windspeed',
'year',
'hour',
'dayofweek',
'holiday',
'workingday']
X_train = train[feature_names]
print(X_train.shape)
X_train.head()
(10886, 11)
season | weather | temp | atemp | humidity | windspeed | year | hour | dayofweek | holiday | workingday | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 9.84 | 12.880 | 75 | 6.0032 | 2011 | 5 | 5 | 0 | 0 |
1 | 1 | 1 | 15.58 | 19.695 | 76 | 16.9979 | 2011 | 10 | 5 | 0 | 0 |
2 | 1 | 1 | 14.76 | 16.665 | 81 | 19.0012 | 2011 | 11 | 5 | 0 | 0 |
3 | 1 | 1 | 17.22 | 21.210 | 77 | 19.0012 | 2011 | 12 | 5 | 0 | 0 |
4 | 1 | 2 | 18.86 | 22.725 | 72 | 19.9995 | 2011 | 13 | 5 | 0 | 0 |
X_test = test[feature_names]
print(X_test.shape)
X_test.head()
(6493, 11)
season | weather | temp | atemp | humidity | windspeed | year | hour | dayofweek | holiday | workingday | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 10.66 | 11.365 | 56 | 26.0027 | 2011 | 0 | 3 | 0 | 1 |
1 | 1 | 1 | 10.66 | 13.635 | 56 | 0.0000 | 2011 | 1 | 3 | 0 | 1 |
2 | 1 | 1 | 10.66 | 13.635 | 56 | 0.0000 | 2011 | 2 | 3 | 0 | 1 |
3 | 1 | 1 | 10.66 | 12.880 | 56 | 11.0014 | 2011 | 3 | 3 | 0 | 1 |
4 | 1 | 1 | 10.66 | 12.880 | 56 | 11.0014 | 2011 | 4 | 3 | 0 | 1 |
label_name = "count"
y_train = train[label_name]
print(y_train.shape)
y_train.head()
(10886,)
0 1
1 36
2 56
3 84
4 94
Name: count, dtype: int64
Score
RMSLE
과대평가 된 항목보다는 과소평가 된 항목에 페널티를 준다.
오차(Error)를 제곱(Square)해서 평균(Mean)한 값의 제곱근(Root)으로 값이 작을수록 정밀도가 높다.
0에 가까운 값이 나올수록 정밀도가 높은 값이다.
Submissions are evaluated one the Root Mean Squared Logarithmic Error (RMSLE)
$$ \sqrt{\frac{1}{n} \sum{i=1}n (\log(pi + 1) - \log(a_i+1))2 } $$
- \({n}\) is the number of hours in the test set
- \(p_i\) is your predicted count
- \(a_i\) is the actual count
\(\log(x)\) is the natural logarithm
좀 더 자세한 설명은 : RMSLE cost function
잔차(residual)에 대한 평균에 로그를 씌운 값이다. => 과대평가 된 항목보다 과소평가 된 항목에 페널티를 주기 위해
정답에 대한 오류를 숫자로 나타낸 값으로 값이 클수록 오차가 크다는 의미다.
값이 작을수록 오류가 적다는 의미를 나타낸다.
이미지 출처 : 위키피디아 https://ko.wikipedia.org/wiki/로그
from sklearn.metrics import make_scorer
def rmsle(predicted_values, actual_values):
# 넘파이로 배열 형태로 바꿔준다.
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 = (log_predict - log_actual) ** 2
difference = np.square(difference)
# 평균을 낸다.
mean_difference = difference.mean()
# 다시 루트를 씌운다.
score = np.sqrt(mean_difference)
return score
rmsle_scorer = make_scorer(rmsle)
rmsle_scorer
make_scorer(rmsle)
Cross Validation 교차 검증
일반화 성능을 측정하기 위해 데이터를 여러 번 반복해서 나누고 여러 모델을 학습한다.
이미지 출처 : https://www.researchgate.net/figure/228403467_fig2_Figure-4-k-fold-cross-validation-scheme-exampleKFold 교차검증
- 데이터를 폴드라 부르는 비슷한 크기의 부분집합(n_splits)으로 나누고 각각의 폴드 정확도를 측정한다.
- 첫 번째 폴드를 테스트 세트로 사용하고 나머지 폴드를 훈련 세트로 사용하여 학습한다.
- 나머지 훈련 세트로 만들어진 세트의 정확도를 첫 번째 폴드로 평가한다.
- 다음은 두 번째 폴드가 테스트 세트가 되고 나머지 폴드의 훈련 세트를 두 번째 폴드로 정확도를 측정한다.
- 이 과정을 마지막 폴드까지 반복한다.
- 이렇게 훈련 세트와 테스트세트로 나누는 N개의 분할마다 정확도를 측정하여 평균값을 낸 게 정확도가 된다.
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
k_fold = KFold(n_splits=10, shuffle=True, random_state=0)
RandomForest
from sklearn.ensemble import RandomForestRegressor
max_depth_list = []
model = RandomForestRegressor(n_estimators=100,
n_jobs=-1,
random_state=0)
model
RandomForestRegressor(bootstrap=True, criterion='mse', maxdepth=None,
maxfeatures='auto', maxleafnodes=None,
minimpuritydecrease=0.0, minimpuritysplit=None,
minsamplesleaf=1, minsamplessplit=2,
minweightfractionleaf=0.0, nestimators=100, njobs=-1,
oobscore=False, randomstate=0, verbose=0, warmstart=False)
%time score = cross_val_score(model, X_train, y_train, cv=k_fold, scoring=rmsle_scorer)
score = score.mean()
# 0에 근접할수록 좋은 데이터
print("Score= {0:.5f}".format(score))
CPU times: user 43 s, sys: 1.74 s, total: 44.8 s
Wall time: 16.4 s
Score= 0.33092
Train
# 학습시킴, 피팅(옷을 맞출 때 사용하는 피팅을 생각함) - 피처와 레이블을 넣어주면 알아서 학습함
model.fit(X_train, y_train)
RandomForestRegressor(bootstrap=True, criterion='mse', maxdepth=None,
maxfeatures='auto', maxleafnodes=None,
minimpuritydecrease=0.0, minimpuritysplit=None,
minsamplesleaf=1, minsamplessplit=2,
minweightfractionleaf=0.0, nestimators=100, njobs=-1,
oobscore=False, randomstate=0, verbose=0, warmstart=False)
# 예측
predictions = model.predict(X_test)
print(predictions.shape)
predictions[0:10]
(6493,)
array([ 12.43, 5.07, 4.44, 3.65, 3.2 , 6.38, 38.77,
104.51, 235.12, 135.72])
# 예측한 데이터를 시각화 해본다.
fig,(ax1,ax2)= plt.subplots(ncols=2)
fig.set_size_inches(12,5)
sns.distplot(y_train,ax=ax1,bins=50)
ax1.set(title="train")
sns.distplot(predictions,ax=ax2,bins=50)
ax2.set(title="test")
[Text(0.5,1,'test')]
Submit
submission = pd.read_csv("data/sampleSubmission.csv")
submission
submission["count"] = predictions
print(submission.shape)
submission.head()
(6493, 2)
datetime | count | |
---|---|---|
0 | 2011-01-20 00:00:00 | 12.43 |
1 | 2011-01-20 01:00:00 | 5.07 |
2 | 2011-01-20 02:00:00 | 4.44 |
3 | 2011-01-20 03:00:00 | 3.65 |
4 | 2011-01-20 04:00:00 | 3.20 |
submission.to_csv("data/Score_{0:.5f}_submission.csv".format(score), index=False)
참고 :
-
랜덤포레스트
-
2019.12.4 19:12
0