In [1]:
%matplotlib inline
from preamble import *
plt.rcParams['image.cmap'] = "gray"

3. Unsupervised Learning and Preprocessing

3.1 Types of unsupervised learning

  • 비지도 변환 (Unsupervised Transform)
    • 차원 축소 (Dimensionality Reduction)
  • 군집 (Clustering)

3.2 Challenges in unsupervised learning

  • 비지도 학습은 데이터 과학자가 데이터를 더 잘 이해하고 싶을 때 탐색적 분석 (EDA) 단계에 많이 활용
  • 지도 학습의 전처리 단뎨에서도 많이 활용

3.3 Preprocessing and Scaling

3.3.1 Different Kinds of Preprocessing

  • 스케일링 (Scaling):

    • 각 데이터 특성들에 대해 다음과 같은 선형 변환을 통해 전체 특성들의 통계적 분포(스케일)를 동일하게 맞추는 과정.
    • 각 특성(Column단위)들의 통계치를 이용한 변환 수행
    • 종류

      • StandardScaler $$\dfrac{x_i – mean(x)}{stdev(x)}$$
        • 각 특성의 평균을 0, 분산을 1로 변경
        • 각 특성의 최소값 및 최대값을 제한하지 않음
        • 단점: Outlier (이상치)에 영향을 많이 받음

      • RobustScaler $$\dfrac{x_i – Q_2(x)}{Q_3(x) – Q_1(x)}$$
        • 중앙값(median, $Q_2(x)$)과 IQR(Inter-Quartile Range, $Q_3(x) – Q_1(x)$)을 이용해서 척도를 표준화하는 방법
        • median 값이 0이 되도록 함
        • 전체 특성 값과 매우 동떨어진 특성 값에 영향을 받지 않음

      • MinMaxScaler $$\dfrac{x_i – min(x)}{max(x) – min(x)}$$
        • 모든 특성이 정확하게 0과 1사이에 위치하도록 데이터를 변경
        • 2차원 데이터인 경우 모든 데이터가 x축의 0과 1사이, y축의 0과 1사이 사각 영역에 위치하게 됨
    • 참고

  • 정규화 (Nomalizer)

    • 각 특성 벡터의 유클리디안 길이가 1이 되도록 데이터 포인트를 조정
    • 즉, 2차원 데이터인 경우 지름이 1인 원에 데이터 포인트를 투영하고 3차원 데이터인 경우 지름이 1인 구에 데이터 포인트를 투영
    • 각 데이터(Row단위)들의 통계치를 이용한 변환 수행
    • 각 데이터가 서로 다른 비율로 스케일이 조정됨
    • 특성 벡터의 길이는 무관하고 데이터의 방향(각도)만이 중요할때 많이 사용
    • 종류
      • l1
      • l2 (기본)
      • max
In [2]:
mglearn.plots.plot_scaling()

3.3.2 Applying Data Transformations

In [3]:
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(y_train.shape)
print()
print(X_test.shape)
print(y_test.shape)
(426, 30)
(426,)

(143, 30)
(143,)
In [4]:
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
  • 스케일링을 수행할 scaler에게 fit 함수를 호출함. 이때 훈련 데이터만 넘겨줌
In [5]:
scaler.fit(X_train)
Out[5]:
MinMaxScaler(copy=True, feature_range=(0, 1))
  • 실제로 훈련 데이터의 스케일을 조정하려면 scaler의 transform 메소드를 호출함.
In [6]:
# transform data
X_train_scaled = scaler.transform(X_train)

# print dataset properties before and after scaling
print("transformed shape: {}".format(X_train_scaled.shape))

# axis=0 --> 426개의 데이터들에 대해 동일한 Colume에 속한 각 특성값들에 대해 MinMaxScaling을 수행함
print("per-feature minimum before scaling:\n {}".format(X_train.min(axis=0))) 
print("per-feature maximum before scaling:\n {}".format(X_train.max(axis=0)))
print("per-feature minimum after scaling:\n {}".format(X_train_scaled.min(axis=0)))
print("per-feature maximum after scaling:\n {}".format(X_train_scaled.max(axis=0)))
transformed shape: (426, 30)
per-feature minimum before scaling:
 [   6.981    9.71    43.79   143.5      0.053    0.019    0.       0.
    0.106    0.05     0.115    0.36     0.757    6.802    0.002    0.002
    0.       0.       0.01     0.001    7.93    12.02    50.41   185.2
    0.071    0.027    0.       0.       0.157    0.055]
