Post

[파이썬 라이브러리를 활용한 머신러닝] 비지도학습 및 데이터 전처리(1)

파이썬 라이브러리를 활용한 머신러닝 책 내용 정리 포스트

[파이썬 라이브러리를 활용한 머신러닝] 비지도학습 및 데이터 전처리(1)

비지도 학습(unsuperivsed learning 이란?

  • 알고 있는 출력값이나 정보 없이 학습 알고리즘을 자르쳐야 하는 모든 종류의 머신러닝
  • 입력 데이터만으로 데이터에서 지식을 추출해야함

3.1 비지도 학습의 종류


비지도 변환(unsupervised transformation): 새롭게 표현하여 사람이나 다른 머신러닝 알고리즘에 원래 데이터보다 쉽게 해석할 수 있도록 만드는 알고리즘

  • 차원 축소(dimensionality reduction): 특성이 많은 고차원 데이터를 특성의 술르 줄이면서 꼭 필요한 특징을 포함한 데이터로 표현하는 방법
    • 시각화를 위해 데이터셋을 2차원으로 변경하는 경우
  • 데이터를 구성하는 단위나 성분 찾기
    • 많은 텍스트 문서에서 주제를 추출하는 것

군집(clustering) : 데이터를 비슷한 것끼리 그룹으로 묶는 방법

  • 업로드한 사진을 분류할 때 같은 사람이 찍힌 사진을 같은 그룹으로 묶는 것

3.2 비지도 학습의 도전 과제


Goal: 알고리즘이 뭔가 유용한 것을 학습했는지 평가하는 것

보통 레이블이 없는 데이터에 적용하기 때문에 무엇이 올바른 출력인지 모르는 경우가 존재해 직접 확인하는 것이 유일한 방법일 떄가 많음

  • 탐색적 분석 단계에서 많이 사용함

비지도 학습은 지도 학습의 전처리 단계에서도 사용

  • 새롭게 표현된 데이터를 이용해 지도 학습의 정확도가 높아지기도 하며 메모리와 시간도 절약할 수 있음

스케일 조정은 비지도 방식으로 수행되는 전처리 기법이다.

1
2
3
4
5
6
7
8
9
10
pip install mglearn
pip install --upgrade joblib==1.1.0

import sklearn
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
import warnings
warnings.filterwarnings("ignore")

3.3 데이터 전처리와 스케일 조정


데이터의 특성 값을 조정 : 신경망이나 SVM 같은 데이터의 스케일에 민감하게 반응하는 알고리즘을 사용하는 경우

1
2
# 데이터셋의 스케일을 조정하거나 전처리하는 방법
mglearn.plots.plot_scaling()

3.3.1 여러 가지 전처리 방법

  • StandardScaler각 특성의 평균을 0, 분산을 1로 변경하여 모든 특성이 같은 크기를 가지게 하는 방법으로 특성의 최솟값과 최대값 크기를 제한하지 않는다.
  • RobustScaler은 같은 스케일을 갖게 된다는 통계적 측면에서 StandardScaler과 비슷하지만 평균과 분산 대신 중간 값(median)과 사분위 값(quartile)을 사용하며 전체 데이터와 아주 동떨어진 데이터 포인트(이상치) 영향을 주지 않는다.

이상치(outlier): 보통 관측된 데이터의 범위에서 많이 벗어난 데이터

  • MinMaxScaler은 정확하게 0과 1 사이에 위치하며 2차원 데이터셋인 경우, 모든 데이터가 x축의 0과 1, y축의 0과 1사이의 사각 영역에 존재한다.
  • Normalizer는 특성 벡터의 유클리드안 길이가 1이 되도록 데이터 포인트를 조정되며 지름이 1인 원에 데이터 포인트를 투영 즉, 각 데이터 포인트가 다른 비율로 스케일이 조정된다. 또한, 특성 벡터의 길이는 상관없고 데이터의 방향(또는 각도)만이 중요하다.

3.3.2 데이터 변환 적용하기

스케일을 조정하는 전처리 메서드들은 보통 지도 학습 알고리즘을 적용하기 전에 적용한다.

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
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
60
61
62
63
64
65
66
67
68
69
70
71
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()

X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state = 1)

