(0강) NumPy

💡
선형대수의 계산식을 파이썬으로 구현하는 툴인 NumPy에 대해 알아본다.

numpy란?

Numerical Python의 약자로 각종 과학계산을 위한 라이브러리이다. 선형대수의 각종 계산식을 Python으로 구현할 수 있도록 도와준다. ScipyPandasPythorchTensorflow 등 여러 라이브러리가 내부적으로 numpy를 사용한다. C로 구현된 고성능 과학 계산용 패키지로 Python에서도 굉장히 빠른 연산이 가능하다. 또한 반복문 없이 데이터 배열을 처리할 수 있도록 되어 있다.

ndarray

numpy의 기본 자료형은 ndarray이다. Python의 list와는 다르게 단 한개의 data type만 저장할 수 있다. list는 각 요소가 실제 값을 갖지 않고, 해당하는 값의 주소를 가지고 있는 형태로 해당 값에 접근하려면 이중으로 접근해야하는 문제가 있다. 허나, ndarray는 메모리 공간 상에서 실제 붙어있는 형태로 값을 가지고 있으며 메모리의 공간이 일정하기 때문에 연산이 매우 빠르다. 예시는 아래와 같다.

# list example
list_a = [1,2,3]
list_b = [3,2,1]
print(list_a[0] is list_b[2])  # True

# ndarray example
import numpy as np

ndarray_a = np.array([1,2,3], np.int64)
ndarray_b = np.array([3,2,1], np.int64)

print(ndarray_a[0] is ndarray_b[2])  # False

ndim, shape, dtype, nbytes

ndim은 array의 rank(차원)을 반환한다. shape은 array의 형태를 반환한다. dtype은 elements의 type을 반환한다. nbytes는 해당 array가 차지하는 메모리를 반환한다. 예시는 아래와 같다.

# examples
example_array1 = np.array([1,2,3], np.int64)
example_array2 = np.array([[1],[2],[3]], np.float64)
print(example_array1.ndim)  # 1
print(example_array2.ndim)  # 2
print(example_array1.shape)  # (3,)
print(example_array2.shape)  # (3, 1)
print(example_array1.dtype)  # int64
print(example_array2.dtype)  # float64
print(example_array1.nbytes)  # 24
print(example_array2.nbytes)  # 24

Handling Shape

reshape() 메서드는 array의 shape를 변경한다. element의 갯수와 순서는 동일하게 유지된다. 원하는 차원의 형태를 전달하면 되며, 1은 해당 차원의 갯수를 제한하지 않는 키워드이다. Inplace 연산이 아니므로, 변경한 형태를 저장하고자 한다면 재할당해야한다. flatten()은 다차원 array를 1차원 array로 변환하는 것으로, reshape(-1,)과 동일한 값을 반환한다.

 # reshape example
 test_matrix = [[1, 2, 3, 4], [1, 2, 5, 8]]
 np.array(test_matrix)  # shape == (2, 4)
 test_matrix.reshape(2,2,-1)
 # output
 '''
 array([[[1, 2],
         [3, 4]],
        [[1, 2],
         [5, 8]]])
 '''

 # flatten example
 test_matrix.flatten()  # array([1, 2, 3, 4, 1, 2, 5, 8])
 test_matrix.reshape(-1, )  # array([1, 2, 3, 4, 1, 2, 5, 8])

Indexing & Slicing

list 객체보다 더 자유롭게 Indexing과 Slicing을 할 수 있다. Indexing시에는 ,를 사용해 element에 접근할 수 있고, Slicing 시에는 행과 열 모두를 지정할 수 있다. Indexing은 dimension을 유지하지 않고, Slicing은 유지하는 특징에 유의해야 한다.

 # indexing example
 test_exmaple = np.array([[1, 2, 3], [4.5, 5, 6]], int)
 test_exmaple[0][2]  # 3
 test_exmaple[0, 2]  # 3

 # slicing example
 test_exmaple[:, :2]
 # output
 '''
 array([[1, 2],
        [4, 5]])
 '''

 # different point of indexing and slicing
 test_exmaple[1:, :]  # array([[4, 5, 6]])
 test_exmaple[1, :]  # array([4, 5, 6])

Creation Functions

