[ 머신러닝 ] 랜덤 포레스트
2023. 9. 11. 17:35ㆍ머신러닝
728x90
1. hotel 데이터셋 알아보기
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
# csv 파일이 구글드라이브에 있습니다.
hotel_df = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/9. 머신러닝과 딥러닝/hotel.csv')
hotel_df.head()
# 결과
pd.set_option('display.max_columns', 100)
hotel_df.head()
# 결과
hotel_df.info()
# 결과
# 설명
- hotel: 호텔 종류
- is_canceled: 취소 여부
- lead_time: 예약 시점으로부터 체크인 될 때까지의 기간(얼마나 미리 예약했는지)
- arrival_data_year: 예약 연도
- arrival_date_month: 예약 월
- arrival_date_week: 예약 수
- arrival_date_day: 예약 일
- stays_in_weekend_nights: 주말을 끼고 얼마나 묵었는지
- stays_in_week_nights: 평일을 끼고 얼마나 묵었는지
- adults: 성인 인원수
- children: 어린이 인원 수
- babies: 아기 인원 수
- meal: 식사
- country: 지역
- distribution_channel: 어떤 방식으로 예약했는 지
- is_repeated_guest: 예약한적이 있는 고객인지
- previous_cancellations: 예약을 취소한 횟수
- previous_bookings_not_canceled: 예약을 취소하지 않고 정상 숙박한 횟수
- reserved_room_type: 희망 룸 타입
- assigned_room_type: 실제 배정된 룸 타입
- booking_changes: 예약 후 서비스가 변경된 횟수
- deposit_type: 요금 납부 방식
- days_in_waiting_list: 예약을 위해 기다린 날짜
- customer_type: 고객 타입
- adr: 특정일에 높아지거나 낮아지는 가격
- required_car_parking_spaces: 주차 공간을 요구했는 지
- total_of_special_requests: 특별한 요청사항
- reservation_status_date: 예약한 날짜
- name: 이름
- email: 이메일
- phone-number: 휴대폰 번호
- credit_card: 카드번호
# df에서 필요한것만 넣기
hotel_df.drop(['credit_card', 'email', 'name', 'phone-number', 'reservation_status_date'], axis=1, inplace=True)
hotel_df.head()
# 결과
hotel_df.describe()
# 아웃라이어 있나 확인
# 결과
sns.displot(hotel_df['lead_time'])
# 결과
sns.boxplot(y = hotel_df['lead_time'])
# 결과
sns.barplot(x=hotel_df['distribution_channel'], y=hotel_df['is_canceled'])
# undefined 데이터 수 확인 필요
# 결과
hotel_df['distribution_channel'].value_counts()
# 결과
sns.barplot(x=hotel_df['hotel'], y=hotel_df['is_canceled'])
# 결과
sns.barplot(x=hotel_df['arrival_date_year'], y=hotel_df['is_canceled'])
# 결과
plt.figure(figsize=(15,5))
sns.barplot(x=hotel_df['arrival_date_month'], y=hotel_df['is_canceled'])
# 결과
# 월별로 x축을 정렬하기
import calendar
print(calendar.month_name[1])
print(calendar.month_name[2])
print(calendar.month_name[3])
# 결과
months = []
for i in range(1, 13):
months.append(calendar.month_name[i])
months
# 결과
# order로 정렬 가능
plt.figure(figsize=(15,5))
sns.barplot(x=hotel_df['arrival_date_month'], y=hotel_df['is_canceled'], order=months)
# 결과
sns.barplot(x=hotel_df['is_repeated_guest'], y=hotel_df['is_canceled'])
# 처음 온 사람이 취소 확률이 더 높다.
# 결과
sns.barplot(x=hotel_df['deposit_type'], y=hotel_df['is_canceled'])
# 결과
hotel_df['deposit_type'].value_counts()
# 결과
plt.figure(figsize=(15,15))
sns.heatmap(hotel_df.corr(), cmap='coolwarm', vmax=1, vmin=-1, annot=True) # vmin~vmax 이 사이값으로 정규화 시켜라 / annot=True 네모 박스안에 숫자를 넣어라
# red: 양의 상관관계, blue: 음의 상관관계 -> 색이 진할수록 깊은 관계가 있음
# 결과
hotel_df.isna().mean()
# 결과
hotel_df = hotel_df.dropna()
hotel_df.head()
# 결과
hotel_df[hotel_df['adults']==0]
# 결과
hotel_df['people'] = hotel_df['adults'] + hotel_df['children'] + hotel_df['babies']
# 결과
hotel_df.head()
# 결과
hotel_df[hotel_df['people']==0]
# 결과
hotel_df = hotel_df[hotel_df['people'] != 0]
hotel_df
# 결과
hotel_df['total_nights'] = hotel_df['stays_in_week_nights'] + hotel_df['stays_in_weekend_nights']
# 결과
hotel_df[hotel_df['total_nights'] == 0]
# 결과
hotel_df['arrival_date_month'].apply(lambda x: 'spring' if x in ['March', 'April', 'May'] else 'summer' if x in ['June', 'July', 'August'] else 'fall' if x in ['September', 'October', 'November'] else 'winter')
# 결과
season_dic = {'spring': [3,4,5], 'summer': [6,7,8], 'fall': [9,10,11], 'winter':[12,1,2]}
new_season_dic = {}
for i in season_dic:
for j in season_dic[i]:
new_season_dic[calendar.month_name[j]] = i
new_season_dic
# 결과
hotel_df['season'] = hotel_df['arrival_date_month'].map(new_season_dic)
hotel_df.head()
# 결과
hotel_df['expected_room_type'] = (hotel_df['reserved_room_type'] == hotel_df['assigned_room_type']).astype(int)
hotel_df.head()
# 결과
hotel_df['cancel_rate'] = hotel_df['previous_cancellations'] / (hotel_df['previous_cancellations'] + hotel_df['previous_bookings_not_canceled'])
hotel_df.head()
# 결과
hotel_df[hotel_df['cancel_rate'].isna()] # NaN이 나온 사람들은 처음 방문한 사람들
# 결과
hotel_df[~hotel_df['cancel_rate'].isna()] # 0.0이라는 건 방문한 적은 있으나 cancel이 없는 경우
# 결과
hotel_df[hotel_df['cancel_rate'] > 0 ] # 캔슬한적이 있는 경우
# 결과
hotel_df['cancel_rate'] = hotel_df['cancel_rate'].fillna(-1)
hotel_df.info()
# 결과
<class 'pandas.core.frame.DataFrame'>
Int64Index: 118728 entries, 0 to 119389
Data columns (total 32 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 hotel 118728 non-null object
1 is_canceled 118728 non-null int64
2 lead_time 118728 non-null int64
3 arrival_date_year 118728 non-null int64
4 arrival_date_month 118728 non-null object
5 arrival_date_week_number 118728 non-null int64
6 arrival_date_day_of_month 118728 non-null int64
7 stays_in_weekend_nights 118728 non-null int64
8 stays_in_week_nights 118728 non-null int64
9 adults 118728 non-null int64
10 children 118728 non-null float64
11 babies 118728 non-null int64
12 meal 118728 non-null object
13 country 118728 non-null object
14 distribution_channel 118728 non-null object
15 is_repeated_guest 118728 non-null int64
16 previous_cancellations 118728 non-null int64
17 previous_bookings_not_canceled 118728 non-null int64
18 reserved_room_type 118728 non-null object
19 assigned_room_type 118728 non-null object
20 booking_changes 118728 non-null int64
21 deposit_type 118728 non-null object
22 days_in_waiting_list 118728 non-null int64
23 customer_type 118728 non-null object
24 adr 118728 non-null float64
25 required_car_parking_spaces 118728 non-null int64
26 total_of_special_requests 118728 non-null int64
27 people 118728 non-null float64
28 total_nights 118728 non-null int64
29 season 118728 non-null object
30 expected_room_type 118728 non-null int64
31 cancel_rate 118728 non-null float64
dtypes: float64(4), int64(18), object(10)
memory usage: 29.9+ MB
hotel_df['country'].dtype # dtype('o') object만 'O'라고 나옴
-------------------------------------------------------------
# 결과
dtype('O')
hotel_df['people'].dtype
-------------------------------
# 결과
dtype('float64')
obj_list = []
for i in hotel_df.columns:
if hotel_df[i].dtype == 'O':
obj_list.append(i)
obj_list
--------------------------------------
# 결과
['hotel',
'arrival_date_month',
'meal',
'country',
'distribution_channel',
'reserved_room_type',
'assigned_room_type',
'deposit_type',
'customer_type',
'season']
for i in obj_list:
print(i,hotel_df[i].nunique())
------------------------------------------
# 결과
hotel 2
arrival_date_month 12
meal 5
country 177
distribution_channel 5
reserved_room_type 9
assigned_room_type 11
deposit_type 3
customer_type 4
season 4
hotel_df['meal'].value_counts()
--------------------------------
# 결과
BB 91789
HB 14429
SC 10547
Undefined 1165
FB 798
Name: meal, dtype: int64
hotel_df.drop(['country', 'meal'], axis=1, inplace=True)
obj_list.remove('country')
obj_list.remove('meal')
hotel_df = pd.get_dummies(hotel_df, columns=obj_list)
hotel_df.head()
# 결과
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(hotel_df.drop('is_canceled', axis=1), hotel_df['is_canceled'], test_size=0.3, random_state=10)
2. 랜덤 포레스트(Random Forest)
- Decision Tree는 매우 훌륭한 모델이지만 학습 데이터에 오버피팅하는 경향이 있음(가지치기 같은 방법을 통해 부작용을 최소화 할 수 있지만 부족함)
- 학습을 통해 구성해 놓은 다수의 나무들(Decision Tree)로부터 분류 결과를 취합해서 결론을 얻는 방식의 모델
- Decision Tree 기반의 Bagging 앙상블 모델(두개 이상의 모델을 섞어서 사용하는 경우)
- Bagging은 같은 모델을 섞어서 씀
- 굉장히 인기있는 모델이며, 사용성이 쉽고 성능도 꾀 우수한 편
2-1. 앙상블(Ensemble) 모델
- 여러개의 머신러닝 모델을 이용해 최적의 답을 찾아내는 기법
<앙상블 모델의 여러 특징>
- 보팅(Voting):
- 다른 알고리즘 model을 조합해서 사용
- 모델에 대해 투표로 결과를 도출
- 배깅(Bagging):
- 같은 알고리즘 내에서 다른 sample 조합을 사용
- 샘플 중복 생성을 통해 결과를 도출(K-Fold처럼 데이터 셋 샘플링(조합)을 다르게 하는 듯..)
- 부스팅(Boosting):
- 이전 오차를 보완해가면서 가중치를 부여
- 성능이 매우 우수하지만, 잘못된 레이블이나 아웃라이어에 대해 필요 이상으로 민감할 수 있음
- 스태킹(Stacking):
- 여러 모델을 기반으로 예측된 결과를 통해 meta 모델이 다시한번 예측
- 성능 좋은 모델에 한번 더 학습하여 성능을 극으로 끌어 올릴때 활용하지만, 데이터에 너무 친화적으로되어 과대적합을 유발할 수 있음(특히 데이터셋이 적은 경우)
from sklearn.ensemble import RandomForestClassifier
rf=RandomForestClassifier()
rf.fit(X_train, y_train)
# 결과
pred1 = rf.predict(X_test)
pred1
-------------------------------
# 결과
array([0, 0, 0, ..., 0, 0, 0])
proba1 = rf.predict_proba(X_test)
proba1
----------------------------------
# 결과
array([[0.98, 0.02],
[1. , 0. ],
[0.71, 0.29],
...,
[1. , 0. ],
[0.97, 0.03],
[0.93, 0.07]])
# 첫번째 테스트 데이터에 대한 예측 결과
proba1[0]
--------------------------------------
# 결과
array([0.98, 0.02])
# 모든 테스트 데이터에 대한 호텔 예약을 취소할 확률만 출력
proba1[:, 1]
-------------------------------------------------------
# 결과
array([0.02, 0. , 0.29, ..., 0. , 0.03, 0.07])
3. ROC Curve
- 이진 분류의 성능을 측정하는 도구
- 민감도와 특이도로 그려지는 곡선을 의미
- FPR(False Positive Rate)
- 특이도. 거짓 양성 비율
- FP / TN + FP
- 실제값은 음성이지만, 양성으로 잘못 분류
- TPR(True Positive Rate)
- 민감도. 참인 양성비율
- TP / FN + TP
- 실제로 양성이고 양성으로 잘 분류한 것
4. AUC(Area Under the ROC Curve)
- ROC 커브와 직선 사이의 면적을 의미
- AUC 값의 범위는 0.5~1이며, 값이 클수록 예측의 정확도가 높음
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, roc_auc_score
accuracy_score(y_test, pred1) # 예측을 한쪽으로 몰아치지는 않았다는걸 알 수 있음
---------------------------------------------------------------------------------------------------
# 결과
0.8642578399168983
confusion_matrix(y_test, pred1) # 예측을 한쪽으로 몰아치지는 않았다는걸 알 수 있음
-------------------------------------------------------------------------------
# 결과
array([[20816, 1542],
[ 3293, 9968]])
print(classification_report(y_test, pred1)) # support는 데이터 갯수
------------------------------------------------------------------
# 결과
precision recall f1-score support
0 0.86 0.93 0.90 22358
1 0.87 0.75 0.80 13261
accuracy 0.86 35619
macro avg 0.86 0.84 0.85 35619
weighted avg 0.86 0.86 0.86 35619
roc_auc_score(y_test, proba1[:, 1])
# ROC 곡선 아래 면적을 계산하는 함수
----------------------------------------
# 결과
0.9306581926200015
# 하이퍼파라미터 수정 (max_depth=30을 적용)
rf2 = RandomForestClassifier(max_depth=30, random_state=10)
rf2.fit(X_train, y_train)
proba2 = rf2.predict_proba(X_test)
roc_auc_score(y_test, proba2[:, 1])
# 하이퍼 파라미터 적용 전: 0.9307184561495241
# 하이퍼 파라미터 적용(max_depth=30을 적용) 후: 0.9320285483491656
-----------------------------------------------------------------
# 결과
0.9320285483491656
# 하이퍼파라미터 수정 (max_depth=50을 적용)
rf2 = RandomForestClassifier(max_depth=50, random_state=10)
rf2.fit(X_train, y_train)
proba2 = rf2.predict_proba(X_test)
roc_auc_score(y_test, proba2[:, 1])
# 하이퍼 파라미터 적용 전: 0.9307184561495241
# 하이퍼 파라미터 적용(max_depth=30을 적용) 후: 0.9320285483491656
# 하이퍼 파라미터 적용(max_depth=50을 적용) 후: 0.9303745518246758
----------------------------------------------------------------
0.9303745518246758
# 하이퍼파라미터 수정 (min_samples_split=5 을 적용)
rf2 = RandomForestClassifier(min_samples_split=5, random_state=10)
rf2.fit(X_train, y_train)
proba2 = rf2.predict_proba(X_test)
roc_auc_score(y_test, proba2[:, 1])
# 하이퍼 파라미터 적용 전: 0.9307184561495241
# 하이퍼 파라미터 적용(max_depth=30 을 적용) 후: 0.9320285483491656
# 하이퍼 파라미터 적용(max_depth=50 을 적용) 후: 0.9303745518246758
# 하이퍼 파라미터 적용(min_samples_split=5 을 적용) 후: 0.931436154565479
-----------------------------------------------------------------------
# 결과
0.931436154565479
# 하이퍼파라미터 수정 (min_samples_split=7 을 적용)
rf2 = RandomForestClassifier(min_samples_split=7, random_state=10)
rf2.fit(X_train, y_train)
proba2 = rf2.predict_proba(X_test)
roc_auc_score(y_test, proba2[:, 1])
# 하이퍼 파라미터 적용 전: 0.9307184561495241
# 하이퍼 파라미터 적용(max_depth=30 을 적용) 후: 0.9320285483491656
# 하이퍼 파라미터 적용(max_depth=50 을 적용) 후: 0.9303745518246758
# 하이퍼 파라미터 적용(min_samples_split=5 을 적용) 후: 0.931436154565479
# 하이퍼 파라미터 적용(min_samples_split=7 을 적용) 후: 0.9312578210627522
------------------------------------------------------------------------
# 결과
0.9312578210627522
# 하이퍼파라미터 속성이 개별적으로 높다해도, 콜라보했을 때 좋다라는 보장이 없다.
# 하이퍼파라미터에 넣는 값을 알고리즘을 공부하면서 중요 속성 및 그 범위값은 알 필요가 있다.
5. 하이퍼 파라미터 최적의 값을 찾는 방법
- GridSearchCV: 원하는 모든 하이퍼 파라미터를 적용하여 최적의 값을 찾아주는 방법
from sklearn.model_selection import GridSearchCV
params = {
'max_depth': [None, 10, 30, 50],
'min_samples_split': [2, 3, 5, 7, 10]
}
rf3 = RandomForestClassifier(random_state=10)
grid_df = GridSearchCV(rf3, params, cv=5) # cv: 데이터 교차검증
grid_df.fit(X_train, y_train)
# 결과
grid_df.best_params_
# params 중에서 최고의 하이퍼파라미터 조건을 보여줌
---------------------------------------------------
# 결과
{'max_depth': 30, 'min_samples_split': 2}
rf3 = RandomForestClassifier(max_depth=30, min_samples_split=2, random_state=10)
rf3.fit(X_train, y_train)
proba2 = rf3.predict_proba(X_test)
roc_auc_score(y_test, proba2[:, 1])
# 하이퍼 파라미터 적용(max_depth=30 을 적용, min_samples_split=5 을 적용) 후: 0.9320878540030826
# 최적의 하이퍼 파라미터 값 적용(max_depth=30 을 적용, min_samples_split=2 를 적용) 후: 0.9320285483491656
# cv: 데이터 교차검증을 넣었기 떄문에, 런타임 다시 실행 시 이 최적의 값 적용한 결과가 더 안좋게 나왔을수도...
-------------------------------------------------------------------------------------------------------
# 결과
0.9320285483491656
6. 피쳐 중요도(Feature Importances)
- Decision Tree 에서 노드를 분기할 때 해당 피쳐가 클래스를 나누는데 얼마나 영향을 미쳤는지 표기하는 척도
- 0이면 클래스를 구분하는데 해당 피쳐가 선택되지 않았다는 것이며, 1이면 해당 피쳐가 클래스를 완벽하게 나눴다는 것을 의미
proba3 = rf3.predict_proba(X_test)
proba3
---------------------------------------
# 결과
array([[0.96333333, 0.03666667],
[0.98747831, 0.01252169],
[0.67944316, 0.32055684],
...,
[1. , 0. ],
[0.90383261, 0.09616739],
[0.97 , 0.03 ]])
roc_auc_score(y_test, proba3[:, 1])
-------------------------------------
# 결과
0.9320285483491656
rf3.feature_importances_
-----------------------------------------------------------------------
# 결과
array([1.27137138e-01, 2.14563953e-02, 4.68663037e-02, 5.96305446e-02,
2.16746957e-02, 3.25152451e-02, 1.02347526e-02, 5.40307416e-03,
8.84445875e-04, 2.02141842e-03, 2.44460558e-02, 3.14871409e-03,
2.18776128e-02, 3.06142123e-03, 9.51803193e-02, 2.23244236e-02,
5.75968205e-02, 1.36759899e-02, 3.53588232e-02, 2.80447856e-02,
3.80039740e-02, 7.62870375e-03, 6.72543460e-03, 3.44290597e-03,
4.03857944e-03, 2.04861894e-03, 2.63415501e-03, 1.84105707e-03,
3.95932072e-03, 3.62772728e-03, 3.05844877e-03, 3.56107858e-03,
2.38180451e-03, 3.26791942e-03, 2.91845948e-03, 3.01678974e-03,
9.42477811e-03, 2.46969262e-04, 1.08307976e-02, 0.00000000e+00,
5.74350945e-03, 9.03907596e-04, 6.54901146e-04, 4.05826969e-03,
2.30412987e-03, 1.25861929e-03, 1.10018824e-03, 3.65382816e-04,
3.43650984e-05, 1.10160554e-02, 1.51175712e-03, 1.19112472e-03,
5.19366703e-03, 2.61859311e-03, 1.46746839e-03, 1.15967758e-03,
3.98805744e-04, 1.35937049e-04, 1.43906056e-04, 2.28125116e-06,
6.99747351e-02, 9.55569434e-02, 4.52910589e-04, 3.08796348e-03,
4.79153629e-04, 1.67417487e-02, 1.19682713e-02, 3.45258733e-03,
4.02311808e-03, 4.35533780e-03, 3.44818237e-03])
1.27137138e-01 # 전체 중요도중에서 1.2% 정도의 가중치. 0에 가까울수록 필요없는 feature다.
------------------------------------------------------------------------------------
# 결과
0.127137138
feat_imp = pd.DataFrame({
'features': X_train.columns,
'importances': rf3.feature_importances_
})
feat_imp
# 결과
top10 = feat_imp.sort_values('importances', ascending=False).head(10)
top10
# 결과
plt.figure(figsize=(5,10))
sns.barplot(x='importances', y='features', data=top10)
# 결과
728x90
반응형
'머신러닝' 카테고리의 다른 글
[ 머신러닝 ] KMEans (0) | 2023.09.11 |
---|---|
[ 머신러닝 ] lightGBM (0) | 2023.09.11 |
[ 머신러닝 ] 서포트 벡터 머신 (0) | 2023.09.06 |
[ 머신러닝 ] 로지스틱 회귀 (0) | 2023.09.06 |
[ 머신러닝 ] 의사결정나무 (0) | 2023.09.06 |