주성분 분석(PCA)
차원과 차원 축소
6-2 kmeans를 공부할 때 사용한 이미지는 300장이었지만 실제 서비스를 진행한다면 이미지들을 계속해서 등록해야 할 것이므로 저장공간이 부족할 수 있다.
나중에 군집이나 분류에 영향을 끼치지 않으면서 업로드된 사진의 용량을 줄이는 방법
데이터가 가진 속성을 특성이라고 부를 수 있다. 과일 사진 한 장은 100x100 인 10000개의 픽셀을 가지고 있으므로 10000개의 특성을 가지고 있다고 할 수 있다.
머신러닝에서는 이런 특성을 차원이라고 부르는데 그렇다면 이 10000개의 차원을 줄일 수 있다면 저장공간을 절약할 수 있을 것이다.
차원 축소 알고리즘
3장에서 특성이 많으면 선형 모델의 성능이 높아지고 훈련 데이터에 쉽게 과대적합된다는 것을 배웠다.
차원 축소는 데이터를 가장 잘 나타내는 일부 특성을 선택하여 데이터 크기를 줄이고 지도학습 모델의 성능을 향상시킬 수 있는 방법이다.
주성분 분석 PCA
줄어든 차원에서 다시 원본 차원으로 손실을 최대한 줄이면서 복원할 수도 있다.
주성분 분석은 데이터에 있는 분산이 큰 방향을 찾는 것으로 이해할 수 있다. 분산 : 데이터가 퍼져있는 정도
주성분 분석은 분산이 큰 방향을 표현하는 벡터를 찾는 것
주성분 : 분산이 큰 방향을 표현하는 벡터
방법은 기존 특성을 모은 벡터를 Transpose해서 곱해주어서 공분산 행렬을 만들어주고
이 공분산 행렬의 고유벡터에 모든 특성을 정사형시켰을 때 분산이 최대가 된다.
이 부분은 따로 포스팅 하려고 한다.
PCA 클래스
300개의 과일 데이터를 사용해서
from sklearn.decomposition import PCA
pca = PCA(n_components=50) #주성분을 50개만 찾도록 지정
pca.fit(fruits_2d)
print(pca.components_.shape) #components_ 에 주성분 데이터가 들어가 있다.
#'_'는 하이퍼파라미터가 아닌 학습된 데이터에 붙는다.(구분하기 위해)
#(50, 10000) 50개의 행과 10000개의 열로 이루어져 있다.
draw_fruits(pca.components_.reshape(-1, 100, 100))
#다시 100x100으로 reshape해서 주성분의 이미지를 그려볼 수 있다.
print(fruits_2d.shape)
#(300, 10000) 원래의 특성
fruits_pca = pca.transform(fruits_2d)
print(fruits_pca.shape)
#(300, 50) transform은 10000개의 특성을 50개로 변환해준다.
#300개의 샘플이 각각 50개의 특성으로 줄어든 것 1/200만큼의 데이터 size가 줄어들었다.
원본 데이터 재구성
fruits_inverse = pca.inverse_transform(fruits_pca)
print(fruits_inverse.shape)
#(300, 10000) 다시 특성이 300,10000으로 바뀜
fruits_reconstruct = fruits_inverse.reshape(-1, 100, 100) #이미지를 보기위해 reshape
for start in [0, 100, 200]:
draw_fruits(fruits_reconstruct[start:start+100])
print("\n")
1/200만큼 줄어들었지만 구분할 수 있을 정도로 재구성이 된 것을 볼 수 있다.
설명된 분산
print(np.sum(pca.explained_variance_ratio_))
#0.9215808173798626 원래 훈련 데이터의 92%만큼의 분산을 보존하고 있다.
#각 주성분 50개가 얼마만큼 분산을 잘 유지했는지 알려주는 값
plt.plot(pca.explained_variance_ratio_)
10개의 주성분이 대부분의 분산을 표현하고 있다.
이후 50개까지는 거의 필요없는것으로 보인다.
다른 알고리즘과 같이 사용하기
transform()을 통해서 다른 모델과 연결해서 사용
분류기와 함께 사용
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
target = np.array([0] * 100 + [1] * 100 + [2] * 100) #LogisticRegression사용하기 위해 타겟 설정
from sklearn.model_selection import cross_validate
scores = cross_validate(lr, fruits_2d, target) #교차 검증
print(np.mean(scores['test_score']))
print(np.mean(scores['fit_time']))
#0.9966666666666667 99%로 맞춤
#1.1039844512939454 훈련 시간
scores = cross_validate(lr, fruits_pca, target) #주성분50개 데이터로 훈련
print(np.mean(scores['test_score']))
print(np.mean(scores['fit_time']))
#1.0
#0.021912097930908203 훈련 시간 1/50
주성분으로 변환한 데이터로 훈련했을 때 정확도와 시간 둘 다 상승했다.
pca = PCA(n_components=0.5) #설명된 분산의 비율
# 1이상: 주성분의 개수 1미만 : 설명된 분산의 비율
pca.fit(fruits_2d)
print(pca.n_components_)
#2 설명된 분산 50% -> 주성분 개수 2개
fruits_pca = pca.transform(fruits_2d) # 특성 변환
print(fruits_pca.shape)
#(300, 2) 특성도 2개만 존재
scores = cross_validate(lr, fruits_pca, target) #교차 검증 진행
print(np.mean(scores['test_score']))
print(np.mean(scores['fit_time']))
#0.9933333333333334 특성을 2개만 사용했음에도 불구하고 정확도 0.99
#0.034157419204711915
설명된 분산의 비율을 하이퍼파라미터로 지정해 주어서 따로 그래프를 그려보지 않고 바로 훈련 진행
위에서는 기존샘플의 분산의 50%만큼 지정한 모습니다.
군집과 함께 사용
kmeans 와 같이 사용해보면
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3, random_state=42)
km.fit(fruits_pca)
print(np.unique(km.labels_, return_counts=True))
#(array([0, 1, 2], dtype=int32), array([110, 99, 91]))
#시각화
for label in range(0, 3):
data = fruits_pca[km.labels_ == label]
plt.scatter(data[:,0], data[:,1])
plt.legend(['apple', 'banana', 'pineapple'])
plt.show()
n_components값을 0.5로 했을 때 특성을 2개만 갖기 때문에 그래프로 그릴 수 있다.
x축 : pca1 y축 pca2 로 2개의 특성을 축으로 그래프를 그린 모습니다.
차원을 2,3개정도로 줄인다면 시각화에 장점을 가질 수 있다.
참고
박해선,혼자 공부하는 머신러닝, 한빛미디어, 2021,318~335p