np.arrange()는 range와 비슷하나, step에 실수를 지정할 수 있다. np.ones()np.zeros()np.full()이 함수들은 해당 shape에 모든 값을 해당값으로 초기화한다. np.empty()는 값을 초기화하지 않고 해당 메모리 공간만을 잡아놓는다. np.ones_like()와 같이 like가 붙는 함수들은 특정 array를 인자로 받아 동일한 shape의 array를 반환한다. np.identity()는 대각선의 값만 1인 2차원 정방행렬을 반환한다. np.eye()는 행과 열의 갯수가 다른 행렬을 생성할 수 있으며, 대각선의 위치도 지정할 수 있다. np.diag()를 사용하면 대각선의 값을 array 형태로 반환 받을 수 있다. np.random 내에는 uniform()normal()exponention() 등 임의의 값을 생성하는 함수가 존재한다.

Operation Functions

sum()mean()std() 등의 함수와 메서드가 존재한다. 해당 연산은 axis에 따라 이루어진다. axis는 각 차원에 대해서 가장 바깥부터 0,1,2..순으로 지정되며, -1은 가장 안쪽의 차원을 지정한다.두 개의 array를 한 개의 array로 합칠수도 있다. vstack은 위 아래로 붙이는 개념이며, hstack은 좌우로 붙이는 개념이다. concatenate는 axis를 지정할 수 있다. np.newaxis를 지정하면, 새로운 축이 생긴다.

array에는 dot product나, Transpose와 같은 행렬연산을 수행할수 있다. 두 array의 shape이 같은 경우 같은 위치에 있는 element끼리 연산이 일어나며, 이를 element-wise operation이라 칭한다. 두 array의 차원이 다른 경우에는 broadcasting 연산이 일어나며, 적은 차원의 array의 값을 확장하여 연산한다.

Comparison

ndarray는 반복문 없이 모든 요소에 조건을 확인할 수 있다. 조건에 따라 Boolean값을 반환하며, 이를 활용해 Indexing을 수행할 수도 있다.numpy내의 np.logical_andnp.logical_notnp.logical_or 메서드로 반환받은 Boolean 값을 활용할 수도 있다. np.where는 조건문을 통해 True인 인덱스를 반환받을 수도 있고, True인 경우와 False인 경우에 반환할 값을 설정할 수 있다. np.isnan이나, np.isfintie를 통해서도 조건을 판단할 수 있다. np.argmax나 np.argmin을 통해 해당 array나 axis의 최대 및 최소값의 인덱스를 반환받을 수 있다.

Boolean & Fancy Index

array[condition]의 형태로 condition의 조건에 해당하는 element만 출력할 수 있다. list 혹은 array의 형태로 array[another_array]를 전달하면 마찬가지로 해당하는 element만 출력할 수 있다. 이와 같은 형태는 array.take(another_array)로 동일하게 사용할 수 있다.

NumPy File I/O

np.loadtxtnp.savetxt와 같은 메서드를 제공하여, numpy의 객체는 text type으로도 입출력이 가능하다. 하지만, Text File보다는 Pickle형태의 File을 주로 사용한다. np.loadnp.save 메서드를 더 자주 활용하며, File 형식은 .npy로 저장한다.


(1강) What is Vector?

💡
벡터의 기본 개념과 연산 등에 대해 알아본다.

Vector란?

Vector는 숫자를 원소로 가지는 list 혹은 array를 의미한다. Vector는 n차원의 공간에서의 한 점을 의미한다. 이 점은 원점으로부터의 상대적 위치를 의미한다. numpy에서는 통상 행벡터로 값을 처리한다. Vector는 xx로 표기하며, Vector의 element는 [x1,x2,...,xd][x1,x2,...,xd]로 표현된다.

Vector Operation

Vector와 Scalar 연산은 broadcasting이 일어나고, 원점으로 부터의 이동에 길이적 변화가 생긴다. Shape이 같은 Vector간에는 동일한 위치의 element간의 연산이 일어난다.

벡터의 기본 개념과 기본 연산의 예시

Norm

Vector의 Norm은 x|x|로 표현되며, 원점으로부터의 거리를 의미한다. Norm에는 L1과 L2가 존재한다.

L1과 L2 Norm의 계산 수식

거리에 대한 개념을 다르게 세운 것이기 때문에 L1과 L2는 기하학적인 성질이 완전히 다르다. 2차원에서 L1은 마름모의 형태, L2는 원의 형태를 가진다. 이 둘은 목적과 필요에 따라 모두 사용된다.

