사이킷런과 머신러닝 2강

shshin·2021년 7월 16일
0
post-thumbnail

1 사이킷런 소개와 특징

사이킷런 (scikit-learn)은 파이썬 머신러닝 라이브러리 중 가장 많이 사용되는 라이브러리이다.

사이킷런의 특징

  • 파이썬 기반의 다른 머신러닝 패키지도 사이킷런 스타일의 API를 지향할 정도로 쉽고 가장 파이썬스러운 API를 제공한다.
  • 머신러닝을 위한 매우 다양한 알고리즘과 개발을 위한 편리한 프레임워크와 API를 제공한다.
  • 오랜 기간 실전 환경에서 검증됐으며, 많은 환경에서 사용되는 성숙한 라이브러리이다.
conda install scikit-learn

pip install scikit-learn

import sklearn

print(sklearn.__version__)

2 첫번째 머신러닝 만들어 보기 - 붓꽃 품종 예측하기

사이킷런을 통해 첫 번쨰로 만들어볼 머신러닝 모델은 붓꽃 데이터 세트로 붓꽃의 품종을 분류하는 것이다. 붓꽃 데이터 세트는 꽃잎의 길이와 너비, 꽃받침의 길이와 너비 피처를 기반으로 꽃의 품종을 예측하기 위한 것이다.

분류(Classification)은 대표적인 지도학습 방법 중 하나이다.

지도학습은 학습을 위한 다양한 피처와 분류 결정값인 레이블 데이터로 모델을 학습한 뒤, 별도의 테스트 데이터 세트에서 미지의 레이블을 예측한다.

즉, 지도학습이란 명확한 정답이 주어진 데이터를 먼저 학습한 뒤 미지의 정답을 예측하는 방식이다.

데이터 셋 로딩 후 DataFrame으로 변환을 해보자.

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.data

iris_label = iris.target

#iris target 값 : iris_label = iris.target
#iris target 명 : iris.target_names

iris_df = pd.DataFrame(data = iris_data, columns = iris.feature_names)
iris_df['label'] = iris.target
iris_df.head()

>>>

	sepal length (cm)	sepal width (cm)	petal length (cm)	petal width (cm)	label
0	5.1			3.5			1.4			0.2			0
1	4.9			3.0			1.4			0.2			0
2	4.7			3.2			1.3			0.2			0
3	4.6			3.1			1.5			0.2			0
4	5.0			3.6			1.4			0.2			0

피처는 sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) 가 있고, Target(결정값) 으로는 레이블 0,1,2 가 있다. 0이 Setosa품종, 1이 versicolor 품종, 2가 virginica이다.

테스트세트와 학습데이터로 나누기 위해 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)

X_train : 학습용 데이터 세트
X_test : 테스트용 데이터 세트
y_train : 학습용 레이블 데이터 세트
y_test : 테스트용 레이블 데이터 세트

머신러닝 분류 알고리즘의 하나인 의사 결정 트리를 이용해 학습과 예측을 수행해보자.

dt_clf = DecisionTreeClassifier(random_state = 11)

#train set fitting (학습하기)
dt_clf.fit(X_train, y_train)

#예측하기 (학습한 데이터가 아닌 test데이터로 예측)
pred = dt_clf.predict(X_test)

from sklearn.metrics import accuracy_score

#y_test(테스트 레이블:실제 값)과 prediction비교
print("예측 정확도 : %.2f" %accuracy_score(y_test,pred))

>>>
예측 정확도 : 0.93

정리하자면,

  1. 데이터 세트 분리 : train_test_split으로 테스트와 학습용 데이터를 분리시킨다.

  2. 모델 학습 : 학습 데이터를 기반으로 ML 알고리즘을 적용해 모델을 fit 시켜줍니다.

  3. 예측 수행 : 학습된 ML모델을 이용해 테스트 데이터의 분류 (즉, 붓꽃 종류)를 예측한다.

  4. 평가 : 이렇게 예측된 결과값과 테스트 데이터의 실제 결과값을 비교해 ML 모델 성능을 펴아한다. (위에서는 accuracy score가 사용됨)


3 사이킷런의 기반 프레임워크 익히기

3-1 Estimator 이해 및 fit(), predict() 메서드

