차원 축소 개요
- 차원 축소는 매우 많은 피처로 구성된 다차원 데이터셋의 차원을 축소해 새로운 차원의 데이터셋을 생성하는 것
- 피처가 수백 개 이상이면 상대적으로 적은 차원에서 학습된 모델보다 예측 신뢰도가 떨어진다
- 또, 피처가 많으면 피처들끼리 상관관계가 높을 수도 있다 → 다중공선성 문제
- 차원을 축소하여 피처 개수를 줄이면 직관적으로 데이터를 해석할 수 있다
- 피처 선택: 특정 피처에 종속성이 강한 불필요한 피처는 아예 제거하고 데이터의 특징을 잘 나타내는 주요 피처만 선택하는 것
- 피처 추출: 기존 피처를 저차원의 중요 피처로 압축해서 추출하는 것
- 새롭게 추출된 피처는 기존 피처와는 완전히 다른 값이 된다
- 피처를 함축적으로 잘 설명할 수 있는 또 다른 공간으로 매핑하여 추출함
- 기존 피처가 전혀 인지하기 어려웠던 잠재적 요소를 추출하는 것을 의미함
- 매우 많은 차원을 가지고 있는 이미지, 텍스트 데이터에서 차원 축소를 통해 잠재적 의미를 찾아주는데 많이 활용
- 매우 많은 픽세롤 이루어진 이미지 데이터에서 잠재된 특성을 피처로 도출해 함축적 형태의 이미지 변환과 압축을 수행함
- 이렇게 변환된 이미지는 훨씬 적은 차원이기 때문에 이미지 분류 수행 시에 과적합 위험이 줄어들어 성능을 높일 수 있음
- 텍스트 문서의 숨겨진 의미를 추출함
- 차원 축소 알고리즘은 문서 내 단어들의 구성에서 숨겨져 있는 semantic 의미나 topic을 잠재 요소로 간주하고 찾아낼 수 있다
- 매우 많은 픽세롤 이루어진 이미지 데이터에서 잠재된 특성을 피처로 도출해 함축적 형태의 이미지 변환과 압축을 수행함
PCA
PCA 개요
- PCA는 대표적인 차원 축소 기법이다
- 여러 변수 간에 존재하는 상관관계를 이용해 이를 대표하는 주성분을 추출하고 차원을 축소한다
- PCA로 차원을 축소할 때, 기존 데이터의 정보 유실을 최소화하기 위하여 가장 높은 분산을 가지는 데이터의 축을 찾아 이 축으로 차원을 축소함 → 이것이 '주성분'이 됨
- 데이터 변동성이 가장 큰 방향으로 축을 생성하고, 새롭게 생성된 축으로 데이터를 투영하는 방식이다.
- 가장 큰 데이터 변동성을 기반으로 첫 번째 벡터 축을 생성, 두 번째 축은 이 벡터 축에 직각이 되는 직교 벡터를 축으로 한다. 세 번째는 다시 두 번째의 직교 벡터로 하는 방식
- 이렇게 생성된 벡터 축에 원본 데이터를 투영하면 벡터 축의 개수만큼의 차원으로 차원이 축소됨
붓꽃 품종 데이터로 실습하기
- iris data는 4개의 속성으로 되어있는데, 2개의 PCA 차원으로 압축하여 기존 데이터셋과 압축된 데이터셋이 어떻게 달라졌는지 확인해보자
from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
iris = load_iris()
col = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
iris_df = pd.DataFrame(iris.data, columns=col)
iris_df['target'] = iris.target
iris_df.head(3)
sepal_length | sepal_width | petal_length | petal_width | target | |
---|---|---|---|---|---|
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 |
markers=['^', 's', 'o']
for i, marker in enumerate(markers):
x_axis_data = iris_df[iris_df['target']==i]['sepal_length']
y_axis_data = iris_df[iris_df['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의 경우 다른 품종과 명확하게 구분이 가능하다
versicolor, virginica의 경우는 sepal width와 sepal length만으로는 분류가 어려운 복잡한 조건이다
- 개별 속성 scaling하기 → StandardScaler
PCA는 여러 속성의 값을 연산해야 하므로 속성의 스케일에 영향을 받는다. 따라서, 각 속성값을 동일한 스케일로 변환하는 것이 필요하다.
from sklearn.preprocessing import StandardScaler
iris_scaled = StandardScaler().fit_transform(iris_df.iloc[:, :-1])
- PCA 데이터로 변환하기
n_componenets는 PCA로 변환할 차원의 수를 의미
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled)
print(iris_pca.shape)
(150, 2)
#iris_pca는 변환된 PCA데이터셋을 넘파이 배열로 가지고 있으므로 이를 데이터프레임으로 변환
pca_cols = ['pca_compo_1', 'pca_compo_2']
iris_df_pca = pd.DataFrame(iris_pca, columns=pca_cols)
iris_df_pca['target'] = iris.target
iris_df_pca.head(3)
pca_compo_1 | pca_compo_2 | target | |
---|---|---|---|
0 | -2.264703 | 0.480027 | 0 |
1 | -2.080961 | -0.674134 | 0 |
2 | -2.364229 | -0.341908 | 0 |
- 2개의 속성으로 변환된 데이터셋을 2차원상에서 시각화해보자
markers=['^', 's', 'o']
#pca_compo_1을 x축, pca_compo_2를 y축
for i, marker in enumerate(markers):
x_axis_data = iris_df_pca[iris_df_pca['target']==i]['pca_compo_1']
y_axis_data = iris_df_pca[iris_df_pca['target']==i]['pca_compo_2']
plt.scatter(x_axis_data, y_axis_data, marker=marker, label=iris.target_names[i])
plt.legend()
plt.xlabel('pca_compo_1')
plt.ylabel('pca_compo_2')
plt.show()
PCA변환 후에도 setosa는 명확하게 구분이 가능하다
versicolor와 virginica는 pca_compo_1을 기반으로 서로 조금 겹치지만 비교적 잘 구분됐다
→ 첫 번째 새로운 축인 pca_compo_1이 원본 데이터의 변동성을 잘 반영했기 때문이다
→ explained_variance_ratio_ 속성은 전체 변동성에서 개별 pca components 별로 차지하는 변동성 비율을 제공한다
print(pca.explained_variance_ratio_)
[0.72962445 0.22850762]
pca를 2개 요소로만 변환해도 원본 데이터의 변동성을 95%나 설명할 수 있다
- 이번에는 원본 데이터와 pca 변환된 데이터에 각각 분류를 적용하고 비교해보자
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)
print('원본 데이터 교차 검증 개별 정확도:', scores)
print('원본 데이터 평균 정확도:', np.mean(scores))
원본 데이터 교차 검증 개별 정확도: [0.98 0.94 0.96]
원본 데이터 평균 정확도: 0.96
pca_X = iris_df_pca[['pca_compo_1', 'pca_compo_2']]
scores_pca = cross_val_score(rcf, pca_X, iris.target, scoring='accuracy', cv=3)
print('PCA 변환 데이터 교차 검증 개별 정확도:', scores_pca)
print('PCA 변환 데이터 평균 정확도:', np.mean(scores_pca))
PCA 변환 데이터 교차 검증 개별 정확도: [0.88 0.88 0.88]
PCA 변환 데이터 평균 정확도: 0.88
원본 데이터 대비 예측 정확도는 PCA 변환 차원 개수에 따라 성능이 떨어질 수 밖에 없다.
8%의 정확도 하락은 큰 감소이지만, 4개의 속성이 2개로, 속성 개수가 50% 감소한 것을 고려한다면, PCA 변환 후에도 원본 데이터의 특성을 상당히 잘 유지하고 있음을 알 수 있다.
좀 더 많은 피처를 가진 데이터를 적은 PCA component 기반으로 변환하고 예측 정확도를 비교해보자
- UCI Machine Learning Repository의 Credit Card Clients Data Set 이용
- https://archive.ics.uci.edu/dataset/350/default+of+credit+card+clients
import pandas as pd
df = pd.read_excel('/content/default of credit card clients.xls', header=1, sheet_name='Data').iloc[0:, 1:]
print(df.shape)
df.head(3)
(30000, 24)
LIMIT_BAL | SEX | EDUCATION | MARRIAGE | AGE | PAY_0 | PAY_2 | PAY_3 | PAY_4 | PAY_5 | ... | BILL_AMT4 | BILL_AMT5 | BILL_AMT6 | PAY_AMT1 | PAY_AMT2 | PAY_AMT3 | PAY_AMT4 | PAY_AMT5 | PAY_AMT6 | default payment next month | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 20000 | 2 | 2 | 1 | 24 | 2 | 2 | -1 | -1 | -2 | ... | 0 | 0 | 0 | 0 | 689 | 0 | 0 | 0 | 0 | 1 |
1 | 120000 | 2 | 2 | 2 | 26 | -1 | 2 | 0 | 0 | 0 | ... | 3272 | 3455 | 3261 | 0 | 1000 | 1000 | 1000 | 0 | 2000 | 1 |
2 | 90000 | 2 | 2 | 2 | 34 | 0 | 0 | 0 | 0 | 0 | ... | 14331 | 14948 | 15549 | 1518 | 1500 | 1000 | 1000 | 1000 | 5000 | 0 |
3 rows × 24 columns
#칼럼명 변경
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)
#각 속성끼리 상관도 구하기
import seaborn as sns
import matplotlib.pyplot as plt
corr = X_features.corr()
plt.figure(figsize=(14,14))
sns.heatmap(corr, annot=True, fmt='.1g')
<Axes: >
BILL_AMT1~ 6까지 6개의 속성끼리 상관도가 0.9 이상, PAY_1~ 6까지도 상관도가 높음
이렇게 높은 상관도를 가진 속성들은 소수의 PCA만으로도 자연스럽게 이 속성들의 변동성을 수용할 수 있다
BILL_AMT1~ 6까지를 2개의 component로 PCA 변환하고 개별 component의 변동성을 알아보자
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
cols_bill = ['BILL_AMT'+str(i) for i in range(1,7)]
print('대상 속성명:', cols_bill)
scaler = StandardScaler()
df_cols_scaled = scaler.fit_transform(X_features[cols_bill])
pca = PCA(n_components=2)
pca.fit(df_cols_scaled)
print('PCA components별 변동성:', pca.explained_variance_ratio_)
대상 속성명: ['BILL_AMT1', 'BILL_AMT2', 'BILL_AMT3', 'BILL_AMT4', 'BILL_AMT5', 'BILL_AMT6']
PCA components별 변동성: [0.90555253 0.0509867 ]
rcf = RandomForestClassifier(n_estimators=300, random_state=156)
scores = cross_val_score(rcf, X_features, y_target, scoring='accuracy', cv=3)
print('CV=3인 경우의 개별 fold 세트별 정확도', scores)
print('평균 정확도:{0:.4f}'.format(np.mean(scores)))
CV=3인 경우의 개별 fold 세트별 정확도 [0.8083 0.8196 0.8232]
평균 정확도:0.8170
#원본 데이터에 먼저 스케일링 적용
scaler = StandardScaler()
df_scaled = scaler.fit_transform(X_features)
#6개의 컴포넌트를 가진 PCA변환 수행
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인 경우의 PCA 변환된 개별 Fold 세트별 정확도:', scores_pca)
print('PCA 변환 데이터셋 평균 정확도:{0:.4f}'.format(np.mean(scores_pca)))
CV=3인 경우의 PCA 변환된 개별 Fold 세트별 정확도: [0.7911 0.7965 0.8028]
PCA 변환 데이터셋 평균 정확도:0.7968
- PCA는 차원 축소를 통헤 데이터를 쉽게 인지하는데 활용할 수 있다
- 차원 축소는 컴퓨터 비전 분야에서 활발하게 적용된다. 특히, 얼굴 인식의 경우 Eigen-face라고 불리는 PCA 변환으로 원본 얼굴 이미지를 변환하여 사용하는 경우가 많다
'Data Science > 파이썬 머신러닝 완벽 가이드' 카테고리의 다른 글
[sklearn] (40) 차원 축소, SVD(Singular Value Decomposition) (0) | 2023.07.17 |
---|---|
[sklearn] (39) 차원 축소, LDA(Linear Discriminant Analysis) (0) | 2023.07.17 |
[sklearn] (37) 회귀 실습 - 캐글 주택 가격: 고급 회귀 기법 (0) | 2023.07.05 |
[sklearn] (36) 회귀 실습 - kaggle 자전거 대여 수요 예측하기 (0) | 2023.06.30 |
[sklearn] (35) 회귀 트리 RandomForestRegressor, DecisionTreeRegressor... (0) | 2023.06.27 |