print(X_train.shape)
print(X_test.shape)

"""
(426, 30)
(143, 30)
"""

from sklearn.preprocessing import MinMaxScaler

# 전처리
scaler = MinMaxScaler()
scaler.fit(X_train)

# 데이터 변환(스케일이 조정된 데이터 저장)
X_train_scaled = scaler.transform(X_train)
# 스케일이 조정된 후 데이터셋의 속성을 출력
print("변횐된 후 크기:", X_train_scaled.shape)
print("스케일 조정 전 특성별 최소값:\n", X_train.min(axis = 0))
print("스케일 조정 전 특성별 최대값:\n", X_train.max(axis = 0))
print("스케일 조정 후 특성별 최소값:\n", X_train_scaled.min(axis = 0))
print("스케일 조정 후 특성별 최대값:\n", X_train_scaled.max(axis = 0))

"""
변횐된 후 크기: (426, 30)
스케일 조정 전 특성별 최소값:
 [6.981e+00 9.710e+00 4.379e+01 1.435e+02 5.263e-02 1.938e-02 0.000e+00
 0.000e+00 1.060e-01 5.024e-02 1.153e-01 3.602e-01 7.570e-01 6.802e+00
 1.713e-03 2.252e-03 0.000e+00 0.000e+00 9.539e-03 8.948e-04 7.930e+00
 1.202e+01 5.041e+01 1.852e+02 7.117e-02 2.729e-02 0.000e+00 0.000e+00
 1.566e-01 5.521e-02]
스케일 조정 전 특성별 최대값:
 [2.811e+01 3.928e+01 1.885e+02 2.501e+03 1.634e-01 2.867e-01 4.268e-01
 2.012e-01 3.040e-01 9.575e-02 2.873e+00 4.885e+00 2.198e+01 5.422e+02
 3.113e-02 1.354e-01 3.960e-01 5.279e-02 6.146e-02 2.984e-02 3.604e+01
 4.954e+01 2.512e+02 4.254e+03 2.226e-01 9.379e-01 1.170e+00 2.910e-01
 5.774e-01 1.486e-01]
스케일 조정 후 특성별 최소값:
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0.]
스케일 조정 후 특성별 최대값:
 [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
 1. 1. 1. 1. 1. 1.]
"""

# 테스트 데이터 변환
X_test_scaled = scaler.transform(X_test)
# 스케일이 조정된 후 테스트 데이터의 속성을 출력
print("스케일 조정 후 특성별 최소값:\n", X_test_scaled.min(axis = 0))
print("스케일 조정 후 특성별 최대값:\n", X_test_scaled.max(axis = 0))

"""
스케일 조정 후 특성별 최소값:
 [ 0.0336031   0.0226581   0.03144219  0.01141039  0.14128374  0.04406704
  0.          0.          0.1540404  -0.00615249 -0.00137796  0.00594501
  0.00430665  0.00079567  0.03919502  0.0112206   0.          0.
 -0.03191387  0.00664013  0.02660975  0.05810235  0.02031974  0.00943767
  0.1094235   0.02637792  0.          0.         -0.00023764 -0.00182032]
스케일 조정 후 특성별 최대값:
 [0.9578778  0.81501522 0.95577362 0.89353128 0.81132075 1.21958701
  0.87956888 0.9333996  0.93232323 1.0371347  0.42669616 0.49765736
  0.44117231 0.28371044 0.48703131 0.73863671 0.76717172 0.62928585
  1.33685792 0.39057253 0.89612238 0.79317697 0.84859804 0.74488793
  0.9154725  1.13188961 1.07008547 0.92371134 1.20532319 1.63068851]
"""
  • 전처리는 훈련 데이터로만 fit()하고, 테스트 데이터에는 transform()만 적용해야 한다.
    • 테스트 데이터는 훈련 기준으로 변환되므로, 0~1 범위를 벗어날 수도 있음.
  • 테스트에 fit()을 하면 데이터 누수가 발생해 모델 성능이 부정확하게 평가된다.

