일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 코사인 유사도
- 기초통계
- 회귀분석
- word2vec
- 파이썬 pandas
- 머신러닝
- 결정계수
- 모두의 딥러닝
- numpy
- F분포
- 텐서플로2와 머신러닝으로 시작하는 자연어처리
- student t분포
- Pandas
- Django
- 감성분석
- 차원축소
- rnn
- 은준아 화이팅
- 텍스트 분류
- 히스토그램
- 기술통계학
- 구글 BERT의 정석
- 다층 퍼셉트론
- 밑바닥부터 시작하는 딥러닝2
- 밑바닥부터 시작하는 딥러닝
- 자연어 처리
- 군집화
- 최소자승법
- 가설검정
- 오래간만에 글쓰네
- Today
- Total
데이터 한 그릇
차원 축소 개요와 PCA개요 본문
- 차원 축소란
- PCA 개요
차원 축소란?
머신러닝 차원 축소 편에서는 대표적인 차원 축소 알고리즘인 PCA, LDA, SVD, NMF에 대해서 살펴볼 예정이다. 차원 축소란 매우 많은 피처로 구성된 데이터 세트의 차원을 축소해 새로운 차원의 데이터 세트를 생성하는 것이다. 일반적으로 차원이 증가할수록 데이터 포인트 간의 거리가 기하급수적으로 증가한다. 따라서 수많은 데이터 피처를 가지고 있는 데이터 세트 경우에는 상대적으로 적은 피처를 가지고 있는 데이터 세트에 비해서 예측도가 떨어진다. 따라서 데이터 피처를 줄여 차원을 줄이는 차원 축소를 해야만 한다.
차원 축소는 피처 선택(feature selection)과 피처 추출(feature extraction)로 나눌 수 있다. 피처 선택은 말 그대로 특정 피처에 강하게 종속되어 있는 피처를 제거하고 데이터의 특징을 잘 선택하는 피처를 선택하는 것을 말한다. 피처 추출이란 기존 피처를 단순 압축이 아닌, 피처를 함축적으로 더 잘 설명할 수 있는 또 다른 공간으로 매핑해 추출하는 것이다. 이러한 것들은 기존 피처가 전혀 인지하기 어려웠던 잠재적인 요소를 추출하는 것과 같다.
이처럼 차원 축소는 단순히 데이터의 압축만을 의미하지 않는다. 왜냐하면 잠재적인 요소를 발견하는 기능도 존재하기 때문이다. 후자의 기능이 차원 축소의 더 중요한 의미라고 할 수 있다. 이때 PCA, SVD, NMF는 데이터 속에서 잠재적인 요소를 발견하는 대표적인 차원 축소 알고리즘이다. 이러한 차원 축소는 이미지나 텍스트에서 사용된다.
PCA 개요
PCA는 가장 대표적인 차원 축소 기법이다. PCA는 여러 변수간에 존재한 상관계수를 이용하여 이를 대표하는 주성분(Principal Component)를 추출하여 차원을 축소한다. PCA는 차원 축소를 할 때 기존 데이터의 정보 유실을 최소화 하기 위해서 가장 높은 분산을 보이는 데이터 축을 찾아 이 축을 기준으로 차원을 축소한다.
PCA는 제일 먼저 가장 큰 분산(variance)을 기반으로 하여 첫 번째 벡텉 축을 생성한다. 그리고 첫 번째 벡터 축에 직각이 되는 벡터를 두 번째 축으로 한다. 세 번째로는 두 번째 축과 직각이 되는 세 번째 벡터를 생성한다.
- 데이터의 분산, 변동성이 가장 큰 축을 첫 번째 벡터로 삼는다.
- 첫 번째 벡터와 직각을 이루는 축을 두 번째 벡터로 삼는다.
- 두 번째 벡터와 직각을 이루는 축을 세 번째 벡터로 삼는다.
벡터 축의 개수만큼의 차원으로 원본 데이터는 차원 축소가 된다. 이처럼 pca, 주성분 분석은 본래 피처의 개수에 비해서 더 작은 주성분으로 데이터의 총 변동성을 설명할 수 있다.(위에서 구한 벡터가 주성분이라고 할 수 있다.)
PCA를 선형대수 관점에서 해석해 보면, 입력 데이터의 공분산 행렬(Covariance Matrix)을 고유값 분해하고, 이렇게 구한 고유벡터에 입력 데이터를 선형 변화하는 것이다. 이 고유벡터가 PCA의 주성분 벡터로서 입력 데이터의 분산이 큰 방향을 나타낸다. 고윳값은 바로 이 고유벡터의 크기를 나타내며 동시에 입력 데이터의 분산을 나타낸다. 이 의미를 자세히 알기 위해서는 선형 변환, 공분산 행렬과 고유벡터에 대해서 알아봐야 한다.
1)선형 변환
특정 벡터에 행렬 A를 곱해 새로운 벡터로 변환하는 것을 의미한다. 이를 특정 벡터를 하나의 공간에서 다른 공간으로 투영하는 개념으로도 볼 수 있다. 이 경우 행렬을 바로 공간으로 가정하는 것. (특정 공간을 의미하는 어떤 벡터에 행렬 A를 곱하면서 다른 공간으로 투영하는 것)
2)공분산 행렬
일반적으로 분산은 한 개의 특정 변수의 데이터의 변동성을 의미하지만 공분산은 두 변수 간의 변동성을 의미한다. 예를 들어서 사람 키 = X, 몸무게 = Y라고 한다면, COV(X,Y)는 공분산을 의미하며 COV(X,Y) >0 을 의미하면 X가 증가할 때 Y도 증가함을 의미한다. 공분산 행렬은 여러 변수와 관련된 공분산을 포함하는 정방형 행렬이다.
X | Y | Z | |
X | 3.0 | -0.71 | -0.24 |
Y | -0.71 | 4.5 | 0.28 |
Z | -0.24 | 0.28 | 0.91 |
위의 표는 세 변수 X,Y,Z 간의 분산을 나타낸다. 대각선을 가로지르는 수치는 변수 자신의 분산을 의미한다. X의 분산은 3.0, Y의 변수는 4.5 그리고 Z의 변수는 0.91의 수치를 가진다. 그리고 COV(X,Y)는 -0.71, COV(X,Z)는 -0.24를 가진다.
공분상 행렬은 정방행렬(Diagonal Matrix)이며 대칭행렬(Symmetric Matrix)이다. 정방행렬은 열과 행이 같은 행렬을 지칭한다. (ex, 2x2) 정방행렬 중에서 대각 원소를 중심으로 원소 값이 대칭되는 행렬을 대칭행렬 이라고 부른다. (ex, A^t = A) 이때 위의 표와 같이 공분산 행렬은 정방행렬이면서, 가운데 대각선 원소를 기준으로 대칭인 대칭행렬이다. 대칭행렬은 고유값 분해와 관련해 매우 좋은 특성을 가진다. 대칭행렬은 항상 고유벡터를 직교행렬로 고유값을 정방 행렬로 대각화 할 수 있다.
입력 데이터의 공분산 행렬을 C라고 한다면, 공분산 행렬의 특징으로 인해서 다음과 같이 분해될 수 있다.
$$C = P\sum P^{T}$$
이때 P는 n x n의 직교행렬이며 시그마는 n x n 정방행렬 P^T는 행렬 P의 전치 행렬이다. 위 식은 고유벡터 행렬과 고유값 행렬로 다음과 같이 대응된다. 즉 공분산 C는 직교 행렬 * 고유값 정방 행렬 * 고유벡터 직교 행렬의 전치 행렬로 분해된다.
3)고유벡터
고유벡터는 행렬 A를 그 벡터에 곱하더라도 벡터의 방향을 바꾸지 않고 그 크기만 변하는 벡터를 지칭한다. 즉 Ax = ax(A는 행렬, x는 고유벡터, a는 스칼라 값)를 의미한다. 정방 행렬은 최대 그 차원 수만큼의 고유벡터를 가질 수 있다. 예를 들어서 2x2 행렬은 두 개의 고유벡터를 3x3 행렬은 3개의 고유벡터를 가질 수 있다. 이렇듯 고유벡터는 행렬이 작용하는 힘의 방향과 관계가 있어서 행렬을 분해하는 데 사용된다.
이와 같은 식이 의미하는 바는 입력 데이터의 공분산 행렬이 고유벡터와 고유값으로 분해될 수 있으며 이렇게 분해된 고유벡터를 이용해 입력 데이터를 선형 변환하는 방식이 PCA라는 것이다.
PCA는 다음과 같은 단계를 밟아간다.
- 입력 데이터 세트의 공분산 행렬을 생성
- 공분산 행렬의 고유벡터와 고유값을 계산한다.
- 고유값이 가장 큰 순으로 K개 만큼 고유벡터를 추출한다.
- 고유값이 가장 큰 순으로 추출된 고유벡터를 이용해 새롭게 입력 데이터를 변환한다.
다음은 파이썬 내장 데이터 셋에 있는 붓꽃 데이터 셋에 PCA 알고리즘을 적용한 코드이다.
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
iris = load_iris()
columns = ['sepal_length','sepal_width','petal_length','petal_width']
irisDF = pd.DataFrame(iris.data, columns = columns)
irisDF['target'] = iris.target
irisDF.head(3)
사이킷런 내장 데이터 셋에서 아이리스 데이터를 로드한다. 로드한 이후에 칼람명을 위 코드처럼 작성하고 데이터 프레임으로 만든다. target데이터도 이후에 생성해준다.
# 각 품종에 따라 원본 붓꽃 데이터 세트가 어떻게 분포돼 있는지 2차원으로 시각화
# setosa = 세모, versicolor = 네모, virginica = 동그라미, 순서대로 데이터는 0 1 2 의 타겟값을 가지고 있음
markets =['^','s','o']
for i,marker in enumerate(markets):
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()
#분류 하는데 이 두 가지 피처만으로는 한계가 있음을 알 수 있다.
#pca를 통해서 4가지의 피처를 2가지로 축소를 하고 축소된 두 가지를 통해서 데이터를 2차원으로 시각화 진행
아이리스 데이터 프레임에서 두 개의 변수만을 이용하여 각 품종의 두 개의 변수의 변동성을 알아보려고 했다. x축에는 각 품종의 sepal_length를 설정하고 y축에는 각 품종의 sepal_width를 설정했다.
#pca는 scaler에 영향을 받는다. 따라서 피처를 압축하기 전에 각 변수를 동일한 스케일로 변환해야만 한다.
from sklearn.preprocessing import StandardScaler
iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:,:-1])
pca를 적용하기 전에 필수적으로 거쳐야할 주의사항이 있다. 바로 StandardScaler를 통해서 전처리를 거쳐야만 한다. 아이리스 데이터 프레임에서 타겟 데이터를 제외하고 scaler를 해준다.
from sklearn.decomposition import PCA
#pca 객체 생성
pca = PCA(n_components = 2)
#pca에 스케일된 데이터 학습
pca.fit(iris_scaled)
#객체를 통해서 입력 데이터 변환
iris_pca = pca.transform(iris_scaled)
print(iris_pca.shape)
현재 아이리스 데이터 셋의 피처의 개수는 4개이다. pca로 차원 축소를 하여 2개로 줄이려고 한다. (2개의 벡터는 가장 큰 변동성을 기준으로 벡터가 만들어짐) irisDF_pca = pca.fit_transform(iris_scaled)를 하여 변환시키고 shape를 통해서 구조를 확인한다. 확인 결과 칼람이 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(3)
축소가 된 데이터는 아직 데이터 프레임의 형태가 아니므로 칼럼명을 위의 pca_colums 으로 지어주고 데이터 프레임을 만든다. 그리고 타겟 데이터를 추가해줘서 최종적인 데이터 프레임을 만든다.
markers = ['^','s','o']
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로 차원축소된 데이터를 다시 시각화해본다. 앞서서는 sepal_width, sepal_length로 각 품종의 변동성을 확인했다면 이번에는 pca를 통해서 발견한 두 개의 잠재적인 요소(가장 큰 변동성 방향을 기준으로 벡터 생성)로 각 품종을 분류해본다.
#새롭게 추출된 축들이 얼마나 변동성을 잘 반영했는지 파악할 수 있다.
#객체.explained_variance_ratio_
#두 개의 합은 약 95퍼정도, 따라서 두 개의 축을 통해서도 데이터의 변동성의 95퍼를 설명할 수 있다.
print(pca.explained_variance_ratio_)
pca 객체는 발견된 벡터들이 데이터들의 변동성을 몇 퍼센트 설명하는지 나타낼 수 있다. (pca.explained_variance_ratio_)
[0.72962445 0.22850762]
위와 같은 결과값을 반환하는데 첫 번째 요소는 데이터의 72% 정도의 변동성을 설명하고 두 번째 요소는 22% 정도의 변동성을 설명한다. 따라서 pca를 통해서 발견된 두 개의 요소를 통해서 총 95%의 변동성을 설명 가능하다.
#전과 후의 성능 비교
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.metrics import accuracy_score
import numpy as np
rcf = RandomForestClassifier(random_state=156)
score = cross_val_score(rcf, iris.data, iris.target, scoring = 'accuracy', cv = 3)
print('랜덤 포레스트의 교차 검증 개별 정확도 :' , score)
print('랜덤 포레스트 교차 검증 평균 정확도 :', np.mean(score))
앙상블의 배깅 기법인 랜덤 포레스트를 만들고 pca로 차원축소를 하기 전의 모델 성능과 한 이후의 모델 성능을 비교해보겠다. 비교를 위해서 cross_val_score를 이용한다. 먼저 pca 하기 이전의 iris.data와 iris.target 을 사용한다. 결과값으로 다음과 같은 값을 반환한다.
랜덤 포레스트의 교차 검증 개별 정확도 : [0.98 0.94 0.96]
랜덤 포레스트 교차 검증 평균 정확도 : 0.96
#피처를 절반이나 줄였음에도 불구하고 성능이 상대적으로 덜 떨어졌다.
pca_x = irisDF_pca[['pca_component_1', 'pca_component_2']]
scores_pca = cross_val_score(rcf, pca_x, iris.target, scoring='accuracy', cv=5)
print(scores_pca)
print(np.mean(scores_pca))
앞서 pca를 한 이후에 만든 최종적인 데이터 프레임을 이용하여 타겟 데이터 이외의 데이터를 추출한다. 그 이후에 cross_val_score에 삽입하여 성능을 비교해본다. 결과를 확인해보면 평균 fold의 정확도는 0.9 정도를 보인다. 성능이 6%정도 떨어졌지만 피처를 절반 이상 줄였음에도 6% 밖에 떨어지지 않았기 때문에 pca가 정보 손실이 적다는 것을 알 수 있다.(6%의 하락이 적은 수치라는 것을 의미하진 않는다)
'머신러닝 > 차원 축소' 카테고리의 다른 글
차원축소(LDA, SVD,NMF) (0) | 2021.05.12 |
---|