221011 Day31

유예지·2022년 10월 11일

1007 복습

1

>>> from sklearn.preprocessing import LabelEncoder
	import pandas as pd
	import numpy as np
    
>>> arr = np.array(['TV', '냉장고','전자레인지','컴퓨터','선풍기','선풍기','믹서','믹서'] * 10)
	arr
    
>>> encoder = LabelEncoder()
    encoder.fit(arr)    #fit : 범위를 잡아주는 정도
    labels = encoder.fit_transform(arr)
    labels    

2

>>> from sklearn.datasets import load_iris
>>> iris = load_iris()
	iris
    
>>> iris_data = iris['data']
	iris_label = iris['target']
    
>>> iris_dt = pd.DataFrame(data = iris_data, columns = iris['feature_names'])
	iris_dt
    
>>> iris_dt['label'] = iris['target']
	iris_dt    
  • label 칼럼의 숫자를 문자로(target_names로) 바꾸기
>>> iris.keys()
>>> iris.target_names

#내가 생각한 방법
>>> iris_dt['label'] = np.where(iris_dt['label'] == 0, 'setosa',
                                np.where(iris_dt['label'] == 1, 'versicolor', 'virginica'))
    iris_dt
    
#다른 방법 - 일대일 대응
>>> label_mapping = {0 : 'setosa', 1 : 'versicolor', 2 : 'virginica'}

    iris_dt['label'].map(label_mapping)
    iris_dt['label'] = iris_dt['label'].map(label_mapping)    

3

>>> d = {'col1': [1, 11], 'col2': [2, 22], 'col3': [3, 33]}
    df_dict = pd.DataFrame(d) 
    df_dict
    
#df를 list로
>>> df_dict.values.tolist()    

#내가 생각한 방법
>>> list(df_dict['col1']) + list(df_dict['col2']) + list(df_dict['col3'])

>>> df_dict.values.flatten().tolist()
>>> list(df_dict.values.flatten())

4

>>> from sklearn.tree import DecisionTreeClassifier
    from sklearn.metrics import accuracy_score
    from sklearn.model_selection import train_test_split
    
#7:3 으로 나누자
>>> X_train, X_test, y_trian, y_test = train_test_split(iris_data, iris_label, test_size = 0.3)    

(3) Model Selection 모듈

② 교차 검증 (Cross Validation)

참고 자료 : http://naver.me/xFrvoUdJ

※ 학습 데이터 / 검증 데이터 / 테스트 데이터
데이터를 크게 training set, validation set, test set 으로 나눌 수 있다

⊙학습 데이터(training set)
원래 주어진 데이터 -> 모형 수립용으로 사용

⊙검증 데이터(validation set)
모형 성능을 개선하는 데 사용, 모의 test set 이라 생각하면 된다

⊙테스트 데이터(test set)
모형의 성능(정확도)를 평가 -> 모형 적합과 모형 선택이 끝난 후 최종 모형의 오류를 측정

# 파일부르기
iris = load_iris()

# data(정보)와 target(매치대상) 저장
iris_data = iris.data
iris_label = iris.target

# 학습과 테스트 구분
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_label)
                                     
# 의사결정 트리 
dt_clf = DecisionTreeClassifier()      
    
dt_clf.fit(X_train, y_train)
y_pred = dt_clf.predict(X_test)      # 학습실행, 예측

accuracy_score(y_test, y_pred)

고정된 학습 데이터와 테스트 데이터로 평가를 하다 보면 고정된 테스트 데이터에만 최적의 성능을 발휘할 수 있도록 편향되게 모델을 유도하는 경우가 생기게 된다
결국 해당 테스트 데이터에만 과적합(Overfitting)되는 학습 모델이 만들어져 다른 테스트용 데이터가 들어올 경우에는 성능이 저하되어버린다

교차 검증은 이러한 데이터 편중을 막기 위해 별도의 여러 세트로 구성된 학습 데이터 세트와 검증 데이터 세트에서 학습과 평가를 수행하는 것이다

대부분의 머신러닝 모델의 성능 평가는 '교차 검증' 기반으로 1차 평가를 한 뒤에, 최종적으로 테스트 데이터 세트에 적용해 평가한다
머신러닝에 사용되는 데이터 세트를 세분화해서 '학습, 검증 / 테스트' 데이터 세트로 나눈다
즉 테스트 데이터 세트 외에 '검증 데이터 세트' 를 둬서 최종 평가 '이전에' 학습된 모델을 다양하게 평가하는데 사용하는 것이다

