Python/라이브러리

[파이썬 라이브러리]numpy.linalg 함수 분석(1)(det,cond,matrix_rank,inv,pinv)

WooJi 2024. 11. 9. 00:59
반응형

오늘은 numpy.linalg 라이브러리에서 자주 사용하는 다섯 가지 함수에 대해서 분석해보았다.

numpy.linalg.det()

linalg.det(a)

  • 행렬식을 계산하는 함수다.

인자(Parameters)

a: (…, M, M) array_like
행렬식을 계산할 행렬이 들어간다.

반환형(Returns)

det: array_like
행렬 a의 행렬식을 반환한다.

사용 예시 코드

import numpy as np

a=np.array([[1,2],
            [5,6]])
print(np.linalg.det(a))

#---------------------------------
b=np.array([[[1,2],
             [3,4]],
            [[5,6],
             [2,8]],
            [[9,10],
             [11,15]]])

print(np.linalg.det(b))

실행결과

-3.999999999999999

[-2. 28. 25.]

$2 \times 2$ 행렬은 $ad-bc$라는 행렬식 공식으로 행렬식을 계산할 수 있다. 위 행렬을 공식에 따라 계산하면 -4가 출력되어야 하지만 -3.999999999999999가 출력된다.
이는 컴퓨터의 부동소수점 연산의 한계에 의한 것이다. 컴퓨터로 소수점를 표현하는 데에 한계가 있기 때문에 사용할 때, 오차 범위를 정해서 적당한 값으로 반올림 또는 반내림이 필요하다.

행렬식은 기본적으로 정방행렬에 대해서만 성립한다. 3차원 행렬(각 평면이 정방행렬일 때)이 입력되는 경우, 각 층별로 각 정방행렬의 행렬식을 계산하여 리스트 형태로 반환한다.


numpy.linalg.cond()

linalg.cond(x, p=None)

행렬의 조건수를 계산하는 함수다.
7가지의 norm 계산 방법을 통해 조건수를 계산한다.

인자(Parameters)

x: (…, M, N) array_like
조건수를 찾을 행렬을 입력한다.

 

p: {None, 1, -1, 2, -2, inf, -inf, ‘fro’}, optional
norm의 종류를 지정하는 인자다.
default값은 2다.

   
p norm for matrices
None 2-norm, 특이값 분해를 이용해 계산
‘fro’ 프로베니우스(Frobenius) norm으로 계산
inf max(sum(abs(x), axis=1)), 각 열의 합 중 최댓값을 이용
-inf min(sum(abs(x), axis=1)), 각 열의 최솟값을 이용
1 max(sum(abs(x), axis=0)), 각 행의 최댓값을 이용
-1 min(sum(abs(x), axis=0)), 각 열의 최솟값을 이용
2 2-norm (largest sing. value),유클리드(Euclidean) 노름을 사용
-2 smallest singular value, 가장 작은 특이값을 이용

반환형(Returns)

c: {float, inf}
주어진 행렬의 조건수를 계산한다.

사용 예시 코드

import numpy as np

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

print(np.linalg.cond(a))
print(np.linalg.cond(a, 'fro'))
print(np.linalg.cond(a, np.inf))
print(np.linalg.cond(a, -np.inf))
print(np.linalg.cond(a, 1))
print(np.linalg.cond(a, -1))
print(np.linalg.cond(a, 2))
print(np.linalg.cond(a, -2))

실행결과

1.4142135623730951
3.1622776601683795
2.0
1.0
2.0
1.0
1.4142135623730951
0.7071067811865475

함수 사용 시 주의할 점

inf 사용 시 np.inf로 사용

위 코드에 보이는 것과 같이 p인자에 inf를 넣고 싶다면 np.inf를 넣어야 제대로 작동한다.


numpy.linalg.matrix_rank()

linalg.matrix_rank(A, tol=None, hermitian=False, *, rtol=None)

특이값 분해 방법을 이용해 행렬의 계수(Rank)를 계산하는 함수다.

인자(Parameters)

A: {(M,), (…, M, N)} array_like
계수를 계산할 행렬이 들어간다.

 

