본문 바로가기
프로그래밍/파이썬 공부하기

Python 공부하기 - 7-1 (Numpy)

by _OlZl 2024. 6. 30.

2024.06.30 - [프로그래밍/파이썬 공부하기] - Python 공부하기 - 6 (세트)

 

Python 공부하기 - 6 (세트)

2024.01.27 - [프로그래밍/파이썬 공부하기] - Python 공부하기 - 5 (딕셔너리, 튜플) Python 공부하기 - 5 (딕셔너리, 튜플)2024.01.26 - [프로그래밍/파이썬 공부하기] - Python 공부하기 - 4 (리스트) Python 공부

olzl07.tistory.com

이전까지 파이썬의 여러 자료형들과 그 관련 함수들에 대해 알아봤다면, 오늘부터는 여러 라이브러리들에 대해 알아볼 겁니다. 일단 Numpy, Pandas, Matplotlib, Tensorflow 이런 것들에 대해 공부해볼 생각이고, Math 같은 기본적인 것들에 대해서도 좀 알아볼 생각입니다. 우선 오늘은 첫번째로 Numpy에 관해 알아보겠습니다.


NumPy

NumPy는 Python에서 수치 연산을 위해 만든 가장 기본적이고 대중적으로 쓰이는 라이브러리 중 하나입니다. NumPy는 "Numeric Python"의 약자이며 대규모 다차원 배열과 행렬 연산에 필요한 다양한 함수와 메소드를 제공합니다. NumPy는 데이터 분석, 데이터 처리, 선형 대수, 머신 러닝 등 다양한 분야에서 널리 사용되고 있습니다.

 

  • Numpy의 특징

먼저, Numpy 배열은 모두 동일한 타입을 가져, 메모리를 효율적으로 사용할 수 있습니다.

또, 내부적으로 C언어로 구현되어 있는데, 이 덕분에 복잡한 수학적 연산도 빠르게 처리할 수 있습니다.

여러 함수들을 제공하여 기본적인 산술 연산부터 복잡한 선형대수 연산까지 수많은 연산을 간편하게 수행할 수 있게 하기도 합니다.

 

이런 특징들 덕분에 Numpy는 머신러닝, 딥러닝 등 AI에 관련한 분야에서도 정말 활발하게 쓰이고 있습니다.


차원에 따른 데이터 표현

본격적으로 numpy 배열에 대해 알아보기 전에, numpy 배열에는 차원이라는 개념이 존재하는데, 이게 꽤 중요하므로 차원에 따른 데이터들의 표현에 대해 좀 알아보겠습니다.

 

머신러닝, 딥러닝 등에서는 이제 '텐서 (tensor)'라고 하는 새로운 개념이 등장합니다. 텐서는 데이터를 위한 컨테이너라고 생각하시면 됩니다. 보통 '○차원 텐서' 같은 표현을 쓰곤 하는데 여기서 ○차원은 말 그대로 우리가 흔히 생각하는 차원이라고 보시면 됩니다. 

그럼 지금부터 ○차원 텐서의 종류에 대해 알아보겠습니다.

 

  • 0D 텐서 (스칼라)

하나의 숫자만 담고 있는 텐서0D 텐서, 스칼라라고 부릅니다. numpy.ndim 속성을 사용하면 넘파이 배열의 차원을 확인할 수 있는데, 이를 통해 차원을 알아보겠습니다.

import numpy as np

array = np.array(3)
print(array.ndim)

# 출력 결과 : 0

 

  • 1D 텐서 (벡터)

숫자(스칼라)의 배열을 1D 텐서, 벡터라고 부릅니다. 벡터는 1차원입니다.

import numpy as np

array = np.array([1, 2, 3, 4, 5])
print(array.ndim)

# 출력 결과 : 1

 

  • 2D 텐서 (행렬)

벡터의 배열이 2D 텐서, 행렬입니다. 행렬에는 행과 열 2개의 축이 있고, 따라서 2차원입니다.

import numpy as np

array = np.array([[5, 78, 2, 34, 0], 
                  [6, 79, 3, 35, 1], 
                  [7, 80, 4, 36, 2]]) 
print(array.ndim)

 

  • 3D 텐서

행렬을 하나의 배열로 만들면 직육면체의 형태처럼 되는 3D 텐서가 만들어집니다. (벡터, 행렬 같이 따로 부르는 용어가 있는지는..? 잘 모르겠습니다.) 

흑백 이미지 같은 애들이 대표적인 3D 텐서의 예시입니다.

import numpy as np

scalar = np.array([[[5, 78, 2, 34, 0], 
                   [6, 79, 3, 35, 1], 
                   [7, 80, 4, 36, 2]], 
                  [[5, 78, 2, 34, 0], 
                   [6, 79, 3, 35, 1], 
                   [7, 80, 4, 36, 2]], 
                  [[5, 78, 2, 34, 0], 
                   [6, 79, 3, 35, 1], 
                   [7, 80, 4, 36, 2]]]) 
