728x90
1. PCA (Principal Component Analysis)
차원 축소
PCA
- 고차원의 원본 데이터를 저차원의 부분 공간으로 투영하여 데이터를 축소하는 기법
- 예를 들어 10차원의 데이터를 2차원의 부분 공간으로 부영하여 데이터를 축소
- PCA는 원본 데이터가 가지는 데이터 변동성을 가장 중요한 정보로 간주하며 이 변동성에 기반한 원본 데이터 투영으로 차원 축소를 수행
원본 데이터의 변동성이 가장 큰 방향으로 순차적으로 축들을 생성하고 이렇게 생성된 축으로 데이터를 투영하는 방식
PCA, 주성분 분석은 원본 데이터의 Feature 개수에 비해 매우 작은 주성분으로 원본 데이터의 총 변동성을 대부분 설명할 수 있는 분석법이다
PCA 변환
- 원본 데이터의 공분산 행렬(Covariance Matrix) 추출
- 공분산 행렬을 고유벡터와 고유값 분해
- 원본 데이터를 고유 벡터로 선형 변환
고유벡터는 PCA의 주성분 벡터로서 입력 데이터의 분산이 큰 방향을 나타낸다
고유값은 고유벡터의 크기를 나타내며, 동시에 입력 데이터의 분산을 나타낸다
공분산 행렬 (Covariance Matrix)
선형 변환과 고유 벡터 / 고유값
공분산 행렬의 고유값 분해
PCA 변환과 수행 절차
- 입력 데이터 세트의 Covariance Matrix(공분산 행렬) C 생성
- 공분산 행렬의 Eigenvector & Eigenvalue(고유벡터와 고유값) 계산
- 고유값이 가장 큰 순으로, 축소하고자 하는 차원의 개수만큼 고유벡터 추출
- 추출된 고유벡터를 이용해 데이터 변환
2. Scikit - Learn PCA
Scikit - Learn PCA Class
- n_components: PCA 축의 개수 = 변환 차원
- PCA를 적용하기 전 입력 데이터의 개별 Feature들을 Scaling해야 함
- PCA는 여러 Feature들의 값을 연산해야 하므로 Feature들의 scale에 영향을 받음
- 따라서 여러 속성을 PCA로 압축하기 전에 각 Feature들의 값을 동일한 scale로 변환하는 것이 필요
- 일반적으로 평균이 0, 분산이 1인 표준 정규 분포로 변환: StandarScalar
- PCA 변환이 완료된 Scikit - Learn PCA 객체는 전체 변동성에서 개별 PCA component별로 차지하는 변동성 비율을 explained_variance_ratio 속성으로 제공함
- PCA Component로 원본 데이터의 몇 %를 설명할 수 있는지 확인 필요
- PCA로 100% 원본 데이터를 설명할 수 없음
일반적으로 지도학습의 경우에는 fit()이후 predict()를 사용했으나,
비지도학습의 경우에는 fit()이후 transform() or fit_transform()을 사용한다.
3. 실습 - PCA를 이용한 붓꽃 데이터 세트의 차원 축소
붓꽃 데이터로 PCA 변환을 위한 데이터 로딩 및 시각화
from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
# 사이킷런 내장 데이터 셋 API 호출
iris = load_iris()
# 넘파이 데이터 셋을 Pandas DataFrame으로 변환
columns = ['sepal_length','sepal_width','petal_length','petal_width']
irisDF = pd.DataFrame(iris.data , columns=columns)
irisDF['target']=iris.target
irisDF.head(3)
# setosa는 세모, versicolor는 네모, virginica는 동그라미로 표현
markers = ["^", "s", "o"]
# setosa의 target 값은 0, versicolor는 1, virginica는 2. 각 target 별로 다른 shape으로 scatter plot
for i, marker in enumerate(markers):
x_axis_data = irisDF[irisDF["target"] == i]["sepal_length"]
y_axis_data = irisDF[irisDF["target"] == i]["sepal_width"]
plt.scatter(x_axis_data, y_axis_data, marker=marker, label=iris.target_names[i])
plt.legend()
plt.xlabel("sepal length")
plt.ylabel("sepal width")
plt.show()
- 평균이 0, 분산이 1인 정규분포로 원본 데이터를 변환
from sklearn.preprocessing import StandardScaler
# Target 값을 제외한 모든 속성 값을 StandardScaler를 이용하여 표준 정규 분포를 가지는 값들로 변환
iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:, :-1])
iris_scaled.shape
# (150, 4)
- PCA 변환 수행
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
#fit( )과 transform( ) 을 호출하여 PCA 변환 데이터 반환
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled)
print(iris_pca.shape)
# (150, 2)
# PCA 환된 데이터의 컬럼명을 각각 pca_component_1, pca_component_2로 명명
pca_columns = ["pca_component_1", "pca_component_2"]
irisDF_pca = pd.DataFrame(iris_pca, columns=pca_columns)
irisDF_pca["target"] = iris.target
irisDF_pca.head(3)
- PCA로 차원 축소된 Feature들로 데이터 산포도 시각화
# setosa를 세모, versicolor를 네모, virginica를 동그라미로 표시
markers = ["^", "s", "o"]
# pca_component_1 을 x축, pc_component_2를 y축으로 scatter plot 수행.
for i, marker in enumerate(markers):
x_axis_data = irisDF_pca[irisDF_pca["target"] == i]["pca_component_1"]
y_axis_data = irisDF_pca[irisDF_pca["target"] == i]["pca_component_2"]
plt.scatter(x_axis_data, y_axis_data, marker=marker, label=iris.target_names[i])
plt.legend()
plt.xlabel("pca_component_1")
plt.ylabel("pca_component_2")
plt.show()
- 각 PCA Component별 변동성 비율
print(pca.explained_variance_ratio_)
# [0.72962445 0.22850762]
- 원본 데이터와 PCA 변환된 데이터 기반에서 예측 성능 비교
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import numpy as np
rcf = RandomForestClassifier(random_state=156)
scores = cross_val_score(rcf, iris.data, iris.target,scoring='accuracy',cv=3)
print('원본 데이터 교차 검증 개별 정확도:',scores)
print('원본 데이터 평균 정확도:', np.mean(scores))
원본 데이터 교차 검증 개별 정확도: [0.98 0.94 0.96]
원본 데이터 평균 정확도: 0.96
pca_X = irisDF_pca[["pca_component_1", "pca_component_2"]]
scores_pca = cross_val_score(rcf, pca_X, iris.target, scoring="accuracy", cv=3)
print("PCA 변환 데이터 교차 검증 개별 정확도:", scores_pca)
print("PCA 변환 데이터 평균 정확도:", np.mean(scores_pca))
PCA 변환 데이터 교차 검증 개별 정확도: [0.88 0.88 0.88]
PCA 변환 데이터 평균 정확도: 0.88
일반적으로 PCA변환 후 원본 데이터 대비 성능이 떨어지지 않는 경우: 각 Feature들 별로 상관도가 높은 경우
4. 실습 - PCA를 이용한 신용카드 데이터 세트의 차원 축소
신용카드 데이터 세트 PCA 변환
- 데이터 로드 및 컬럼명 변환
# header로 의미없는 첫행 제거, iloc로 기존 id 제거
import pandas as pd
pd.set_option('display.max_columns', 30)
df = pd.read_excel('pca_credit_card.xls', header=1, sheet_name='Data').iloc[:,1:]
print(df.shape)
df.head(3)
# (30000, 24)
df.rename(columns={'PAY_0':'PAY_1','default payment next month':'default'}, inplace=True)
y_target = df['default']
X_features = df.drop('default', axis=1)
X_features.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 30000 entries, 0 to 29999
Data columns (total 23 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 LIMIT_BAL 30000 non-null int64
1 SEX 30000 non-null int64
2 EDUCATION 30000 non-null int64
3 MARRIAGE 30000 non-null int64
4 AGE 30000 non-null int64
5 PAY_1 30000 non-null int64
6 PAY_2 30000 non-null int64
7 PAY_3 30000 non-null int64
8 PAY_4 30000 non-null int64
9 PAY_5 30000 non-null int64
10 PAY_6 30000 non-null int64
11 BILL_AMT1 30000 non-null int64
12 BILL_AMT2 30000 non-null int64
13 BILL_AMT3 30000 non-null int64
14 BILL_AMT4 30000 non-null int64
15 BILL_AMT5 30000 non-null int64
16 BILL_AMT6 30000 non-null int64
17 PAY_AMT1 30000 non-null int64
18 PAY_AMT2 30000 non-null int64
19 PAY_AMT3 30000 non-null int64
20 PAY_AMT4 30000 non-null int64
21 PAY_AMT5 30000 non-null int64
22 PAY_AMT6 30000 non-null int64
dtypes: int64(23)
memory usage: 5.3 MB
Feature간 상관도 시각화
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
corr = X_features.corr()
plt.figure(figsize=(14,14))
sns.heatmap(corr, annot=True, fmt='.1g')
plt.show()
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# BILL_AMT1 ~ BILL_AMT6까지 6개의 속성명 생성
cols_bill = ["BILL_AMT" + str(i) for i in range(1, 7)]
print("대상 속성명:", cols_bill)
# 2개의 PCA 속성을 가진 PCA 객체 생성하고, explained_variance_ratio_ 계산을 위해 fit( ) 호출
scaler = StandardScaler()
df_cols_scaled = scaler.fit_transform(X_features[cols_bill])
pca = PCA(n_components=2)
pca.fit(df_cols_scaled)
print("PCA Component별 변동성:", pca.explained_variance_ratio_)
대상 속성명: ['BILL_AMT1', 'BILL_AMT2', 'BILL_AMT3', 'BILL_AMT4', 'BILL_AMT5', 'BILL_AMT6']
PCA Component별 변동성: [0.90555253 0.0509867 ]
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
rcf = RandomForestClassifier(n_estimators=300, random_state=156)
scores = cross_val_score(rcf, X_features, y_target, scoring='accuracy', cv=3 )
print('CV=3 인 경우의 개별 Fold세트별 정확도:',scores)
print('평균 정확도:{0:.4f}'.format(np.mean(scores)))
CV=3 인 경우의 개별 Fold세트별 정확도: [0.8083 0.8196 0.8232]
평균 정확도:0.8170
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# 원본 데이터셋에 먼저 StandardScaler적용
scaler = StandardScaler()
df_scaled = scaler.fit_transform(X_features)
# 6개의 Component를 가진 PCA 변환을 수행하고 cross_val_score( )로 분류 예측 수행.
pca = PCA(n_components=6)
df_pca = pca.fit_transform(df_scaled)
scores_pca = cross_val_score(rcf, df_pca, y_target, scoring="accuracy", cv=3)
print("CV=3 인 경우의 PCA 변환된 개별 Fold세트별 정확도:", scores_pca)
print("PCA 변환 데이터 셋 평균 정확도:{0:.4f}".format(np.mean(scores_pca)))
CV=3 인 경우의 PCA 변환된 개별 Fold 세트별 정확도: [0.7909 0.7965 0.8017]
PCA 변환 데이터 셋 평균 정확도:0.7964
5. 실습 - PCA를 이용한 사람들의 하루 식습관 데이터 세트의 차원 축소
사람들의 하루 식습관 데이터를 만든 후, PCA를 이용하여 데이터를 시각화
import pandas as pd
from sklearn.preprocessing import StandardScaler
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
df = pd.DataFrame(columns=['calory', 'breakfast', 'lunch', 'dinner', 'exercise', 'body_shape'])
df.loc[0] = [1200, 1, 0, 0, 2, 'Skinny']
df.loc[1] = [2800, 1, 1, 1, 1, 'Normal']
df.loc[2] = [3500, 2, 2, 1, 0, 'Fat']
df.loc[3] = [1400, 0, 1, 0, 3, 'Skinny']
df.loc[4] = [5000, 2, 2, 2, 0, 'Fat']
df.loc[5] = [1300, 0, 0, 1, 2, 'Skinny']
df.loc[6] = [3000, 1, 0, 1, 1, 'Normal']
df.loc[7] = [4000, 2, 2, 2, 0, 'Fat']
df.loc[8] = [2600, 0, 2, 0, 0, 'Normal']
df.loc[9] = [3000, 1, 2, 1, 1, 'Fat']
### 1-1) 주성분 분석을 위해 데이터 전처리 과정이 필요로 합니다.(25점)
1. 데이터 특징만으로만 구성된 dataframe을 생성해주고 StandardScaler를 통해 전처리 해주세요.(dataframe 변수명은 X로 해주세요)
2. 데이터프레임을 1개 더 생성하여, 데이터별 클래스를 저장해주세요. body_shape정보는 추후 차원 축소된 데이터를 시각화할 때, 데이터의 정보가 충분히 유지되었는지 확인하기 위해 사용됩니다.(변수명은 Y로 해주세요)
X = df.drop(['body_shape'], axis=1, inplace=False)
Y = df[['body_shape']]
scaled_X = StandardScaler().fit_transform(X)
scaled_X
array([[-1.35205803, 0. , -1.3764944 , -1.28571429, 1. ],
[ 0.01711466, 0. , -0.22941573, 0.14285714, 0. ],
[ 0.61612771, 1.29099445, 0.91766294, 0.14285714, -1. ],
[-1.18091145, -1.29099445, -0.22941573, -1.28571429, 2. ],
[ 1.89972711, 1.29099445, 0.91766294, 1.57142857, -1. ],
[-1.26648474, -1.29099445, -1.3764944 , 0.14285714, 1. ],
[ 0.18826125, 0. , -1.3764944 , 0.14285714, 0. ],
[ 1.04399418, 1.29099445, 0.91766294, 1.57142857, -1. ],
[-0.15403193, -1.29099445, 0.91766294, -1.28571429, -1. ],
[ 0.18826125, 0. , 0.91766294, 0.14285714, 0. ]])
np.cov(matrix.T) or np.cov(matrix, rowvar=False)
- np.cov( )는 일반적으로 matrix의 row가 variable(feature)로 취급되고, column이 variable들의 observation으로 인식
- rowvar=True면 matrix의 row가 variable(feature)로 취급되지만, rowvar=False면 matrix의 column이 variable(feature)로 취급
- matrix가 column마다 feature가 존재하는 형태의 DataFrame이라면 Transpose를 하거나 rowvar=False로 처리
## 1-2) X dataframe의 공분산 행렬을 구해주시고, 고유값과 고유벡터를 구해주세요(25점)
1. 공분산 행렬, 고유값, 고유벡터는 변수에 값이 담겨져 있어야 합니다.
cov_matrix = np.cov(scaled_X.T)
cov_matrix
array([[ 1.11111111, 0.88379717, 0.76782385, 0.89376551, -0.93179808],
[ 0.88379717, 1.11111111, 0.49362406, 0.81967902, -0.71721914],
[ 0.76782385, 0.49362406, 1.11111111, 0.40056715, -0.76471911],
[ 0.89376551, 0.81967902, 0.40056715, 1.11111111, -0.63492063],
[-0.93179808, -0.71721914, -0.76471911, -0.63492063, 1.11111111]])
각 Eigenvalue에 해당하는 Eigenvector가 column vector 형태로 나오기 때문에
인덱스 형태로 가져오기 위해서는 Transpose를 해줘야 한다 -> eig_vecs.T[index]
eig_vals, eig_vecs = np.linalg.eig(cov_matrix)
print(f"Eigenvectors:\n{eig_vecs}\n")
print(f"Eigenvalues:\n{eig_vals}")
Eigenvectors:
[[-0.508005 -0.0169937 -0.84711404 0.11637853 0.10244985]
[-0.44660335 -0.36890361 0.12808055 -0.63112016 -0.49973822]
[-0.38377913 0.70804084 0.20681005 -0.40305226 0.38232213]
[-0.42845209 -0.53194699 0.3694462 0.22228235 0.58954327]
[ 0.46002038 -0.2816592 -0.29450345 -0.61341895 0.49601841]]
Eigenvalues:
[4.0657343 0.8387565 0.07629538 0.27758568 0.2971837 ]
# 1-3) 5차원 데이터를 고유벡터로 사영시켜주세요.(25점)
1. 첫번째 고유벡터를 이용해서 사영시켜주시면 됩니다.
2. 힌트 : a 벡터를 b벡터에 사용시키는 공식인
dot(a,b) / magnitude of b 를 이용해서 사영시킬 수 있습니다.
projected_X = np.dot(scaled_X, eig_vecs.T[0]) / np.linalg.norm(eig_vecs.T[0])
projected_X
array([ 2.22600943, 0.0181432 , -1.76296611, 2.73542407, -3.02711544,
2.14702579, 0.37142473, -2.59239883, 0.39347815, -0.50902498])
# 1-4) 시각화를 진행하겠습니다.(25점)
1. 주성분을 x축으로 하고, 1차원 데이터이므로 y축은 0으로 통일시킵니다.
2. 데이터의 이해를 위해서 클래스를 데이터 마지막 column에 포함시켜주세요
3. 마지막 그림과 같이 산점도를 출력해주시면 됩니다.:
results = pd.DataFrame(projected_X, columns=['PC1'])
results['y-axis'] = 0.0
results['label'] = Y
results
sns.scatterplot(x='PC1', y='y-axis', data=results, hue='label', s=50)
plt.title('PCA result')
728x90