per-feature maximum before scaling:
 [   28.11     39.28    188.5    2501.        0.163     0.287     0.427
     0.201     0.304     0.096     2.873     4.885    21.98    542.2
     0.031     0.135     0.396     0.053     0.061     0.03     36.04
    49.54    251.2    4254.        0.223     0.938     1.17      0.291
     0.577     0.149]
per-feature minimum after scaling:
 [ 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.]
per-feature maximum after scaling:
 [ 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.]
  • 테스트 데이터도 변환해줌
    • [주의] 테스트 데이터 변환시에 항상 훈련 데이터들의 통계치만을 사용
    • 즉, 테스트 데이터에 대해 다음과 같은 공식을 통하여 변환 $$\dfrac{x_{test_{ij}} – x_{train_{min_j}}}{x_{train_{max_j}} – x_{train_{min_j}}}$$
    • 위 식에서 i는 각 데이터 인덱스, j는 각 데이터들에 대한 특성 인덱스. 즉, min_j와 max_j는 동일한 j번째 특성들 전체에 대한 최소 및 최대값을 의미
In [7]:
# transform test data
X_test_scaled = scaler.transform(X_test)

# print test data properties after scaling
print("per-feature minimum after scaling:\n{}".format(X_test_scaled.min(axis=0)))
print("per-feature maximum after scaling:\n{}".format(X_test_scaled.max(axis=0)))
per-feature minimum after scaling:
[ 0.034  0.023  0.031  0.011  0.141  0.044  0.     0.     0.154 -0.006
 -0.001  0.006  0.004  0.001  0.039  0.011  0.     0.    -0.032  0.007
  0.027  0.058  0.02   0.009  0.109  0.026  0.     0.    -0.    -0.002]
per-feature maximum after scaling:
[ 0.958  0.815  0.956  0.894  0.811  1.22   0.88   0.933  0.932  1.037
  0.427  0.498  0.441  0.284  0.487  0.739  0.767  0.629  1.337  0.391
  0.896  0.793  0.849  0.745  0.915  1.132  1.07   0.924  1.205  1.631]

3.3.3 Scaling training and test data the same way

In [8]:
from sklearn.datasets import make_blobs
# make synthetic data
X, _ = make_blobs(n_samples=50, centers=5, random_state=4, cluster_std=2)
# split it into training and test sets
X_train, X_test = train_test_split(X, random_state=5, test_size=.1)

# plot the training and test sets
fig, axes = plt.subplots(1, 3, figsize=(13, 4))
axes[0].scatter(X_train[:, 0], X_train[:, 1], c=mglearn.cm2(0), label="Training set", s=60)
axes[0].scatter(X_test[:, 0], X_test[:, 1], marker='^', c=mglearn.cm2(1), label="Test set", s=60)
axes[0].legend(loc='upper left')
axes[0].set_title("Original Data")

# scale the data using MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

# visualize the properly scaled data
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")

# rescale the test set separately
# so test set min is 0 and test set max is 1
# DO NOT DO THIS! For illustration purposes only.
test_scaler = MinMaxScaler()
test_scaler.fit(X_test)
X_test_scaled_badly = test_scaler.transform(X_test)

# visualize wrongly scaled data
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()
  • 위에서 첫번째 그래프와 두번째 그래프는 축의 눈금이 변한 것을 제외하면 동일함

[단축 메소드 사용]

  • fit --> transform
  • fit_transform
In [9]:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# calling fit and transform in sequence (using method chaining)
X_scaled = scaler.fit(X_train).transform(X_train)
# same result, but more efficient computation
X_scaled_d = scaler.fit_transform(X_train)

3.3.4 The effect of preprocessing on supervised learning

  • C: 규제의 강도를 결정하는 매개변수
    • 높은 C 값: 규제의 감소 --> 훈련 세트에 최대로 맞춤
    • 낮은 C 값: 규제의 증대 --> 계수 백터(w)를 최대로 0에 가깝게 만듦
In [10]:
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(C=100)
svm.fit(X_train, y_train)
print("Test set accuracy: {:.2f}".format(svm.score(X_test, y_test)))
Test set accuracy: 0.63
  • MinMaxScaler 사용