① 학습 데이터를 다시 분할하여 학습 데이터와 검증 데이터로 나눈다
(검증 데이터 -> 학습된 모델의 성능을 1차 평가)
② 모든 학습/검증 과정이 완료된 후, 테스트 데이터 세트를 통해 최종적으로 성능을 평가한다

  • K 폴드 교차 검증 K-fold CV
    원 데이터를 k개의 데이터로 등분한 후 k-1개는 학습용, 1개는 검증(테스트)용으로 지정하여 k번 학습과 성능 추정을 수행한 뒤, 이 K개의 평가로 평균 낸 결과를 가지고 예측 성능을 평가한다
    시간이 다소 오래걸리나, 과적합에 대한 우려가 적다
    k = 10 또는 20 이 많이 쓰인다
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.datasets import load_iris
import numpy as np
# 파일부르기
iris = load_iris()

# data(정보)와 target(매치대상) 저장
iris_data = iris.data
iris_label = iris.target

# 학습과 테스트 구분
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_label)
                                     
# 의사결정 트리 
dt_clf = DecisionTreeClassifier()      

#5개의 폴드 세트로 분리하는 KFold 객체 생성
kfold = KFold(n_splits = 5)    

#폴드 세트별 정확도를 담을 리스트 객체 생성
cv_accuracy = []
#KFold 객체의 split()를 호출하면 폴드 별 학습용, 검증용 테스트의 로우 인덱스를 반환한다 (중요)
for train_index, test_index in kfold.split(iris_data):
    #kfold.split()로 반환된 인덱스를 이용해 학습용, 검증용 테스트 데이터 추출
    X_train, X_test = iris_data[train_index], iris_data[test_index]
    y_train, y_test = iris_label[train_index], iris_label[test_index]
    
    #학습 및 예측
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    
    #정확도 측정
    accuracy = accuracy_score(y_test, pred)
    cv_accuracy.append(accuracy)
    
#개별 iteration별 정확도를 합하여 평균 정확도 계산
    print("개별 검증 정확도: ", cv_accuracy)
print('평균 검증 정확도: ', np.mean(cv_accuracy)) 
  • stratified K 폴드

불균형한 분포도를 가진 레이블 데이터 집합(특정 레이블 값이 특이하게 많거나 매우 적어서 값의 분포가 한쪽으로 치우치는)을 위한 K 폴드 방식

KFold로 분할된 레이블 데이터 세트가 전체 레이블 값의 분포도를 반영하지 못하는 문제를 해결해준다

원본 데이터의 레이블 분포를 먼저 고려한 뒤, 이 분포와 동일하게 학습과 검증 데이터 세트를 분배한다

150개의 데이터에 0번, 1번, 2번 클래스가 30개, 50개, 70개가 포함 되어있다고 가정하면, 5 fold로 나눈 테스트 set(한 세트당 30개 데이터)에는 0,1,2번 클래스가 각 6개, 10개, 14개가 포함되어야 이상적이다. (Stratified K-fold는 3:5:7의 비율로 추출)

StratifiedKFold는 레이블 데이터 분포도에 따라 학습/검증 데이터를 나누기 때문에 split() 메서드에 인자로 피처 데이터 세트뿐만 아니라 레이블 데이터 세트도 반드시 필요하다 -> split(X, y)

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold
iris = load_iris()
features = x = iris_data = iris.data
labels = y = iris_label = iris.target

dt_clf = DecisionTreeClassifier()

s_kfold = StratifiedKFold(n_splits = 3)
cv_accuacy = []

#StratifiedKFold의 split() 호출 시 반드시 레이블 데이터 세트도 추가 입력 필요
for train_index, test_index in s_kfold.split(features, labels):
    print(train_index)
    print(test_index)
    
    #split()으로 반환된 인덱스를 이용해 학습용, 검증용 테스트 데이터 추출
    X_train, X_test = features[train_index], features[test_index]
    y_train, y_test = labels[train_index], labels[test_index]
    
    #학습 및 예측
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    
    #정확도 측정
    acc = accuracy_score(y_test, pred)
    cv_accuracy.append(acc)
    print("개별정확도", acc)