tol: array_like, float, optional
특이값 계산 시 특이값의 한계점이다. tol보다 작거나 같은 값은 0으로 간주해 계수 계산에 영향을 미치지 않는다.

 

hermitian: bool, optional
입력한 행렬이 에르미트(hermitian) 행렬이라면 True를 입력해 특이값 계산이 효율적으로 이루어지게 한다.

 

rtol: array_like, float, optional
상대적 허용 오차를 의미하는 인자다.
tol 인자와 rtol 인자는 둘 중에 하나만 지정될 수 있다.

반환형(Returns)

rank: array_like
주어진 행렬 A의 계수를 반환한다.

사용 예시 코드

import numpy as np

print(np.linalg.matrix_rank(np.eye(4))) # Full rank matrix
I=np.eye(4); I[-1,-1] = 0. # rank deficient matrix
print(np.linalg.matrix_rank(I))
print(np.linalg.matrix_rank(np.ones((4,)))) # 1 dimension - rank 1 unless all 0
print(np.linalg.matrix_rank(np.zeros((4,))))

실행 결과

4
3
1
0

np.linalg.inv()

linalg.inv(a)

  • 역행렬을 계산하는 함수다.
  • 행렬 A가 주어졌을 때, $A@A^{-1}=A^{-1}@A=numpy.eye(A.shape[0])$을 만족하는 $A^{-1}$을 반환한다.

인자(Parameters)

a: (…, M, M) array_like
역행렬을 계산할 행렬이 들어간다.

반환형(Returns)

ainv: (…, M, M) ndarray or matrix
행렬 a의 역행렬을 반환한다.

에러(Error)

LinAlgError
행렬 a가 정방행렬이 아니거나 역행렬을 구할 수 없다면 LinAlgError를 반환한다.

코드

import numpy as np

a = np.array([[1, 2], 
              [3, 4]])

ainv = np.linalg.inv(a)
print(ainv)

b = np.array([[[1., 2.], [3., 4.]], [[1, 3], [3, 5]]])
print(np.linalg.inv(b))

실행 결과

[[[-2.    1.  ]
  [ 1.5  -0.5 ]]

 [[-1.25  0.75]
  [ 0.75 -0.25]]]

2차원 행렬이 입력되는 경우, 곧장 역행렬을 반환한다.

3차원 행렬(평면이 정방행렬인 경우에 한하여)이 입력되는 경우, 각 평면의 역행렬을 계산하여 반환한다.

함수 사용 시 주의할 점

부동 소수점 연산의 한계

코드

import numpy as np

a = np.array([[1, 2], 
              [3, 4]])

ainv = np.linalg.inv(a)

print(ainv@a)
print(a@ainv)

print(np.allclose(a @ ainv, np.eye(2)))
print(np.allclose(ainv @ a, np.eye(2)))

결과

[[1.00000000e+00 0.00000000e+00]
 [1.11022302e-16 1.00000000e+00]]

[[1.0000000e+00 0.0000000e+00]
 [8.8817842e-16 1.0000000e+00]]

True

True

$2 \times 2$ 행렬로 inv함수를 이용해 역행렬을 구하고 다시 원래 행렬에 곱하여 단위행렬이 나오는지 해보았다.
$10^{-16}$ 정도의 아주 작은 오차가 발생한다. 역행렬을 공부할 때, if문 조건에 $inv(A)@A==eye()$ 로 작성했다가 계속 오작동했던 경험이 있었다. 아주 작은 오차때문에 조건문이 False를 반환했던 것이다.
이 역시 부동소수점 계산의 오차로 인한 것이며, 따라서 단위행렬과 비교할 때 allclose와 같은 오차 허용 함수를 사용해주어야 문제가 발생하지 않는다.

특이 행렬에 근접한 경우

입력된 행렬이 특이 행렬(행렬식값이 0인 행렬)에 근접할 경우, 함수는 LinAlgError를 발생시
키지 않고 inv를 반환한다.

코드

import numpy as np
a = np.array([[2,4,6],
              [2,0,2],
              [6,8,14]])

print(np.linalg.det(a))