L1과 L2 Norm의 기하학적 성질의 예시

L1 Norm은 element의 절대값의 합으로, x1=i=1dxi||\mathbf{x}||_1 = \sum_{i=1}^{d}{|x_i|} 이다. L2Norm은 피타고라스 정리를 통한 유클리드거리를 구하는 것으로, x2=i=1dxi2||\mathbf{x}||_2 = \sqrt{\sum_{i=1}^{d}{x_{i}^2}}이다.

 # l1 norm example
 def l1_norm(x) :
     x_norm = np.abs(x)
     x_norm = np.sum(x_norm)
     return x_norm

 # l2 norm example
 def l2_norm(x) :
     x_norm = x*x
     x_norm = np.sum(x_norm)
     x_norm = np.sqrt(x_norm)
     return x_norm

두 Vector의 관계

Vector간의 거리를 계산할 때에는 두 Vector간의 차이를 이용해 계산한다. 두 Vector xx와 yy의 거리는 ZeroVectorZeroVector와 xyx−y의 거리와 동일하다. 즉, xy|x−y|은 두 Vector간의 거리가 된다.

제2 코사인 법칙을 통해 각도도 구할 수 있는데, 각도는 오직 L2 Norm을 통해서만 구할 수 있다. 수식은 다음과 같다. cosθ=x22+y22xy222x2y2cosθ=\frac{||x||_2^2+||y||_2^2−||x−y||_2^2}{2||x||2||y||2}

위 수식으로부터 분자를 쉽게 계산하는 방법은 내적이다. 이를 이용하면 다음과 같이 정리할 수 있다.

cosθ=2<x,y>2x2y2cosθ=\frac{2<x,y>}{2||x||_2||y||_2}

<x,y><x,y>는 각 Vector의 내적의 표현으로 i=1dxiyi∑^d_{i=1}x^iy^i와 동일하다. 내적은 np.inner함수로 구현할 수 있으며, 이를 코드로 옮기면 다음과 같다.

 def angle(x, y) :
     v = np.inner(x, y) / (l2_norm(x) * l2_norm(y))
     theta = np.arccos(v)
     return theta

두 Vector 사이의 내적이란?

내적은 정사영(orthogonal projection)된 Vector의 길이와 관련이 있다. y\mathbf{y}에 정사영된 Proj(x)Proj(\mathbf{x})는 코사인 법칙에 의해 xcosθ||\mathbf{x}||\cos\theta와 동일하게 된다. 내적은 Proj(x)Proj(\mathbf{x})y||\mathbf{y}||만큼 조정한 값을 의미한다. 이를 통해 유사도를 측정하는데 사용할 수도 있다.

내적 계산의 의미


(2강) What is Matrix?

💡
행렬의 개념과 연산, 그리고 벡터공간에서 가지는 의미를 알아본다.

Matrix란?

Matrix는 Vector를 원소로 가지는 2차원 listarray를 의미한다. 마찬가지로 numpy에서는 행 Vector를 기본단위로 생각한다. Matrix는 n개의 Vector를 가지고, Vevtor는 m개의 element를 가진다. Matrix는 X\mathbf{X}로 표현된다. Matrix의 element인 Vector는 [x1,x2,...,xn][\mathbf{x_1}, \mathbf{x_2}, ... , \mathbf{x_n}]과 같이 표현된다. Matrix의 n번째 Vector이면서, m번째 element는 xnmx_{nm}으로 표현된다.

행렬과 벡터와 요소의 개념

Matrix의 이해 I

Matrix는 n차원 공간에서의 여러 점으로 생각할 수 있다. 데이터의 집합으로 생각할 수 있는 것이다. i번째 데이터의 j번째 변수의 값을 구하는 등에 사용될 수 있다.

벡터 공간에서의 행렬의 의미

이러한 이해 방식은 Broadcasting이나 element-wise operation 등으로 데이터를 조작하는데 유용하게 사용될 수 있다.

행렬 간 덧셈과 뺄셈 연산의 예시

Transpose 등으로 전치행렬을 만들 수 있으며, 이 때 xijx_{ij}xjix_{ji}가 된다.

Matrix의 곱셈

