모델평가란 무엇일까?¶
학습 알고리즘으로 만든 모델의 성능을 평가하기 위한 전략이 필요하다.
모델 예측은 성능이 높아야 유용하다. 하나의 단순한 모델을 만드는 것은 쉬운작업일지도 모르지만, 정말 품질이 좋은 모델을 만드는 것은 어려운 작업이다.
따라서 다양한 알고리즘 중 적합한 것을 택하기 위해서 모델 평가는 필수이다.
모델을 훈련한 데이터로 모델이 얼마나 잘 수행되는지 평가한다면 원하는 목표를 달성하지 못한다.
목표는 훈련 데이터에서 잘 동작하는 모델이 아니라 이전에 본 적 없는 데이터(예를 들어, 새로운 고객, 새로운 범죄, 새로운 이미지)에서 잘 동작하는 모델이다.
→ 평가 방법은 이전에 본 적 없는 데이터에서 모델이 얼마나 좋은 예측을 만드는지 알 수 있어야 한다.
훈련세트와 테스트세트¶
- 검증(또는 홀드아웃) : 데이터의 일부를 테스트용으로 떼어놓는 것이다.
- 검증에서 샘플(특성과 타깃)은 두 개의 세트로 나뉜다. 전통적으로 이를 훈련세트와 테스트세트라고 부른다.
기존 검증 순서¶
- 테스트 세트를 따로 떼어놓고 마치 이전에 본 적 없는 데이터처럼 취급한다.
- 그 다음 훈련 세트의 특성과 타깃 벡터를 사용해 최선의 예측을 만드는 방법을 모델 훈련을 통해 가르친다.
- 마지막으로 훈련 세트에서 훈련한 모델을 이전에 본 적 없는 외부 데이터처럼 가장한 테스트 세트에서 얼마나 잘 동작하는지 평가한다.
→ 이 검증 방법에는 두가지 약점이 있다.
- 모델 성능은 테스트로 나뉜 일부 샘플에 의해 결정된다.
- 그리고 전체 가용 데이터를 사용하여 모델을 훈련하고 테스트하지 못한다.
k-폴드 교차검증(KFCV)¶
위의 검증 방법의 단점을 극복할 수 있는 좋은 방법이다. 데이터를 폴드라고 부르는 k개의 부분으로 나눈다. k-1개 폴드를 하나의 훈련 세트로 합쳐 모델을 훈련하고 남은 폴드를 테스트 세트처럼 사용하게 된다. 이를 k번 동안 반복하고 반복마다 다른 폴드를 테스트 세트로 사용한다. k번 반복에서 얻은 모델 성능을 평균하여 최종 성능을 산출한다.
교차 검증 모델 만들기¶
실전에서 모델이 얼마나 잘 작동할지 평가 하기 위해 데이터 전처리 파이프라인을 만들고 모델을 훈련한 다음 교차검증으로 평가한다.
# 라이브러리 임포트
from sklearn import datasets # 임의의 데이터셋 생성
from sklearn import metrics
from sklearn.model_selection import KFold, cross_val_score # 교차검증
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression # 로지스틱회귀 모델
from sklearn.preprocessing import StandardScaler # 표준화 스케일링
# 숫자 데이터셋 로드
digits = datasets.load_digits()
# 특성 행렬 생성
import pandas as pd
features = digits.data
# 특성 행렬 데이터프레임으로 보기
features_df = pd.DataFrame(features)
features_df
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.0 | 0.0 | 5.0 | 13.0 | 9.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 6.0 | 13.0 | 10.0 | 0.0 | 0.0 | 0.0 |
1 | 0.0 | 0.0 | 0.0 | 12.0 | 13.0 | 5.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 11.0 | 16.0 | 10.0 | 0.0 | 0.0 |
2 | 0.0 | 0.0 | 0.0 | 4.0 | 15.0 | 12.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 5.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3.0 | 11.0 | 16.0 | 9.0 | 0.0 |
3 | 0.0 | 0.0 | 7.0 | 15.0 | 13.0 | 1.0 | 0.0 | 0.0 | 0.0 | 8.0 | ... | 9.0 | 0.0 | 0.0 | 0.0 | 7.0 | 13.0 | 13.0 | 9.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 0.0 | 1.0 | 11.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 2.0 | 16.0 | 4.0 | 0.0 | 0.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1792 | 0.0 | 0.0 | 4.0 | 10.0 | 13.0 | 6.0 | 0.0 | 0.0 | 0.0 | 1.0 | ... | 4.0 | 0.0 | 0.0 | 0.0 | 2.0 | 14.0 | 15.0 | 9.0 | 0.0 | 0.0 |
1793 | 0.0 | 0.0 | 6.0 | 16.0 | 13.0 | 11.0 | 1.0 | 0.0 | 0.0 | 0.0 | ... | 1.0 | 0.0 | 0.0 | 0.0 | 6.0 | 16.0 | 14.0 | 6.0 | 0.0 | 0.0 |
1794 | 0.0 | 0.0 | 1.0 | 11.0 | 15.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 2.0 | 9.0 | 13.0 | 6.0 | 0.0 | 0.0 |
1795 | 0.0 | 0.0 | 2.0 | 10.0 | 7.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 2.0 | 0.0 | 0.0 | 0.0 | 5.0 | 12.0 | 16.0 | 12.0 | 0.0 | 0.0 |
1796 | 0.0 | 0.0 | 10.0 | 14.0 | 8.0 | 1.0 | 0.0 | 0.0 | 0.0 | 2.0 | ... | 8.0 | 0.0 | 0.0 | 1.0 | 8.0 | 12.0 | 14.0 | 12.0 | 1.0 | 0.0 |
1797 rows × 64 columns
# 타깃 벡터 생성
target = digits.target
# 타깃 벡터 데이터프레임으로 보기
target_df = pd.DataFrame(target)
target_df
0 | |
---|---|
0 | 0 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
... | ... |
1792 | 9 |
1793 | 0 |
1794 | 8 |
1795 | 9 |
1796 | 8 |
1797 rows × 1 columns
# 표준화스케일러 객체 생성
standardizer = StandardScaler()
# 로지스틱 회귀 객체 생성
logit = LogisticRegression()
# 표준화한 다음 로지스틱 회귀 실행을 하는 파이프라인 생성
pipeline = make_pipeline(standardizer, logit)
pipeline
Pipeline(steps=[('standardscaler', StandardScaler()),
('logisticregression', LogisticRegression())])
사이킷런의 pipline 패키지는 교차검증 기법을 사용할 때 이 규칙을 손쉽게 구현할 수 있도록 도와준다. 먼저 데이터를 전처리(예를 들면 stadardizer)하고 모델(로지스틱 회귀인 logit)을 훈련하는 파이프라인을 만든다.
# k-폴드 교차 검증
kf = KFold(n_splits=10, shuffle=True, random_state=1)
kf
KFold(n_splits=10, random_state=1, shuffle=True)
KFC는 각 샘플이 다른 샘플과 독립적으로 생성된다고 가정한다(즉 데이터는 독립 동일 분포:IID). 데이터가 IID라면 폴드를 나누기 전에 샘플을 섞는 것이 좋다. 사이킷 런에서는 shuffle=True로 지정하여 섞을 수 있다.
# K-폴드 교차검증 수행
cv_results = cross_val_score(pipeline, #파이프라인
features, #특성행렬
target, #타깃 벡터
cv=kf, #교차검증 기법
scoring="accuracy", #평가 지표
n_jobs=-1) # 모든 CPU 코어 사용
df_cv_results = pd.DataFrame(cv_results)
df_cv_results
0 | |
---|---|
0 | 0.977778 |
1 | 0.988889 |
2 | 0.961111 |
3 | 0.944444 |
4 | 0.977778 |
5 | 0.983333 |
6 | 0.955556 |
7 | 0.988827 |
8 | 0.977654 |
9 | 0.938547 |
위의 코드에서 n_splits=10
로10개의 폴드를 사용하여 k-폴드 교차검증을 수행했다. 평가 점수는 cv_results에 저장되어 있다.
# 평균 계산
cv_results.mean()
0.9693916821849783
KFCV를 사용할 때 고려해야 할 중요한 점이 세가지¶
- KFC는 각 샘플이 다른 샘플과 독립적으로 생성된다고 가정한다(즉 데이터는 독립 동일 분포:IID). 데이터가 IID라면 폴드를 나누기 전에 샘플을 섞는 것이 좋다. 사이킷 런에서는 shuffle=True로 지정하여 섞을 수 있다.
- KFCV를 사용하여 분류기를 평가할 때, 각 타깃 클래스의 샘플이 거의 같은 비율로 폴드에 담기는 것이 좋다(계층별 k-폴드 라고 부른다).
예를 들어 성별 타깃 벡터 중 80% 샘플이 남성이라면, 각 폴드도 80% 남성과 20% 여성 샘플로 이루어져야 한다. 사이킷런에서는 KFold StartifiedFolod로 바꾸어 계층별 K-폴드 교차검증을 수행할 수 있다. - 검증 세트나 교차검증을 사용할 때 훈련세트에서 데이터를 전처리하고 이 변환을 훈련세트와 테스트 세트에 모두 적용하는것이 중요하다.
예를 들면 표준화 객체 standardizer의fit 메소드를 호출하여 훈련 세트의 평균과 분산을 계산한다. 그 다음 이 변환을 transform 메소드를 사용해 훈련 세트와 테스트 세트에 모두 적용한다.
# 훈련세트 / 데이터 세트 나누어주는 라이브러리
from sklearn.model_selection import train_test_split
# 훈련 세트와 테스트 세트 생성
features_train, features_test, target_train, target_test = train_test_split(features, target, test_size=0.1, random_state=1)
# 훈련 세트로 standardizerd의 fit 메소드 호출 : 훈련세트에서 데이터를 전처리
standardizer.fit(features_train) # 훈련셋
StandardScaler()
# 훈련 세트와 테스트 세트에 standardizerd 모두 적용 : standardizer 변환을 훈련세트와 테스트 세트에 모두 적용
features_train_std = standardizer.transform(features_train)
features_test_std = standardizer.transform(features_test)
위에 처럼 하는 이유는 테스트 세트를 모르는 척 하기 위해서이다. 이 전처리 객체를 훈련 세트와 테스트 세트에 있는 모든 샘플로 훈련한다면 테스트 세트의 정보가 훈련 세트로 유출 된것이다.
이 규칙은 특성 선택 같은 모든 전처리 단계에 적용된다.
사이킷런의 pipline 패키지는 교차검증 기법을 사용할 때 이 규칙을 손쉽게 구현할 수 있도록 도와준다. 먼저 데이터를 전처리(예를 들면 stadardizer)하고 모델(로지스틱 회귀인 logit)을 훈련하는 파이프라인을 만든다.
# 파이프라인 생성
pipeline = make_pipeline(standardizer, logit)
pipeline
Pipeline(steps=[('standardscaler', StandardScaler()),
('logisticregression', LogisticRegression())])
그 다음 해당 파이프라인으로 KFCV를 실행하면 사이킷런이 모든 작업을 알아서 처리한다.
# K-폴드 교차검증 수행
cv_results = cross_val_score(pipeline, #파이프라인
features, #특성행렬
target, #타깃 벡터
cv=kf, #교차검증 기법 - KFold
scoring="accuracy", #평가 지표
n_jobs=-1) # 모든 CPU 코어 사용
df_cv_results = pd.DataFrame(cv_results)
df_cv_results
0 | |
---|---|
0 | 0.977778 |
1 | 0.988889 |
2 | 0.961111 |
3 | 0.944444 |
4 | 0.977778 |
5 | 0.983333 |
6 | 0.955556 |
7 | 0.988827 |
8 | 0.977654 |
9 | 0.938547 |
cross_val_score에는 아직 이야기하지 않은 중요한 세 개의 매개변수가 존재한다.
- cv는 교차검증 기법을 결정한다. (k-폴드를 가장 많이 사용하지만 다른 방식도 있다.)
- scoring 매개변수 : 이 장의 다른 여러 레시피에서 설명할 모델 성공의 측정 방법 결정
- n_jobs1 = -1은 사이킷런에게 가용한 모든 코어를 사용하도록 지시한다. 코어를 모두 동시에 사용해 작업의 속도를 높여준다.
- LOOCV는 LeaveOneOut 클래스에 구현되어있다. 폴드의 수 k가 샘플의 개수와 같다.
LeaveOneOut 클래스는 KFold(n_splits=n)과 동일하다.(n은 샘플 개수).
- KFold와 StartifiedKFolod의 n_splits 매개변수 기본값은 5이다.
ShuffleSplit¶
는 반복 횟수에 상관없이 훈련폴드와 테스트 폴드 크기를 임의로 지정할 수 있다. train_size, test_size 매개변수에는 사용할 샘플 개수 또는 비율을 입력한다.
반복마다 랜덤하게 분할하기 때문에 하나의 샘플이 여러 번 테스트 폴드에 포함될 수 있다. 계층별 교차검증을 위한 StartifiedShiffleSplit도 있다.
# 반복 횟수에 상관없이 훈련폴드와 테스트 폴드 크기를 임의로 지정
from sklearn.model_selection import ShuffleSplit
# ShuffleSplit 분할기 생성 : 훈련폴드로 50%, 테스트 폴드로 20%를 사용하여 10번 반복
ss = ShuffleSplit(n_splits=10, train_size=0.5, test_size=0.2, random_state=42)
# ShuffleSplit 교차검증 수행
cv_results2 = cross_val_score(pipeline, #파이프라인
features, #특성행렬
target, #타깃 벡터
cv=ss, #교차검증 기법 - ShuffleSplit
scoring="accuracy", #평가 지표 - 정확도
n_jobs=-1) # 모든 CPU 코어 사용
df_cv_results2 = pd.DataFrame(cv_results2)
df_cv_results2
0 | |
---|---|
0 | 0.961111 |
1 | 0.983333 |
2 | 0.955556 |
3 | 0.966667 |
4 | 0.977778 |
5 | 0.966667 |
6 | 0.950000 |
7 | 0.950000 |
8 | 0.952778 |
9 | 0.966667 |
# 교차검증 점수 비교
df_r = pd.concat([df_cv_results,df_cv_results2], axis=1)
df_r
0 | 0 | |
---|---|---|
0 | 0.977778 | 0.961111 |
1 | 0.988889 | 0.983333 |
2 | 0.961111 | 0.955556 |
3 | 0.944444 | 0.966667 |
4 | 0.977778 | 0.977778 |
5 | 0.983333 | 0.966667 |
6 | 0.955556 | 0.950000 |
7 | 0.988827 | 0.950000 |
8 | 0.977654 | 0.952778 |
9 | 0.938547 | 0.966667 |
# 평균 계산
cv_results2.mean()
0.9630555555555554
RepeatedKFold¶
교차검증을 반복하여 실행할 수 있는 것이다. 다음 코드에서 교차검증을 5번 반복하여 5*10으로 총 50개의 교차검증 점수가 생성된다. n_splits 매개변수 기본값은 5이고 n_repeats기본 값은 10이다.
from sklearn.model_selection import RepeatedKFold
# RepeatedFold 분할기 생성
# random_state는 호출할 때마다 동일한 학습/테스트용 데이터 세트를 생성하기 위해 주어지는 난수 값
rfk = RepeatedKFold(n_splits=10, n_repeats=5, random_state=42)
# 교차검증 수행
cv_results2 = cross_val_score(pipeline, #파이프라인
features, #특성행렬
target, #타깃 벡터
cv=rfk, #교차검증 기법 - RepeatedFold
scoring="accuracy", #평가 지표 - 정확도
n_jobs=-1) # 모든 CPU 코어 사용
df_cv_results2 = pd.DataFrame(cv_results2)
df_cv_results2
# 검증 점수 개수 확인
len(cv_results2)
50
df_cv_results2
0 | |
---|---|
0 | 0.977778 |
1 | 0.977778 |
2 | 0.966667 |
3 | 0.955556 |
4 | 0.972222 |
5 | 0.966667 |
6 | 0.966667 |
7 | 0.972067 |
8 | 0.966480 |
9 | 0.932961 |
10 | 0.988889 |
11 | 0.977778 |
12 | 0.972222 |
13 | 0.972222 |
14 | 0.950000 |
15 | 0.966667 |
16 | 0.966667 |
17 | 0.983240 |
18 | 0.983240 |
19 | 0.966480 |
20 | 0.977778 |
21 | 0.950000 |
22 | 0.972222 |
23 | 0.988889 |
24 | 0.977778 |
25 | 0.955556 |
26 | 0.955556 |
27 | 0.988827 |
28 | 0.977654 |
29 | 0.966480 |
30 | 0.983333 |
31 | 0.955556 |
32 | 0.961111 |
33 | 0.977778 |
34 | 0.955556 |
35 | 0.961111 |
36 | 0.977778 |
37 | 0.977654 |
38 | 0.966480 |
39 | 0.966480 |
40 | 0.972222 |
41 | 0.977778 |
42 | 0.955556 |
43 | 0.972222 |
44 | 0.961111 |
45 | 0.988889 |
46 | 0.961111 |
47 | 0.932961 |
48 | 0.988827 |
49 | 0.988827 |
'파이썬 > 머신러닝' 카테고리의 다른 글
머신러닝 이진 분류모델의 예측 평가와 임곗값 평가 (0) | 2022.11.24 |
---|---|
머신러닝 기본 회귀 모델과 기본 분류 모델의 평가방법 - score(), predict() (0) | 2022.11.23 |
넘파이numpy 와 서킷런sklearn을 활용한 데이터 이상치 확인 및 해결 (0) | 2022.11.17 |
서킷런 sklearn으로 수치형 데이터 전처리 [스케일링 - 표준화, 로버스트, MinMax, 정규화] (1) | 2022.11.16 |
머신러닝 데이터 학습 유형 - 지도학습 / 비지도학습 (0) | 2022.11.15 |
댓글