사이킷런에서는 분류 알고리즘을 구현한 클래스를 Classifier로, 회귀 알고리즘을 구현한 클래스를 Regressor로 지칭한다. 이들을 합쳐서 Estimator 클래스라고 부른다.

지도학습의 모든 알고리즘을 구현한 클래스를 통칭해서 Estimator라고 부르는데, 이 클래스는 fit()과 predict()를 내부에서 구현하고 있다.

fit()으로 변환을 위하 ㄴ사전 구조를 맞추면 이후 입력 데이터의 차원 변환, 클러스터링, 피처 추출등의 실제 작업은 transform()으로 수행한다.

3-2 내장된 데이터 세트

  • data : 피처의 데이터 세트
  • target : 레이블 값
  • target_names : 개별 레이블의 이름
  • feature_names : 피처의 이름
keys = iris_data.keys()
print('붓꽃 데이터 세트의 키들 :', keys)

>>>
붓꽃 데이터 세트의 키들 : dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])

4 Model Selection 모듈 소개

4-1 교차 검증

과적합은 모델이 학습 데이터에만 과도하게 최적화되어, 실제 예측을 다른 데이터로 수행할 경우 예측 성능이 과도하게 떨어지는 것을 말한다. 이러한 문제점을 개선하기 위해 교차 검증을 이용해 더 다양한 학습과 평가를 수행한다.

K 폴드 교차 검증

k개의 데이터 폴드 세트를 만들어서 k번만큼 각 폴트 세트에 학습과 검증 평가를 반복적으로 수행하는 방법이다.

KFold 클래스를 이용해 붓꽃 데이터 세트를 교차 검증하고 예측 정확도를 알아보자.

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

kfold = KFold(n_splits=5)


n_iter = 0
cv_accuracy = []
for train_index, test_index in kfold.split(features):
    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)
    
    print('{0} 교차검증 정확도 : {1}' .format(n_iter, accuracy))
    cv_accuracy.append(accuracy)
    
print('\n 평균 검증 정확도 : ' , np.mean(cv_accuracy))

>>>
1 교차검증 정확도 : 1.0
2 교차검증 정확도 : 0.9667
3 교차검증 정확도 : 0.8667
4 교차검증 정확도 : 0.9333
5 교차검증 정확도 : 0.7333

 평균 검증 정확도 :  0.9

Stratified K 폴드

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

Stratified K 폴드는 K 폴드가 레이블 데이터 집합이 원본 데이터 집합의 레이블 분포를 학습 및 테스트 세트에 제대로 분배하지 못하는 경우의 문제를 해결해 준다.

K 폴드의 문제를 확인해보자.

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('학습레이블 데이터 분포: \n', label_train.value_counts())
    print('검증레이블 데이터 분포: \n', label_test.value_counts())
    
    
>>>

학습레이블 데이터 분포: 
 1    50
2    50
Name: label, dtype: int64
검증레이블 데이터 분포: 
 0    50
Name: label, dtype: int64
학습레이블 데이터 분포: 
 0    50
2    50
Name: label, dtype: int64
검증레이블 데이터 분포: 
 1    50
Name: label, dtype: int64
학습레이블 데이터 분포: 
 0    50
1    50
Name: label, dtype: int64
검증레이블 데이터 분포: 
 2    50
Name: label, dtype: int64

현재 학습 데이터가 골고루 분포되지 않아 제대로 학습이 되지 않고 있음을 알 수 있다.

이를 Stratified K 폴드로 해결하면,

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('학습레이블 데이터 분포: \n', label_train.value_counts())
    print('검증레이블 데이터 분포: \n', label_test.value_counts())
    
>>>

학습레이블 데이터 분포: 
 2    34
0    33
1    33
Name: label, dtype: int64
검증레이블 데이터 분포: 
 0    17
1    17
2    16
Name: label, dtype: int64
학습레이블 데이터 분포: 
 1    34
0    33
2    33
Name: label, dtype: int64
검증레이블 데이터 분포: 
 0    17
2    17
1    16
Name: label, dtype: int64
학습레이블 데이터 분포: 
 0    34
1    33
2    33
Name: label, dtype: int64
검증레이블 데이터 분포: 
 1    17
2    17
0    16
Name: label, dtype: int64

0,1,2 레이블이 학습과 검증 데이터에 골고루 분포되어있음을 알 수 있다.

