<데이터 준비하기>
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
<데이터 불러오기>
wine.head()
4번째 열(class)는 타깃값으로 0이면 레드 와인, 1이면 화이트 와인임 -> 화이트 와인이 양성 클래스 => 전체 와인 데이터에서 화이트 와인 골라내기
<데이터 프레임의 유용한 메소드 2가지>
info()
메소드 - 데이터프레임의 각 열의 데이터 타입과 누락된 데이터가 있는지 확인wine.info()
output
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6497 entries, 0 to 6496
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 alcohol 6497 non-null float64
1 sugar 6497 non-null float64
2 pH 6497 non-null float64
3 class 6497 non-null float64
dtypes: float64(4)
memory usage: 203.2 KB
ㄴ 6,497개의 샘플 & 4개의 열
describe()
메소드 - 열에 대한 간략한 통계를 출력해줌 (최소, 최대, 평균값)wine.describe()
값의 스케일이 다 다르면 StandardScaler
클래스를 사용해서 특성을 표준화 해야함.
<데이터 프레임을 넘파이 배열로 바꾸기>
data = wine[['alcohol','sugar','pH']].to_numpy()
target = wine['class'].to_numpy()
<훈련 세트와 테스트 세트 나누기>
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
data, target, test_size = 0.2, random_state = 42)
# test_size 를 지정하지 않으면 25%를 테스트 세트로 지정/ 여기선 샘플 개수가 충분히 많으므로 20%로 나눔
<훈련 세트와 테스트 세트 크기 확인하기>
print(train_input.shape, test_input.shape) # (5197, 3) (1300, 3)
<StandardScaler로 훈련 세트 전처리하기 & 테스트 세트 변환하기>
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
<로지스틱 회귀 모델 훈련하기>
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_scaled, train_target)
print(lr.score(train_scaled, train_target)) # 0.7808350971714451
print(lr.score(test_scaled, test_target)) # 0.7776923076923077
ㄴ 과소적합된듯 & 점수도 안 높다
<로지스틱 회귀가 학습한 계수와 절편 출력하기>
print(lr.coef_, lr.intercept_)
# [[ 0.51270274 1.6733911 -0.68767781]] [1.81777902]
사이킷런에서 DecisionTreeClassifier
클래스로 결정 트리 모델을 훈련할 수 있다
<결정 트리 모델 훈련 및 정확도 평가하기>
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state = 42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target)) # 0.996921300750433
print(dt.score(test_scaled, test_target)) # 0.8592307692307692
ㄴ 과대 적합된 모델 ~~
plot_tree()
함수 - 결정 트리를 그림으로 출력해준다<plot_tree() 로 전달해서 트리 그려보기>
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize = (10,7))
plot_tree(dt)
plt.show()
<트리의 깊이를 제한해서 출력하기>
-max_depth
를 1로 주면 루트 노드 제외하고 하나의 노드를 더 확장해서 그림
filled
매개변수에서 클래스에 맞게 노드의 색 칠함feature_names
매개변수에는 특성의 이름을 전달할 수 있음plt.figure(figsize=(10,7))
plot_tree(dt, max_depth = 1, filled = True, feature_names = ['alcohol','sugar','pH'])
plt.show()
노드 안에는
이렇게 구성되어 있다.
ex)
당도가 -0.239와 같거나 작으면 왼쪽 가지로, 그렇지 않으면 오른쪽 가지로 감
= 왼쪽이 Yes, 오른쪽이 No
filled = True
로 지정하면 어떤 클래스의 비율이 높아지면 점점 진한 색으로 표시됨
ex) 루트노드는 총 5,197개의 샘플, 음성 클래스가 1,258개, 양성 클래스가 3,939개이다
지니불순도 = 1 - ((1258 / 5197)^2 + (3939 / 5197)^2) = 0.367
지니불순도는 0.5가 되면 최악이 되고 0이 되면 가장 작음 (=순수 노드)
가지치기 하는 가장 간단한 방법 = 자라날 수 있는 트리의 최대 깊이를 지정하는 것!
<최대 깊이 지정해서 DecisionTreeClassifier 모델 만들기>
dt = DecisionTreeClassifier(max_depth = 3, random_state = 42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target)) # 0.8454877814123533
print(dt.score(test_scaled, test_target)) # 0.8415384615384616
<plot_tree()로 그리기>
plt.figure(figsize = (20, 15))
plot_tree(dt, filled = True, feature_names = ['alcohol','sugar','pH'])
plt.show()
결정 트리 알고리즘의 장점 = 표준화 전처리를 할 필요가 X
<전처리하기 전 훈련 세트와 테스트 세트로 결정 트리 모델을 훈련하기>
dt = DecisionTreeClassifier(max_depth = 3, random_state = 42)
dt.fit(train_input, train_target)
print(dt.score(train_input, train_target)) # 0.8454877814123533
print(dt.score(test_input, test_target)) # 0.8415384615384616
<트리 그리기>
plt.figure(figsize = (20, 15))
plot_tree(dt, filled = True, feature_names = ['alcohol','sugar','pH'])
plt.show()
ㄴ당도가 1.625 보다 크고 4.325 보다 작은 와인중에 알코올 도수가 11.025와 같거나 작은게 레드와인, 그 외는 다 화이트 와인으로 예측함
<특성 중요도 계산하기>
feature_importances_
속성에 저장되어있음print(dt.feature_importances_) # [0.12345626 0.86862934 0.0079144 ]
두번째 특성인 당도가 가장 특성 중요도가 높고 그 다음이 알코올 도수, pH 순이다. 이 값들을 모두 더하면 1
이 됨 !
특성 중요도를 활용하면 결정 트리 모델을 특성 선택에 활용할 수 있음
: 테스트 세트를 사용하지 않고 모델이 과대적합인지, 과소적합인지 판단하는 방법 (= 훈련 세트를 또 나누는 것 !)
테스트 세트와 훈련 세트를 나눠주고
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
data, target, test_size = 0.2, random_state = 42)
이번에는 sub
과 val
로 나눠서 훈련 세트와 검증 세트로 나눠준다
sub_input, val_input, sub_target, val_target = train_test_split(
train_input, train_target, test_size = 0.2, random_state = 42)
<모델 평가하기>
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state = 42)
dt.fit(sub_input, sub_target)
print(dt.score(sub_input, sub_target)) # 0.9971133028626413
print(dt.score(val_input, val_target)) # 0.864423076923077
ㄴ 과대적합됨
검증 세트를 뗄때, 너무 조금 떼면 검증 점수가 불안정함 !!
이럴 때 교차 검증을 이용하면 안정적인 검증 점수를 얻고 훈련에 더 많은 데이터를 사용할 수 있다
교차 검증은 검증 세트를 떼어 내어 평과하는 과정을 여러번 반복함 -> 평균을 내서 최종 검증 점수를 얻음
<3-폴드 교차 검증>
교차검증을 할 때는 cross_validate()
함수를 이용함
ㄴ 사용법
: 평가할 모델 객체를 첫 번째 매개변수로 전달함 -> 훈련 세트 전체를 cross_validate()
함수에 전달함
<사용해보기>
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)
output
{'fit_time': array([0.01728225, 0.00762796, 0.00763679, 0.00999308, 0.00713062]), 'score_time': array([0.00136471, 0.00090051, 0.00106382, 0.00113153, 0.00095987]), 'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}
ㄴ fit_time, score_time, test_score 키를 가진 딕셔너리 반환
교차 최종 검증 점수를 평균을 내서 얻음 !!
import numpy as np
print(np.mean(scores['test_score'])) # 0.855300214703487
주의할 점 !!
cross_validate()
는 훈련 세트를 섞어서 폴드를 나누지 않음. 그래서 훈련 세트를 섞으려면 분할기
를 지정해야한다 !!
사이킷 런에서 분할기 지정할 때 회귀 모델이면 KFold
분할기를, 분류 모델이면 StratifiedKFold
를 사용
<훈련 세트 섞어서 10-폴드 교차 검증 수행하기>
from sklearn.model_selection import StratifiedKFold
# n_splits 매개변수는 몇(k) 폴드 교차 검증을 할지 정함
splitter = StratifiedKFold(n_splits = 10, shuffle = True, random_state = 42)
scores = cross_validate(dt, train_input, train_target, cv = splitter)
print(np.mean(scores['test_score'])) # 0.8574181117533719
모델 파라미터 : 머신러닝 모델이 학습하는 파라미터
하이퍼 파라미터 : 모델이 학습할 수 없어서 사용자가 지정해야하는 파라미터
하이퍼 파라미터를 튜닝하는 작엄
: 라이브러리가 제공하는 기본 값을 그대로 사용해 모델 훈련 -> 검증 세트의 점수나 교차 검증을 통해서 매개변수를 조금씩 바꿔봄 -> 바꿔가면서 훈련하고 교차검증 수행하기
중요한 점
매개변수를 하나 바꾸고 최적값 찾고 그 다음 매개변수를 바꿔서 최적값 찾는 거 안됨 !!
-> 매개변수들을 동시에 바꿔가며 최적의 값을 찾아야 함 !
근데 매개변수 많아지면 복잡해지기 때문에 사이킷런에서 제공하는 그리드 서치
를 사용하면 된다
ㄴ GridSearchCV
클래스로 하이퍼파라미터 탐색과 교차 검증을 한 번에 수행 가능
<탐색할 매개변수와 탐색할 값의 리스트를 딕셔너리로 만들기>
from sklearn.model_selection import GridSearchCV
params = {'min_impurity_decrease' : [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}
<그리드 서치 객체 만들기>
gs = GridSearchCV(DecisionTreeClassifier(random_state = 42), params, n_jobs = -1)
<훈련하기>
fit()
메소드는 그리드 서치 객체에선 결정 트리 모델 min_impurity_decrease
값을 바꿔가며 총 5번 실행함
cv의 매개변수 기본 값은 5이기 때문에 min_impurity_decrease 값마다 5-폴드 교차 검증을 수행해서 결국 5 x 5 = 25 개의 모델을 훈련함
gs.fit(train_input, train_target)
훈련이 끝나면 모델 중 검증 점수가 가장 높은 모델의 매개변수 조합으로 전체 훈련 세트에서 자동으로 다시 모델을 훈련하고 이 모델이 best_estimator_
에 저장되어있음
<점수 출력>
dt = gs.best_estimator_
print(dt.score(train_input, train_target)) # 0.9615162593804117
<최적의 매개변수가 저장되어 있는 bestparams속성 불러오기>
print(gs.best_params_) # {'min_impurity_decrease': 0.0001}
ㄴ 0.0001이 가장 좋은 값으로 선택됐구나 ~~
각 매개변수에서 수행한 교차 검증의 평균 점수는 cv_results_
속성의 mean_test_score
에 저장되어있음
<5번의 교차 검증으로 얻은 점수 출력하기>
print(gs.cv_results_['mean_test_score'])
output
[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
ㄴ 첫 번째 값이 가장 큰데, 수동으로 고르는 것 보다 넘파이 argmax() 함수를 이용해서 가장 큰 값의 인덱스를 추출할 수 있다
<argmax() 써보기>
best_index = np.argmax(gs.cv_results_['mean_test_score'])
print(gs.cv_results_['params'][best_index]) #{'min_impurity_decrease': 0.0001}
ㄴ 동일하군
과정 정리
매개변수의 값이 수치일 때 범위나 간격 미리 정하기 어렵기도 하고 너무 많은 매개변수 조건이 있어서 그리드 서치 수행시간이 오래걸릴 수 있다 !!
-> 랜덤 서치
를 사용하자
랜덤서치에는 매개변수 값의 목록을 전달하는 것이 아니라 매개변수를 샘플링할 수 있는 확률분포 객체를 전달함
<확률 분포 클래스 임포트하기>
from scipy.stats import uniform, randint
randint
는 정수값을, uniform
은 실수 값을 주어진 범위에서 고르게 뽑음
ex) randint 사용법
rgen = randint(0,10)
np.unique(rgen.rvs(1000), return_counts = True)
output
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
array([113, 97, 99, 89, 111, 104, 101, 100, 91, 95]))
ex) uniform 사용법
ugen = uniform(0,1)
ugen.rvs(10)
output
array([0.97348211, 0.86306205, 0.90612032, 0.58106758, 0.69506978,
0.74482672, 0.640922 , 0.10973736, 0.40981152, 0.7782244 ])
이제 탐색할 매개변수의 딕셔너리를 만들꺼고 여기에선 min_samples_leaf
매개변수를 탐색 대상에 추가할 것임 (=리프노드가 되기 위한 최소 샘플의 개수)
params = {
'min_impurity_decrease' : uniform(0.0001, 0.001), # 0.0001 ~ 0.001 사이 실숫값 샘플링
'max_depth' : randint(20, 50), # 20~50 사이의 정수값 샘플링
'min_samples_split' : randint(2, 25),
'min_samples_leaf' : randint(1, 25),
}
ㄴ 샘플링 횟수는 RandomizedSearchCV
의 n_iter
매개변수에 지정함
<매개변수 지정하고 훈련하기>
from sklearn.model_selection import RandomizedSearchCV
gs = RandomizedSearchCV(DecisionTreeClassifier(random_state = 42), params,
n_iter = 100, n_jobs = -1, random_state = 42)
gs.fit(train_input, train_target)
<최적의 매개변수 조합 출력>
print(gs.best_params_)
output
{'max_depth': 39, 'min_impurity_decrease': 0.00034102546602601173, 'min_samples_leaf': 7, 'min_samples_split': 13}
<최고의 교차 검증 점수 확인>
print(np.max(gs.cv_results_['mean_test_score'])) # 0.8695428296438884
<테스트 세트 성능 확인하기>
dt = gs.best_estimator_
print(dt.score(test_input, test_target)) # 0.86
정형 데이터 : 어떤 구조로 되어있는 데이터들 ex) CSV, 데이터베이스, 엑셀
비정형 데이터 : 데이터베이스나 엑셀로 표현하기 어려운 것들 ex) 음악, 사진
앙상블 학습 : 정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘 -> 이는 대부분 결정 트리를 기반으로 만들어짐
: 앙상블 학습의 대표 주자로 안정적인 성능을 갖고 있음
ㄴ 결정 트리의 숲을 만드는 것과 비슷!! => 결정 트리의 예측을 사용해서 최종 예측을 만듦
랜덤 포레스트는 각 트리를 훈련하기 위한 데이터를 랜덤하게 만드는데 중복해서 샘플을 만든다 !! (부트스트랩 방식으로)
그리고 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 후 이 중에서 최선의 분할을 찾음
랜덤 포레스트는 랜덤하게 선택한 샘플과 특성을 사용하기 때매 훈련 세트에 과대적합되는 것을 막아주고 검증 세트와 테스트 세트에서 안정적인 성능을 얻을 수 있음
<데이터셋 불러오고 훈련 세트와 테스트 세트로 나누기>
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
wine = pd.read_csv('https://bit.ly/wine_csv_data')
data = wine[['alcohol','sugar','pH']].to_numpy()
target = wine['class'].to_numpy()
train_input, test_input, train_target, test_target = train_test_split(
data, target, test_size = 0.2, random_state = 42)
<cross_validate()로 교차 검증 수행하기>
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_jobs = -1, random_state = 42) # n_jobs = -1로 지정해서 병렬로 교차 검증 수행
scores = cross_validate(rf, train_input, train_target,
return_train_score = True, n_jobs = -1)
# return_train_score = True로 지정하면 검증 점수 랑 훈련 세트에 대한 점수도 같이 반환해줌, 얘의 기본 값은 False임
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
output
0.9973541965122431 0.8905151032797809
ㄴ 과대적합됨..
결정 트리의 장점 !! = 특성 중요도를 계산한다 / 랜덤 포레스트의 특성 중요도 = 각 결정 트리의 특성 중요도 취합한 것
<훈련 세트 훈련하고 특성 중요도 출력하기>
rf.fit(train_input, train_target)
print(rf.feature_importances_) # [0.23167441 0.50039841 0.26792718]
RandomForestClassifier
은 자체적으로 모델을 평가하는 점수를 얻을 수 있다 !!
<OOB점수 평균내서 출력하기>
rf = RandomForestClassifier(oob_score = True, n_jobs = -1, random_state = 42)
rf.fit(train_input, train_target)
print(rf.oob_score_) # 0.8934000384837406
ㄴ OOB
점수를 사용하면 교차 검증을 대신할 수 있어서 훈련 세트에 더 많은 샘플들을 사용할 수 있다 ~~
엑스트라 트리는 랜덤 포레스트와 달리 부트스트랩을 사용하지 않는다 = 결정 트리를 만들 때 전체 훈련 세트를 사용함. 하지만 노드를 분할할 때 무작위로 분할한다 !!
성능은 좀 낮아져도 많은 트리를 앙상블하기 때문에 과대적합을 막고 검증 세트의 점수를 높이는 효과가 있음
<엑스트라트리로 모델 교차 검증점수 확인하기>
from sklearn.ensemble import ExtraTreesClassifier
et = ExtraTreesClassifier(n_jobs = -1, random_state = 42)
scores = cross_validate(et, train_input, train_target,
return_train_score = True, n_jobs = -1)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9974503966084433 0.8887848893166506
<특성 중요도>
et.fit(train_input, train_target)
print(et.feature_importances_) # [0.20183568 0.52242907 0.27573525]