15 분 소요

1. 사이킷런을 이용하여 붓꽃(Iris) 데이터 품종 예측하기

  • 사이킷런 모듈 정리
    • sklearn.datasetes : 사이킷런에서 자체적으로 제공하는 데이터 세트를 생성하는 모듈의 모임
    • sklearn.model_selection : 학습 데이터와 검증 데이터, 예측 데이터로 데이터를 분리하거나 최적의 하이퍼 파라미터로 평가하기 위한 다양한 모듈의 모임
      • 하이퍼 파라미터
        • 머신러닝 알고리즘 별로 최적의 학습을 위해 직접 입력하는 파라미터들 통칭
        • 하이퍼 파라미터를 통해 머신러닝 알고리즘의 성능을 튜닝할 수 있다.

1) 데이터 세트 분리

  • 데이터를 학습 데이터 및 테스트 데이터로 분리
  • train_test_split

  • 붓꽃 데이터 세트를 로딩
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
import sklearn
import pandas as pd

# 붓꽃 데이터 세트를 로딩
iris = load_iris()
  • Iris 데이터 세트에서 feature, target 값 가져와서 DataFrame 생성
# iris.data는 Iris 데이터 세트에서 피처(feature)만으로 된 데이터를 가지고 있음 (numpy) 
iris_data = iris.data

# iris.target은 붓꽃 데이터 세트에서 레이블 값을 가지고 있음 (numpy) 
iris_label = iris.target
print('iris target값:', iris_label) # [0 0 0 ... 1 1 1 ... 2 2 2 ...]
print('iris target명:', iris.target_names)    # ['setosa' 'versicolor' 'virginica']

# 붓꽃 데이터 세트를 자세히 보기 위해 DataFrame으로 변환합니다. 
iris_df = pd.DataFrame(data=iris_data, columns=iris.feature_names)
iris_df['label'] = iris.target
iris_df.head(3)
  • 데이터 세트 분리
from sklearn.model_selection import train_test_split
#1.데이터 세트 분리
#train_test_split(피쳐데이터셋,레이블데이터세트,테스트데이터 세트 비율, 난수발생값)
**X_train, X_test, y_train, y_test** = **train_test_split**(iris_data, iris_label, 
                                                    test_size=0.2, random_state=11)

2) 모델 학습

  • 학습 데이터를 기반으로 머신러닝 알고리즘을 적용해 모델을 학습
  • fit

  • DecisionTreeClassifier 객체 생성 및 모델 학습
    • ML 알고리즘은 의사 결정 트리(Decision Tree) 알고리즘을 구현한 DecisionTreeClassifier를 적용함
from sklearn.tree import DecisionTreeClassifier

# DecisionTreeClassifier 객체 생성 
dt_clf = DecisionTreeClassifier(random_state=11)

#2. 모델학습
# 학습 수행 
dt_clf.fit(X_train, y_train)

3) 예측 수행

  • 학습된 모델을 이용해 테스트 데이터의 분류를 예측
  • predict
#3. 예측수행
# 학습이 완료된 DecisionTreeClassifier 객체에서 테스트 데이터 세트로 예측 수행. 
**pred** = dt_clf.predict(X_test)

4) 평가

  • 예측된 결과값과 테스트 데이터의 실제 결과값을 비교 → 모델 성능 평가
#4. 평가
from sklearn.metrics import accuracy_score
print('예측 정확도: {0:.4f}'.format(**accuracy_score**(y_test,**pred**))) # 0.9333

2. 교차 검증

  • 과적합(Overfitting)
    • 고정된 학습 데이터와 테스트 데이터로 평가를 하다보면 테스트 데이터에만 최적의 성능을 발휘할 수 있도록 편향되게 모델을 유도하는 경향이 생긴다.
    • 결국 해당 테스트 데이터에만 과적합되는 학습 모델이 만들어져 다른 테스트용 데이터가 들어올 경우에는 성능이 저하되게 된다.
    • 이러한 문제점을 개선하기 위해 교차 검증을 이용해 다양한 학습과 평가를 수행한다.

