Doby's Lab

Kaggle - Learn Feature Engineering : Principal Component Analysis 본문

Data Science/Feature Engineering

Kaggle - Learn Feature Engineering : Principal Component Analysis

도비(Doby) 2022. 10. 22. 13:28

[이 글은 Kaggle의 Learn을 토대로 작성되었습니다. 문제가 될 시 삭제 조치하겠습니다.]
(오역되어 잘못 설명된 부분이 있을 수 있습니다.)

 

https://www.kaggle.com/code/ryanholbrook/principal-component-analysis

 

Principal Component Analysis

Explore and run machine learning code with Kaggle Notebooks | Using data from FE Course Data

www.kaggle.com

 

Category

  • Introduction
  • Principal Component Analysis
  • PCA for Feature Engineering
  • Example - 1985 Automobiles

Introduction

전 포스팅에서는 Clustering 모델을 기반으로 한 Featrue Engineering에 대해 배워보았습니다. 이번에 배워볼 것은 Principal Component Analysis입니다. Clustering이 Proximity를 기반으로 Dataset을 분할한 것에 대조되어, PCA는 Data의 변화(variation)를 기반으로 분할됩니다. PCA는 데이터 간의 중요한 관계를 찾을 수도 있고, informative 한 feature를 만들어낼 수도 있습니다.

 

+ PCA는 일반적으로 Stardardiztion이 된 데이터에 적용합니다. 표준화된 데이터에서 'variation'을 'correlation(상관관계)'라고 하고, 표준화되지 않은 'variation'은 'covariance(공분산)'이라 합니다. (이 강좌에서 쓰이는 모든 데이터는 PCA를 하기 전에 Standardization이 적용되어 있습니다.)


Principal Component Analysis

'Abalone(전복)' Dataset에서 천 마리 정도의 전복의 신체적 측정이 담겨있습니다. 이 중에 'Height'와 'Diameter'라는 feature를 봅시다. 우리는 데이터에서 Sample마다 모두 다르다는 것을 표현한 'axes of variation(변화의 축)'을 상상할 수 있습니다. figure를 넣어 보면 한 축은 데이터의 'origial feature'에 따라 하나가 나타나고, 다른 하나는 한 축에 대해 수직적으로 나타납니다.

우리는 각 축에 대해 이름을 지어줄 수 있습니다. 왼쪽 아래에서 오른쪽 위로 가는 것을 'Size', 왼쪽 위에서 오른쪽 아래로 가는 것을 'Shape'라 지어줍시다. 

 

'Height'와 'Diameter'로 abalone을 표현하는 대신 'Size'와 'Shape'로도 figure를 표현할 수 있습니다.

이것이 PCA의 전체적인 아이디어입니다. Original Feature로 표현하는 대신에 Axes of Variation을 통해 표현하고, 이러한 Axes of Variation이 새로운 feature가 되는 것입니다.

새로운 feature, PCA는 Original Feature의 Linear Combinations(선형적인 합성(?))으로 만들어집니다.

df["Size"] = 0.707 * X["Height"] + 0.707 * X["Diameter"]
df["Shape"] = 0.707 * X["Height"] - 0.707 * X["Diameter"]

이러한 새로운 Feature들을 데이터의 주성분(Principal Components)라고 부릅니다. 그리고, Linear Combination을 하면서 생긴 가중치는 Loadings라고 부릅니다.

 

Original Dataset에 feature가 얼마나 있느냐에 따라 그만큼 주성분을 만들 수 있습니다.

 

주성분의 Loding은 부호와 크기를 통해 variation을 표현합니다.

Features \ Components Size(PC1) Shape(PC2)
Height 0.707 0.707
Diameter 0.707 -0.707

Loading을 통해서 다음을 알 수 있습니다. 'Size'는 'Height'와 'Diameter'가 같은 방향으로 변화하고 있고, 'Shape'는 반대 방향으로 변화하고 있다. 그리고, 각 성분에서 Loading의 크기가 같은데 이는 feature가 동등하게 기여하고 있다는 것을 말합니다.

 

PCA는 각 성분의 변화량을 보여줍니다. 아래 figure에서 'Size'가 'Shape'보다 데이터를 따라 더 변화량이 많다는 것을 보여주고 있습니다. PCA는 각 성분의 Explained Variance를 통해 이를 정확하게 표현합니다.

'Size' 성분이 'Height'와 'Diameter'사이의 변화량의 다수를 차지하지만, 변화량이 많다고 해서 predictior로써 무조건 좋다는 것은 아니라는 걸 기억해야 합니다. 어떤 것을 예측하냐에 따라 다릅니다.

 

(+ 각 성분의 변화량을 보여준다는 것에서 'Size'가 'Shape'보다 변화량이 많다는 것을 이해하기 위해서는 위에서 가져온 figure를 보면 이해가 됩니다. 축을 따른 데이터의 변화량을 보면 'Size'는 축을 따라 증가하지만, 'Shape'는 축을 따라가도 큰 변화량이 없습니다.)


PCA for Feature Engineering