print("정확도 모음: ", cv_accuracy) 

#평균 검증 정확도
np.mean(cv_accuracy).round(4)

stratified K 폴드의 경우 원본 데이터의 레이블 분포도 특성을 반영한 학습 및 검증 데이터 세트를 만들 수 있으므로, 왜곡된 레이블 데이터 세트에서는 반드시 stratified K 폴드를 이용해 교차 검증을 해야한다

  • 교차 검증을 보다 간편하게 -> cross_val_score()
    cross_val_score(estimator= , X= , y= , scoring= , cv= , ...)
    -estimator : 사이킷런의 분류 알고리즘 클래스인 Classifier (또는 회귀 알고리즘 클래스 Regressor)
    -X : 피처 데이터 세트
    -y : 레이블 데이터 세트
    -scoring : 예측 성능 평가 지표
    -cv : 교차 검증 폴드 수
from sklearn.model_selection import cross_val_score, cross_validate

iris = load_iris()
data = features = X = iris_data = iris.data
label = labels = y = iris_label = iris.target
dt_clf = DecisionTreeClassifier()

#성능 지표는 정확도(accuracy), 교차 검증 세트 3개
scores = cross_val_score(dt_clf, data, label, scoring = 'accuracy', cv=3)
scores

#평균 검증 정확도
np.mean(scores).round(4)

cross_val_score()는 cv로 지정된 횟수만큼 scoring 파라미터로 지정된 평가 지표로 평가 결과값을 배열로 반환한다

cross_val_score() API는 내부에서 Estimator를 학습(fit), 예측(predict), 평가(evaluation)시켜주므로 간단하게 교차 검증을 수행할 수 있다
붓꽃 데이터의 cross_val_score() 수행 결과와 StratifiedKFold 의 수행 결과를 비교해보면 각 교차 검증별 정확도와 평균 검증 정확도가 모두 동일함을 볼 수 있다
이는 cross_val_score() 가 내부적으로 StratifiedKFold 를 이용하기 때문이다

비슷한 API로 cross_validate() 가 있다
cross_val_score()는 단 하나의 평가 지표만 가능하지만 cross_validate()는 여러개의 평가 지표를 반환할 수 있다
보통은 cross_val_score() 하나로도 대부분의 경우에 적용할 수 있다

③ GridSearchCV

Grid search (격자 탐색) 은 모델 파라미터에 넣을 수 있는 값들을 순차적으로 입력한 뒤에 가장 높은 성능을 보이는 파라미터들을 찾는 탐색 방법을 말한다
Grid는 '격자'라는 뜻으로, 촘촘하게 파라미터를 입력하면서 테스트를 하는 방식이다

하이퍼 파라미터 (hyper parameter, 초매개변수)란
사전적 정의로는 모델을 생성할 때, 사용자가 직접 설정하는 변수를 말한다.
랜덤 포레스트 모델에서는 트리의 개수, 트리의 깊이는 몇까지 할 것인지 등,
딥러닝 모델에서는 layer의 개수, 에폭(학습횟수)의 수 등이 된다.
딥러닝에서 파라미터는 가중치이므로 기존의 파라미터를 하이퍼 파라미터 라고 한다.

Grid Search의 단점: 모든 하이퍼 파라미터를 일률적으로 한번씩 실행하므로 시간이 많이 걸린다

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV, train_test_split

#데이터를 로딩하고, 학습 데이터와 테스트 데이터 분리
iris = load_iris()

X_train, X_test, y_train, y_test = \
train_test_split(iris.data, iris.target, test_size = 0.2, random_state = 121)

dt_clf = DecisionTreeClassifier()

#파라미터를 딕셔너리 형태로 설정
grid_parameters = {'max_depth' : [1,2,3], 'min_samples_spilt' : [2,3]}
#3*2 = 6, 총 6회에 걸쳐 하이퍼 파라미터를 순차적으로 변경하면서 수행

GridSearchCV 객체의 메서드 fit()에 X_train, y_train 를 인자로 입력 ->
.fit(X_train, y_train) 수행 -> train을 cv에 입력된 수 만큼 분할,
grid_parameters로 정의된 하이퍼 파라미터('max_depth', 'min_samples_spilt')를 순차적으로 변경하면서 학습/평가 수행 -> 그 결과를 cv_results_ 속성에 기록(딕셔너리 형태)