1) K 폴드 교차검증

  • K개의 데이터 폴드 세트를 만들어서 K번만큼 각 폴드 세트에 학습, 검증, 평가를 반복적으로 수행하는 방법
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
import numpy as np

iris = load_iris()
features = iris.data
label = iris.target
dt_clf = DecisionTreeClassifier(random_state=156)

# 5개의 폴드 세트로 분리하는 KFold 객체와 폴드 세트별 정확도를 담을 리스트 객체 생성.
kfold = KFold(n_splits=5)       # shuffle 디폴트 옵션이 false -> 순차적으로 분리됨. 
cv_accuracy = []
print('붓꽃 데이터 세트 크기:',features.shape[0])   # 150  
n_iter = 0

# 120개, 30개로 나눠서 120개로 학습, 30개로 테스트 -> 이걸 5번 반복한다.
# KFold객체의 split( ) 호출하면 폴드 별 학습용, 검증용 테스트의 로우 인덱스를 array로 반환  
for train_index, test_index  in **kfold.split(features)**:
    # kfold.split( )으로 반환된 인덱스를 이용하여 학습용, 검증용 테스트 데이터 추출
    ## train_index : numpy (fancy indexing) -> 인덱스에 해당하는 값들을 리턴
    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)
    
# 개별 iteration별 정확도를 합하여 평균 정확도 계산 
print('\n## 평균 검증 정확도:', np.mean(cv_accuracy))
  • 결과

      #1 교차 검증 정확도 :1.0, 학습 데이터 크기: 120, 검증 데이터 크기: 30
      #1 검증 세트 인덱스:[ 0  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]
        
      #2 교차 검증 정확도 :0.9667, 학습 데이터 크기: 120, 검증 데이터 크기: 30
      #2 검증 세트 인덱스:[30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59]
        
      #3 교차 검증 정확도 :0.8667, 학습 데이터 크기: 120, 검증 데이터 크기: 30
      #3 검증 세트 인덱스:[60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89]
        
      #4 교차 검증 정확도 :0.9333, 학습 데이터 크기: 120, 검증 데이터 크기: 30
      #4 검증 세트 인덱스:[ 90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119]
        
      #5 교차 검증 정확도 :0.7333, 학습 데이터 크기: 120, 검증 데이터 크기: 30
      #5 검증 세트 인덱스:[120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149]
        
      ## 평균 검증 정확도: 0.9
    

2) Stratified K 폴드

  • 불균형한(imbalanced) 분포도를 가진 레이블 데이터 집합을 위한 K 폴드 방식
  • 불균형한 분포도를 가진 레이블 데이터 집합 : 특정 레이블 값이 특이하게 많거나 매우 적어서 값의 분포가 한쪽으로 치우치는 것.
    • 예 : 대출 사기 데이터 예측 :
      • 대출 사기 여부를 뜻하는 레이블 (대출 사기 :1, 정상 대출 : 0)
      • 대부분의 데이터는 정상 대출. 매우 작은 비율로 1 레이블 값이 존재
      • 특정 데이터 세트에는 1이 상대적으로 많이 들어가 있고, 다른 반보고 학습/테스트 데이터 세트에는 그렇지 못한 결과가 발생할 수 있음
      • 따라서 원본 데이터와 유사한 대출 사기 레이블 값의 분포를 학습/테스트 세트에도 유지하는 것이 매우 중요하다.
  • Stratified K 폴드는 이처럼 K 폴드가 레이블 데이터 집합이 원본 데이터 집합의 레이블 분포를 학습/테스트 세트에 제대로 분배하지 못하는 경우의 문제를 해결한다.
  • Stratified 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: int6
  • 레이블 값은 0,1,2 값 모두 50개로 동일.
  • 하지만 데이터를 보면 같은 품종끼리 서로 몰려있음 → K=3인 KFold로 테스트했을 때 문제 발생
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
  • 학습은 1,2번으로만 하고, 테스트는 0번으로만 테스트하게 됨. 즉 학습 데이터와 테스트 데이터가 서로 완전히 분리되어있음

    → 0의 경우를 전혀 학습하지 못함

    → 검증 에측 정확도는 0이 됨

  • 따라서 학습도 0,1,2로 구성되어 있고, 테스트 데이터도 0,1,2로 고르게 구성되어 있어야 학습이 제대로 일어난다.
  • ⇒ stratified KFold 사용
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

