차원 축소란?
PCA(Principal Component Analysis, 주성분 분석)
주성분 분석(PCA)은 원본 데이터의 피처 개수에 비해
매우 작은 주성분으로 원본 데이터의 총 변동성을 대부분 설명할 수 있는 분석법이다.
PCA는 대표적인 차원 축소 기법이며, 여러 변수 간에 존재하는 상관관계를 이용해 이를 대표하는 주성분을 추출해 차원을 축소하는 기법이며, PCA는 입력 데이터의 변동성이 가장 큰 축을 구하고, 다시 이 축에 직각인 축을 반복적으로 축소하려는 차원 개수만큼 구한 뒤 입력 데이터를 이 축들에 투영해 차원을 축소하는 방식이다.
PCA로 차원을 축소할 때는 기존 데이터의 정보 유실이 최소화되는 것이 당연하기 때문에, PCA는 가장 높은 분산을 가지는 데이터의 축을찾아 이 축으로 차원을 축소하는데, 이것이 PCA의 주성분이 되며, 분산이 데이터의 특성을 가장 잘 나타내는 것으로 간주한다.
PCA는 제일 먼저 가장 큰 데이터 변동성(Variance)을 기반으로 첫 번째 벡터 축을 생성하고, 두 번째 축은 이 벡터 축에 직각이 되는 벡터(직교 벡터)를 축으로 한다. 세 번째 축은 다시 두 번째 축과 직각이 되는 벡터를 설정하는 방식으로 축을 생성하고, 이렇게 생성된 벡터 축에 원본 데이터를 투영하면 벡터 축의 개수만큼의 차원으로 원본 데이터가 차원 축소된다.
PCA를 선형대수 관점에서 해석해보면, 입력 데이터의 공분산 행렬이 고유벡터와 고유값으로 분해될 수 있으며, 이렇게 분해된 고유벡터를 이용해 입력 데이터를 선형 변환하는 방식이 PCA이다.
- 고유벡터: PCA의 주성분 벡터로서 입력 데이터의 분산이 큰 방향을 나타낸다. 행렬을 곱하더라도 방향이 변하지 않고 그 크기만 변하는 벡터를 지칭한다. 여러 개가 존재하며, 정방 행렬은 최대 그 차원 수만큼의 고유벡터를 가질 수 있다.
- 고윳값: 고유벡터의 크기를 나타내며, 동시에 입력 데이터의 분산을 나타낸다
- 공분산: 두 변수 간의 변동을 의미.
- 공분산 행렬: 여러 변수와 관련된 공분산을 포함하는 정방행렬이며, 개별 분산값을 대각 원소로 하는 대칭행렬이다.
- 대칭행렬: 항상 고유벡터를 직교행렬로, 고윳값을 정방 행렬로 대각화할 수 있다.
[PCA 수행 단계]
- 입력 데이터 세트의 공분산 행렬 생성
- 공분산 행렬의 고유벡터와 고유값 계산
- 고유값이 가장 큰 순으로 K개(PCA 변환 차수만큼) 만큼 고유벡터 추출
- 고유값이 가장 큰 순으로 추출된 고유벡터를 이용해 새롭게 입력 데이터 변환
붓꽃 데이터 세트를 활용한 PCA 수행
[주요 수행 내용]
- PCA는 많은 속성으로 구성된 원본 데이터를 그 핵심을 구성하는 데이터로 압축한 것.
- 붓꽃 데이터 세트는 sepal length, sepal width, petal length, petal width 의 4개의 속성으로 되어 있다.
- 4개의 속성을 2개의 PCA 차원으로 압축해 원래 데이터 세트와 압축된 데이터 세트를 비교
[사이킷런으로 붓꽃 데이터 로딩 및 시각화를 위한 DataFrame으로 변환]
붓꽃 데이터를 load_iris() API를 이용해 로딩한 뒤 시각화를 위해 DataFrame으로 변환한다.
from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
iris = load_iris()
# 넘파이 데이터 세트를 판다스 DataFrame으로 변환
columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
irisDF = pd.DataFrame(iris.data, columns = columns)
irisDF['target']=iris.target
irisDF.head()
[품종에 따른 데이터 분포 파악을 위한 시각화 - 2차원]
2차원으로 표현하므로 두 개의 속성인 sepal length와 sepal width를 X축, Y축으로 해 품종 데이터 분포를 나타낸다.
#setosa는 세모, versicolor는 네모, virginica는 동그라미로 표현
markers = ['^', 's', 'o']
# setosa의 타깃값은 0, versicolor는 1, virginica는 2. 각 target 별로 다른 모양으로 산점도로 표시
for i, marker in enumerate(markers):
x_axis_data = irisDF[irisDF['target']==i]['sepal_length']
y_axis_data = irisDF[irisDF['target']==i]['sepal_width']
plt.scatter(x_axis_data, y_axis_data, marker=marker, label=iris.target_names[i])
plt.legend()
plt.xlabel('sepal_length')
plt.ylabel('sepal_width')
plt.show()
▶ setosa 품종: sepal width가 3.0보다 크고, sepal length가 6.0 이하인 곳에 일정하게 분포
Versicolor, Verginica: sepal width와 sepal length 조건만으로는 분류가 어려움
[사이킷런의 StandardScaler를 이용한 스케일링]
붓꽃 데이터 세트에 바로 PCA를 적용하기 전에 개별 속성을 함께 스케일링 해야 하는데, 왜냐하면 PCA는 여러 속성의 값을 연산해야 하므로 속성의 스케일에 영향을 받기 때문이다. 따라서 여러 속성을 PCA로 압축하기 전에 각 속성값을 동일한 스케일로 변환하는 것이 필요하다.
사이킷런의 StandardScaler를 이용해 평균이 0, 분산이 1인 표준 정규 분포로 iris 데이터 세트의 속성값들을 변환한다.
from sklearn.preprocessing import StandardScaler
# Target 값을 제외한 모든 속성 값을 StandardScaler를 이용해 표준 정규 분포를 가지는 값들로 변환
iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:, :-1])
[PCA로 4개 속성을 2개로 압축한 뒤 분포를 2차원으로 시각화]
스케일링이 적용된 데이터 세트에 PCA를 적용해 4차원(4개 속성)의 붓꽃 데이터를 2차원(2개의 PCA 속성) PCA 데이터로 변환한다. 사이킷런은 PCA 변환을 위해 PCA 클래스를 제공하며, 생성 파라미터로 n_components를 입력받는다. 이는 PCA로 변환할 차원의 수를 의미하므로 여기서는 2로 설정하고, 이후에 fit(입력 데이터 세트)과 transform(입력 데이터 세트)을 호출해 PCA로 변환을 수행한다.
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
# fit()과 transform()을 호출해 PCA 변환 데이터 반환
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled)
print(iris_pca.shape)
▶ PCA 객체의 transform() 메서드를 호출해 원본 데이터 세트를 (150, 2)의 데이터 세트로 iris_pca 객체 변수로 변환하였다.
[iris_pca 를 DataFrame으로 변환]
iris_pca는 변환된 PCA 데이터 세트를 150*2 넘파이 행렬로 가지고 있으므로, 이를 DataFrame으로 변환한 뒤 데이터값을 확인한다.
# PCA 변환된 데이터의 칼럼명을 각각 pca_component_1, pca_component_2 로 명명
pca_columns = ['pca_component_1', 'pca_component_2']
irisDF_pca = pd.DataFrame(iris_pca, columns=pca_columns)
irisDF_pca['target']=iris.target
irisDF_pca.head()
[2개의 속성으로 변환된 데이터 세트 시각화 - 2차원]
pca_component_1을 X축, pca_component_2를 Y축으로 해서 분포도 확인
#setosa는 세모, versicolor는 네모, virginica는 동그라미로 표현
markers = ['^', 's', 'o']
# pca_component_1을 X축, pca_component_2를 y축으로 scatter plot 수행
for i, marker in enumerate(markers):
x_axis_data = irisDF_pca[irisDF_pca['target']==i]['pca_component_1']
y_axis_data = irisDF_pca[irisDF_pca['target']==i]['pca_component_2']
plt.scatter(x_axis_data, y_axis_data, marker=marker, label=iris.target_names[i])
plt.legend()
plt.xlabel('pca_component_1')
plt.ylabel('pca_component_2')
plt.show()
▶ PCA의 첫 번째 새로운 축인 pca_component_1이 원본 데이터의 변동성을 잘 반영하여 Versicolor와 Virginica는 pca_component_1 축을 기반으로 서로 겹치는 부분이 일부 존재하나, 비교적 잘 구분되었다.
[PCA Component 별로 원본 데이터의 변동성 반영 여부 파악]
PCA 변환을 수행한 PCA 객체의 explained_variance_ratio_ 속성은 전체 변동성에서 개별 PCA 컴포넌트 별로 차지하는 변동성 비율 제공
print(pca.explained_variance_ratio_)
▶ pca_component_1 의 변동성은 72.9%, pca_component_2의 변동성은 22.8를 차지한다. 따라서 PCA를 2개 요소로만 변환해도 원본 데이터의 변동성을 약 95% 설명할 수 있다.
[원본 데이터 세트와 PCA로 변환된 데이터 세트에 각각 분류 적용 후 비교]
Estimator는 RandomForestClassifier를 이용하고 cross_val_score()로 3개의 교차 검증 세트로 정확도 결과를 비교한다.
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import numpy as np
# 원본 데이터
rcf = RandomForestClassifier(random_state=156)
scores = cross_val_score(rcf, iris.data, iris.target, scoring='accuracy', cv=3)
# PCA 변환 데이터
pca_X = irisDF_pca[['pca_component_1', 'pca_component_2']]
scores_pca = cross_val_score(rcf, pca_X, iris.target, scoring='accuracy', cv=3)
# 결과 출력
print('원본 데이터 교차 검증 개별 정확도:', scores)
print('원본 데이터 평균 정확도:', np.mean(scores))
print('PCA 변환 데이터 교차 검증 개별 정확도:', scores_pca)
print('PCA 변환 데이터 평균 정확도:', np.mean(scores_pca))
▶ 원본 데이터 세트 대비 예측 정확도는 PCA 변환 차원 개수에 따라 예측 성능이 떨어질 수 밖에 없으나, 속성 개수가 50% (4개에서 2개) 감소한 것을 고려한다면 PCA 변환 후 예측 성능의 정확도가 8% 떨어진 것은 PCA 변환 후에도 원본 데이터의 특성을 상당 부분 유지하고 있음을 알 수 있다.
신용카드 데이터 세트를 통한 PCA 수행
위의 붓꽃 데이터 세트보다 더 많은 피처를 가진 데이터를 세트를 적은 PCA 컴포넌트 기반으로 변환한 뒤, 예측 영향도가 어떻게 되는지 변환된 PCA 데이터 세트에 기반해서 비교해 본다.
[사용 데이터 세트]
# header로 의미 없는 첫 행 제거, iloc로 기존 id 제거
import pandas as pd
df = pd.read_excel("../Data/06. pca_credit_card.xls", header=1, sheet_name='Data').iloc[0:,1:]
print(df.shape)
df.head()
▶ 신용카드 데이터 세트는 30,000개의 레코드와 24개의 속성을 가지고 있다.
이 중에서 'default payment next month(다음달 연체 여부)' 속성이 Target 값이며, '연체'일 경우 1, '정상납부'가 0이다.
[칼럼명 변경, 변수 저장 등]
원본 데이터 세트에 PAY_0 다음에 PAY_2 칼럼이 있으므로 PAY_0 칼럼을 PAY_1 칼럼으로 변환하고
'default payment next month' 칼럼명도 너무 길기 때문에 'default'로 변경한다. 또한 이 칼럼을 y_target 변수로 별도 저장하고 피처 데이터는 이 default 칼럼을 제외한 별도의 DataFrame으로 만든다.
df.rename(columns={'PAY_0':'PAY_1', 'default payment next month':'default'}, inplace=True)
y_target = df['default']
X_features = df.drop('default', axis=1)
[속성 간의 상관도를 구한 뒤 시각화]
해당 데이터 세트는 23개의 속성 데이터 세트가 있으나 각 속성끼리 상관도가 매우 높다.
DataFrame의 corr() 를 이용해 각 속성 간의 상관도를 구한 뒤, 이를 시본(seaborn)의 heatmap으로 시각화한다.
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
corr = X_features.corr()
plt.figure(figsize=(14, 14))
sns.heatmap(corr, annot=True, fmt='.1g')
▶ BILL_AMT1 ~ BILL_AMT6 6개 속성끼리의 상관도가 대부분 0.9 이상으로 매우 높다
PAY_1 ~ PAY_6까지의 속성 역시 상관도가 높다. 높은 상관도를 가진 속성들은 소수의 PCA 만으로도 자연스럽게 변동성을 수용할 수 있다.
[BILL_AMT1 ~ BILL_AMT6 속성을 2개의 컴포넌트로 PCA 변환]
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# BILL_AMT1 ~ 6 까지 6개의 속성명 생성
cols_bill = ['BILL_AMT'+str(i) for i in range(1, 7)]
print('대상 속성명:', cols_bill)
# 2개의 PCA 속성을 가진 PCA 객체 생성하고, explained_variance_ratio_ 계산을 위해 fit 호출
scaler = StandardScaler()
df_cols_scaled = scaler.fit_transform(X_features[cols_bill])
pca = PCA(n_components=2)
pca.fit(df_cols_scaled)
print('PCA Component 별 변동성:', pca.explained_variance_ratio_)
▶ 단 6개의 PCA 컴포넌트만으로도 6개 속성의 변동성을 약 95% 이상 설명할 수 있으며 특히 첫 번째 축으로 90%의 변동성을 수용할 정도로 이 6개 속성의 상관도가 매우 높다
[원본 데이터 세트와 PCA 변환한 데이터 세트의 분류 예측 결과 상호 비교]
각 데이터 세트에 랜덤 포레스트를 이용해 타깃 값이 디폴트 값을 3개의 교차 검증 세트로 분류 예측한다.
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import numpy as np
# 원본 데이터
rcf = RandomForestClassifier(n_estimators=300, random_state=156)
scores = cross_val_score(rcf, X_features, y_target, scoring='accuracy', cv=3)
# PCA 변환 데이터
scaler = StandardScaler()
df_scaled = scaler.fit_transform(X_features)
# 6개의 컴포넌트를 가진 PCA 변환 수행 후, cross_val_score()로 분류 예측 수행
pca = PCA(n_components=6)
df_pca = pca.fit_transform(df_scaled)
scores_pca = cross_val_score(rcf, df_pca, y_target, scoring='accuracy', cv=3)
# 결과 출력
print('CV=3 인 경우 개별 Fold 세트별 정확도:', scores)
print('평균 정확도:{0:.4f}'.format(np.mean(scores)))
print('CV=3 인 경우 PCA 변환된 개별 Fold 세트별 정확도:', scores_pca)
print('PCA 변환 데이터 세트 평균 정확도:{0:.4f}'.format(np.mean(scores_pca)))
▶ 전체 23개 속성의 약 1/4 수준인 6개의 PCA 컴포넌트 만으로도 원본 데이터를 기반으로 한 분류 예측 결과보다 약 1~2% 예측 성능 저하만 발생했다. 이는 PCA의 뛰어난 압축 능력을 잘 보여준다고 볼 수 있다.
PCA가 더 활발하게 적용되는 영역은 컴퓨터 비전(Computer Vision) 분야이며, 특히 얼굴 인식의 경우 Eigen-face라고 불리는 PCA 변환으로 원본 얼굴 이미지를 변환해 사용하는 경우가 많다.
다음 내용
[출처]
파이썬 머신러닝 완벽 가이드
UCI Machine Learning Repository
'[파이썬 Projects] > <파이썬 머신러닝>' 카테고리의 다른 글
[머신러닝] 차원 축소 - NMF (1) | 2024.10.24 |
---|---|
[머신러닝] 차원 축소 - LDA (0) | 2024.10.24 |
[머신러닝] 차원 축소(Dimension Reduction) (0) | 2024.10.24 |
[머신러닝] 회귀 - 캐글 주택 가격 (0) | 2024.10.23 |
[머신러닝] 회귀 - 자전거 대여 수요 예측 (1) | 2024.10.23 |