AI/Machine Learning

[Machine Learning] 차원 축소

byunghyun23 2023. 6. 1. 00:21

머신 러닝에 사용되는 데이터는 분명 우리가 얻고자 하는 결과에 중요한 부분과 중요하지 않은 부분이 있습니다.

여기서 중요하지 않은 부분을 노이즈(noise)라고 부르며, 이것을 제거하는 것이 중요합니다.

차원 축소(dimention reduction)는 노이즈 제거 방법 중 하나로, 많이 사용되고 있습니다.

 

실제로 경험에 의하면 이미지를 이용한 모델 설계 시 CNN(convolutional neural networks)보다 좋은 결과를 얻은 적도 있습니다.

차원 축소를 하는 이유는 차원의 저주(curse of dimentionality)를 해결할 수 있기 때문인데, 여기서 차원의 저주란 데이터의 차원이 커질수록 해당 차원을 표현하기 위한 데이터의 수가 기하급수적으로 커지는 것을 말합니다.

1차원의 데이터가 10개일 때, 이것을 2차원으로 표현하면 10 x 10 = 100이 됩니다.

이것은 차원의 공간을 적절하게 표현하지 못하여(노이즈가 섞여서) 오버피팅될 확률이 높습니다.

 

주성분 분석(principal component analysis)은 가장 유명한 차원 축소 방법입니다.

주성분 분석은 고윳값 분해 또는 특이값 분해를 이용한 방법입니다. 자세한 내용은 링크를 확인하시기 바랍니다.

여기서는 고윳값 분해로만 이야기 하겠습니다. 고윳값 분해후에 데이터를 d차원으로 줄이고 싶은 경우, 고윳값들의 순서대로 d개의 고윳값을 선정하고 그것을 벡터로 만들어 데이터 셋으로 사용합니다.

고윳값 분해 과정 중 공분산 행렬은 데이터 셋 각 피처(feature)의 분산을 확인할 수 있고, 이것은 상관 계수와 표준 편차를 파악할 수 있다는 것입니다.

또한 고유 벡터는 데이터가 어느 정도 흩어져 있는지 그 크기를 파악할 수 있습니다.

예를 들어 d가 2일 때, 아래와 같이 주 성분 2개로 데이터를 표현할 수도 있습니다.

 

즉, 차원 축소를 통해 얻은 주 성분과 원본 데이터를 이용하여 예측했을 때 그 결과가 크게 다르지 않다는 것입니다.

하지만 노이즈를 제거하면서 데이터의 차원을 줄였기 때문에 좀 더 빠른 연산이 가능합니다.

 

실습은 sklearn PCA 적용 전과 적용 후를 RandomForestClassifier를 이용하여 와인 데이터를 분류해 보겠습니다.

차원은 d를 2로 설정하여 (133, 13)에서 (133, 2)로 축소 했습니다.

교윳값은 [24.81797394 18.31760391]로 계산되었으며, 

고유 벡터는 원본 데이터 차원 수에 따라 다음과 같이 계산되었습니다.

[[-0.10418545  0.25670612  0.01387486  0.23907587 -0.10470229 -0.4007005
  -0.42902734  0.29111343 -0.30307602  0.12127653 -0.31609521 -0.38729685
  -0.26283936]
 [-0.49018724 -0.1691503  -0.30746987  0.04459018 -0.34837302 -0.07966456
  -0.0133774  -0.02498633 -0.0415164  -0.50798383  0.26045807  0.14018631
  -0.39850143]]

 

PCA 적용 전 정확도는 0.97, PCA 적용 후 정확도는 0.95입니다.

from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

from sklearn.decomposition import PCA

import pandas as pd
import matplotlib.pyplot as plt

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score


# 데이터 불러오기
raw_wine = datasets.load_wine()

# 피쳐, 타겟 데이터 지정
X = raw_wine.data
y = raw_wine.target

# 트레이닝/테스트 데이터 분할
X_tn, X_te, y_tn, y_te = train_test_split(X, y, random_state=1)

# 데이터 표준화
std_scale = StandardScaler()
std_scale.fit(X_tn)
X_tn_std = std_scale.transform(X_tn)
X_te_std = std_scale.transform(X_te)

# PCA
pca = PCA(n_components=2)
pca.fit(X_tn_std)
X_tn_pca = pca.transform(X_tn_std)
X_te_pca = pca.transform(X_te_std)

# 차원축소 확인
print(X_tn_std.shape)
print(X_tn_pca.shape)

# 공분산 행렬 확인
print(pca.get_covariance())

# 고유값 확인
print(pca.singular_values_)

# 고유벡터 확인
print(pca.components_)

# 설명된 분산
print(pca.explained_variance_)

# 설명된 분산 비율
print(pca.explained_variance_ratio_)


# 데이터프레임 생성
pca_columns = ['pca_comp1', 'pca_comp2']
X_tn_pca_df = pd.DataFrame(X_tn_pca,
                           columns=pca_columns)
X_tn_pca_df['target'] = y_tn
X_tn_pca_df.head(5)


# 라벨 미적용 PCA 데이터
plt.scatter(X_tn_pca_df['pca_comp1'],
            X_tn_pca_df['pca_comp2'],
            marker='o')
plt.xlabel('pca_component1')
plt.ylabel('pca_component2')
plt.show()

# 라벨 적용 PCA 플랏
df = X_tn_pca_df
df_0 = df[df['target']==0]
df_1 = df[df['target']==1]
df_2 = df[df['target']==2]

X_11 = df_0['pca_comp1']
X_12 = df_1['pca_comp1']
X_13 = df_2['pca_comp1']

X_21 = df_0['pca_comp2']
X_22 = df_1['pca_comp2']
X_23 = df_2['pca_comp2']

target_0 = raw_wine.target_names[0]
target_1 = raw_wine.target_names[1]
target_2 = raw_wine.target_names[2]

plt.scatter(X_11, X_21,
            marker='o',
            label=target_0)
plt.scatter(X_12, X_22,
            marker='x',
            label=target_1)
plt.scatter(X_13, X_23,
            marker='^',
            label=target_2)
plt.xlabel('pca_component1')
plt.ylabel('pca_component2')
plt.legend()
plt.show()

# 반복문을 이용해 PCA 플랏
df = X_tn_pca_df
markers=['o','x','^']

for i, mark in enumerate(markers):
    df_i = df[df['target']== i]
    target_i = raw_wine.target_names[i]
    X1 = df_i['pca_comp1']
    X2 = df_i['pca_comp2']
    plt.scatter(X1, X2,
                marker=mark,
                label=target_i)

plt.xlabel('pca_component1')
plt.ylabel('pca_component2')
plt.legend()
plt.show()

# PCA 적용 전/후 예측 정확도 비교
# PCA 적용 전 예측 정확도
clf_rf = RandomForestClassifier(max_depth=2,
                                random_state=0)
clf_rf.fit(X_tn_std, y_tn)

# 예측
pred_rf = clf_rf.predict(X_te_std)

# PCA 적용 전 정확도
accuracy = accuracy_score(y_te, pred_rf)
print(accuracy)

# PCA 적용 후 학습
clf_rf_pca = RandomForestClassifier(max_depth=2,
                                    random_state=0)
clf_rf_pca.fit(X_tn_pca, y_tn)

# 예측
pred_rf_pca = clf_rf_pca.predict(X_te_pca)

# PCA 적용 후 정확도
accuracy_pca = accuracy_score(y_te, pred_rf_pca)
print(accuracy_pca)