728x90
1. LDA (Principal Component Analysis)
LDA
- LDA는 선형 판별 분석법으로 불리며, PCA와 매우 유사함
- PCA와 유사하게 입력 데이터 세트를 저차원 공간에 투영해 차원을 축소하는 기법
- PCA는 비지도학습, LDA는 지도학습(분류) 기법 중 하나
LDA는 지도학습의 분류(Classification)에서 사용하기 쉽도록 개별 Class를 분별할 수 있는 기준을 최대한 유지하면서 차원을 축소함
PCA는 입력 데이터의 변동성이 가장 큰 축을 찾았지만, LDA는 입력 데이터의 결정 값 Class를 최대한으로 분리할 수 있는 축을 찾음
LDA는 같은 Class의 데이터는 최대한 근접해서, 다른 Class의 데이터는 최대한 떨어뜨리는 축 매핑을 함
LDA 차원 축소 방식
- LDA는 특정 공간상에서 Class 분리를 최대화하는 축을 찾기 위해 Class간 분산과 Class 내부 분산의 비율을 최대화 하는 방식으로 차원을 축소함
- Class 간 분산은 최대한 크게 가져가고, Class 내부의 분산은 최대한 작게 가져가는 방식
LDA 접근법
- 분산행렬
- 확률모형 (베이즈룰)
LDA 절차
- 원본 데이터에서 클래스간 분산과, 클래스 내부 분산 행렬을 생성
- 이 두 행렬은 입력 데이터의 결정 값 클래스별로 개별 피처의 평균 벡터(mean vector)를 기반으로 구함
- 클래스 내부 분산 행렬을 $S_{w}$, 클래스 간 분산 행렬을 $S_{B}$를 기반으로 두 행렬을 고유벡터와 고유값 분해
- 고유값이 가장 큰 순으로 K개 (LDA 변환 차수) 고유벡터 추출
- 추출된 고유벡터를 이용하여 새롭게 입력 데이터를 변환
2. 실습 - 붓꽃 데이터 셋에 LDA 적용하기
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
iris = load_iris()
iris_scaled = StandardScaler().fit_transform(iris.data)
lda = LinearDiscriminantAnalysis(n_components=2)
# fit()호출 시 target값 입력
lda.fit(iris_scaled, iris.target)
iris_lda = lda.transform(iris_scaled)
print(iris_lda.shape)
# (150, 2)
- PCA보다 분류 성능이 더 높음
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
lda_columns=['lda_component_1','lda_component_2']
irisDF_lda = pd.DataFrame(iris_lda,columns=lda_columns)
irisDF_lda['target']=iris.target
#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_lda[irisDF_lda['target']==i]['lda_component_1']
y_axis_data = irisDF_lda[irisDF_lda['target']==i]['lda_component_2']
plt.scatter(x_axis_data, y_axis_data, marker=marker,label=iris.target_names[i])
plt.legend(loc='upper right')
plt.xlabel('lda_component_1')
plt.ylabel('lda_component_2')
plt.show()
- 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)
from sklearn.preprocessing import StandardScaler
iris_scaled = StandardScaler().fit_transform(irisDF.iloc[:, :-1])
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)
pca_columns=['pca_component_1','pca_component_2']
irisDF_pca = pd.DataFrame(iris_pca,columns=pca_columns)
irisDF_pca['target']=iris.target
#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()
3. SVD (Singular Value Decomposition)
SVD (Singular Value Decomposition)
- SVD 시각화
- 직접 SVD 시행
SVD 유형
- FUll SVD
- Compact SVD
- 비대각 부분과 대각 원소가 0인 부분을 제거
- 대각 원소가 0: 행 간 의존성이 높을 경우 선형 종속
- Truncated SVD
- 대각 원소 가운데 상위 r개만 추출하여 차원 축소
Truncated SVD 행렬 분해 의미
- SVD는 차원 축소를 위한 행렬 분해를 통해 Latent Factor (잠재 요인)를 찾을 수 있는데, 이렇게 찾아진 Latent Factor는 많은 분야에 활용 (추천 엔진, 문서의 잠재 의미 분석 등)
- SVD로 차원 축소 행렬 분해된 후 다시 분해된 행렬을 이용하여 원복된 데이터 셋을 잡음(Noise)가 제거된 형태로 재구성될 수 있음 (원본 복구가 유일하게 불가능)
- 학습 속도 상승, 다중 - 공선성 문제 줄어듦 -> 모델 성능 상승
- Scikit - Learn에서는 Truncated SVD로 차원을 축소할 때 원본 데이터에 $U\sum$를 적용하여 차원 축소
- 행렬 A를 여러 개의 rank 1 Matrix의 합으로 분해하되, 각 Matrix마다 가중치 $\sigma$가 다르기에 $\sigma$가 큰 행렬만 살려도 A의 정보를 어느정도 보존 가능
- Scaling으로 데이터 세트의 중심이 동일해지면 Truncated SVD와 PCA는 동일한 변환을 수행
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import TruncatedSVD, PCA
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
iris = load_iris()
iris_ftrs = iris.data
# 붓꽃 데이터를 StandardScaler로 변환
scaler = StandardScaler()
iris_scaled = scaler.fit_transform(iris_ftrs)
# 스케일링된 데이터를 기반으로 TruncatedSVD 변환 수행
tsvd = TruncatedSVD(n_components=2)
tsvd.fit(iris_scaled)
iris_tsvd = tsvd.transform(iris_scaled)
# 스케일링된 데이터를 기반으로 PCA 변환 수행
pca = PCA(n_components=2)
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled)
# TruncatedSVD 변환 데이터를 왼쪽에, PCA변환 데이터를 오른쪽에 표현
fig, (ax1, ax2) = plt.subplots(figsize=(9,4), ncols=2)
ax1.scatter(x=iris_tsvd[:,0], y= iris_tsvd[:,1], c= iris.target)
ax2.scatter(x=iris_pca[:,0], y= iris_pca[:,1], c= iris.target)
ax1.set_title('Truncated SVD Transformed')
ax2.set_title('PCA Transformed')
SVD 활용
- 이미지 압축 / 변환
- 추천 엔진
- 문서 잠재 의미 분석
- 의사(pseudo) 역행렬을 통한 모델 예측
4. 실습 - SVD (Singular Value Decomposition)
SVD 개요
# numpy의 svd 모듈 import
import numpy as np
from numpy.linalg import svd
# 4X4 Random 행렬 a 생성
np.random.seed(121)
a = np.random.randn(4, 4)
print(np.round(a, 3))
[[-0.212 -0.285 -0.574 -0.44 ]
[-0.33 1.184 1.615 0.367]
[-0.014 0.63 1.71 -1.327]
[ 0.402 -0.191 1.404 -1.969]]
- SVD 행렬 분해
- Sigma는 대각원소의 값만 가져옴
U, Sigma, Vt = svd(a)
print(U.shape, Sigma.shape, Vt.shape)
print('U matrix:\n',np.round(U, 3))
print('Sigma Value:\n',np.round(Sigma, 3))
print('V transpose matrix:\n',np.round(Vt, 3))
(4, 4) (4,) (4, 4)
U matrix:
[[-0.079 -0.318 0.867 0.376]
[ 0.383 0.787 0.12 0.469]
[ 0.656 0.022 0.357 -0.664]
[ 0.645 -0.529 -0.328 0.444]]
Sigma Value:
[3.423 2.023 0.463 0.079]
V transpose matrix:
[[ 0.041 0.224 0.786 -0.574]
[-0.2 0.562 0.37 0.712]
[-0.778 0.395 -0.333 -0.357]
[-0.593 -0.692 0.366 0.189]]
- 분해된 행렬들을 이용하여 다시 원행렬로 원복
# Sima를 다시 0 을 포함한 대칭행렬로 변환
Sigma_mat = np.diag(Sigma)
a_ = np.dot(np.dot(U, Sigma_mat), Vt)
print(np.round(a_, 3))
[[-0.212 -0.285 -0.574 -0.44 ]
[-0.33 1.184 1.615 0.367]
[-0.014 0.63 1.71 -1.327]
[ 0.402 -0.191 1.404 -1.969]]
compact SVD
- 데이터 의존도가 높은 원본 데이터 행렬 생성
a[2] = a[0] + a[1]
a[3] = a[0]
print(np.round(a,3))
[[-0.212 -0.285 -0.574 -0.44 ]
[-0.33 1.184 1.615 0.367]
[-0.542 0.899 1.041 -0.073]
[-0.212 -0.285 -0.574 -0.44 ]]
# 다시 SVD를 수행하여 Sigma 값 확인
U, Sigma, Vt = svd(a)
print(U.shape, Sigma.shape, Vt.shape)
print('Sigma Value:\n',np.round(Sigma,3))
(4, 4) (4,) (4, 4)
Sigma Value:
[2.663 0.807 0. 0. ]
# U 행렬의 경우는 Sigma와 내적을 수행하므로 Sigma의 앞 2행에 대응되는 앞 2열만 추출
U_ = U[:, :2]
Sigma_ = np.diag(Sigma[:2])
# V 전치 행렬의 경우는 앞 2행만 추출
Vt_ = Vt[:2]
print(U_.shape, Sigma_.shape, Vt_.shape)
# U, Sigma, Vt의 내적을 수행하며, 다시 원본 행렬 복원
a_ = np.dot(np.dot(U_,Sigma_), Vt_)
print(np.round(a_, 3))
(4, 2) (2, 2) (2, 4)
[[-0.212 -0.285 -0.574 -0.44 ]
[-0.33 1.184 1.615 0.367]
[-0.542 0.899 1.041 -0.073]
[-0.212 -0.285 -0.574 -0.44 ]]
Truncated SVD
- Truncated SVD를 이용한 행렬분해: svds 이용
import numpy as np
from scipy.sparse.linalg import svds
from scipy.linalg import svd
# 원본 행렬을 출력하고, SVD를 적용할 경우 U, Sigma, Vt 의 차원 확인
np.random.seed(121)
matrix = np.random.random((6, 6))
print('원본 행렬:\n',matrix)
U, Sigma, Vt = svd(matrix, full_matrices=False)
print('\n분해 행렬 차원:',U.shape, Sigma.shape, Vt.shape)
print('\nSigma값 행렬:', Sigma)
# Truncated SVD로 Sigma 행렬의 특이값을 4개로 하여 Truncated SVD 수행.
num_components = 5
U_tr, Sigma_tr, Vt_tr = svds(matrix, k=num_components)
print('\nTruncated SVD 분해 행렬 차원:',U_tr.shape, Sigma_tr.shape, Vt_tr.shape)
print('\nTruncated SVD Sigma값 행렬:', Sigma_tr)
matrix_tr = np.dot(np.dot(U_tr,np.diag(Sigma_tr)), Vt_tr) # output of TruncatedSVD
print('\nTruncated SVD로 분해 후 복원 행렬:\n', matrix_tr)
원본 행렬:
[[0.11133083 0.21076757 0.23296249 0.15194456 0.83017814 0.40791941]
[0.5557906 0.74552394 0.24849976 0.9686594 0.95268418 0.48984885]
[0.01829731 0.85760612 0.40493829 0.62247394 0.29537149 0.92958852]
[0.4056155 0.56730065 0.24575605 0.22573721 0.03827786 0.58098021]
[0.82925331 0.77326256 0.94693849 0.73632338 0.67328275 0.74517176]
[0.51161442 0.46920965 0.6439515 0.82081228 0.14548493 0.01806415]]
분해 행렬 차원: (6, 6) (6,) (6, 6)
Sigma값 행렬: [3.2535007 0.88116505 0.83865238 0.55463089 0.35834824 0.0349925 ]
Truncated SVD 분해 행렬 차원: (6, 5) (5,) (5, 6)
Truncated SVD Sigma값 행렬: [0.35834824 0.55463089 0.83865238 0.88116505 3.2535007 ]
Truncated SVD로 분해 후 복원 행렬:
[[0.11368271 0.19721195 0.23106956 0.15961551 0.82758207 0.41695496]
[0.55500167 0.75007112 0.24913473 0.96608621 0.95355502 0.48681791]
[0.01789183 0.85994318 0.40526464 0.62115143 0.29581906 0.92803075]
[0.40782587 0.55456069 0.24397702 0.23294659 0.035838 0.58947208]
[0.82711496 0.78558742 0.94865955 0.7293489 0.67564311 0.73695659]
[0.5136488 0.45748403 0.64231412 0.82744766 0.14323933 0.0258799 ]]
Scikit - Learn TruncatedSVD Class를 이용한 변환
from sklearn.decomposition import TruncatedSVD, PCA
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
%matplotlib inline
iris = load_iris()
iris_ftrs = iris.data
# 2개의 주요 component로 TruncatedSVD 변환
tsvd = TruncatedSVD(n_components=2)
tsvd.fit(iris_ftrs)
iris_tsvd = tsvd.transform(iris_ftrs)
# Scatter plot 2차원으로 TruncatedSVD 변환 된 데이터 표현. 품종은 색깔로 구분
plt.scatter(x=iris_tsvd[:,0], y= iris_tsvd[:,1], c= iris.target)
plt.xlabel('TruncatedSVD Component 1')
plt.ylabel('TruncatedSVD Component 2')
- TruncatedSVD vs PCA: 거의 유사
from sklearn.preprocessing import StandardScaler
# iris 데이터를 StandardScaler로 변환
scaler = StandardScaler()
iris_scaled = scaler.fit_transform(iris_ftrs)
# 스케일링된 데이터를 기반으로 TruncatedSVD 변환 수행
tsvd = TruncatedSVD(n_components=2)
tsvd.fit(iris_scaled)
iris_tsvd = tsvd.transform(iris_scaled)
# 스케일링된 데이터를 기반으로 PCA 변환 수행
pca = PCA(n_components=2)
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled)
# TruncatedSVD 변환 데이터를 왼쪽에, PCA변환 데이터를 오른쪽에 표현
fig, (ax1, ax2) = plt.subplots(figsize=(9,4), ncols=2)
ax1.scatter(x=iris_tsvd[:,0], y= iris_tsvd[:,1], c= iris.target)
ax2.scatter(x=iris_pca[:,0], y= iris_pca[:,1], c= iris.target)
ax1.set_title('Truncated SVD Transformed')
ax2.set_title('PCA Transformed')
5. Matrix Factorization & NMF
Matrix Factorization
- 일반적으로 SVD와 같은 행렬 분해 기법을 통칭하는 것
- 행렬 분해를 하게 되면 W행렬과 H행렬은 일반적으로 길고 가는 행렬(M>N)W와 작고 넓은 행렬(M<N)로 분해됨
- 이렇게 분해된 행렬은 Latent Factor(잠재 요소)를 특성으로 가지게 됨
분해 행렬 W는 원본 행에 대해서 이 잠재 요소의 값이 얼마나 되는지에 대응하며
분해 행렬 H는 이 잠재요소가 원본 열 (원본 속성)로 어떻게 구성됐는지를 나타내는 행렬
NMF (Non - Negative Matrix Factorization)
음수를 포함하지 않는 행렬 X를 음수를 포함하지 않는 행렬 W와 H의 곱으로 분해하는 기법
- SVD 분해 방법보다 Error가 크다
- W, H의 값들이 양수이기 때문에 해석가능하다는 장점이 있음
- 값이 더 클수록 새로운 잠재요소의 원래 행렬 X에 관련도가 더 높다고 해석이 가능함
- Non - Negative 데이터는 Non - Negative Values로 설명하는 것이 좋음
CODE Structure
nmf = NMF(n_components)
W = nmf.fit_transform(DataFrame)
H = nmf.components_
nmf = NMF(n_components=20, max_iter=1000)
W = nmf.fit_transform(pivot)
H = nmf.components_
from sklearn.decomposition import NMF
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
%matplotlib inline
iris = load_iris()
iris_ftrs = iris.data
nmf = NMF(n_components=2)
nmf.fit(iris_ftrs)
iris_nmf = nmf.transform(iris_ftrs)
plt.scatter(x=iris_nmf[:,0], y= iris_nmf[:,1], c= iris.target)
plt.xlabel('NMF Component 1')
plt.ylabel('NMF Component 2')
728x90