print(np.linalg.inv(a))  # No errors raised
print(a @ np.linalg.inv(a))

실행결과

1.4210854715201997e-14

[[-1.12589991e+15 -5.62949953e+14  5.62949953e+14]
 [-1.12589991e+15 -5.62949953e+14  5.62949953e+14]
 [ 1.12589991e+15  5.62949953e+14 -5.62949953e+14]]

[[ 0.    0.    0.  ]
 [-0.5   0.75  0.25]
 [ 0.    0.    1.  ]]

위 행렬은 행렬식이 $10^{-14}$ 정도로 0에 근접하다. 하지만 함수는 에러를 발생시키지 않고 역행렬을 반환한다.
반환된 역행렬로 원래 행렬과 곱해보면 단위행렬이 나오지 않고 잘못된 행렬이 출력된다.
이런 행렬을 판별하기 위해서 자료에서는 numpy.linalg.cond() 함수를 사용하라고 한다. 조건수가 $10^k$일 경우, k자리수까지 오차가 발생할 수 있다고 한다.
실제로 위의 행렬의 조건수는 $8.65988 \times 10^17$이다.


numpy.linalg.pinv()

linalg.pinv(a, rcond=None, hermitian=False, *, rtol=)

입력된 행렬의 무어-펜로즈 의사역행렬을 계산하는 함수다.

인자(Parameters)

a: (…, M, N) array_like
의사 역행렬을 구할 행렬이 들어간다.

 

rcond: (…) array_like of float, optional
특이값 분해 시 무시할 특이값의 임계값이다.
default 값은 $1e-15$이다.
rcond * largest_singular_value보다 작거나 같은 특이값들은 0으로 간주한다.
따라서 이 값이 작을수록 더 많은 특이값이 사용되고, 계산이 정확하지만 시간이 오래 걸린다.

 

hermitian: bool, optional
입력 행렬이 Hermitian 행렬인 경우 True를 입력한다.
default 값은 False.
에르미트(Hermitian) 행렬이란 복소수를 성분으로 가지는 정방 행렬 중에서 $A=A^_$를 만족시키는 행렬을 의미한다. \_는 각 성분을 켤레 복소수로 바꾸고 전치한다는 의미다.

무어 펜로즈 의사 역행렬을 구하는 과정에서 특이값 분해가 사용되는데 이때 에르미트 행렬의 SVD(Singular Value Decomposition) 계산 과정에서 특이값과 특이벡터를 구하기 쉽다.
그래서 True를 넣어주어 특이값을 구하는 데에 더 효율적인 방식으로 구하도록 한다.

 

rtol: (…) array_like of float, optional
rcond 와 같은 인자이지만 Array API 호환 가능 매개변수 이름이다.
rcondrtol 둘 중에 하나만 지정할 수 있고 모두 지정되지 않으면 numpy 라이브러리의 default값 $1e-15$가 사용된다. 만약 rtol=None로 지정하면 API 표준 default값이 사용된다.

반환형(Returns)

B: (…, N, M) ndarray
입력된 행렬의 의사역행렬이 반환된다.

에러(Error)

LinAlgError
특이값 분해가 계산되지 않으면 해당 에러를 발생시킨다.

사용 예시 코드

import numpy as np

A=np.array([[1,4],
            [2,8]])
Apinv=np.linalg.pinv(A)
print(Apinv)
print(A@Apinv)

실행결과

[[0.01176471 0.02352941]
 [0.04705882 0.09411765]]

[[0.2 0.4]
 [0.4 0.8]]

의사 역행렬은 원래 행렬에 곱했을 때, 단위행렬에 가깝게 만드는 변환행렬이라고 생각하면 쉽다. 그래서 유일하지 않다.
예시 코드에서도 원래 행렬에 곱해보면 단위행렬은 아니지만 근접한 대칭행렬이 출력됨을 볼 수 있다.


출처

numpy.linalg.det에 대하여
numpy.linalg.cond에 대하여
numpy.linalg.matrix_rank에 대하여
numpy.linalg.inv에 대하여
numpy.linalg.pinv에 대하여

반응형