교차 검증을 보다 간편하게 - cross_val_score()

estimator는 사이킷런의 분류 알고리즘 클래스인 Classifier 또는 회귀 알고리즘 클래스인 Regressor를 의미하고, X는 피처 데이터 세트, y는 레이블 세트, scoring은 예측 성능 평가 지표 기술, cv는 교차 검증 폴드 수를 의미한다.

from sklearn.model_selection import cross_val_score
data = iris.data
label = iris.target

dt_clf = DecisionTreeClassifier(random_state = 156)

#cv 는 교차검증 폴스 개수 
scores = cross_val_score(dt_clf, data, label, scoring = 'accuracy', cv =3)

print('교차 검증별 정확도:', np.round(scores,4))
print('평균 검증 정확도 : ', np.mean(scores))

>>>

교차 검증별 정확도: [0.98 0.94 0.98]
평균 검증 정확도 :  0.9666666666666667

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

사이킷런은 GridSearchCV API를 이용해 Classifier나 Regressor와 같은 알고리즘에 사용되는 하이퍼 파라미터를 순차적으로 입력하면서 편리하게 최적의 파라미터를 도출할 수 있는 방안을 제공한다.

예를들어 결정 트리 알고리즘의 여러 하이퍼 파라미터를 순차적으로 변경하면서 최고 성능을 가지는 파라미터 조합을 찾고자 한다면 다음과 같이 파라미터의 집합을 만들고 이를 순차적으로 적용하면서 최적화를 수행 할 수 있다.

from sklearn.model_selection import GridSearchCV

X_train, X_test, y_train, y_test = train_test_split(data, label, test_size=0.2, random_state = 42)

dt = DecisionTreeClassifier()

parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]}

grid_dt = GridSearchCV(dt, param_grid = parameters, cv =3, refit = True)

grid_dt.fit(X_train, y_train)

print(grid_dt.best_params_)

>>>

{'max_depth': 3, 'min_samples_split': 2}

반드시 fit 과정을 거쳐야한다!

GridSearchCV 클래스의 생성자로 들어가는 주요 파라미터는 다음과 같다.

  • estimator : classifier, regressor, pipeline이 사용된다
  • param_grid : key + 리스트 값을 가지는 딕셔너리가 주어진다.
  • scoring : 예측 성능을 측정할 평가 방법을 지정한다.
  • cv : 교차 검증을 위해 분할되는 학습/테스트 세트의 개수를 지정한다.
  • refit : 디폴트가 true이며 true로 생성 시 가장 최적의 하이퍼 파라미터를 찾은 뒤 입력된 estimator 객체를 해당 하이퍼 파라미터로 재학습시킨다.

5 데이터 전처리

데이터에 미리 처리해야할 기본 사항이 있다. 결손값, 즉 NaN, Null 같은 값은 허용되지 않는다. 이는 피처의 평균값 등으로 간단히 대처 가능하다. 또는 드롭하는 수도 있다.

5-1 데이터 인코딩

머신러닝을 대표하는 인코딩 방식은 레이블 인코딩 (Label encoding)과 원-핫 인코딩(One-Hot encoding)이 있다. 먼저 레이블 인코딩은 카테고리 피처를 코드형 숫자 값으로 변환하는 것이다. 예를들어 먹을 것이 사과 딸기 바나나 오렌지라면 각각을 1,2,3,4로 대응하는 것이다.

(1) Label Encoding

Label encoding은 LabelEncoder 클래스로 구현합니다.

from sklearn.preprocessing imort LabelEncoder

le = LabelEncoder()

items = ['apple','strawberry','strawberry','banana', 'orange','banana']

le.fit(items)

labels = le.transform(items)

print('인코딩 변환값:',labels)

>>>
인코딩 변환값 : [0,1,1,2,3,2]
  1. LabelEncoder를 객체로 생성한 후, fit() 과 transform()으로 레이블 인코딩 수행
  2. fit 으로 학습
  3. transform으로 각 피처를 코드형 숫자 값으로 변환

주의할 점