3) cross_val_score()

  • 위 교차 검증(폴드 세트 설정 → for 루프에서 반복으로 학습 및 테스트 데이터의 인덱스 추출 → 반복적으로 학습과 예측을 수행하고 예측 성능 반환) 과정을 간단하게 하는 방법
  • cross_val_score(estimator, X, y=None, , groups=None, scoring=None, cv=None, n_jobs=None, verbose=0, fit_params=None, pre_dispatch=’2n_jobs’, error_score=np.nan)
    • estimator : Classifier 혹은 Regressor
      • classifier가 입력되면 Stratified K 폴드 방식으로 학습/테스트 세트를 분할
      • Regressor인 경우 Stratified K 폴드 방식으로 분할할 수 없으므로 K 폴드 방식으로 분할
    • X : 피처 데이터 세트
    • y : 레이블 데이터 세트
    • scoring : 예측 성능 평가 지표
    • cv : 교차 검증 폴드 수
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score , cross_validate
from sklearn.datasets import load_iris
import numpy as np

iris_data = load_iris()
dt_clf = DecisionTreeClassifier(random_state=156)

data = iris_data.data
label = iris_data.target

#성능 평가 지표는 정확도인 accuracy로 함
scores = cross_val_score(dt_clf , data , label , scoring='accuracy',cv=3)  #Straified?
print('교차 검증별 정확도:',np.round(scores, 4))
print('평균 검증 정확도:', np.round(np.mean(scores), 4))

4) GridSearchCV - 교차 검증과 최적 하이퍼 파라미터 튜닝을 한번에

  • 하이퍼 파라미터는 머신러닝 알고리즘의 주요 구성 요소이며, 이 값을 조정해 알고리즘의 예측 성능을 개선할 수 있다.
  • 사이킷런은 GridSearchCV API를 이용해 Classifier나 Regressor와 같은 알고리즘에 사용되는 하이퍼 파라미터를 순차적으로 입력 → 편리하게 최적의 파라미터를 도출할 수 있는 방안 제공
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()

### hyper parameter 들을 dictionary 형태로 설정
parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]}
import pandas as pd

# param_grid의 하이퍼 파라미터들을 3개의 train, test set fold 로 나누어서 테스트 수행 설정.  => **6개의 파라미터 조합 X CV 3회 -> 18회의 학습/평가가 이뤄진다**. 
### refit=True 가 default 임. True이면 가장 좋은 파라미터 설정으로 재 학습 시킴.  
grid_dtree = GridSearchCV(dtree, param_grid=parameters, cv=3, refit=True)

# 붓꽃 Train 데이터로 param_grid의 하이퍼 파라미터들을 순차적으로 학습/평가 .
grid_dtree.fit(X_train, y_train)

# GridSearchCV 결과 추출하여 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']]
print('GridSearchCV 최적 파라미터:', grid_dtree.best_params_)
# {'max_depth': 3, 'min_samples_split': 2}

print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_dtree.best_score_))  # 0.9750
# 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))) # 0.9667

3. 데이터 전처리

  • 머신러닝에서 데이터 전처리는 매우 중요하다. 입력값에 따라 결과가 크게 달라지기 때문
  • Null 처리
    • Null 값이 얼마되지 않는 경우 → feature의 평균값으로 대체
    • Null 값이 대부분인 경우 → 해당 feature 드롭
    • Null 값이 애매하게 있는 경우 → 단순히 평균값으로 대체하는 것이 아니라 업무 로직등을 상세하게 검토해야함
  • 문자열 처리
    • 사이킷런의 머신러닝 알고리즘은 문자열 값을 입력값으로 허용하지 않는다.
    • 따라서 모든 문자열 값은 인코딩돼서 숫자형으로 변환해야한다. (주민번호나 단순 문자열 아이디 같은 예측에 도움이 안되는 불필요한 피처일 경우는 그냥 삭제)