3.3.3 QuantileTransformer와 PowerTransformer

QuantileTransformer : 1,000개의 분위를 사용하여 데이터를 균등하게 분포시킴(scikit-learn 0.19.0 에 추가)

  • 이상치에 민감하지 않음
  • 전체 데이터를 0과 1사이로 압축
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.preprocessing import QuantileTransformer, StandardScaler, PowerTransformer

X, y = make_blobs(n_samples = 50, centers = 2, random_state = 4, cluster_std = 1)
X += 3

plt.scatter(X[:, 0], X[:, 1], c = y, s = 30, edgecolors = 'black')
plt.xlim(0, 16)
plt.xlabel('x0')
plt.ylim(0, 10)
plt.ylabel('x1')
plt.title("Original Data")
plt.show()

1
2
3
4
5
6
7
8
9
10
scaler = QuantileTransformer(n_quantiles = 50)
X_trans = scaler.fit_transform(X)

plt.scatter(X_trans[:,0], X_trans[:, 1], c = y, s = 30, edgecolors = 'black')
plt.xlim(0, 5)
plt.xlabel('x0')
plt.ylim(0, 5)
plt.ylabel('x1')
plt.title(type(scaler).__name__)
plt.show()

1
2
3
# 균등 분포
plt.hist(X_trans)
plt.show()

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
print(scaler.quantiles_.shape)

# (50, 2)

x = np.array([[0],[5],[8],[9],[10]])
print(np.percentile(x[:, 0], [0,25, 50, 75, 100]))

# [ 0.  5.  8.  9. 10.]

x_trans = QuantileTransformer(n_quantiles = 5).fit_transform(x)
print(np.percentile(x_trans[:, 0], [0, 25, 50, 75, 100]))

# [0.   0.25 0.5  0.75 1.  ]

# output_distribution : normal 균등 분포 --> 정규 분포
scaler = QuantileTransformer(output_distribution = 'normal', n_quantiles = 50)
X_trans = scaler.fit_transform(X)

plt.scatter(X_trans[:, 0], X_trans[:, 1], c = y, s = 30, edgecolors = 'black')
plt.xlim(-5, 5)
plt.xlabel('x0')
plt.ylim(-5, 5)
plt.ylabel('x1')
plt.title(type(scaler).__name__)
plt.show()

PowerTransformer : 데이터의 특성별롤 정규분포 형태에 가깝도록 변환(scikit-learn 0.20.0 버전에 추가)

  • method 매개변수 : yeo-johnson, box-cox
  • 기본값 : yeo-johnson
1
2
3
plt.hist(X)
plt.title('Original Data')
plt.show()

1
2
3
4
X_trans = QuantileTransformer(output_distribution = 'normal', n_quantiles = 50).fit_transform(X)
plt.hist(X_trans)
plt.title('QuantileTransformer')
plt.show()

1
2
3
4
X_trans = StandardScaler().fit_transform(X)
plt.hist(X_trans)
plt.title('StandardScaler')
plt.show()

1
2
3
4
X_trans = PowerTransformer(method = 'box-cox').fit_transform(X)
plt.hist(X_trans)
plt.title('PowerTransformer box-cox')
plt.show()

1
2
3
4
X_trans = PowerTransformer(method = 'yeo-johnson').fit_transform(X)
plt.hist(X_trans)
plt.title('PowerTransformer yeo-johnson')
plt.show()

3.3.4 훈련 데이터와 테스트 데이터의 스케일을 같은 방법으로 조정하기

지도 학습 모델에서 테스트 세트를 사용하려면 훈련 세트와 테스트 세트에 같은 변환을 적용해야한다.

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
32
33
34
35
36
37
38
39
from sklearn.datasets import make_blobs
# 인위적인 데이터셋 생성
X, _ = make_blobs(n_samples = 50, centers = 5, random_state = 4, cluster_std = 2)
# 훈련 세트와 테스트 세트로 나누기
X_train, X_test = train_test_split(X, random_state = 5, test_size = .1)
# 훈련 세트와 테스트 세트의 산점도 그리기
fig, axes = plt.subplots(1, 3, figsize = (13, 4))
axes[0].scatter(X_train[:, 0], X_train[:, 1], c = mglearn.cm2.colors[0], label = "Training set", s = 60)
axes[0].scatter(X_test[:, 0], X_test[:, 1], marker = '^', c = mglearn.cm2.colors[1], label = "Test set", s = 60)
axes[0].legend(loc = 'upper left')
axes[0].set_title("Original data")

