기록하는삶

[파이썬/Python] Numpy 모듈 (1) 본문

AI/파이썬(Python)

[파이썬/Python] Numpy 모듈 (1)

mingchin 2022. 1. 21. 01:21
728x90
반응형

Numerical Python, Numpy에 대해 정리해본다. 이름에서 알 수 있듯 과학 계산 등을 위한 '수치적인' 기능들을 제공하는 모듈로, 파이썬에서의 행렬과 벡터 연산과 같은 array 연산의 표준으로 pandas 등의 데이터 분석이나 tensorflow, pytorch 등의 DL 프레임워크를 사용하기 위해서도 필수적으로 익혀야하는 것이 numpy이다.

 

0) numpy의 배열, ndarray

나는 처음 배울 때 이게 얼마나 편리하고 강력한지 몰랐지만, 파이썬의 가장 큰 특징 중 하나인 dynamic typing 덕분에 위와 같은 일이 list에서는 가능했다. 이것이 가능한 이유는 파이썬의 list가 실제로 연속된 메모리 공간에 할당되는 것이 아니라, 각각의 원소가 위치한 메모리 주소만을 연속적으로 가지기 때문인데 이것은 큰 장점이기도 하지만 대용량의 데이터에 대해 list를 활용한 연산을 실시할 경우 시간 복잡도 면에서 큰 단점으로 작용하기도 한다.

 

ndarray는 파이썬에서의 이러한 array를 활용한 연산의 단점을 극복하기 위해 dynamic typing을 포기한 것으로 실제 내부적으로 C의 구조를 사용하여 ndarray를 구성, 아래와 같은 장점을 극대화 하였다.

 

- 빠른 연산 속도

- 메모리 효율적

- 선형대수와 관련된 다양한 연산과 기능을 제공

- C, C++, 포트란 등의 언어와 통합 가능

 

따라서 하나의 배열에는 지정된 하나의 data type만을 넣을 수 있으며, 위처럼 엉망으로 던지면 (dtype이 있다면) dtype을 기준으로 혹은 자동으로 하나로 통일한다. 문자열도 넣을 수 있지만 'num'py 이다보니 대부분의 경우 실수 혹은 정수를 다루게 되겠다.

 

넘파이의 ndarray는 그 차원에 따라 1차원 array는 벡터(vector), 2차원 array는 행렬(matrix), 3차원 이상의 array는 n-tensor(3rd or nth order tensor)라 칭하기도 한다.

 

1) array shape과 dtype에 따른 메모리

행렬(martix)까지는 헷갈릴 것이 없지만, 빈번히 사용되는 3-tensor 혹은 4-tensor는 그 shape 표현이 헷갈리기도 한다.

shape은 보통 튜플로 표현하며, 뒤에서부터 읽는다고 생각하는게 편하다. 위 예시의 shape은 5*4*3인데 첫 []를 구성하는 원소가 3개, 다시 [1,2,3]으로 다음 []를 구성하려면 4개, 다시 [[1,2,3],[1,2,3],[1,2,3],[1,2,3]]으로 다음 []를 구성하려면 5개. 이런 느낌으로 튜플을 뒤에서부터 채워나간다고 생각하면 된다.

 

dtype의 경우 64bit를 사용하는 것이 일반적이지만, 경우에 따라 메모리를 줄이고 싶다면 bit를 낮추는 것을 고려할 수 있다 정도만 기억하면 될 것 같다. 8bits = 1 bytes로, 사용된 숫자가 총 3*4*5=60(개) 이므로 각각 60*8 = 480 bytes와 60 * 4 = 240 bytes를 차지하는 것을 확인할 수 있다. 8bits = 1 bytes 라는 사실만 기억해두자.

 

2) reshape, flatten

두 가지 shape 변경 방법을 기억해야한다. 먼저 reshape은 가능한 숫자 조합 내에서 ndarray의 shape을 변경해주는데, 당연히 차원 변경도 가능하다.