print(scalar.ndim)

# 출력 결과 : 3

 

  • 4D 텐서와 그 이상

3D 텐서를 또 배열로 만들면 4D 텐서가 되고, 그걸 또 이으면 5D 텐서도 만들수는 있을 겁니다. 그러나, 보통 딥러닝에서는 0D부터 4D까지의 텐서만 다룹니다.

컬러 이미지 같은 게 대표적인 4D 텐서의 예시입니다.

(코드는 귀찮으니 생략!)


Numpy 배열

  • 자료형

Numpy 배열은 값들이 모두 동일한 데이터 타입을 갖습니다. 이 자료형에는 정수(int), 부호없는 정수(uint), 실수(float), 복소수(complex), 논리(bool) 총 5종류가 있고, 이 뒤에 bit 수를 붙여 이야기합니다.

(ex: int64, float32, int8 등)

 

  • 속성

Numpy 배열은 여러 가지 속성을 가지고 있습니다. 이 속성에는 배열의 차원, 모양, 크기, 데이터 타입 등이 있습니다.

 

각각의 속성은 다음과 같습니다.

 

모양 (shape) : 배열의 크기 (행, 열 등등)

차원 (ndim) : 몇 차원 텐서인지 (몇 차원 배열인지)

데이터 타입 (dtype) : 요소의 자료형

크기 (size) : 배열의 모든 원소 수

메모리 크기 (itmesize) : 요소의 크기 (바이트 단위)

주소 (data) : 배열의 원소를 포함하고 있는 버퍼의 주소

축 바이트 (strides) : 배열 각 차원별로 다음 요소로 점프하는데 필요한 거리를 바이트로 표시한 값을 모은 튜플

 

각각의 속성을 확인하는 함수는 다음과 같습니다.

배열명.shape:모양 확인

배열명.ndim : 차원 확인

배열명.dtype : 데이터 타입 확인

배열명.size : 크기 확인 (요소 수)

배열명.itemsize : 요소의 크기 확인 (메모리)

배열명.data : 배열 주소 확인

배열명.strides : 축들을 건너뛰기 위한 바이트 수 확인

import numpy as np
array = np.array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])

print('array의 shape :', array.shape)
print('array의 ndim :', array.ndim)
print('array의 type :', array.dtype)
print('array의 size :', array.size)
print('array의 itemsize :', array.itemsize)
print('array의 data : ', array.data)
print('array의 strides :', array.strides)
array의 shape : (2, 5)
array의 ndim : 2
array의 type : int64
array의 size : 10
array의 itemsize : 8
array의 data :  <memory at 0x7cbd75a3a810>
array의 strides : (40, 8)

 

위에서 생성한 배열은 [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]] 의 형태입니다. 이 배열은 2행 5열의 2차원 배열이고, 총 크기가 10이므로 shape = (2, 5), ndim = 2이고, 64비트 체제로 작동하기에 dtype = int64, itemsize = 8이며 총 요소 수가 10개이므로 size = 10입니다. 또, 이 배열은 <0x7cbd75a3a810>에 저장되어있음을 알 수 있으며 다음 행으로 가기 위해서는 5개의 요소, 다음 열으로 가기 위해서는 1개의 요소를 건너뛰어야 하므로 strides는 (5*8, 1*8)인 것을 알 수 있습니다.

 

  • 데이터 타입에 따른 요소의 크기

앞서 설명했듯 numpy 배열은 여러 종류의 데이터 타입을 지원합니다. 따라서 데이터 타입에 따라 배열의 각 원소의 크기(itemsize)가 다를 수 있습니다. 다음은 몇 가지 일반적인 데이터 타입에 따른 크기의 예시입니다.

np.int8 1
np.int16 2
np.int32 4
np.int64 8
np.float16 2
np.float32 4
np.float64 8

 

그렇다면 int와 float형 외에 bool이나 str 자료형의 크기는 어떨까요?

bool 자료형은 True 혹은 False 값만 가지기 때문에 주로 1바이트만 차지합니다.

그러나 str 자료형 문자열의 길이에 따라 크기가 달라집니다. 따라서 itemsize를 이용해 크기를 확인하면 -1이 출력됩니다.


Numpy 배열 생성

Numpy 배열 생성법은 다음과 같습니다.

 

1. numpy.array( ) 함수

가장 간단한 방법은 numpy.array() 함수를 이용해 데이터를 ndarray로 바꾸는 것입니다.

(ndarray : N-dimensional array의 줄임말로, 임의의 차원으로 구성된 배열을 의미함. 즉, 1차원 배열, 2차원 배열 ... n차원 배열을모두 ndarray라고 부름 = 다차원 배열)