# MinMaxScaler를 사용해 스케일을 조정
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 스케일이 조정된 데이터의 산점도를 그림
axes[1].scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=mglearn.cm2(0),label="Training set", s=60)
axes[1].scatter(X_test_scaled[:, 0], X_test_scaled[:, 1], marker='^', c=mglearn.cm2(1), label="Test set", s=60)
axes[1].set_title("Scaled Data")

# 테스트 세트의 스케일을 따로 조정
# 테스트 세트의 최솟값은 0, 최댓값은 1
# 예제를 위한 것, 잘못 사용된 예제
test_scaler = MinMaxScaler()
test_scaler.fit(X_test)
X_test_scaled_badly = test_scaler.transform(X_test)

# 잘못 조정된 데이터의 산점도를 그림
axes[2].scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=mglearn.cm2(0), label="training set", s=60)
axes[2].scatter(X_test_scaled_badly[:, 0], X_test_scaled_badly[:, 1], marker='^', c=mglearn.cm2(1), label="test set", s=60)
axes[2].set_title("Improperly Scaled Data")

for ax in axes:
    ax.set_xlabel("Feature 0")
    ax.set_ylabel("Feature 1")
fig.tight_layout()

  • 첫 번째 그래프 : 2차원 원본 그래프
  • 두 번째 그래프 : MinMaxScaler로 스케일을 조정한 그래프
    • 훈련 세트와 테스트 세트에 동일한 transform 메서드를 적용
    • 축의 눈금만 바뀌고 첫 번째 그래프와 동일
    • 모든 특성은 0과 1 사이에 놓여 있음
    • 테스트 데이터(삼각형)의 최솟값과 최댓값은 0과 1이 아님
  • 세 번쨰 그래프 : 스케일을 서로 다른 방식을 조정
    • 훈련 세트와 테스트 세트의 최솟값과 최댓값이 모두 0과 1임
    • 원본 데이터와 매우 다름
    • 배열이 매우 뒤죽박죽되었음
1
2
3
4
5
6
7
# 단축 메서트와 효율적인 방법
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 메소드 체이닝(chaining)을 사용하여 fit과 transform을 연달아 호출
X_scaled = scaler.fit(X_train).transform(X_train)
# 위와 동일하지만 더 효율적
X_scaled_d = scaler.fit_transform(X_train) # 모든 모델에서 효율적 X

3.3.5 지도 학습에서 데이터 전처리 효과

SVC을 이용하여 원래 데이터와 MinMaxScaler, StandScaler 결과를 비교하여 전처리 효과를 확인할 수 있다.

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
32
33
34
35
36
from sklearn.svm import SVC
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state = 0)

svm = SVC(gamma = 'auto')
svm.fit(X_train, y_train)
print("Test set accuracy: {:.2f}".format(svm.score(X_test, y_test)))

# Test set accuracy: 0.63

# 0-1 사이로 스케일 조정
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 조정된 데이터로 SVM 학습
svm.fit(X_train_scaled, y_train)

# 스케일 조정된 테스트 세트의 정확도
print("Scaled Test set accuracy: {:.2f}".format(svm.score(X_test_scaled, y_test)))

# Scaled Test set accuracy: 0.95

# 평균 0, 분산 1을 갖도록 스케일 조정
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 조정된 데이터로 SVM 학습
svm.fit(X_train_scaled, y_train)
# 스케일 조정된 테스트 세트의 정확도
print("SVM test accuracy: {:.2f}".format(svm.score(X_test_scaled, y_test)))

# SVM test accuracy: 0.97
This post is licensed under CC BY 4.0 by the author.