여기서 기억해야 하는 것은 -1을 활용할 수 있다는 것.

마지막 한 자리에 대해 가능한 숫자 조합을 자동으로 찾아준다.

어감 그대로 납작하게 1차원으로 펴주는 것이 flatten이다. 딥러닝의 중간 혹은 마지막 과정에서 빈번하게 사용된다.

 

3) creation functions

① np.arange()

 

range() 함수와 거의 동일하다. 아래처럼 step을 실수 단위로 잡을 수 있다는 것이 가장 큰 특징이다.

② ones, zeros, empty

 

1, 0, 빈 공간으로 채워진 ndarray를 생성한다. shape을 지정해줄 수 있고, 특정 array의 모양을 베껴 만들 수도 있다. empty의 경우 '빈' 것이 할당되면서 아무 메모리 공간이나 사용하는데 이때 메모리 공간을 비우는 것이 아니라 initialize하지 않는 것이라, 이전에 메모리에 할당된 무언가가 있다면 그대로 드러날 수 있으니 유의해야 한다. 

 

③ identity, eye

 

n*n 단위행렬을 생성하거나, 특정 index를 시작으로 대각선의 값들이 1인 행렬을 생성할 수 있다.

np.eye()에는 index인 k값을 지정할 수 있다. 0번째 원소에서 오른쪽 아래로 향하는 대각선이 main으로 index 0을 가지고, 위로 한 칸씩 이동할 때마다 +1, 아래로 한 칸씩 이동할 때마다 -1이다.

 

④ diag

 

대각행렬을 추출하며, 역시나 index k를 지정할 수 있다. 그 의미는 eye와 동일하다.

노란색: k = -1, 파란색: k = 0, 빨간색: k = 1

 

⑤ random sampling

 

uniform 분포, normal 분포 등으로 부터 n개의 샘플을 추출할 수 있다.

 

 

4) operation functions

sum, mean, std, sart, exp, log, sin, cos 등 수많은 연산들을 지원한다. 이때 연산에 따라 연산 실행의 기준이 되는 axis를 지정해주는 것이 가능하다.

좀 헷갈리는데, 위 예시 a의 shape (3,4)의 index 순서가 axis 번호와 같다. 즉 행에 해당하는 3이 axis 0, 열에 해당하는 4가 axis 1로 해당 방향으로의 합(sum)을 구할 수 있다.

np.vstack(), np.hstack()을 이용해 concatenation도 할 수 있다. 의미 그대로 vertical하게 쌓거나 horizontal하게 쌓는다.

이는 np.concatenate() 함수를 이용하는 것과 같다.

다만 vstack과 달리 1차원 벡터 두 개를 쌓아 행렬로 만들어 주지 않기 때문에, concatenate을 시도하는 array 자체를 2차원으로 만들어 놓고 진행해야한다. 원하는 바에 따라 적절하게 사용해야할 것 같다.

5) array operations

array간의 연산(+, -, *, /)은 기본적으로 element-wise operation이다. 즉 같은 위치에 있는 원소들끼리의 연산을 지원한다.

원래 의미의 행렬 곱은 array1.dot(array2) 혹은 @ 연산자를 이용한다.

전치 행렬도 아래 두 가지 방법으로 만들 수 있다.

numpy 연산에서 가장 중요하게 이해해야 하는 개념은 broadcasting이다. 형이 맞지 않는 array들간의 연산도 (가능하다면) 형을 자동으로 맞추어 준 뒤 연산이 진행된다.

쉽게 생각하면 동일한 모양을 복사 붙여넣기하여 이어 붙여서 원래 shape과 같은 모양으로 만들 수 있다면(이 과정이 broadcasting이다.) 자동으로 해당 broadcasting이 진행된 후에 연산을 수행한다.

 

comparisons, boolean 연산, fancy index는 다음 글에서 다뤄보겠다.

728x90
반응형