XY\mathbf{X}\mathbf{Y}와 같은 행렬의 곱셈은 X\mathbf{X}의 i번째 행 벡터와 Y\mathbf{Y}의 j번째 열 벡터의 내적 값을 갖는 행렬이라고 생각할 수 있다. 때문에, 행렬곱셈이 성립하기 위해서는 X\mathbf{X}의 행벡터의 element와 Y\mathbf{Y}의 열벡터의 element의 수가 동일해야한다.

행렬 간 곱셈 연산의 예시

행렬의 곱셈은 순서에 따라 큰 차이가 있기 때문에, XY\mathbf{X}\mathbf{Y}YX\mathbf{Y}\mathbf{X}는 전혀 다른 의미를 갖는다. numpy에서 행렬의 곱셈은 @ 기호를 사용한다. np.inner는 두 행 벡터를 대상으로 수행되며, 수학적으로 보았을 때에는 뒤에오는 벡터를 전치하여 내적한 값을 반환한다. 즉, np.inner(X, Y)XY=(kxikykj)\mathbf{X}\mathbf{Y} = \left( \sum_{k}{x_{ik}y_{kj}} \right)와 동일하다.

np.inner 메서드의 연산의 예시

Matrix의 이해 II

Matrix는 Vector 공간에서 사용되는 연산자로써 이해할 수 있다. 위의 행렬곱셈을 통해 m차원의 공간에 있던 x\mathbf{x}를 n차원 공간의 z\mathbf{z}로 변환할 수 있다는 의미이다. Matrix는 차원을 이동시켜주는 함수이자 수식의 역할을 할 수 있고, 이를 통해 패턴추출이나 데이터 압축 등의 기능을 수행할 수 있다. 수학의 모든 선형 변환은 행렬곱으로 계산할 수 있다.

행렬을 벡터 공간에서의 연산자로 이해할 수 있다.

역행렬의 의미

어떤 행렬 A\mathbf{A}의 역행렬은 A1\mathbf{A^{-1}}로 표현되며, 해당 연산을 되돌리는 개념으로 이해할 수 있다. 역행렬은 다음과 같은 수식을 만족한다. AA1=A1A=I\mathbf{A}\mathbf{A^{-1}} = \mathbf{A^{-1}}\mathbf{A} = \mathbf{I} (여기에서 I\mathbf{I}는 항등행렬을 의미한다.)

역행렬은 정방행렬이면서, 동시에 행렬식이 0이 아닌 경우에만 구할 수 있다. numpy에서는 np.inv를 통해 역행렬을 구할 수 있다.

역행렬 계산의 예시

정방행렬이 아닌 경우에는, 유사역행렬 혹은 무어-팬로즈 역행렬을 구할 수 있다. 이를 통해 n차원으로 이동시켰던 z\mathbf{z}를 m차원의 x\mathbf{x}로 되돌릴 수 있다.

유사 역행렬과 무어-팬로즈 역행렬의 예시

연립방정식과 유사역행렬

유사역행렬을 통해 i<=ji<=j인 경우, (aij)(a_{ij})(bi)(b_{i})가 주어진 상황에서 이를 만족하는 (xj)(x_j)를 구하는 문제를 풀 수 있다. Ax=b\mathbf{Ax} = \mathbf{b}에서 x\mathbf{x}A\mathbf{A}의 유사역행렬과 b\mathbf{b}의 곱이 된다.

nmn ≤ m이면, 유사역행렬 식에 의해 다음과 같이 정리될 수 있다. x=AT(AAT)1b\mathbf{x} = \mathbf{A^T(AA^T)^{-1}b}

유사 역행렬을 통해 연립 방정식의 해를 찾는 예시

선형회귀분석과 무어-팬로즈 역행렬

i>=ji >= j인 경우는 곧 데이터가 변수보다 많은 경우를 의미한다. 이와 같은 경우, 무어-팬로즈 역행렬을 이용해 선형회귀선을 구할 수 있다. 이는 다음과 같이 표현할 수 있다. Xβ=y\mathbf{X}\beta = \mathbf{y}

이 때, 완전히 모든 경우를 만족하는 해를 찾을 수 없기 때문에, y^\mathbf{\hat{y}}y\mathbf{y}오차가 최소한이 되는 β\beta를 찾는다. 무어-팬로즈 역행렬의 정리에 따라 다음과 같이 정리될 수 있다. β=(XTX)1XTy\beta = \mathbf{(X^TX)^{-1}X^Ty}

무어-팬로즈 역행렬을 통해 선형회귀분석의 계수를 찾는 예시

+ Recent posts