numpy.array() 함수는 리스트, 튜플, 다른 배열 등을 ndarray로 변환해줍니다.

import numpy as np

lst = [1, 2, 3, 4, 5]
tupl = (1, 2, 3, 4)
array1 = np.array(lst)
array2 = np.array(tupl)

print(array1)
print(array2)
[1, 2, 3, 4, 5]
[1, 2, 3, 4]

 

 

2. numpy.arange( ) 함수

numpy.arange() 함수를 이용해서도 numpy 배열을 만들 수 있습니다. numpy.arange() 함수를 이용하면 특정 구간 안에서 일정 간격의 값으로 구성된 배열을 생성할 수 있습니다. 사용법은 range 함수와 동일하게 시작값, 끝값, 간격을 입력해주고 그에 맞춰 배열을 생성합니다.

import numpy as np

array1 = np.arange(0, 10, 2)
array2 = np.array(5, 28, 4)

print(array1)
print(array2)
[0 2 4 6 8]
[ 5  9 13 17 21 25]

 

numpy는 메모리를 최대한 효율적으로 활용하려 합니다. 따라서 위와 같이 두 자리수와 한 자리수가 섞여있는 배열은 모든 요소를 두 자리수로 만들고 한 자리수의 앞에 공백을 넣어 출력하는 걸 볼 수 있습니다.

 

 

3. 특이한 배열 생성 함수

numpy.zeros(크기) : 모든 원소가 0인 배열을 생성. 이 함수는 모델 초기화나 임시 데이터 생성 등에 유용하게 사용됩니다.

numpy.ones(크기) : 모든 원소가 1인 배열을 생성. 이 함수는 가중치 초기화 등에 사용됩니다.

numpy.empty(크기) : 초기화되지 않은 원소로 구성된 배열을 생성. 어떤 값이 들어있는지 알 수 없음.

numpy.full(크기, 지정값) : 모든 원소가 지정된 값인 배열을 생성.

numpy.eye(크기) : 주어진 크기의 단위 행렬 생성.

import numpy as np

print(np.zeros((4, 2)))
print(np.ones((2, 4, 3)))
print(np.empty((3)))
print(np.full((2, 2), 7))
print(np.eye(4))
[[0. 0.]
 [0. 0.]
 [0. 0.]
 [0. 0.]]
 
[[[1. 1. 1.]
  [1. 1. 1.]
  [1. 1. 1.]
  [1. 1. 1.]]

 [[1. 1. 1.]
  [1. 1. 1.]
  [1. 1. 1.]
  [1. 1. 1.]]]
  
[7.74860419e-304 7.74860419e-304 7.74860419e-304]

[[7 7]
 [7 7]]
 
[[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]]

Numpy 배열의 연산

numpy 배열의 연산은 리스트와는 달리 배열의 각 요소에 개별적으로 적용되며, 적용된 새 배열을 생성해 결과를 반환한다는 특징이 있습니다.

 

  • 덧셈

numpy 배열의 덧셈은 + 연산자나 np.add() 함수를 이용해 수행할 수 있습니다.

import numpy as np

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

print(a + 1)
print(a + b)
print(np.add(b, 1))
print(np.add(a, b))
[2 3 4]
[5 7 9]
[5 6 7]
[5 7 9]

 

  • 뺄셈

numpy 배열의 뺄셈은 - 연산자나 np.subtract() 함수를 이용해 수행할 수 있습니다.

import numpy as np

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

print(a - 1)
print(a - b)
print(np.subtract(b, 1))
print(np.subtract(a, b))
[0 1 2]
[-3 -3 -3]
[3 4 5]
[-3 -3 -3]

 

 

  • 곱셈

numpy 배열의 곱셈은 * 연산자나 np.multiply() 함수를 이용해 수행할 수 있습니다.

import numpy as np

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

print(a * 2)
print(a * b)
print(np.multiply(b, 3))
print(np.multiply(a, b))
[2 4 6]
[ 4 10 18]
[12 15 18]
[ 4 10 18]

 

  • 나눗셈

numpy 배열의 나눗셈은 / 연산자나 np.divide() 함수를 이용해 수행할 수 있습니다.

import numpy as np

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

print(a / 2)
print(a / b)
print(np.divide(b, 10))
print(np.divide(a, b))
[0.5 1.  1.5]
[0.25 0.4  0.5 ]
[0.4 0.5 0.6]
[0.25 0.4  0.5 ]

 

  • 나머지

numpy 배열의 나머지는 % 연산자나 np.mod() 함수를 이용해 수행할 수 있습니다.

import numpy as np

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

print(a % 2)
print(a % b)
print(np.mod(b, 3))
print(np.mod(a, b))
[1 0 1]
[1 2 3]
[1 2 0]
[1 2 3]

 

Numpy가 생각보다 분량이 많아 2편에 나눠서 작성해야 할 듯합니다.