Python/Numpy

[Numpy] 파이썬 SVD 차원 축소 예제 : np.linalg.svd() 또는 LA.svd()

jimmy_AI 2022. 1. 20. 17:35
반응형

파이썬 넘파이 특이값 분해(SVD) 차원 축소 : LA.svd()

이번 글에서는 파이썬 넘파이에서 특이값 분해(SVD)를 이용하여

\(U, \Sigma, V\)의 3가지 행렬로 분리된 결과를 가져와보고,

이를 이용하여 차원 축소를 진행하는 예시까지 간단하게 살펴보도록 하겠습니다.

 

우선, 오늘 예제에서 사용할 간단한 2 * 3 행렬을 선언해 보겠습니다.

import numpy as np

A = np.array([[-1, 1, 0],
              [0, -1, 1]])

 

 

파이썬 full SVD vs truncated SVD 예제

오늘 설명할 파이썬의 np.linalg.svd (혹은 import numpy.linalg as LA 선언 시 LA.svd)

함수는 full SVD와 truncated SVD의 두 가지 형태를 모두 지원합니다.

 

full SVD와 truncated SVD의 차이는 아래 그림처럼 요약이 가능합니다.

이제 각 경우에 대해서 \(U, \Sigma, V\)를 각각 찾아보도록 하겠습니다.

 

먼저, full SVD 형태입니다. 기본적으로 svd 함수 실행 시 full SVD로 지정됩니다.

np.linalg.svd(A)

# 결과
(array([[-0.70710678,  0.70710678],
        [ 0.70710678,  0.70710678]]),
 array([1.73205081, 1.        ]),
 array([[ 4.08248290e-01, -8.16496581e-01,  4.08248290e-01],
        [-7.07106781e-01, -2.78976253e-16,  7.07106781e-01],
        [ 5.77350269e-01,  5.77350269e-01,  5.77350269e-01]]))

3가지 output이 튜플 형태로 묶여서 등장하고 있습니다.

순서대로 \(U, \Sigma\)의 대각 성분(0인 부분 제외), \(V^T\)를 의미하고 있습니다.

 

따라서, \(U, \Sigma, V\)는 위 결과에서 다음과 같이 추출해낼 수 있습니다.

U, Sigma, V = np.linalg.svd(A)

Sigma = np.diag(list(Sigma) + [0])[:2] # full rank에서 빠진 rank 만큼 0을 채우고 대각화 -> rank 만큼 행 추출

V = V.T # 실제 결과는 V.T 형태였으므로 다시 전치하여 원래 V를 구함

 

이번에는 truncated SVD 형태입니다. full_matrices 인자의 값을 False로 지정하면 됩니다.

np.linalg.svd(A, full_matrices = False)

# 결과
(array([[-0.70710678,  0.70710678],
        [ 0.70710678,  0.70710678]]),
 array([1.73205081, 1.        ]),
 array([[ 4.08248290e-01, -8.16496581e-01,  4.08248290e-01],
        [-7.07106781e-01, -2.78976253e-16,  7.07106781e-01]]))

\(U, \Sigma\)의 대각 성분까지는 동일하게 추출되었으나, \(V^T\)가 간소화 되었습니다.

3 * 2 행렬과 같이 위아래로 긴 형태라면 \(U\) 부분이 간소화된 형태가 나타나게 됩니다.

 

여기서 \(U, \Sigma, V\)를 추출하는 방법은 full SVD보다는 더 간단합니다.

U, Sigma, V = np.linalg.svd(A, full_matrices = False)

Sigma = np.diag(Sigma)

V = V.T

\(\Sigma\)가 대각 행렬 형태이기 때문에 더 쉽게 추출이 가능했습니다.

반응형

파이썬 SVD 이용 차원 축소 예제

이제 위에서 얻어낸 SVD의 결과를 바탕으로 차원 축소를 진행해보겠습니다.

 

만일, 위의 2 * 3 행렬 A의 데이터를 \(\Sigma\)의 첫 번째 성분까지만 이용하여

차원 축소를 진행하려면 아래 그림과 같은 원리가 적용될 것입니다.

full SVD의 결과를 기준으로 차원 축소 행렬 A'의 결과를 만들어 보겠습니다.

(truncated SVD에서도 마찬가지 수행이 가능합니다.)

U, Sigma, V = np.linalg.svd(A)

U_Sigma = U[:, 0:1] * Sigma[0:1]
A_prime = U_Sigma @ v[0:1, :]

A_prime

# 결과
array([[-0.5,  1. , -0.5],
       [ 0.5, -1. ,  0.5]])

2차, 3차 주성분까지 사용하는 경우에는 0:1 부분을 0:2, 0:3 식으로 바꿔주시면 됩니다.

 

차원 축소한 결과의 설명력은 \(\Sigma\)의 값을 이용해서 쉽게 구할 수 있습니다.

U, Sigma, V = np.linalg.svd(A)

Sigma[0:1].sum() / Sigma.sum()
# 0.6339745962155613

마찬가지로 2번째, 3번째 주성분까지 사용한 경우, 0:1 부분을 0:2, 0:3 처럼 바꾸시면 됩니다.

첫 번째 주성분만 사용한 경우, 약 63% 정도의 설명력을 가지고 있었습니다.