ML) IRIS, WINE - PCA

Like Sunnysideup·2024년 11월 20일
post-thumbnail

PCA (Principal Component Analysis, 주성분 분석)

  • PCA는 고차원 데이터를 저차원으로 변환하여 데이터의 주요 패턴을 추출하고, 데이터의 분산을 가장 잘 설명하는 새로운 축(주성분)을 생성하는 기법
  • 데이터 압축, 시각화, 노이즈 제거, 특징 추출 등에 널리 사용

1. PCA의 주요 목표

  • 차원 축소: 데이터의 정보 손실을 최소화하면서 특성(feature)의 수를 줄임
  • 분산 최대화: 데이터의 분산(정보)을 가장 많이 포함하는 축을 찾음
  • 데이터 시각화: 고차원 데이터를 2D 또는 3D 공간으로 변환하여 시각적으로 이해할 수 있게 함

2. PCA의 동작 원리

1) 데이터 중심화 (Mean Centering) : 각 특성(feature)의 평균을 계산하고, 데이터를 평균이 0이 되도록 조정
2) 공분산 행렬 계산 : 데이터의 공분산 행렬을 계산하여 특성 간 상관관계를 파악
3) 고유값 분해 또는 SVD 수행 : 공분산 행렬을 고유값 분해(Eigen Decomposition)하거나 SVD(Singular Value Decomposition)를 수행
- 고유값: 각 주성분의 중요도 (데이터 분산의 크기).
- 고유벡터: 주성분(Principal Component)을 나타냄 (새로운 축의 방향).
4) 주성분 선택 : 고유값이 큰 순서대로 주성분을 선택. 상위 𝑘개의 주성분을 선택하여 데이터 차원을 𝑘로 축소
5) 데이터 변환 : 원본 데이터를 새로운 주성분 공간으로 투영

3. PCA의 주요 속성

  • 비선형 관계를 설명하지 못함: PCA는 선형 변환 기법으로, 데이터의 비선형 패턴을 반영하지 못함
  • 정보 손실 가능성: 차원을 줄이는 과정에서 일부 정보가 손실될 수 있음
  • 속도와 메모리 효율성: PCA는 큰 데이터셋에서 공분산 행렬 계산과 고유값 분해가 계산적으로 비쌈

IRIS 데이터

1. 데이터 불러오기 & Scaling

import pandas as pd
from sklearn.datasets import load_iris

iris = load_iris()

from sklearn.preprocessing import StandardScaler

iris_ss = StandardScaler().fit_transform(iris.data)
iris_ss[:5]
# 결과 : 
array([[-0.90068117,  1.01900435, -1.34022653, -1.3154443 ],
       [-1.14301691, -0.13197948, -1.34022653, -1.3154443 ],
       [-1.38535265,  0.32841405, -1.39706395, -1.3154443 ],
       [-1.50652052,  0.09821729, -1.2833891 , -1.3154443 ],
       [-1.02184904,  1.24920112, -1.34022653, -1.3154443 ]])

2. PCA 적용

from sklearn.decomposition import PCA

def get_pca_data(ss_data, n_components=2):
    pca = PCA(n_components=n_components)
    pca.fit(ss_data)

    return pca.transform(ss_data), pca

# 데이터의 차원을 2로 줄인 결과(iris_pca)와 PCA 객체(pca)를 반환
iris_pca, pca = get_pca_data(iris_ss, 2)
iris_ss.shape, iris_pca.shape
# 결과 : 
((150, 4), (150, 2))