#param_grid의 하이퍼 파라미터를 3개(cv=3)의 train, test로 나누어 테스트 수행
#refit = True 가 디폴트 값, True 이면 가장 좋은 파라미터 설정으로 재학습시킴
grid_tree = GridSearchCV(dt_clf, param_grid = grid_parameters, cv = 3, refit = True)

#GridSearchCV 객체의 메서드 fit()에 'X_train, y_train' 를 인자로 입력
#붓꽃 학습 데이터로 param_grid의 하이퍼 파라미터를 순차적으로 학습/평가
grid_tree.fit(X_train, y_train)

#GridSearchCV 결과 추출
grid_tree.cv_results_

#DataFrame 으로 변환
import pandas as pd
pd.DataFrame(grid_tree.cv_results_)
pd.DataFrame(grid_tree.cv_results_).iloc[:, 5:]

DataFrame을 통해 총 6개의 결과를 볼 수 있으며, 이는 하이퍼 파라미터 'max_depth', 'min_samples_spilt' 을 순차적으로 총 6번 변경하면서 학습 및 평가를 수행했음을 나타낸다
params 칼럼 : 수행할 때 마다 적용된 하이퍼 파라미터 값
rank_test_score 칼럼 : 하이퍼 파라미터 별 성능이 좋은 score 순위 (1일 때의 파라미터가 최적의 하이퍼 파라미터임을 의미)
split_test_score 칼럼 : cv가 3인 경우, 즉 3개의 폴딩 세트에서 각각 테스트한 성능 수치
mean_test_score 칼럼 : split_test_score 칼럼 값들의 평균값

#cv_results_의 rank_test_score 값이 1일 때
#최적 파라미터
grid_tree.best_params_

#최고 정확도
grid_tree.best_score_

pred = grid_tree.predict(X_test)
pred
accuracy_score(y_test, pred)

(4) 데이터 전처리

① 데이터 인코딩

  • 원-핫 인코딩 (One-Hot Encoding)
    여러개의 속성 중 단 한개의 속성만 '1'로 표시한다

레이블 인코딩으로 문자열 값을 숫자형 카테고리 값으로 변환하면 숫자가 가진 크고 작음에 대한 특성으로 인해 몇몇 ML 알고리즘에는 예측 성능이 떨어지는 경우가 발생할 수 있다
숫자 1로 변환된 것보다 숫자 2로 변환된 것에 가중치가 더 부여되거나 더 중요하게 인식하는 가능성이 생길 수 있는 것이다
원-핫 인코딩은 레이블 인코딩의 이러한 문제점을 해결하기 위해 사용된다

원-핫 인코딩은 행 형태로 되어있는 피처의 고유 값을 '열 형태'로 차원을 변환한 뒤, 고유 값에 해당하는 칼럼에만 '1'을 표시하고, 나머지 칼럼에는 '0'을 표시하는 방식이다

원-핫 인코딩은 사이킷런에서 OneHotEncoder 클래스로 변환이 가능하며,
입력값으로 '2차원 데이터' 가 필요하고
OneHotEncoder 로 변환한 값이 희소 행렬(Sparse Matrix) 형태이므로 이를 다시 'toarray()' 메서드를 이용해 밀집 행렬(Dense Matrix)로 변환해야한다

>>> from sklearn.preprocessing import OneHotEncoder

>>> items = ['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']
#원본 데이터는 8개, 고유 값은 6개
	    
#2차원 arrary로 변환 -> 데이터가 가로가 아닌 세로로
>>> items = np.array(items).reshape(-1, 1)

#원-핫 인코딩 적용
>>>	encoder = OneHotEncoder()
    encoder.fit(items)    #fit : 행렬의 전체적인 틀을 잡는다
    encoder.transform(items)

>>> one_hot_labels = encoder.transform(items)

#희소 행렬을 밀집 행렬로 변환
>>>	one_hot_labels.toarray()

>>> one_hot_labels.shape

8개의 레코드와 1개의 칼럼을 가진 원본 데이터가 8개의 레코드와 6개의 칼럼을 가진 데이터로 변환되었음을 볼 수 있다

