차원 축소
차원 축소는 많은 피처로 구성된 다차원 데이터 셋의 차원을 축소해 새로운 차원의 데이터 셋을 생성하는 것이다.
일반적으로 차원이 증가할수록 데이터 포인트 간의 거리가 기하급수적으로 멀어지게 되고, 희소(sparse)한 구조를 가지게 된다.
수백 개 이상의 피처로 구성된 데이터 셋읭 경우 상대적으로 적은 차원에서 학습된 모델보다 에측 신뢰도가 떨어진다. 또한 피처가 많은 경우 개별 피처 간의 상관관계가 높을 가능성이 크다. 선형 회귀와 같은 선형 모델에서는 입력 변수 간의 상관관계가 높을 경우 이로 인한 다중 공선성 문제로 모델의 예측 성능이 떨어진다.
다중공선성
: 일부 변수가 다른 변수와 상관도가 높아, 데이터 분석 시 부정적인 영향을 미치는 현상
수십개 이상의 피처가 있는 데이터의 경우 이를 시각적으로 표현해 데이터의 특성을 파악하기는 불가능 하다.
이 경우 3차원 이하의 차원 축소를 통해서 시각적으로 데이터를 압축해서 표현할 수 있다. 또한 차원 축소를 할 경우 학습 데이터의 크기가 줄어들어서 학습에 필요한 처리능력도 줄 일 수 있다.
일반적으로 차원 축소는 feature selection과 feature extraction 으로 나눌 수 있다.feature selection
은 말 그대로 특정 피처에 종속성이 강한 불필요한 피처는 아예 제거하고, 데이터의 특징을 잘 나타내는 주요 피처만 선택하는 것이다.feature extraction
은 기존 피처를 저차원의 중요 피처로 압축해서 추출하는 것이다.
새롭게 추출된 중요 특성은 기존의 피처가 압축된 것이므로 기존의 피처와는 완전히 다른 값이 된다.
feature extraction
기존 피처를 단순 압축이 아닌, 함축적으로 더 잘 설명할 수 있는 또 다른 공간으로 매핑해 추출하는 것이다.
학생을 평가하는 다양한 요소로 모의고사 성적, 종합 내신 성적 , 수능성적등 관련된 여러 가지 피처로 돼있는 데이터 셋이라면 이를 학업 성취도
, 커뮤니케이션 능력
,문제 해결력
과 같은 더 함축적인 요약 특성으로 추출할 수 있다.
함축적인 특성 추출은 기존 피처가 전혀 인지하기 어려웠던 잠재적인 요소를 추출하는 것을 의미한다.
차원 축소 알고리즘이 자주 사용되는 영역은 텍스트 문서의 숨겨진 의미를 추출하는 것이다.
문서는 많은 단어로 구성돼 있다. 문서를 만드는 사람은 어떤 의미나 의도를 가지고 문서를 작성하면서 단어를 사용하게 된다. 일반적으로 사람의 경우 문서를 읽으면서 이 문서가 어떤 의미나 의도를 가지고 작성됐는지 쉽게 인지할 수 있다.
차원 축소 알고리즘은 문서 내 단어들의 구성에서 숨겨져 있는 시맨틱 의미나 토픽을 잠재 요소로 간주하고 이를 찾아 낼 수 있다. SVD와 NMF는 이러한 시맨틱 토픽 모델링을 위한 기반 알고리즘으로 사용된다.
PCA
PCA는 가장 대표적인 차원 축소 기법이다. PCA는 여러 변수 간에 존재하는 상관관계를 이용해 이를 대표하는 주성분을 추출해 차원을 축소하는 기법이다.
PCA는 기존 데이터의 정보 유실 최소화를 위해 가장 높은 분산을 가지는 데이터 축을 찾아 이축 으로 차원을 축소한다. 이것이 PCA의 주성분이 된다.
키와 몸무게 2개의 피처를 가지고 있는 데이터 셋이 다음과 같이 구성돼 있다고 가정 해보자.
이 2개의 피처를 한개의 주성분을 가진 데이터 셋으로 차원 축소를 할 수있다.
데이터 변동성이 가장 큰 방향으로 축을 생성하고, 새롭게 생성된 축으로 데이터를 투영하는 방식이다.
PCA는 제일 먼저 가장 큰 데이터 변동성을 기반으로 첫 번째 벡터 축을 생성하고,
두 번째 축은 이 벡터 축에 직각이 되는 벡터(직교 벡터)를 축으로 한다.
세 번째 축은 다시 두 번째 축과 직각이 되는 벡터를 설정하는 방식으로 축을 생성한다.
이렇게 생성된 벡터 축에 원본 데이터를 투영하면 벡터 축의 개수만큼의 차원으로 원본 데이터가 차원 축소 된다.
PCA는 많은 속성으로 구성된 원본 데이터를 그 핵심을 구성하는 데이터로 압축한 것이다.
붓꽃 데이터 셋은 sepal length, sepal width, petal length, petal width 의 4개의 속성으로 되어 있는데,
이 4개의 속성을 2개의 PCA 차원으로 압축해 원래 데이터 셋과 압축된 데이터 셋이 어떻게 달라 졌는지 확인해 보자.
먼저 사이킷런의 붓꽃 데이터를 로딩한뒤 시각화 하기 편하게 DataFrame로 변환하자.
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)
sepal_length | sepal_width | petal_length | petal_width | target | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3.0 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
각 품종에 따라 원본 붓꽃 데이터 셋이 어떻게 ㅍ분포돼 있는지 2차원으로 시각화 해보자.
x축 : sepal length y축 : sepal width로 하여 품종 데이터 분포를 나타내자.
#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()
Setosa 품종의 경우 sepal width가 3.0 볻 ㅏ크고, sepal length가 6.0 이하인 곳에 일정하게 분포돼 있습니다.
Versicolor와 virginica의 경우는 sepal width와 sepal lenth 조건만으로는 분류가 어려운 복잡한 조건ㅇ미을 알 수 있습니다.
이제 PCA로 4개 속성을 2개로 압축한뒤 2개의 PCA 속성으로 붓꽃 데이터 품종 분포를 시각화해 봅시다.
먼저 붓꽃 데이터 셋에 바로 PCA를 적용하기 전에 개별 속성을 함꼐 스케일링 해야한다.
PCA는 여러 속성의 값을 연산해야 하므로 속성의 스케일에 영향을 받는다.
따라서 여러 속성을 PCA로 압축하기 전에 각 속ㅇ값을 동일한 스케일로 변환해야 한다.
from sklearn.preprocessing import StandardScaler
# Target 값을 제외한 모든 속성 값을 StandardScaler를 이용하여
# 표준 정규 분포를 가지는 값들로 변환
iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:, :-1])
이제 스케일링이 적용된 데이터 셋에 PCA를 적용해 4차원 의 붓꽃 데이터를 2차원 PCA 데이터로 변환해 보자.
사이킷런은 PCA 변환을 위해 PCA 클래스를 제공한다. PCA 클래스는 생성 파라미터로 n_components를 입력 받는다.
n_components 는 PCA로 변환할 차원의 수를 의미하므로 여기서는 2로 설정한다.
이후에 fit(과 transform을 호출해 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 객체의 transform () 메서드를 호출해 원본 데이터 셋을 (150, 2) 의 데이터 셋으로 iris_pca 객체 변수로 반환했다.
iris_pca는 변환된 PCA 데이터 셋을 150*2 넘파이 행렬로 가지고 있다.
이를 DataFrame로 변환환뒤 데이터 값을 살펴 보자.
# 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_component_1 | pca_component_2 | target | |
---|---|---|---|
0 | -2.264703 | 0.480027 | 0 |
1 | -2.080961 | -0.674134 | 0 |
2 | -2.364229 | -0.341908 | 0 |
이제 2개의 속성으로 PCA 변환된 데이터 셋을 2차원상에서 기가화 해보자.
pca_component_1 속성을 X축으로, pca_component_2 속성을 Y축으로 해서 붗꽃 품종이 어떻게 분포되는지 확인하자.
#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()
CA 변환된 데이터 셋이 원본보다 더 명확하게 분리돼 있다. PCA로 변환 후에는 마치 클러스터링을 적용한 것 같이 품종별로 pca_component_1 값을 기준으로 비교적 명확한 구분이 가능하다.
이는 PCA의 첫 번째 새로운 축인 pca_component_1이 원본 데이터의 변동성을 매우 잘 반영했기 때문이다.
PCA 변환을 수행한 PCA 객체의 explained_variance_ratio_ 속성은 전체 변동성에서 개별 PCA 컴포넌트별로 차지하는 변동성 비율을 제공하고 있다.
print(pca.explained_variance_ratio_)
[0.72962445 0.22850762]
첫 번째 PCA 변환 요소인 pca_component_1 이 전체 변동성의 약 73%를 차지하고 두 번째 변환 요소는 22.8%를 차지 한다.
따라서 PCA를 2개 요소로만 변환해도 원본 데이터의 변동성을 95% 설명할 수 있다.
이번에는 원본 붓꽃 데이터 셋과 PCA로 변환된 데이터 셋에 각각 분류를 적용한 후 결과를 비교하자.
Estimator는 RandomForestClassifier를 이용하고 corss_val_score()로 3개의 교차 검증 셋으로 정확도 결과를 비교한다.
먼저 원본 붓꽃 데이터에 랜덤 포레스트를 적용한 결과는 다음 과 같다.
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
이번에는 기존 4차원 데이터를 2차원으로 PCA 변환환 데이터 세트에 랜덤포레스트를 적용해 보자.
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는 변환 차원 개수에 따라 예측 성능이 떨어질 수 밖에 없다.
고차원의 데이터를 저차원의 데이터로 변환하면 직관적으로 이해하기도 편하며, 데이터의 주축을 이루는 속성이 어떤 것인지도 쉽게 파악할 수 있다.
다음으로는 좀 더 많은 피처를 가진 데이터 셋에 적은 PCA 컴포넌트 기반으로 변환한 뒤, 예측 영향도가 어떻게 되는지 변환된 PCA 데이터 셋에 기반해서 비교해 보겠습니다.
사용 할 데이터는 신용 카드 고객 데이터 셋입니다.
- credit card 데이터 세트 PCA 변환
import pandas as pd
df = pd.read_excel('C:/Users/ariz/Documents/excel/credit_card.xls', sheet_name='Data')
print(df.shape)
df.iloc[:3,:10]
(30001, 25)
Unnamed: 0 | X1 | X2 | X3 | X4 | X5 | X6 | X7 | X8 | X9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | ID | LIMIT_BAL | SEX | EDUCATION | MARRIAGE | AGE | PAY_0 | PAY_2 | PAY_3 | PAY_4 |
1 | 1 | 20000 | 2 | 2 | 1 | 24 | 2 | 2 | -1 | -1 |
2 | 2 | 120000 | 2 | 2 | 2 | 26 | -1 | 2 | 0 | 0 |
신용 카드 데이터는 30000개의 레코드와 24개의 속성을 가지고 있다.
이중에서 'default payment next month' 속성이 Target 값으로 '다음달 연체 여부'를 의미하며 1: 연체, 0: 정상 납부를 의미한다.
알고리즘을 적용하기 전에 전처리를 해주자.
# header로 의미없는 첫행 제거, iloc로 기존 id 제거
import pandas as pd
df = pd.read_excel('C:/Users/ariz/Documents/excel/credit_card.xls', header=1, sheet_name='Data').iloc[0:,1:]
print(df.shape)
df.iloc[:3,:10]
(30000, 24)
LIMIT_BAL | SEX | EDUCATION | MARRIAGE | AGE | PAY_0 | PAY_2 | PAY_3 | PAY_4 | PAY_5 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 20000 | 2 | 2 | 1 | 24 | 2 | 2 | -1 | -1 | -2 |
1 | 120000 | 2 | 2 | 2 | 26 | -1 | 2 | 0 | 0 | 0 |
2 | 90000 | 2 | 2 | 2 | 34 | 0 | 0 | 0 | 0 | 0 |
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
해당 데이터 셋은 23개의 속성 데이터 셋이 있으나 각 속성끼리 상관도가 매우 높다.
DataFrame의 corr() 을 이용해 각 속성간의 상관도를 구한 뒤 이를 히트맵으로 시각화 해보자
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')
BILL_AMT1 ~ BILL_AMT6 6개 속성끼리의 상광도가 대부분 0.9 이상으로 매우 높음을 알 수 있다.
이보다는 낮지만 PAY_1 ~ PAY_6 까지의 속성 역시 상관도가 높다.
이렇게 높은 상관도를 가진 속성들은 소수의 PCA 만으로도 자연스럽게 이 속성들의 변동성을 수용할 수 있다.
이 BILL_AMT1 ~ BILL_AMT6 6ro 속성을 2개의 컴포넌트로 PCA 변환한 뒤 개별 컴포넌트의 변동성을 explained_variance_ratio_속성으로 알아보자.
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 ]
단 2개의 PCA 컴포넌트만으로도 6개 속성의 변동성을 약 91% 설명할 수 있으며 ,
특히 첫 번쨰 PCA 축으로 90%의 변동성을 숭룡할 정도로 이 6개 속성의 상관도가 매우 높다.
이번에는 원본 데이터 셋과 6개의 컴포넌트로 PCA 변환한 데이터 셋의 분류 예측 결과를 상호 비교해 보자.
먼저 원본 데이터 셋에 랜덤 포레스트를 이용해 타깃 값의 디폴트 값을 3개의 교차검증 셋으로 분류 예측했다.
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('평균 정확도:'.format(np.mean(scores)))
CV=3 인 경우의 개별 Fold세트별 정확도: [0.8081 0.8197 0.8232]
평균 정확도:0.8170
3개의 교차 검증 셋에서 평균 예측 정확도는 약 81.71%를 나타 냈다.
이번에는 6개의 컴포넌트로 PCA 변환한 데이터 셋에 대해서 동일하게 분류 예측을 적용해 보자.
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 변환 데이터 셋 평균 정확도:'.format(np.mean(scores_pca)))
CV=3 인 경우의 PCA 변환된 개별 Fold세트별 정확도: [0.7902 0.7976 0.8033]
PCA 변환 데이터 셋 평균 정확도:0.7970
전체 23개 속성의 약 1/4 수준인 6개의 PCA 컴포넌트만으로도 원본 데이터를 기반으로 한 분류 예측 결과보다 약 1~2 %정도의 예측 성능 저하만 발생했다.
1~2%의 예측 저하가 미비하다고 하긴 힘들지만 전체 속성의 1/4 만으로 이정도 수치 예측 성능을 유지하는 것이 대단한 것이다.
본문 내용은 파이썬 머신러닝 완벽 가이드 (권철민 저)을 요약정리한 내용입니다.