# 참고 
iris_pca
# 결과 : 
array([[-2.26470281,  0.4800266 ],
       [-2.08096115, -0.67413356],
       [-2.36422905, -0.34190802],
       [-2.29938422, -0.59739451], ... 

# PCA 수행 전, 각 특성(feature)의 평균값을 계산해 저장한 속성
# 각 값은 데이터셋의 네 개 특성(sepal length, sepal width, petal length, petal width)에 대한 평균값
pca.mean_
# 결과 : 
array([-1.69031455e-15, -1.84297022e-15, -1.69864123e-15, -1.40924309e-15])

# PCA가 학습한 주성분(Principal Components) 벡터
# 이 값은 데이터의 분산을 가장 잘 설명하는 방향(벡터)을 의미
# PCA는 입력 데이터의 분산을 가장 크게 설명하는 축을 찾으며, 각 축은 주성분 벡터로 표현
# pca.components_의 각 행(row)은 하나의 주성분 벡터
# 첫 번째 행: 첫 번째 주성분 벡터 (데이터 분산이 가장 큰 방향). 데이터가 가장 넓게 퍼져 있는 방향을 나타냄.
# 두 번째 행: 두 번째 주성분 벡터. 첫 번째 주성분과 직교(orthogonal)하며, 그다음으로 큰 분산을 설명

pca.components_
# 결과 : 
array([[ 0.52106591, -0.26934744,  0.5804131 ,  0.56485654],
       [ 0.37741762,  0.92329566,  0.02449161,  0.06694199]])

3. 데이터프레임 변환

def get_pd_from_pca(pca_data, cols=['PC1', 'PC2']):
    return pd.DataFrame(pca_data, columns=cols)
    
iris_pd_pca = get_pd_from_pca(iris_pca)
iris_pd_pca['species'] = iris.target
iris_pd_pca.head()
# 결과 : 아래 테이블 

sns.pairplot(iris_pd_pca, hue='species')
# 결과 : 아래 이미지 

4. Explained Variance Ratio

# PCA를 적용한 객체
def print_variance_ratio(pca):
	# 각 주성분(Principal Component)이 전체 데이터 분산에서 차지하는 비율을 반환 
    # 값의 합은 1.0 (100%)에 근접
    print('Explained variance ratio: {}'.format(pca.explained_variance_ratio_))
    # 누적 설명된 분산 비율(Cumulative Explained Variance Ratio).
    # 첫 번째 주성분부터 선택된 주성분까지 누적된 분산 비율
    print('Cumulative explained variance ratio: {}'.format(np.cumsum(pca.explained_variance_ratio_)))
    
print_variance_ratio(pca)
# R:
Explained variance ratio: [0.72962445 0.22850762]
Cumulative explained variance ratio: [0.72962445 0.95813207]

5. RandomForestClassifier + CV

  • 원본 데이터와 PCA 축소 데이터를 비교해 차원 축소가 모델 성능에 미치는 영향을 평가
  • PCA는 고차원 데이터를 다룰 때 계산 효율성을 높이고, 과적합을 줄이는 데 유용
  • 하지만, 정보 손실로 인해 성능이 다소 저하
  • PCA를 사용할지 여부는 데이터와 문제의 특성에 따라 결정
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

# 데이터를 5개로 나누어 교차 검증 (5-Fold Cross Validation)을 수행
# 정확도를 기준으로 성능 평가
def rf_scores(X, y, cv=5):
    rf = RandomForestClassifier(random_state=4, n_estimators=100)
    scores = cross_val_score(rf, X, y, scoring='accuracy', cv=cv)
    print('CV accuracy scores: {}'.format(scores))

# 원본 데이터 결과 
rf_scores(iris_ss, iris.target)
# R : 
CV accuracy scores: [0.96666667 0.96666667 0.93333333 0.96666667 1.        ]
# 각 fold에서의 정확도 점수 
# 평균 정확도: 약 96.67%
# 랜덤 포레스트는 원본 데이터에서 높은 정확도를 보임

# PCA 데이터, 2개의 주성분 결과 
pca_X = iris_pd_pca[['PC1', 'PC2']]
rf_scores(pca_X, iris.target)
# R :
CV accuracy scores: [0.83333333 0.93333333 0.83333333 0.9        1.        ]
# 평균 정확도: 약 88.0%
# PCA를 사용해 차원을 축소했기 때문에 일부 정보가 손실되어 성능이 다소 감소
# 차원을 축소한 상태에서도 비교적 좋은 성능을 유지

WINE 데이터

1. 데이터 불러오기

import pandas as pd

wine_url = '.../dataset/wine.csv'
wine = pd.read_csv(wine_url, index_col=0)

wine_y = wine['color']
wine_X = wine.drop('color', axis=1)

2. Scaling / PCA 적용

wine_ss = StandardScaler().fit_transform(wine_X)

pca_wine, pca = get_pca_data(wine_ss, 2)
pca_wine.shape
# R :
(6497, 2)

print_variance_ratio(pca)
# R : 
Explained variance ratio: [0.25346226 0.22082117]
Cumulative explained variance ratio: [0.25346226 0.47428343]

pca_columns = ['PC1', 'PC2']
pca_wine_pd = get_pd_from_pca(pca_wine, pca_columns)
pca_wine_pd['color'] = wine_y.values

sns.pairplot(pca_wine_pd, hue='color')

3. RandomForestClassifier + CV

rf_scores(wine_ss, wine_y)
# R : 
CV accuracy scores: [0.99384615 0.99615385 0.99615089 0.98691301 0.99615089]

pca_X = pca_wine_pd[pca_columns]
rf_scores(pca_X, wine_y)
# R : 
CV accuracy scores: [0.98230769 0.98230769 0.98845266 0.97459584 0.9799846 ]

pca_wine, pca = get_pca_data(wine_ss, 3)
print_variance_ratio(pca)
# R :
Explained variance ratio: [0.25346226 0.22082117 0.13679223]
Cumulative explained variance ratio: [0.25346226 0.47428343 0.61107566]

4. 주성분 3개 CASE

cols = ['PC1', 'PC2', 'PC3']
pca_wine_pd = get_pd_from_pca(pca_wine, cols)

pca_X = pca_wine_pd[cols]
rf_scores(pca_X, wine_y)
# R : 
CV accuracy scores: [0.98307692 0.97923077 0.9899923  0.97690531 0.98614319]

pca_wine_plot = pca_X
pca_wine_plot['color'] = wine_y.values

import plotly.express as px

fig = px.scatter_3d(pca_wine_plot, x='PC1', y='PC2', z='PC3', color='color')
fig.update_layout(margin = dict(l=0, r=0, b=0, t=0))
fig.show()

profile
Perfect timing to be a Newbie

0개의 댓글