In [11]:
# preprocessing using 0-1 scaling
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

# learning an SVM on the scaled training data
svm.fit(X_train_scaled, y_train)

# scoring on the scaled test set
print("Scaled test set accuracy: {:.2f}".format(svm.score(X_test_scaled, y_test)))
Scaled test set accuracy: 0.97
  • StandardScaler 사용
In [12]:
# preprocessing using zero mean and unit variance scaling
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)

# learning an SVM on the scaled training data
svm.fit(X_train_scaled, y_train)

# scoring on the scaled test set
print("SVM test accuracy: {:.2f}".format(svm.score(X_test_scaled, y_test)))
SVM test accuracy: 0.96

3.4 Dimensionality Reduction, Feature Extraction and Manifold Learning

  • Principal Component Analysis (PCA)
    • 가장 간단하고 흔히 사용하는 대표적인 기법
  • Non-negative matrix factorization (NMF)
    • 특성 추출에 널리 사용
  • t-distributed stochastic neighbor embedding (t-SNE)
    • 2차원 산점도를 이용해 시각화 용도로 많이 활용

3.4.1 Principal Component Analysis (PCA)

In [13]:
mglearn.plots.plot_pca_illustration()
  • 그래프 1
    • 성분 1: 원본 데이터에서 분산이 가장 큰 벡터 (원본 데이터에서 가장 많은 정보를 담고 있음)
    • 성분 2: 성분 1 벡터와 직각인 방향 중 분간이 가장 큰 벡터
      • 2차원에서는 성분 1 벡터와 직각인 벡터가 1개만 존재
      • 고차원에서는 여러개의 직각 벡터 존재 가능
    • 위와 같은 방법을 거쳐 찾은 두 개의 성분 1, 2를 주성분(Principal Component)라고 함
  • 그래프 2
    • 주성분 1과 주성분 2를 각각 x축과 y축에 나란하도록 회전한 그림
    • 회전하기 전에 각 특성값에서 평균을 빼서 중심을 원점에 맞추었음
  • 그래프 3
    • 주성분 1만 남기는 차원 축소 (Dimensionality Reduction) 수행 결과
    • 2차원 원본 데이터가 1차원 데이터로 변환됨
  • 그래프 4
    • 주성분 1 벡터를 원래의 특성 공간으로 다시 옮김
    • 최종적으로 원본 데이터에서 노이즈를 제거하고 좀 더 나은 데이터 시각화 제공
In [14]:
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a)
[[1 2 3]
 [4 5 6]]
In [15]:
v = np.cov(a)
print(v)
[[ 1.  1.]
 [ 1.  1.]]
In [16]:
from numpy import linalg as la
e = la.eig(v)
print(e[0])
print(e[1])
[ 2.  0.]
[[ 0.707 -0.707]
 [ 0.707  0.707]]
Applying PCA to the cancer dataset for visualization
  • 유방함 데이터의 특성값의 개수는 30개
  • 30개의 특성들에 대한 산점도 그래프를 그리면 총 435개의 산점도가 산출됨
    • $_{30}C_2 = \displaystyle \frac{30!}{2!28!} = \displaystyle \frac{30 \times 29}{2}=15 \times 29 = 435$
  • 이렇게 데이터 특성 개수가 많을 때 (데이터 차원이 높을때) 보다 더 쉽게 특성 데이터들을 시각화 하는 방법
    • 악성(Malignant)와 양성(Benign)에 대해 30개 특성의 히스토그램을 그리는 것
In [17]:
print("cancer.data.shape: {}".format(cancer.data.shape))

fig, axes = plt.subplots(15, 2, figsize=(10, 20))
malignant = cancer.data[cancer.target == 0]
benign = cancer.data[cancer.target == 1]

ax = axes.ravel()

for i in range(30):
    _, bins = np.histogram(cancer.data[:, i], bins=50)
    ax[i].hist(malignant[:, i], bins=bins, color=mglearn.cm3(0), alpha=.5)
    ax[i].hist(benign[:, i], bins=bins, color=mglearn.cm3(2), alpha=.5)
    ax[i].set_title(cancer.feature_names[i])
    ax[i].set_yticks(())
ax[0].set_xlabel("Feature magnitude")
ax[0].set_ylabel("Frequency")
ax[0].legend(["malignant", "benign"], loc="best")
fig.tight_layout()
cancer.data.shape: (569, 30)