PCA로 Feature Engineering을 하는 것에는 크게 두 가지 방법이 있습니다.

 

첫 번째는 Descriptive Technique입니다. 성분은 변화에 대해 설명하기 때문에 Component와 Target 간의 MI Score를 측정하여 어떤 Component를 Feature로 사용하면 좋을지에 대해 생각해볼 수 있습니다. 또한, MI Score가 높은 Component를 가지고 Clustering을 하여 좋은 Feature를 만들 수 있습니다.

 

두 번째 방법은 Component 자체를 Feature로 쓰는 방법입니다. Component는 데이터의 변화를 직접적으로 표현한 것이기 때문에 Original Feature보다 더 informative 합니다. 이러한 사례에서 사용될 수 있습니다.

  • Dimensionality Reduction
  • Anomaly Detection
  • Noise Reduction
  • Decorrelation

[PCA를 적용할 때 기억할 점]

  • PCA는 무조건 Numerical Feature에서 작동합니다.
  • PCA는 스케일에 예민하기 때문에 사용하기 전에 무조건 Scaling을 거쳐야 합니다.
  • Outlier는 결과에 과도한 영향을 미치기 때문에 제거하거나 제한하는 것을 고려해야 합니다.

Example - Automobiles

Automobile dataset에서 새로운 feature를 발견하기 위해 PCA를 Descriptive Technique으로써 사용해봅시다.

 

'price'와 MI Score가 높은 네 가지 feature를 사용해봅시다. PCA를 하기 전 Standarization 해주는 것을 잊지 맙시다.

features = ["highway_mpg", "engine_size", "horsepower", "curb_weight"]

X = df.copy()
y = X.pop('price')
X = X.loc[:, features]

# Standardize
X_scaled = (X - X.mean(axis=0)) / X.std(axis=0)

사이킷런에서 제공하는 PCA estimator를 가지고 주성분을 만들어봅시다.

from sklearn.decomposition import PCA

# Create principal components
pca = PCA()
X_pca = pca.fit_transform(X_scaled)

# Convert to dataframe
component_names = [f"PC{i+1}" for i in range(X_pca.shape[1])]
X_pca = pd.DataFrame(X_pca, columns=component_names)

X_pca.head()

fit_transform을 마친 PCA 객체에는 속성에 components_에 Loading을 포함합니다.

(X_pca로 변환된 column도 component라 부르고, loading 또한 component라 부르는 PCA에는 특정한 것을 지칭하는 말이 없습니다. 그렇지 않으면, 적합한 이름이 없기 때문입니다.)

이제 Loading을 데이터프레임으로 만들어주겠습니다.

loadings = pd.DataFrame(
    pca.components_.T,  # transpose the matrix of loadings
    columns=component_names,  # so the columns are the principal components
    index=X.columns,  # and the rows are the original features
)
loadings

component의 loading의 크기와 부호가 어떤 식으로 데이터가 퍼져있는지 알려준다는 것을 기억합시다. 첫 번째 component(PC1)은 연비가 나쁜 대형 차량과 연비가 좋은 소형 차량 간의 대비를 보여줍니다. 우리는 PC1에 'Luxury/Economic Axis'라고 부를 수 있습니다.  아래 figure는 'Luxury/Economic Axis'에 따라 대부분 달라지는 것을 보여줍니다.

이제 주성분들의 MI Score를 알아봅시다. 당연하게도 PC1이 가장 높은 점수를 받는 것에도 불구하고, 비록 다른 주성분들이 작은 변화량이었지만 price(target)과 의미 있는 relationship이 있다는 것을 알 수 있습니다. 이러한 사례로 보았을 때, PC1에 제한되지 않고, 다른 주성분들도 가치가 있다는 것을 알 수 있습니다.

def make_mi_scores(X, y, discrete_features):
    mi_scores = mutual_info_regression(X, y, discrete_features=discrete_features)
    mi_scores = pd.Series(mi_scores, name="MI Scores", index=X.columns)
    mi_scores = mi_scores.sort_values(ascending=False)
    return mi_scores
    
mi_scores = make_mi_scores(X_pca, y, discrete_features=False)
mi_scores

PC3는 'horsepower'와 'curb_weight'사이에 대조를 이루는 것을 보여줍니다.

(['horsepower','PC3'] = 0.731093, ['curb_weight', 'PC3'] = -0.678369)

PC3를 기준으로 데이터프레임을 내림차순으로 정렬한다면, 마력(horsepower)이 높고 차량 중량(curb_weight)이 낮은 상태로 정렬이 되어 나타날 것입니다.

# Show dataframe sorted by PC3
idx = X_pca["PC3"].sort_values(ascending=False).index
cols = ["make", "body_style", "horsepower", "curb_weight"]
df.loc[idx, cols]

이러한 대조적인 것을 시각화하려면, ratio feature를 하나 만들어야 합니다.

Descriptive Technique의 결론: PCA를 통해 효과적인 Feature를 만들어낸다.

df["sports_or_wagon"] = X.curb_weight / X.horsepower
sns.regplot(x="sports_or_wagon", y='price', data=df, order=2);

 

728x90