Categories
주성분분석_기초
빅데이터 분석기사 실기 학습을 하는 중이다.
관련 정보들을 블로그에 기록하여 틈날 때마다 보려고 한다.
출처를 미리 공개한다.
- 윤종식 지음, “ADP 실기 데이터 분석 전문가”, 데이터 에듀, 2021
- 오일석 지음, “기계학습”, 한빛아카데미, 2020
주성분 분석 원리
Q. USArrests 데이터는 1973년 미국 50개주 10만명의 인구 당 체포된 세 가지 강력범죄수(assault, murder, rape)와 각 주마다 도시에 거주하는 인구의 비율(%)로 구성되어 있다. 주성분 분석을 수행하여 해당 데이터의 변수들을 가장 잘 요약하는 주성분을 구하고 해석해보자.
A. 먼저 주성분 분석이 어떻게 행해지는지 원리부터 알아보자. 오일석 교수님이 지으신 기계학습 책에 따라 설명하도록 하겠다. 만약 다음과 같은 훈련집합이 존재한다고 쳐보자.
import numpy as np
length_x1 = np.sqrt(5)
x_1 = (2, 1)
length_x2 = np.sqrt(20)
x_2 = (2, 4)
length_x3 = np.sqrt(17)
x_3 = (4, 1)
length_x4 = np.sqrt(25)
x_4 = (4, 3)
주성분 분석은 데이터를 원점 중심으로 옮겨 놓는 일부터 시작한다.
$x_i = x_i - \mu$
원점 중심으로 옮겨 놓은 후 각 샘플의 좌표는 다음과 같다.
# 샘플들의 x좌표 평균을 구하면
print(f"x좌표 평균 = {(2 + 2 + 4 + 4) / 4}")
# 샘플들의 y좌표 평균을 구하면
print(f"y좌표 평균 = {(1 + 4 + 1 + 3) / 4}")
# 따라서 원점 중심 변환 후 좌표는
P_x1 = (2 - 3, 1 - 2.25)
P_x2 = (2 - 3, 4 - 2.25)
P_x3 = (4 - 3, 1 - 2.25)
P_x4 = (4 - 3, 3 - 2.25)
P_xi_list = (P_x1, P_x2, P_x3, P_x4)
print(f"변환 후 좌표 : {P_xi_list}")
출력 :
> x좌표 평균 = 3.0
> y좌표 평균 = 2.25
> 변환 후 좌표 : ((-1, -1.25), (-1, 1.75), (1, -1.25), (1, 0.75))
주성분 분석시 사용하는 변환식은 다음과 같다.
$변환 후 좌표 z = (W^t)x$
이때 W = (단위벡터)
만약 단위벡터를 $(1, 0)^T$ 로 사용한다면
각 샘플의 좌표는 다음과 같은 코드로 표현된다.
단위벡터 = np.array([[1], [0]])
x1_dot = np.dot(P_x1, 단위벡터)
x2_dot = np.dot(P_x2, 단위벡터)
x3_dot = np.dot(P_x3, 단위벡터)
x4_dot = np.dot(P_x4, 단위벡터)
print(x1_dot, x2_dot, x3_dot, x4_dot)
출력 : [-1.][-1.][1.][1.]
고차원 데이터를 저차원데이터로 변환하면 이렇게 정보 손실이 일어난다. 이처럼 주성분 분석에 의한 변환은 정보 손실을 일으키는데, 정보 손실이 적을 수록 좋은 축이다.
따라서 위와 같은 예의 경우 정보 손실을 적게 하기 위해서는 단위벡터를 변경해야 한다.
단위벡터 = np.array([[1 / np.sqrt(2)], [1 / np.sqrt(2)]])
x1_dot = np.dot(P_x1, 단위벡터)
x2_dot = np.dot(P_x2, 단위벡터)
x3_dot = np.dot(P_x3, 단위벡터)
x4_dot = np.dot(P_x4, 단위벡터)
print(x1_dot, x2_dot, x3_dot, x4_dot)
출력 : [-1.59099026] [0.53033009] [-0.1767767] [1.23743687]
코드의 결과를 보면 네 좌표 모두 서로 다르게 변환 됬음을 알 수 있다. 정보 손실이 훨씬 적을 것으로 보인다.
즉 주성분 분석의 목적은 다음과 같다.
- 정보 손실의 최소화
- 저차원으로 변환
기계학습에서는 이처럼 훈련집합 X가 주어지면 정보 손실을 최소화하는 q개 벡터, 즉 변환행렬 W를 찾는 것이 목적이다.
하지만 일단 위의 최초 문제를 빠르고 쉽게 푸는 것이 지금 우리의 주 목적이므로, 일단 원리는 여기까지만 알아두고 문제 풀기에 집중해보자.
주성분 분석 (R)
파이썬과 R로 모두 데이터 분석이 가능하지만, 엘리스 국비교육을 들으며 자격증을 공부하느라 시간이 많이 없다고 생각하기 때문에 원리를 볼 때는 파이썬을 사용하고, 데이터분석을 할 때는 좀 더 사용이 편한 R 언어를 이용해서 데이터분석을 하려 한다.
# 먼저 R 내장 데이터 셋을 불러온다.
library(datasets)
# 내장 데이터에 있는 USArrests 데이터 셋을 불러온다.
data(USArrests)
# 그런 다음 불러온 데이터 셋의 헤드 부분을 살펴보자.
head(USArrests)
위와 같이 각 컬럼의 척도 차이가 상당하다.
이게 무슨 뜻이냐면 “Assault” 컬럼의 경우 비율이 아닌 발생한 범죄의 수를 나타내었고, 나머지 컬럼은 비율(%)로 나타냈기 때문에 척도 차이가 크다는 것이다. 즉 샘플간의 정규화가 필요하다.
그 전에 산점도를 그려서 각 변수간의 관련성을 파악해보자.
pairs(USArrests, panel=panel.smooth, main="USArrests data")
위와 같이 산점도를 통해 “Murder”와 “UrbanPop” 비율 간의 관련성이 작아보이는 것을 확인할 수 있다.
보통 주성분 분석을 진행할 땐 두 가지 함수가 쓰인다.
- prcomp: 특이값 분해 방법을 이용해서 데이터를 표준화하여 주성분 분석을 수행한다.
- princomp: 공분산행렬, 상관행렬 방법을 이용해서 주성분 분석을 수행한다.
하지만 R help에 따르면 SVD가 정확성에 있어서 좀 더 낫다는 정보가 있다.
즉 princomp보다 prcomp 함수가 좀 더 선호된다고 한다.
관련 정보 링크
책에서는 princomp() 함수를 이용해서 분석을 수행했다.
US.prin<-princomp(USArrests, cor=TRUE)
summary(US.prin)
수행 후에 위와 같이 표가 도출되는데 각각의 변수는 다음을 뜻한다.
Standard deviation == 주성분의 표준 편차
Proportion of Variance == 주성분의 기여율
Cumulative Proportion == 누적 기여율
보통 첫 번째 주성분부터 차례대로 기여율을 합한 누적 기여율이 85% 이상이 되면 해당 지점까지를 주성분의 수로 결정한다. 즉 여기서는 주성분의 수를 두 개로 결정한다.
여기서 US.prin$loadings 를 취해주면 네 개의 변수가 각 주성분에 기여하는 가중치인 주성분계수가 제시된다.
위와 같이 제1주성분은 (0.536 * “Murder”) + (0.583 * “Assault”) + (0.278 * “UrbanPop”) + (0.543 * Rape)의 선형결합식으로 이루어져 있음을 알 수 있다.
이제 주성분들의 선형식을 통해 새롭게 계산된 각 행별 좌표를 확인해보자.
결정하기로 한 주성분이 Comp.1과 Comp.2 였으므로, 원래 있던 각 행마다의 데이터들 대신, 변환 후의 주성분 좌표를 쓰면 된다.
개인적인 데이터 분석
biplot 함수는 제1주성분과 제2주성분으로 이루어진 좌표평면 상에 원 데이터 행들의 주성분 점수를 산점도의 형태로 나타내고, 각 변수에 대한 주성분 계수를 화살표로 시각화하여 그래프로 표현해준다.
biplot(US.prin, scale=0)
개인적으로 위에 대해 데이터 분석을 해보았다.
- Murder 컬럼과 Assault 컬럼은 서로 간의 벡터 방향, 길이가 제일 유사하므로 선형 상관관계가 가장 크다.
- Murder 컬럼과 UrbanPop 컬럼은 주성분 계수 벡터가 서로 수직이므로 상관성이 가장 적다.
- California 행은 다른 행보다 거리상으로 더 떨어져있다. 위 플롯은 모든 변수를 주성분 1, 2에 대해 정규화한 것임으로, 정규화 해도 거리가 저만큼 Comp1축으로 벌어진다는 것은 Comp1과 관련해서 이상치의 가능성이 있는 것으로 보인다. 따라서 이상치가 포함되어 있는지 조사하는 과정이 필요하다. (Mississippi나 Vermont 행 등에도 조사가 필요하다.)