1) 데이터 인코딩

  • 레이블 인코딩 (Label encoding)
    • 카테고리 피처를 코드형 숫자 값으로 변환
    • 예 : 상품 데이터의 상품 구분이 TV, 냉장고, 전자레인지, 컴퓨터, 선풍기, 믹서로 돼 있다면 → TV : 1, 냉장고: 2, 전자레인지: 3, 컴퓨터: 4, 선풍기 :5, 믹서 : 6과 같은 숫자형 값으로 변환하는 것
      from sklearn.preprocessing import LabelEncoder
        
      items=['TV','냉장고','전자렌지','컴퓨터','선풍기','선풍기','믹서','믹서']
        
      # LabelEncoder를 객체로 생성한 후 , fit( ) 과 transform( ) 으로 label 인코딩 수행. 
      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) 우선 숫자형태의 값으로 변환되어 있어야 함 2) 2차원 데이터로 변환되어 있어야 함

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)

print('2차원 데이터로 변환후')
print(labels)
--------------------------
[[0]
 [1]
 [4]
 [5]
 [3]
 [3]
 [2]
 [2]]

# 원-핫 인코딩을 적용합니다. 
oh_encoder = OneHotEncoder()
oh_encoder.fit(labels)
oh_labels = oh_encoder.transform(labels)
print('원-핫 인코딩 데이터')
print(oh_labels.toarray())
-----------------------------
[[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.]]

print('원-핫 인코딩 데이터 차원')
print(oh_labels.shape)   # (8, 6)
  • Pandas로 쉽게 원핫 인코딩 가능
import pandas as pd

df = pd.DataFrame({'item':['TV','냉장고','전자렌지','컴퓨터','선풍기','선풍기','믹서','믹서'] })
pd.get_dummies(df) #pandas 에서 제공하는 원-핫 인코딩 함수(아주 간편함)

2) 피처 스케일링과 정규화

  • 서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업을 feature scaling 이라고 한다.
  • 대표적인 방법으로 표준화(Standardization)와 정규화(Normalization)이 있다.
  • 표준화
    • 데이터의 피처 각각이 평균이 0이고 분산이 1인 가우시간 정규 분포를 가진 값으로 변환하는 것을 의미한다.
    • 표준화를 통해 변환될 피처 x의 새로운 i번째 데이터 : xi_new
    • xi_new = (xi - mean(x))/stdev(x)
  • 정규화
    • 서로 다른 피처의 크기를 통일하기 위해 크기를 변환해주느 ㄴ개념
    • 예 : 피처 A는 거리를 나타내는 변수로 값이 0 ~ 100KM로 주어지고, 피처 B는 금액을 나타내는 속성으로 값이 0 ~ 100,000,000,000원으로 주어진다면 이 변수를 모두 동일한 크기 단위로 비교하기 위해 값을 모두 최소 0~ 최대 1의 값으로 변환하는 것.
    • 즉, 개별 데이터의 크기를 모두 똑같은 단위로 변경하는 것
  • StandardScaler
    • 표준화를 지원하기 위한 클래스
    • 개별 피처를 평균이 0이고 분산이 1인 가우시안 분포를 따르도록 변환해줌
    • RBF 커널을 이용하는 서포트 벡터 머신이나 선형 회귀, 로지스틱 회귀는 데이터가 가우시안 분포를 가지고 있다고 가정하고 구현됐기 때문에 표준화를 적용하는 것이 중요하다.

    StandardScaler 적용 전

      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())
      iris_df
    

    StandardScaler 적용 후

      from sklearn.preprocessing import StandardScaler
        
      # StandardScaler객체 생성
      scaler = StandardScaler()
      # StandardScaler 로 데이터 셋 변환. 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.mean())
      print('\nfeature 들의 분산 값')
      print(iris_df_scaled.var())
        
      iris_df_scaled
    
    • 모든 컬럼 값의 평균이 0에 아주 가까운 값으로, 분산은 1에 아주 가까운 값으로 변환됐음을 알 수 있다.
  • MinMaxScaler
    • 정규화를 지원하는 클래스이며 0과 1사이의 값으로 정규화 (음수값의 경우 -1과 1사이의 값으로 변환)
    • 데이터의 분포가 정규분포가 아니라고 판단되는 경우 사용됨
      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
    
  • Scaler를 이용하여 학습 데이터와 테스트 데이터에 fit(), transform(), fit_transform() 적용 시 유의사항
    • Scaler 객체를 이용해 학습 데이터 세트로 fit()과 transform()을 적용하면 테스트 데이터 세트로는 다시 fit()을 수행하지 않고 바로 transform()을 수행해야함
    • 즉 학습 데이터로 fit()이 적용된 스케일링 기준 정보를 그대로 테스트 데이터에 적용해야한다.
    • 그렇지 않고 테스트 데이터로 다시 새로운 스케일링 기준 정보를 만들게 되면 → 학습 데이터와 테스트 데이터의 스케일링 기준 정보가 서로 달라지기 때문에 → 올바른 예측 결과를 도출하지 못할 수 있다.
