일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 빌트인객체
- 로만
- 인가
- SUM
- 제너릭
- JSON
- implements
- 직렬화
- 모던 자바스크립트 딥 다이브
- javascript
- integer
- LeetCode
- 파견
- java
- 릿코드
- Roman
- 메모리
- 자바
- jre
- Two
- JVM
- 할당
- 이냐
- 딥다이브
- 모던자바스크립트
- JDK
- 제네릭
- 부탁드립니다
- strict mode
- 비전공
- Today
- Total
AnnSEo.log
[파이썬 머신러닝 완벽가이드] CH02 본문
사이킷런(scikit-learn) : 머신러닝 라이브러리 중 가장 많이 사용되는 라이브러리
- 파이썬 기반 다른 머신러닝 패키지도 사이킷런 스타일의 API를 지향할 정도로 쉽고 가장 파이썬스러운 API 제공
- 머신러닝을 위한 다양한 알고리즘과 개발에 편리한 프레임워크와 API 제공
cmd 관리자 모드로 사이킷런 설치(주피터에서 실시했을 때 나처럼 권한 없음 뜨면! 아니면 그냥 주피터 코드창으로)
conda install scikit-learn
pip install scikit-learn
import sklearn
print(sklearn.version)
0.24.2
02 첫 번째 머신러닝 만들어 보기 - 붓꽃 품종 예측
품종 분류(Classification) - 지도학습 중 하나
지도학습 : 명확한 정답이 있는 데이터를 먼저 학습한 뒤 미지의 정답을 예측하는 방식
데이터 세트 : 학습을 위해 주어진 데이터 세트
테스트 데이터 세트 : 머신러닝 모델의 예측 성능을 평가하기 위해 별도로 주어진 데이터 세트
- sklearn.datasets : 사이킷런에서 자체 제공하는 데이터 세트 생성 모듈
- sklearn.tree : 트리 기반 ML 알고리즘을 구현한 클래스 모임
- sklearn.model_selection : 학습 데이터와 검증 데이터, 예측 데이터로 데이터를 분리하거나 최적의 하이퍼 파라미터로 평가하기 위한 다양한 모듈
- 하이퍼 파라미터 : 머신러닝 알고리즘별로 최적의 학습을 위해 입력하는 파라미터들을 통칭. 이를 통해 머신러닝 알고리즘 성능을 튜닝할 수 있음
붓꽃 데이터 세트를 생성하는 데는 load_iris()를 이용, ML 알고리즘은 의사 결정 트리 알고리즘으로 이를 구현한 DecisionTreeClassifier를 적용
import sklearn
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import pandas as pd
# 붓꽃 데이터 세트 로딩
iris = load_iris()
# iris.data는 Iris 데이터 세트에서 피처만으로 된 데이터를 numpy로 가지고 있음
iris_data = iris.data
# iris.target은 붓꽃 데이터 세트에서 레이블 데이터를 numpy로 가지고 있음
iris_label = iris.target
print('iris target 값:', iris_label)
print('iris target 명:', iris.target_names)
# 붓꽃 데이터 세트를 자세히 보기 위해 Dataframe으로 변환
iris_df = pd.DataFrame(data=iris_data, columns=iris.feature_names)
iris_df['label'] = iris.target
iris_df.head(3)
# 학습용 데이터와 테스트용 데이터 분리
# iris_data : 피쳐 데이터 세트
# iris_label : 레이블 데이터 세트(label)
# test_size=0.2 : 전체 데이터 세트 중 테스트 데이터 세트 비율
# random_state : 호출할 때마다 같은 학습/테스트 용 데이터 세트를 생성하기 위해 주어지는 난수 발생값
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_label, test_size=0.2, random_state=11)
0 → setosa
1 → versicolor
2 → virginica
학습용 데이터와 테스트용 데이터 분리 → train_test_split() API 사용
# DecisionTreeClassifier 객체 생성
dt_clf = DecisionTreeClassifier(random_state=11)
# 학습 수행
dt_clf.fit(X_train, y_train)
# 학습이 완료된 DecisionTreeClassifier 객체에서 테스트 데이터 세트로 예측 수행
pred = dt_clf.predict(X_test)
from sklearn.metrics import accuracy_score
print('예측 정확도: {0:.4f}'.format(accuracy_score(y_test, pred)))
예측 정확도: 0.9333
분류 예측 프로세스
- 데이터 세트 분리 : 데이터를 학습 데이터와 테스트 데이터로 분리
- 모델 학습 : 학습 데이터를 기반으로 ML 알고리즘을 적용해 모델을 학습시킴
- 예측 수행 : 학습된 ML 모델을 이용해 테스트 데이터의 분류를 예측
- 평가 : 예측된 결과값과 테스트 데이터의 실제 결과값을 비교해 ML 모델 성능을 평가
03 사이킷런의 기반 프레임워크 익히기
- Estimator 이해 및 fit(), predict() 메서드
- fit() : ML 모델 학습
- predict() : 학습된 모델 예측
사이킷런에서는 분류 알고리즘을 구현한 클래스를 Classifier로, 회귀 알고리즘을 구현한 클래스를 Regressor로 지칭하고 이 둘을 합쳐서 Estimator 클래스라고 부른다.
cross_val_score() 같은 경우 evaluation 함수, GridSerachCV 같은 하이퍼 파라미터 튜닝을 지원하는 클래스의 경우 이 Estimator를 인자로 받음
인자로 받은 Estimator에 대해 cross_val_score(), GridSerachCV.fit() 함수 내에서 이 Estimator의 fit()과 predict()를 호출해 평가하거나 하이퍼 파라미터 튜닝을 수행함
사이킷런에서 비지도학습인 차원 축소, 클러스터링, 피처 추출(Feature Extraction) 등을 구현한 클래스 역시 대부분 fit(), transform() 를 적용함
fit과 transform을 한 번에 실행하는 fit_transform()도 있으나 따로 하는 것과 차이가 있음 → 이건 비지도학습 챕터에서 설명
일반적인 머신러닝 모델을 구축하는 주요 프로세스는
- Feature processing(피처 처리, 가공, 변경, 추출)
- ML 알고리즘 학습/예측 수행
- 모델 평가
사이킷런에서 연습용 예제 데이터를 갖다 쓸 수도 있는데, 예제 데이터는 일반적으로 딕셔너리 형태로 되어 있음
키는 보통 data, target, target_name, feature_names, DESCR 로 구성
- data : 피처의 데이터 세트를 가리킴
- target : 분류 시 레이블 값, 회귀일 때는 숫자 결과값 데이터 세트
- target_names : 개별 레이블 이름
- feature_names : 피쳐 이름
- DESCR : 데이터 세트에 대한 설명과 각 피처의 설명
data, target은 넘파이 배열(ndarray)
target_names, feature_names → 넘파이 배열 또는 파이썬 리스트 타입
DESCR → String
print('\n feature_name의 type:', type(iris_data.feature_names))
print(' feature_name의 shape:', len(iris_data.feature_names))
print(iris_data.feature_names)
print('\n target_name의 type:', type(iris_data.target_names))
print(' target_name의 shape:', len(iris_data.target_names))
print(iris_data.target_names)
print('\n data의 type:', type(iris_data.data))
print(' data의 shape:', len(iris_data.data))
print(iris_data['data'])
print('\n target의 type:', type(iris_data.target))
print(' target의 shape:', iris_data.target.shape)
print(iris_data.target)
04 Model Selection 모듈 소개
train_test_split()
- test_size : 전체 데이터에서 테스트 데이터 세트 크기를 얼마로 샘플링할 것인가를 결정. 디폴트는 0.25로 25%
- train_size : 전체 데이터에서 학습용 데이터 세트 크기를 얼마로 샘플링할 것인가를 결정. test_size parameter를 통상적으로 사용하기 때문에 train_size는 잘 사용하지 않음
- shuffle : 데이터 분리하기 전에 데이터를 미리 섞을지 결정함. 디폴트는 True. 데이터를 분산시켜 좀 더 효율적인 학습 및 테스트 데이터 세트를 만드는데 사용함
- random_state : random_state는 호출할 때마다 동일한 학습/테스트용 데이터 세트를 생성하기 위해 주어지는 난수 값. train_test_split() 호출 시 무작위로 데이터를 분리하므로 지정하지 않으면 수행할 때마다 다른 학습/테스트용 데이터를 생성함.
- train_test_split() 반환값은 튜플 형태임. 순차적으로 학습용 데이터의 피처 데이터 세트, 테스트용 데이터 피처 데이터 세트, 학습용 데이터 레이블 데이터 세트, 테스트용 데이터이 레이블 데이터 세트가 반환됨
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
iris = load_iris()
dt_clf = DecisionTreeClassifier()
train_data = iris.data
train_label = iris.target
dt_clf.fit(train_data, train_label)
# 학습 데이터 세트로 예측 수행
pred = dt_clf.predict(train_data)
print('예측 정확도:', accuracy_score(train_label, pred))
# 예측 정확도: 1.0
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
dt_clf = DecisionTreeClassifier()
iris_data = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.3, random_state=121)
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
print('예측 정확도: {0:.4f}'.format(accuracy_score(y_test, pred)))
# 예측 정확도: 0.9556
모델이 학습 데이터에만 과도하게 최적화되어 실제 예측은 제대로 못하는 과적합(Overfitting) 현상이 발생할 수 있어 교차 검증을 실시함
교차검증은 별도의 여러 세트로 구성된 학습 데이터 세트와 검증 데이터 세트에서 학습과 평가를 수행하는 것
→ 대부분의 ML 모델의 성능 평가는 교차 검증 기반으로 1차 평가를 한 뒤에 최종적으로 테스트 세트에 적용해 평가하는 프로세스임
머신러닝에 사용되는 데이터 세트를 세분화해서 학습, 검증, 테스트 데이터 세트로 나눌 수 있고 테스트 데이터 세트 외의 별도의 검증 데이터 세트를 둬서 최종 평가 이전에 학습된 모델을 다양하게 평가하는데 사용함
- K 폴드 교차 점증
- K개의 데이터 폴드 세트를 만들어서 K번만큼 각 폴드 세트에 학습과 검증 평가를 반복
- ex) 5 폴드 교차 검증
- 5개의 폴드된 데이터 세트를 학습과 검증을 위한 데이터 세트로 변경하고, 5번 평가를 수행한 뒤, 5개의 평가를 평균한 결과를 가지고 예측 성능을 평가
- 먼저, 데이터 세트를 5등분함
- 첫 번째 반복에서는 처음부터 4개 등분을 학습데이터 세트, 마지막 5번째 등분 하나를 검증 데이터 세트로 설정하고
- 학습 데이터 세트에서 학습 수행, 검증 데이터 세트에서 평가 수행
- 첫 번재 평가 수행하고 나면 두 번째에서도 비슷하게 학습, 평가 작업하되,
- 학습 데이터와 검증 데이터를 변경함(처음부터 3개 등분까지, 그리고 마지막 5번째 등분을 학습 데이터 세트로, 4번째 등분 하나를 검증 데이터 세트로 설정)
n_iter = 0
# KFold 객체의 split()를 호출하면 폴드별 학습용, 검증용 테스트의 로우 인덱스를 array로 반환
for train_index, test_index in kfold.split(features):
#kfold.split()으로 반환된 인덱스를 이용해 학습용, 검증용 테스트 데이터 추출
X_train, X_test = features[train_index], features[test_index]
y_train, y_test = label[train_index], label[test_index]
# 학습 및 예측
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
n_iter += 1
# 반복 시마다 정확도 측정
accuracy = np.round(accuracy_score(y_test, pred), 4)
train_size = X_train.shape[0]
test_size = X_test.shape[0]
print('\n#{0} 교차 검증 정확도:{1}, gkrtmq epdlxj zmrl: {2}, 검증 데이터 크기: {3}'
.format(n_iter, accuracy, train_size, test_size))
print('#{0} 검증 세트 인덱스:{1}'.format(n_iter, test_index))
cv_accruacy.append(accuracy)
# 개별 iteration별 정확도 합해 평균 정확도 계산
print('\n## 평균 검증 정확도:', np.mean(cv_accruacy))
위로 볼 때, split() 함수는 첫 번째 교차 검증에서는 0~29번까지, 두 번째는 30~59번까지 ~~ 각각 검증 세트 인덱스를 생성했고 이를 기반으로 검증 세트를 추출하게 된다.
- Stratified K 폴드
- 불균형한 분포도를 가진 레이블 데이터 집합을 위한 K 폴드 방식
import pandas as pd
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df['label']=iris.target
iris_df['label'].value_counts()
0 50
1 50
2 50
Name: label, dtype: int64
kfold = KFold(n_splits=3)
n_iter =0
for train_index, test_index in kfold.split(iris_df):
n_iter +=1
label_train= iris_df['label'].iloc[train_index]
label_test= iris_df['label'].iloc[test_index]
print('## 교차 검증: {0}'.format(n_iter))
print('학습 레이블 데이터 분포:\n', label_train.value_counts())
print('검증 레이블 데이터 분호:\n', label_test.value_counts())
## 교차 검증: 1
학습 레이블 데이터 분포:
1 50
2 50
Name: label, dtype: int64
검증 레이블 데이터 분호:
0 50
Name: label, dtype: int64
## 교차 검증: 2
학습 레이블 데이터 분포:
0 50
2 50
Name: label, dtype: int64
검증 레이블 데이터 분호:
1 50
Name: label, dtype: int64
## 교차 검증: 3
학습 레이블 데이터 분포:
0 50
1 50
Name: label, dtype: int64
검증 레이블 데이터 분호:
2 50
Name: label, dtype: int64
from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=3)
n_iter=0
for train_index, test_index in skf.split(iris_df, iris_df['label']):
n_iter += 1
label_train=iris_df['label'].iloc[train_index]
label_test=iris_df['label'].iloc[test_index]
print('## 교차 검증: {0}'.format(n_iter))
print('학습 레이블 데이터 분포:\n', label_train.value_counts())
print('검증 레이블 데이터 분포:\n', label_test.value_counts())
## 교차 검증: 1
학습 레이블 데이터 분포:
2 34
0 33
1 33
Name: label, dtype: int64
검증 레이블 데이터 분포:
0 17
1 17
2 16
Name: label, dtype: int64
## 교차 검증: 2
학습 레이블 데이터 분포:
1 34
0 33
2 33
Name: label, dtype: int64
검증 레이블 데이터 분포:
0 17
2 17
1 16
Name: label, dtype: int64
## 교차 검증: 3
학습 레이블 데이터 분포:
0 34
1 33
2 33
Name: label, dtype: int64
검증 레이블 데이터 분포:
1 17
2 17
0 16
Name: label, dtype: int64
dt_clf = DecisionTreeClassifier(random_state=156)
skfold = StratifiedKFold(n_splits=3)
n_iter=0
cv_accuracy=[]
# StratifiedKFold의 split() 호출 시 반드시 레이블 데이터 세트도 추가 입력 필요
for train_index, test_index in skfold.split(features, label):
# split()으로 반환된 인덱스를 이용해 학습용, 검증용 테스트 데이터 추출
X_train, X_test = features[train_index], features[test_index]
y_train, y_test = label[train_index], label[test_index]
# 학습 및 예측
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
# 반복 시마다 정확도 측정
n_iter += 1
accuracy = np.round(accuracy_score(y_test, pred), 4)
train_size = X_train.shape[0]
test_size = X_test.shape[0]
print('\n#{0} 교차 검증 정확도: {1}, 학습 데이터 크기: {2}, 검증 데이터 크기: {3}'
.format(n_iter, accuracy, train_size, test_size))
print('#{0} 검증 세트 인덱스:{1}'.format(n_iter, test_index))
cv_accuracy.append(accuracy)
# 교차 검증별 정확도 및 평균 정확도 계산
print('\n## 교차 검증별 정확도:', np.round(cv_accuracy, 4))
print('## 평균 검증 정확도:', np.mean(cv_accuracy))
#1 교차 검증 정확도: 0.98, 학습 데이터 크기: 100, 검증 데이터 크기: 50
#1 검증 세트 인덱스:[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 100 101
102 103 104 105 106 107 108 109 110 111 112 113 114 115]
#2 교차 검증 정확도: 0.94, 학습 데이터 크기: 100, 검증 데이터 크기: 50
#2 검증 세트 인덱스:[ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 67
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 116 117 118
119 120 121 122 123 124 125 126 127 128 129 130 131 132]
#3 교차 검증 정확도: 0.98, 학습 데이터 크기: 100, 검증 데이터 크기: 50
#3 검증 세트 인덱스:[ 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 83 84
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 133 134 135
136 137 138 139 140 141 142 143 144 145 146 147 148 149]
## 교차 검증별 정확도: [0.98 0.94 0.98]
## 평균 검증 정확도: 0.9666666666666667
- KFold로 데이터 학습하고 예측하는 코드를 보면,
- 폴드 세트를 설정하고
- for 루프에서 반복으로 학습 및 테스트 데이터의 인덱스 추출
- 반복적으로 학습과 예측을 수행하고 예측 성능 반환
- cross_val_score(estimator, X, y=None, scoring=None, cv=None, n_jobs=1, verbose=0, fit_params=None, pre_disptach='2*n_jobs')
- estimator : 사이킷런 분류 알고리즘 클래스인 Classfier 또는 회귀 알고리즘 클래스인 Regressor를 의미
- X : 피처 데이터 세트
- y : 레이블 데이터 세트
- scoring : 예측 성능 평가 지표를 기술
- cv : 교차 검증 폴드 수
- 배열 형태로 성능 지표 측정값을 반환
- classifer → Stratified K 폴드 방식으로 레이블값의 분포에 따라 학습/테스트 세트를 분할
- 회귀인 경우 K 폴드 방식으로 분할
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score, cross_validate
from sklearn.datasets import load_iris
iris_data = load_iris()
dt_clf = DecisionTreeClassifier(random_state=156)
data = iris_data.data
label =iris_data.target
# 성능 지표는 정확도(accuracy), 교차 검증 세트는 3개
scores = cross_val_score(dt_clf, data, label, scoring='accuracy', cv=3)
print('교차 검증별 정확도:', np.round(scores, 4))
print('평균 검증 정확도:', np.round(np.mean(scores), 4))
교차 검증별 정확도: [0.98 0.94 0.98]
평균 검증 정확도: 0.9667
cross_val_score()는 cv 로 지정된 횟수만큼 scoring 파라미터로 지정된 평가 지표로 평가 결과값을 배열로 반환함
그리고 일반적으로 이를 평균해 평가 수치로 사용한다.
cross_val_score() API는 내부에서 Estimator를 학습(fit), 예측(predict), 평가(evaluation)시켜주므로 간단하게 교차검증을 수행할 수 있다.
비슷한 API로 cross_validate()가 있음 → 여러개의 평가 지표를 반환할 수 있고 학습 데이터에 대한 성능 평가 지표와 수행 시간도 같이 제공된다.
- 사이킷런은 GridSerachCV API를 이용해 분류나 회귀 같은 알고리즘에 사용되는 하이퍼 파라미터를 순차적으로 입력하면서 편리하게 최적의 파라미터를 도출할 수 있는 방안을 제공한다.
- 예를 들어, 결정 트리 알고리즘은 여러 하이퍼 파라미터를 순차적으로 변경하면서 최고 성능을 가지는 파라미터 조합을 찾고자 한다면 다음과 같이 파라미터 집합을 만들고 이를 순차적으로 적용하면서 최적화를 수행할 수 있다.
- GridSerachCV는 교차 검증을 기반으로 이 하이퍼 파라미터의 최적 값을 찾게함
- 데이터 세트를 cross-validation을 위한 학습/테스트 세트로 자동으로 분할한 뒤, 하이퍼 파라미터 그리드에 기술된 모든 파라미터를 순차적으로 적용해 최적의 파라미터를 찾을 수 있게 해준다.
- 사용자가 튜닝하고자 하는 여러 종류의 하이퍼 파라미터를 다양하게 테스트하면서 최적의 파라미터를 편리하게 찾게 해주지만 동시에 순차적으로 파라미터를 테스트하므로 수행시간이 상대적으로 오래걸린다.
- GridSerachCV 클래스의 생성자로 들어가는 주요 파라미터
- estimator : classifier, regressor, pipeline
- param_grid : key + 리스트 값을 가지는 딕셔너리가 주어짐. estimator 튜닝을 위해 파라미터명과 사용될 여러 파라미터 값을 지정
- scoring : 예측 성능을 측정할 평가 방법을 지정함. 보통은 사이킷런 성능 평가 지표를 지정하는 문자열로 지정하나 별도의 성능 평가 지표 함수도 지정 가능
- cv : 교차 검증을 위해 분할되는 학습/테스트 세트의 개수를 지정
- refit : 디폴트가 True이며 True로 생성 시 가장 최적의 하이퍼 파라미터를 찾을 뒤 입력된 estimator 객체를 해당 하이퍼 파라미터로 재학습시킨다.
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
# 데이터를 로딩하고 학습 데이터와 테스트 데이터 분리
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=121)
dtree = DecisionTreeClassifier()
## 파라미터를 딕셔너리 형태로 설정
parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]}
import pandas as pd
# param_grid의 하이퍼 파라미터를 3개의 train, test set fold로 나눠 테스트 수행 설정
### refit=True가 디폴트, True면 가장 좋은 파라미터 설정으로 재학습시킴
grid_dtree = GridSearchCV(dtree, param_grid=parameters, cv=3, refit=True)
# 붓꽃 학습 데이터로 param_grid의 하이퍼 파라미터를 순차적으로 학습/평가
grid_dtree.fit(X_train, y_train)
# GridSerachCV 결과를 추출해 DataFrame으로 변환
scores_df = pd.DataFrame(grid_dtree.cv_results_)
scores_df[['params', 'mean_test_score', 'rank_test_score', 'split0_test_score'
, 'split1_test_score', 'split2_test_score']]
- GridSearchCV API 사용법
- 결정 트리 알고리즘의 여러 가지 최적화 파라미터를 순차적으로 적용해 붓꽃 데이터를 예측 분석하는데 GridSerachCV 이용
- train_test_split() 을 이용해 학습 데이터와 테스트 데이터를 먼저 분리하고,
- 학습 데이터에서 GridSearchCV를 이용해 최적 하이퍼 파라미터를 추출
- 결정 트리 알고리즘을 구현한 DecisionTreeClassifier의 중요 하이퍼 파라미터인 max_depth, min_samples_split 값을 변화시키면서 최적화를 진행
- 테스트할 하이퍼 파라미터 세트는 딕셔너리 형태로 하이퍼 파라미터 명칭은 문자열 Key 값으로, 하이퍼 파라미터의 값은 리스트 형으로 설정
- 학습 데이터 세트를 GridSerachCV 객체의 fit 메서드에 인자로 입력 → 학습 데이터를 cv에 기술된 폴딩 세트로 분할해 param_gird에 기술된 하이퍼 파라미터를 순차적으로 변경하면서 학습/평가를 수행하고 그 결과를 cv_results_ 속성에 기록함
- cv_results_는 gridsearchcv의 결과 세트로서 딕셔너리 형태로 key 값과 리스트 형태의 value 값을 가짐 → 이걸 판다스의 DataFrame 으로 변환해 내용을 좀 더 쉽게 볼 수 있음
- 결과
- 6개의 결과를 볼 수 있고 하이퍼 파라미터인 max_depth와 min_samples_split을 순차적으로 총 6번 변경하면서 학습 및 평가를 수행
- params 칼럼 : 수행할 때마다 적용된 개별 하이퍼 파라미터값
- rank_test_score : 하이퍼 파라미터별로 성능이 좋은 score 순위를 나타냄. 1이 가장 뛰어난 순이고 이 때의 파라미터가 최적의 하이퍼 파라미터임
- mean_test_score : 개별 하이퍼 파라미터별로 CV의 폴딩 테스트 세트에 대해 총 수행한 평가 평균값
- fit()을 수행하면 최고 성능을 나타낸 하이퍼 파라미터의 값과 그 때의 평가 결과 값이 각각 best_params_, best_score_ 속성에 기록된다.
- 이 속성을 이용해 최적 하이퍼 파라미터의 값과 그 때의 정확도를 알아볼 수 있음
print('GridSearchCV 최적 파라미터:', grid_dtree.best_params_) print('GridSearchCV 최고 정확도:{0:.4f}'.format(grid_dtree.best_score_)) # GridSearchCV의 refit으로 이미 학습된 estimator 반환 estimator = grid_dtree.best_estimator_ # GridSearchCV의 best_estimator_ 는 이미 최적 학습이 됐으므로 별도 학습이 필요 없음 pred = estimator.predict(X_test) print('테스트 데이터 세트 정확도: {0:.4f}'.format(accuracy_score(y_test, pred)))
05 데이터 전처리
ML 알고리즘을 적용하기 전에 데이터에 대해 미리 처리해야할 기본 사항
- NaN, Null 값은 허용되지 않음
- 문자열 값을 입력 값으로 허용하지 않음 → 인코딩돼서 숫자 형으로 변환
- 카테고리형 피처와 텍스트형 피처
- 카테고리형 피처 : 코드 값
- 텍스트형 피처 : 피처 벡터화 등의 기법으로 벡터화하거나 불필요한 피처라고 판단되면 삭제
데이터 인코딩
- 레이블 인코딩(Label encoding)
- 카테고리 피처를 코드형 숫자 값으로 변환
- 예를 들어, TV, 냉장고, 전자레인지, 컴퓨터가 있으면 TV:1. 냉장고:2. 전자레인지:3. 컴퓨터:4 이런 식으로 바꿈
from sklearn.preprocessing import LabelEncoder items=['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서'] # LabelEncoder를 객체로 생성한 후, fit()과 transform()으로 레이블 인코딩 수행 encoder = LabelEncoder() encoder.fit(items) labels = encoder.transform(items) print('인코딩 변환값:', labels) 인코딩 변환값: [0 1 4 5 3 3 2 2] print('인코딩 클래스:', encoder.classes_) 인코딩 클래스: ['TV' '냉장고' '믹서' '선풍기' '전자레인지' '컴퓨터'] print('디코딩 클래스:', encoder.inverse_transform([4, 5, 2, 0, 1, 1, 3, 3])) 디코딩 클래스: ['전자레인지' '컴퓨터' '믹서' 'TV' '냉장고' '냉장고' '선풍기' '선풍기']
- 레이블 인코딩은 간단하게 문자열 값을 숫자형 카테고리 값으로 변환한다
- 하지만 가끔 머신러닝 알고리즘에서 예측 성능이 떨어지는 경우가 있는데 숫자 값의 크고 작음으로 인해 숫자가 크면 더 중요하게 인식되는 등의 가능성이 발생하기 때문이다.
- 레이블 인코딩은 이 문제 때문에 선형 회귀 같은 머신러닝 알고리즘을 적용하지 않는다. (트리 계열의 ML 알고리즘은 문제 없음)
- 이걸 해결하기 위해 등장한게 원-핫 인코딩 방식이다.
- 원-핫 인코딩(One Hot encoding)
- 피처 값의 유형에 따라 새로운 피처를 추가해 고유 값에 해당하는 칼럼에만 1을 표시하고 나머지 칼럼에는 0을 표시하는 방식
- 원-핫의 뜻은 결국 여러 개의 속성 중 단 한 개의 속성만 1로 표시하는 것을 의미
- 더 쉽게 하려면 판다스에서 지원하는 API 중 하나인 get_dummies()를 이용하면 됨 → 문자열 카테고리 값을 숫자 형으로 변환할 필요 없이 바로 변환 가능
from sklearn.preprocessing import OneHotEncoder
import numpy as np
items=['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']
# 먼저 숫자 값으로 변환을 위해 LabelEncoder로 변환
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
# 2차원 데이터로 변환
labels = labels.reshape(-1, 1)
# 원-핫 인코딩 적용
oh_encoder = OneHotEncoder()
oh_encoder.fit(labels)
oh_labels = oh_encoder.transform(labels)
print('원 핫 인코딩 데이터')
print(oh_labels.toarray())
print('원-핫 인코딩 데이터 차원')
print(oh_labels.shape)
원 핫 인코딩 데이터
[[1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 1.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0. 0.]]
원-핫 인코딩 데이터 차원
(8, 6)
import pandas as pd
df= pd.DataFrame({'item':['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서'
]})
pd.get_dummies(df)
피쳐 스케일링과 정규화
피쳐 스케일링(feature scaling) : 서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업
대표적으로 표준화(Standardization)와 정규화(Normalization)가 있음
표준화 : 데이터의 피처 각각이 평균이 0이고 분산이 1인 가우시안 정규 분포를 가진 값으로 변환. 표준화로 변환될 피쳐 값은 원래 값에서 평균 값을 뺀 것을 표준편차로 나눈 값이다.
사이킷런 전처리에서 제공하는 Normalizer 모듈과 일반적인 정규화는 약간 차이가 있는데,
Normalizer 모듈은 선형대수에서의 정규화가 적용됐으며 개별 벡터의 크기를 맞추기 위해 변환하는 것을 의미한다. 즉, 개별 벡터를 모든 피처 벡터의 크기로 나눠 준다.
- StandardScaler
- 표준화 지원 클래스
- 개별 피처를 평균이 0이고 분산이 1인 값으로 변환해 가우시안 정규 분포를 가질 수 있게 데이터를 변환함 → 사이킷런에서 구현한 RBF 커널을 이용하는 서포트 벡터 머신이나 선형 회귀, 로지스틱 회귀 등의 경우 데이터가 가우시안 정규 분포임을 가정하고 구현됐기 때문에 사전에 표준화 하는게 중요함
from sklearn.datasets import load_iris
import pandas as pd
# 붓꽃 데이터 세트 로딩, DataFrame으로 변환
iris = load_iris()
iris_data = iris.data
iris_df = pd.DataFrame(data=iris_data, columns=iris.feature_names)
print('feature 들의 평균 값')
print(iris_df.mean())
print('\nfeature 들의 분산 값')
print(iris_df.var())
feature 들의 평균 값
sepal length (cm) 5.843333
sepal width (cm) 3.057333
petal length (cm) 3.758000
petal width (cm) 1.199333
dtype: float64
feature 들의 분산 값
sepal length (cm) 0.685694
sepal width (cm) 0.189979
petal length (cm) 3.116278
petal width (cm) 0.581006
dtype: float64
from sklearn.preprocessing import StandardScaler
# StandardScaler객체 생성
scaler = StandardScaler()
# StandardScaler로 데이터 세트 변환, fit(), transform() 호출
scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
# transform() 시 스케일 변환된 데이터 세트가 NumPy ndarray로 반환돼 이를 DataFrame으로 변환
iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
print('feature 들의 평균 값')
print(iris_df_scaled.mean())
print('\nfeature 들의 분산 값')
print(iris_df_scaled.var())
feature 들의 평균 값
sepal length (cm) -1.690315e-15
sepal width (cm) -1.842970e-15
petal length (cm) -1.698641e-15
petal width (cm) -1.409243e-15
dtype: float64
feature 들의 분산 값
sepal length (cm) 1.006711
sepal width (cm) 1.006711
petal length (cm) 1.006711
petal width (cm) 1.006711
dtype: float64
- StandardScaler를 이용해 각 피처를 한 번에 표준화해 변환
- StandardScaler 객체를 생성한 후 fit(), transform() 메서드에 변환 대상 피처 데이터 세트를 입력하고 호출해 변환함
- transform()을 호출할 때 스케일 변환된 데이터 세트가 넘파이의 ndarray이므로 DataFrame으로 변환해 평균값과 분산 값을 확인해 보면 0 1에 가까운 값으로 바뀜을 확인
- MinMaxScaler
- 데이터값을 0과 1 사이의 범위 값으로 변환함(음수 값이 있으면 -1에서 1값으로 변환)
- 데이터 분포가 가우시안 분포가 아니면 Min, Max Scale을 적용해볼 수 있음
from sklearn.preprocessing import MinMaxScaler
# MinMaxScaler객체 생성
scaler = MinMaxScaler()
# MinMaxScaler 로 데이터 셋 변환. fit() 과 transform() 호출.
scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
# transform()시 scale 변환된 데이터 셋이 numpy ndarry로 반환되어 이를 DataFrame으로 변환
iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
print('feature들의 최소 값')
print(iris_df_scaled.min())
print('\nfeature들의 최대 값')
print(iris_df_scaled.max())
feature들의 최소 값
sepal length (cm) 0.0
sepal width (cm) 0.0
petal length (cm) 0.0
petal width (cm) 0.0
dtype: float64
feature들의 최대 값
sepal length (cm) 1.0
sepal width (cm) 1.0
petal length (cm) 1.0
petal width (cm) 1.0
dtype: float64
- 학습 데이터와 테스트 데이터의 스케일링 변환 시 유의점
- StandardScaler, MinMaxScaler 와 같은 Scaler 객체를 이용해 데이터의 스케일링 변환 시 fit(), transform(), fit_transform() 메소드 이용함
- Scaler 객체로 학습 데이터 세트로 fit(), transform() 메소드 이용 시, 테스트 데이터 세트로는 다시 fit()을 수행하지 않고 학습 데이터 세트로 fit()을 수행한 결과를 이용해 transform() 변환을 적용해야 한다는 것
- 즉, 학습 데이터로 fit()이 적용된 스케일링 기준 정보를 그대로 테스트 데이터에 적용해야 하며, 그렇지 않고 테스트 데이터로 다시 새로운 스케일링 기준 정보를 만들게 되면 학습 데이터와 테스트 데이터의 스케일링 기준 정보가 달라지기 때문에 올바른 예측 결과를 도출하지 못할 수 있음
scaler = MinMaxScaler()
scaler.fit(train_array)
train_scaled = scaler.transform(train_array)
print('원본 train_array 데이터:', np.round(train_array.reshape(-1),2))
print('Scale된 train_array 데이터:', np.round(train_scaled.reshape(-1),2))
test_scaled = scaler.transform(test_array)
print('원본 test_array 데이터:', np.round(test_array.reshape(-1),2))
print('Scale된 test_array 데이터:', np.round(test_scaled.reshape(-1),2))
103, 104번 줄에서 실행된 걸 보면, 학습 데이터와 테스트 데이터의 스케일링이 맞지 않음을 볼 수 있음
테스트 데이터의 경우 최소값 0, 최대값 5이므로 1/5로 스케일링된다
따라서 원본값 1이 0.2 5가 1로 변환된다.
학습 데이터의 경우 원본값 2가 0.2로 변환되고 10이 1로 변환됨
→ 학습 데이터와 테스트 데이터의 서로 다른 원본값이 동일한 값으로 변환되는 결과 발생
머신러닝 모델은 학습 데이터를 기반으로 학습되기 때문에 반드시 테스트 데이터는 학습 데이터의 스케일링 기준을 따라야 하며, 테스트 데이터의 1 값은 학습 데이터와 동일하게 0.1 값으로 변환돼야 한다.
따라서 테스트 데이터에 다시 fit()을 적용해서는 안되며 학습 데이터로 이미 fit()이 적용된 Scale 객체를 이용해 transform()으로 변환해야 한다.
105번 줄을 보면 테스트 데이터에서 fit()을 호출하지 않고 학습 데이터로 fit()을 수행한 MinMaxScaler 객체의 transform()을 이용해 데이터를 변환한다.
출력 결과를 보면 모두 1/10 수준으로 스케일링되어 학습 데이터, 테스트 데이터 모두 동일하게 변환됨을 확인할 수 있다.
fit_transform()을 적용할 때도 fit()과 transform()을 순차적으로 수행하는 메소드기 때문에 학습 데이터는 무관하나 테스트 데이터에는 절대 사용하면 안된다.
- 가능하면 전체 데이터의 스케일링 변환을 적용한 뒤 학습과 테스트 데이터로 분리
- 1이 여의치 않으면 테스트 데이터 변환 시에는 fit()이나 fit_transform()을 적용하지 않고 학습 데이터로 이미 fit()된 Scaler 객체를 이용해 transform()으로 변환
06 사이킷런으로 수행하는 타이타닉 생존자 예측
- RangeIndex : DataFrame 인덱스의 범위를 나타냄. 전체 로우 수를 의미하고 891 entries이므로 891개의 로우가 존재
- 칼럼 수 12개
- float64 타입의 컬럼이 2개, int64 타입의 컬럼이 5개, object 타입은 string으로 보면 되고 5개가 있음
# 입력 age에 따라 구분 값을 반환하는 함수 설정. DataFrame의 apply lambda 식에 사용
def get_category(age):
cat=''
if age <= -1:cat = 'Unknown'
elif age <= 5:cat = 'Baby'
elif age <= 12: cat = 'Child'
elif age <= 18: cat = 'Teenager'
elif age <= 25: cat = 'Student'
elif age <= 35: cat = 'Young Adult'
elif age <= 60: cat = 'Adult'
else : cat = 'Elderly'
return cat
# 막대그래프의 크기 figure를 더 크게 설정
plt.figure(figsize=(10, 6))
# X축의 값을 순차적으로 표시하기 위한 설정
group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Elderly']
# lambda 식에 위에서 생성한 get_category() 함수를 반환값으로 지정
# get_category(X)는 입력값으로 'Age' 칼럼 값을 받아서 해당하는 cat 반환
titanic_df['Age_cat'] = titanic_df['Age'].apply(lambda x : get_category(x))
sns.barplot(x='Age_cat', y='Survived', hue='Sex', data=titanic_df, order=group_names)
titanic_df.drop('Age_cat', axis=1, inplace=True)
남아있는 문자열 카테고리 피쳐를 숫자형 카테고리 피처로 변환함
인코딩은 사이킷런의 LabelEncoder 클래스를 이용해 레이블 인코딩을 적용함
여기 칼럼을 encode_features() 함수를 새로 생성해 한 번에 변환함
'머신러닝' 카테고리의 다른 글
[파이썬 머신러닝 완벽가이드] CH03 (0) | 2021.09.26 |
---|---|
[파이썬 머신러닝 완벽가이드] CH01 (0) | 2021.09.24 |