pandas 에서는 'get_dummies()' 를 이용하여 더 쉽게 원-핫 인코딩을 할 수 있다
숫자형 값으로의 변환 없이도 바로 변환이 가능하다

>>> items = ['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']
	pd.get_dummies(items)

② 피처 스케일링과 정규화

피처 스케일링(feature scaling) : 서로 다른 변수 값의 범위를 일정한 수준으로 맞추는 작업
대표적인 방법으로 표준화와 정규화가 있다

* 정규화 Normalization

서로 다른 피처의 크기를 모두 똑같은 단위로 변경하는 것
scale을 최소값이 0, 최대값이 1이 되도록 조정하는 것 (0과 1사이의 값이 되도록)
함수를 직접 작성하거나, MinMaxScaler 에 의해 Scaling 한다

from sklearn.datasets import load_iris
iris = load_iris()

iris_data = iris['data']
iris_df = pd.DataFrame(iris_data)
iris_df

iris_df.max()
iris_df.min()
  • 정규화 사용자 정의 함수 생성 (최소값은 '0'으로, 최대값은 '1'로)
def min_max(x):
    return (x - x.min()) / (x.max() - x.min())
    
iris_df.apply(min_max)

#확인
iris_df.apply(min_max).max()

#사용자 정의 함수를 lambda로
iris_df.apply(lambda x: (x - x.min()) / (x.max() - x.min()))
iris_df.apply(lambda x: (x - x.min()) / (x.max() - x.min())).min()
  • 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)
iris_scaled

#fit_transform() 으로 한번에 처리할 수도 있다
iris_scaled = scaler.fit_transform(iris_df)
iris_scaled

#transform() 시 스케일 변환된 데이터 세트가 array로 반횐되므로, 이를 DataFrame 으로 변환
iris_scaled_df = pd.DataFrame(data = iris_scaled, columns = iris.feature_names)
iris_scaled_df

iris_scaled_df.max()
iris_scaled_df.min()

* 표준화 Standardization

표준정규분포; 어떤 단위이든 상관없이 평균을 0, 표준편차(분산)를 1로 조정한다
데이터의 피처 각각이 평균이 0, 분산이 1인 가우시안 정규 분포를 가진 값으로 변환하는 것
평균을 기준으로 얼마나 떨어져 있는지를 나타내고, 2개 이상의 대상이 단위가 다를 때 대상 데이터를 같은 기준으로 볼 수 있게 한다
x - mu / sigma

#표준화 사용자 정의 함수
def standardize(x):
    return (x - x.mean()) / x.std()
    
iris_df
iris_df.apply(standardize)    #각각의 칼럼별로 표준화 된다    
  • StandardScaler 사용하기
from sklearn.preprocessing import StandardScaler

#StandardScaler 객체 생성
scaler = StandardScaler()

#StandardScaler로 데이터 세트 변환 -> fit() 과 transform() 호출
scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
iris_scaled

#transform() 시 스케일 변환된 데이터 세트가 array로 반횐되므로, 이를 DataFrame 으로 변환
iris_scaled_df = pd.DataFrame(data = iris_scaled, columns = iris.feature_names)
iris_scaled_df

iris_scaled_df.mean()
iris_scaled_df.std()

* 스케일링 변환 시 유의점

StandardScaler나 MinMaxScaler와 같은 Scaler 객체를 이용해 데이터의 스케일링 변환 시
fit(), transform(), fit_transform() 메서드를 이용한다

fit() -> 데이터 변환을 위한 기준 정보 설정 (ex. 데이터 세트의 최대/최소값 설정 등)
transform() -> 설정된 정보를 이용해 데이터 변환
fit_transform() -> fit과 transform을 한꺼번에 적용

Scaler 객체를 이용해 X_train에서 먼저 fit을 실행했다면 X_test에서는 fit은 실행하지 않고 transform 만 실행하면 된다
또는 가능하다면 train과 test로 분리하기 전에, 먼저 전체 데이터의 스케일링 변환을 적용한 뒤
train과 test로 분리하는 것이 좋다
이는 X_train과 X_test 따로 정규화시키면 X_train과 X_test의 스케일링 기준 정보가 서로 달라지기 때문에 올바른 예측 결과를 도출할 수 없기 때문이다 (fit을 각각 실행했기 때문에)

0개의 댓글