from sklearn.preprocessing import MinMaxScaler
import numpy as np

# 학습 데이터는 0 부터 10까지, 테스트 데이터는 0 부터 5까지 값을 가지는 데이터 세트로 생성
# Scaler클래스의 fit(), transform()은 2차원 이상 데이터만 가능하므로 reshape(-1, 1)로 차원 변경
train_array = np.arange(0, 11).reshape(-1, 1)
test_array =  np.arange(0, 6).reshape(-1, 1)
print('train_array')
print(train_array)
print('test_array')
print(test_array)
# 최소값 0, 최대값 1로 변환하는 MinMaxScaler객체 생성
scaler = MinMaxScaler()
# fit()하게 되면 train_array 데이터의 최소값이 0, 최대값이 10으로 설정.  
scaler.fit(train_array)
# 1/10 scale로 train_array 데이터 변환함. 원본 10-> 1로 변환됨.
train_scaled = scaler.transform(train_array)
 
print('원본 train_array 데이터:', np.round(train_array.reshape(-1), 2))
# [ 0  1  2  3  4  5  6  7  8  9 10]
print('Scale된 train_array 데이터:', np.round(train_scaled.reshape(-1), 2))
# [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
# 앞에서 생성한 MinMaxScaler에 test_array를 fit()하게 되면 원본 데이터의 최소값이 0, 최대값이 5으로 설정됨 
scaler.fit(test_array)
# 1/5 scale로 test_array 데이터 변환함. 원본 5->1로 변환.  
test_scaled = scaler.transform(test_array)
# train_array 변환 출력
print('원본 test_array 데이터:', np.round(test_array.reshape(-1), 2))
# [0 1 2 3 4 5]
print('Scale된 test_array 데이터:', np.round(test_scaled.reshape(-1), 2))
# [0.  0.2 0.4 0.6 0.8 1. ]
scaler = MinMaxScaler()
scaler.fit(train_array)
train_scaled = scaler.transform(train_array)
print('원본 train_array 데이터:', np.round(train_array.reshape(-1), 2))
# [ 0  1  2  3  4  5  6  7  8  9 10]
print('Scale된 train_array 데이터:', np.round(train_scaled.reshape(-1), 2))
# [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]

# test_array에 Scale 변환을 할 때는 반드시 fit()을 다시 호출하지 않고 train에 사용되었던 스케일 그대로 가져와서 transform() 만으로 변환해야 함. 
test_scaled = scaler.transform(test_array)
print('\n원본 test_array 데이터:', np.round(test_array.reshape(-1), 2))
# [0 1 2 3 4 5]
print('Scale된 test_array 데이터:', np.round(test_scaled.reshape(-1), 2))
# [0.  0.1 0.2 0.3 0.4 0.5]