주성분분석 (PCA)
| topics | 100-데이터분석 & AI 101 머신러닝 103 데이터 분석,처리 |
| types | 이론 실습 |
| tags | |
| references | blog.naver.com/prayer2k/222624821291 lets-start-data.tistory.com/entry/%EB... hongl.tistory.com/129 |
주성분분석 (PCA)
What is PCA?
**주성분분석(Principal Component Analysis, PCA)**은 고차원 데이터를 저차원으로 축소하는 기법이다. 원본 변수들의 선형 결합으로 새로운 축(주성분)을 만들어서, 데이터의 분산을 최대한 보존하면서 차원을 줄인다.
왜 필요한가?
고차원의 저주
- 변수가 너무 많으면 모델 학습이 느려진다
- 과적합(Overfitting) 위험이 크다
- 시각화가 불가능하다 (3차원 이상은 보기 어렵다)
PCA로 해결
- 중요한 정보는 유지하면서 차원을 줄인다
- 노이즈를 제거한다
- 계산 속도가 빨라진다
핵심 아이디어: 데이터의 분산이 큰 방향이 가장 중요한 방향이다. 분산이 작은 방향은 버려도 된다.
PCA vs 랜덤포레스트 변수 중요도
둘 다 변수를 선택하는 방법인데, 뭐가 다를까?
| 방법 | 접근 | 장점 | 단점 |
|---|---|---|---|
| RF Importance | 예측 기여도로 선택 | 해석 쉬움, 직관적 | 트리 모델에만 특화됨 |
| PCA | 분산+노이즈 제거 | 모든 모델에 적용, 정규화 효과 | 해석 어려움 (새로운 축 생성) |
언제 뭘 쓸까?
- 해석이 중요하면 → RF Importance
- 성능이 중요하면 → PCA
- 둘 다 해보고 비교하는 게 제일 좋다
핵심 개념
PCA가 만드는 주성분이란?
중요: PCA가 뽑은 12개의 주성분은 '특정 칼럼' 자체가 아니다. 원본 칼럼들의 선형 결합(조합)으로 만들어진 새로운 좌표축이다.
예를 들어:
- 원본 변수: 키, 몸무게, 나이, 연봉
- 주성분 1: 0.5×키 + 0.3×몸무게 + 0.1×나이 + 0.1×연봉
- 주성분 2: -0.2×키 + 0.1×몸무게 + 0.6×나이 + 0.1×연봉
- ...
분산과 공분산
PCA는 데이터의 분산이 가장 큰 방향을 찾는다.
- 분산(Variance): 데이터가 얼마나 흩어져 있는지
- 공분산(Covariance): 두 변수가 함께 변하는 정도
- 상관계수(Correlation): 공분산을 표준화한 것 (-1 ~ 1)
PCA는 공분산 행렬을 이용해서 분산이 최대인 방향을 찾는다.
구현 방법
기본 사용
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# <span id="1-스케일링-필수"></span>1. 스케일링 (필수!)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# <span id="2-pca-적용"></span>2. PCA 적용
pca = PCA(n_components=12) # 12개 주성분
X_pca = pca.fit_transform(X_scaled)
# <span id="3-설명된-분산-비율-확인"></span>3. 설명된 분산 비율 확인
print(f"설명된 분산 비율: {pca.explained_variance_ratio_.sum():.4f}")
왜 스케일링이 필수일까?
변수마다 단위와 범위가 다르면 (예: 키 cm, 연봉 억) PCA가 제대로 작동 안 한다. 표준화로 모든 변수를 같은 스케일로 맞춰야 한다.
n_components 설정 방법
n_components는 2가지 방식으로 설정 가능하다:
1. 주성분 개수로 지정 (정수)
pca = PCA(n_components=12) # 12개 주성분
2. 분산 비율로 지정 (0~1 실수)
pca = PCA(n_components=0.95) # 분산 95% 설명하는 만큼
최적 주성분 개수 찾기
여러 개수를 시도해서 비교한다:
n_components_list = [10, 20, 50, 100]
pca_results = {}
for n_components in n_components_list:
if n_components <= X_scaled.shape[1]:
pca = PCA(n_components=n_components)
X_pca = pca.fit_transform(X_scaled)
explained_variance_ratio = pca.explained_variance_ratio_.sum()
pca_results[n_components] = {
'X_pca': X_pca,
'explained_variance_ratio': explained_variance_ratio,
'pca': pca
}
print(f"PCA {n_components} components: {explained_variance_ratio:.4f} variance explained")
분산 비율로 찾기
print("\n분산 비율별 주성분 개수:")
variance_thresholds = [0.80, 0.85, 0.90, 0.95]
for threshold in variance_thresholds:
pca = PCA(threshold)
X_pca = pca.fit_transform(X_scaled)
print(f"{threshold*100}% 분산: {pca.n_components_}개 주성분")
실행 결과 예시:
80.0% 분산: 15개 주성분
85.0% 분산: 20개 주성분
90.0% 분산: 30개 주성분
95.0% 분산: 45개 주성분
주성분 개수 결정 기준
일반적인 가이드라인:
- 80~95% 분산: 대부분의 정보를 보존하면서도 차원 축소 효과
- 95% 이상: 거의 모든 정보 보존, 차원 축소 효과 적음
- 80% 미만: 너무 많은 정보 손실 위험
최종 결정은 모델 성능으로:
# <span id="여러-주성분-개수로-모델-학습하고-성능-비교"></span>여러 주성분 개수로 모델 학습하고 성능 비교
for n in [10, 20, 30, 50]:
pca = PCA(n_components=n)
X_pca = pca.fit_transform(X_scaled)
model.fit(X_pca, y)
score = model.score(X_test_pca, y_test)
print(f"n_components={n}: score={score:.4f}")
장점과 단점
장점
- 차원 축소로 계산 속도 향상
- 과적합 방지
- 노이즈 제거
- 모든 머신러닝 모델에 적용 가능
- 다중공선성 문제 해결
단점
- 해석이 어렵다 (새로운 축은 원본 변수의 조합)
- 선형 변환만 가능 (비선형 패턴은 못 잡음)
- 스케일링이 필수 (전처리 필요)
- 원본 데이터로 복원 시 정보 손실
주의사항
1. 반드시 스케일링 먼저
# <span id="잘못된-예"></span>잘못된 예
pca = PCA(n_components=10)
X_pca = pca.fit_transform(X) # ❌ 스케일링 안 함
# <span id="올바른-예"></span>올바른 예
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
pca = PCA(n_components=10)
X_pca = pca.fit_transform(X_scaled) # ✅
2. 테스트 데이터는 transform만
# <span id="학습-데이터"></span>학습 데이터
pca = PCA(n_components=10)
X_train_pca = pca.fit_transform(X_train_scaled)
# <span id="테스트-데이터"></span>테스트 데이터
X_test_pca = pca.transform(X_test_scaled) # fit 안 함!