레이블 인코딩으로 인해 일괄적인 숫자 값으로 변환이 되면서 몇몇 ML 알고리즘에는 이를 적용할 경우 예측 성능이 떨어지는 경우가 발생한다. 이는 숫자 값의 크고 작음에 대한 특성이 작용하기 때문이다. 따라서 ML 알고리즘에서 가중치가 더 부여되거나 더 중요하게 인식할 가능성이 발생되기 때문에 선형 회귀와 같은 ML 알고리즘에는 적용하지 말아야 한다.

(2) One-Hot Encoding

피처 값의 유형에 따라 새로운 피처를 추가해 고유 값에 해당하는 칼럼에만 1을 표시하고 나머지 칼럼에는 0을 표시하는 방식이다.

[One-Hot과 LabelEncoding의 예시이다]

One-Hot Encoder는 변환하기 전에 모든 문자열 값이 숫자형 값으로 변환돼야 한다는 것이며, 두번째는 입력 값으로 2차원 데이터가 필요하다는 점이다.

판다스에는 원-핫 인코딩을 더 쉽게 지원하는 API가 있다. get_dummies() 를 이용하면 된다. 사이킷런의 OneHotEncoder와 다르게 문자열 카테고리 값을 숫자형으로 변환할 필요없이 바로 변환 가능하다.

import pandas as pd

df = pd.DataFrame({'item': ['apple','strawberry','strawberry','banana', 'orange','banana']})

pd.get_dummies(df)

5-2 피처 스케일링 정규화

서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업을 피처 스케일링 이라고 한다. 대표적인 방법으로 표준화 (Standardization)과 정규화(Normalization)이 있다.

(1) StandardScaler - 피처들의 평균을 0, 분산을 1로 변환

개별 피처를 평균이 0이고, 분산이 1인 값으로 변환해준다. 가우시안 정규 분포를 가질 수 있도록 데이터를 변환하는 것은 몇몇 알고리즘에서 매우 종요하다. 특히 사이킷런에서 구현한 RBF 커널을 이용하는 서포트 벡터 머신(Support Vector Machine)이나 선형 회귀(Linear Regression), 로지스틱 회귀(Logistic Regression)는 데이터가 가우시안 분포를 가지고 있다고 가정하고 구현됐기 때문에 사전에 표준화를 적용하는 것은 예측 성능 향상에 중요한 요소가 된다.

Standard Scaler 코드를 보자.

from sklearn.datasets import load_iris
import pandas as pd

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('feature들의 분산값')
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

#StandardScaler 이용

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()

sc.fit(iris_df)
iris_scaled = sc.transform(iris_df)

#scale처리하면 ndarray형태로 반환이 되어 -> DataFrame형태로 변환

iris_df_scaled = pd.DataFrame(data = iris_scaled, columns = iris.feature_names)

print('평균값\n', iris_df_scaled.mean())
print('분산값\n', iris_df_scaled.var())


>>>
평균값
 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
분산값
 sepal length (cm)    1.006711
sepal width (cm)     1.006711
petal length (cm)    1.006711
petal width (cm)     1.006711
dtype: float64


  1. StandardScaler 객체 형성
  2. fit()을 사용하여 데이터 전체를 학습
  3. transform()으로 scaled된 데이터를 다른 변수에 저장
  4. scale처리하면 ndarray형태로 반환이 되어 -> DataFrame형태로 변환

(2) MinMaxScaler

MinMaxScaler는 data값 자체를 0~1 사이로 변환한다. 데이터의 분포가 가우시안 분포가 아닐 경우 Min Max Scale을 적용해 볼 수 있다.

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()

scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)

#다시 ndarray에서 DataFrame으로 변환

iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
print('feature 최소값')
print(iris_df_scaled.min())
print('최대')
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
최대
sepal length (cm)    1.0
sepal width (cm)     1.0
petal length (cm)    1.0
petal width (cm)     1.0
dtype: float64

5-3 학습데이터와 테스트 데이터의 스케일링 변환 시 유의점

  1. 가능하다면 전체 데이터의 스케일링 변환을 적용한 뒤에 학습과 테스트 데이터로 분리
  2. 1이 여의치 않다면 테스트 데이터 변환시에는 fit()이나 fit_transform()을 적용하지 않고 학습 데이터로 이미 fit()된 Scaler 객체를 이용해 transform()으로 변환
profile
안녕하세요, 머신러닝/딥러닝 입문자입니다!

0개의 댓글