프로그래밍/Python

[Python] 넘파이(Numpy)

노력의천재 2020. 11. 23. 16:43

넘파이

※ 라이브러리 사용법

import numpy 

A = numpy.array([1, 2])

print("A == ", A, ", type == ", type(A))
# 결과
A ==  [1 2] , type ==  <class 'numpy.ndarray'>
import numpy as np

A = np.array([1, 2])

print("A == ", A, ", type == ", type(A))
# 결과
A ==  [1 2] , type ==  <class 'numpy.ndarray'>
from numpy import exp

result = exp(1)

print("result == ", result, ", type == ", type(result))
# 결과
result ==  2.718281828459045 , type ==  <class 'numpy.float64'>
from numpy import *

result = exp(1) + log(1.7) + sqrt(2)

print("result == ", result, ", type == ", type(result))
# 결과
result ==  4.663123641894311 , type ==  <class 'numpy.float64'>

넘파이(Numpy)

vector / matrix 생성

  • numpy는 머신러닝 코드 개발할 경우 자주 사용되는 벡터, 행렬 등을 표현하고 연산할 때 반드시 필요한 라이브러리
  • 머신러닝에서는 숫자, 사람, 동물 등의 인식을 하기 위해서는 이미지(image) 데이터를 행렬(matrix)로 변환하는 것이 중요
  • 행렬을 나타내기 위해서 리스트를 사용할 수도 있지만, 행렬 연산이 직관적이지 않고, 오류 가능성이 높기 때문에 행렬 연산을 위해서 numpy 사용이 필수!
# numpy vs list

import numpy as np

# list

A = [[1, 0], [0, 1]]
B = [[1, 1], [1, 1]]

print(A + B)

# numpy

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

print(A + B)
# 결과
[[1, 0], [0, 1], [1, 1], [1, 1]]
[[2 1]
 [1 2]]

 

  • 머신러닝 코드 구현시, 연산을 위해서 vector, matrix 등의 형상(shape), 차원(dimension)을 확인하는 것이 필요!
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])

# vector A, B 출력
print("A == ", A, ", B == ", B)

# vector A, B 형상 출력 => shape
print("A.shape == ", A.shape, ", B.shape == ", B.shape)

# vector A, B 차원 출력 => dimension
print("A.ndim == ", A.ndim, ", B.ndim == ", B.ndim)
# 결과
A ==  [1 2 3] , B ==  [4 5 6]
A.shape ==  (3,) , B.shape ==  (3,)
A.ndim ==  1 , B.ndim ==  1

 

  • 벡터간 사칙연산(+, -, *, /)은 벡터의 각각의 원소에 대해서 수행됨
print("A + B == ", A + B)
print("A - B == ", A - B)
print("A * B == ", A * B)
print("A / B == ", A / B)
# 결과
A + B ==  [5 7 9]
A - B ==  [-3 -3 -3]
A * B ==  [ 4 10 18]
A / B ==  [0.25 0.4  0.5 ]

 

  • 행렬은 벡터와 마찬가지로 np.array[[...], [...], [...])를 사용하여 생성
  • reshape() 함수를 사용하여 벡터를 행렬로 변경하거나, 행렬을 다른 현상의 행렬로 변경할 수 있음
C = np.array([1, 2, 3])

# 벡터 형상 출력
print("C.shape == ", C.shape)

# 벡터를 1x3 행렬로 형 변환
C = C.reshape(1, 3)

# 행렬 형상 출력
print("C.shape == ", C.shape)
# 결과
C.shape ==  (3,)
C.shape ==  (1, 3)

행렬 곱 (dot product)

  • A 행렬과 B 행렬의 곱 (dot product)는 np.dot(A, B) 으로 나타내며, 행렬 A의 열벡터와 행렬 B의 행벡터가 같아야 함
  • 만약 같지 않다면, reshape() 또는 전치행렬(transpose) 등을 사용하여 형 변환을 한 후에 행렬 곱 실행
A = np.array([[1, 2, 3], [4, 5, 6]]) # 2x3 행렬
B = np.array([[-1, -2], [-3, -4], [-5, -6]]) # 3x2 행렬

# (2x3) dot product (3x2) = (2x2) 행렬
C = np.dot(A, B)

# matrix A, B 형상 출력 => shape
print("A.shape == ", A.shape, ", B.shape == ", B.shape)
print("C.shape == ", C.shape)
print(C)
# 결과
A.shape ==  (2, 3) , B.shape ==  (3, 2)
C.shape ==  (2, 2)
[[-22 -28]
 [-49 -64]]

 

  • 행렬 곱은 행렬의 원소 개수가 같아야만 계산할 수 있는 사칙연산의 한계를 벗어남
  • 행렬 곱 조건을 만족하는 다양한 크기의 행렬을 연속으로 만들고
  • 행렬 곱을 연속으로 계산하면서
  • 결과 값을 만들 수 있기 때문에 머신러닝과 이미지 프로세싱 분야에서 자주 사용됨
  • 만약 행렬 곱을 사용하지 않으면, 똑같은 크기를 가지는 특성 값만을 사용해야 하기 때문에 다양한 특성을 갖는 필터 개발이 불가

전치행렬(transpose)

  • 원본 행렬의 행은 열, 열은 행으로 바꾼 행렬
  • 원본 행렬을 A라고하면 전치행렬은 A^T로 나타냄
A = np.array([[1, 2], [3, 4], [5, 6]]) # 3x2 행렬
B = A.T # A의 전치행렬 2x3

print("A.shape == ", A.shape, ", B.shape == ", B.shape)
print(A)
print(B)
# 결과
A.shape ==  (3, 2) , B.shape ==  (2, 3)
[[1 2]
 [3 4]
 [5 6]]
[[1 3 5]
 [2 4 6]]
# vector 전치행렬

C = np.array([1, 2, 3, 4, 5])
D = C.T # C가 vector이므로 transpose X

E = C.reshape(1, 5) # 1x5 matrix
F = E.T # E의 전치행렬 5x1

print("C.shape == ", C.shape, ", D.shape == ", D.shape)
print("E.shape == ", E.shape, ", F.shape == ", F.shape)
print(F)
# 결과
C.shape ==  (5,) , D.shape ==  (5,)
E.shape ==  (1, 5) , F.shape ==  (5, 1)
[[1]
 [2]
 [3]
 [4]
 [5]]

broadcast

  • 행렬의 사칙연산은 기본적으로 두 개의 행렬의 크기가 같은 경우에만 수행이 가능
  • 그러나 numpy에서는 크기가 다른 두 행렬 간의 사칙연산(+, -, *, /)을 할 수 있는데, 이를 브로드캐스트(broadcast) 라고 함
  • 차원이 작은 쪽이 큰 쪽의 행 단위로 반복적으로 크기를 맞춘 후 계산
A = np.array([[1, 2], [3, 4]]) # [ [1, 2], [3, 4] ]
B = 5 # [ [5, 5], [5, 5] ]
C = np.array([4, 5]) # [ [4, 5] ]

print(A + B)
print(A + C)
# 결과
[[6 7]
 [8 9]]
[[5 7]
 [7 9]]

index / slice / iterator

  • 행렬 원소를 명시적으로 접근하기 위해서는 리스트에서 처럼 index / slice 모두 사용 가능함
A = np.array([10, 20, 30, 40, 50, 60]).reshape(3, 2)

print("A.shape == ", A.shape)
print(A)
# 결과
A.shape ==  (3, 2)
[[10 20]
 [30 40]
 [50 60]]
print("A[0, 0] == ", A[0, 0], ", A[0, 1] == ", A[0, 1])
print("A[1, 0] == ", A[1, 0], ", A[1, 1] == ", A[1, 1])
print("A[2, 0] == ", A[2, 0], ", A[2, 1] == ", A[2, 1])
# 결과
A[0, 0] ==  10 , A[0, 1] ==  20
A[1, 0] ==  30 , A[1, 1] ==  40
A[2, 0] ==  50 , A[2, 1] ==  60
print("A[0:-1, 1:2] == ",A[0:-1, 1:2])
# 결과
A[0:-1, 1:2] ==  [[20]
 [40]]
print("A[ :, 0] == ", A[ :, 0])
print("A[ :, :] == ", A[ :, :])
# 결과
A[ :, 0] ==  [10 30 50]
A[ :, :] ==  [[10 20]
 [30 40]
 [50 60]]

 

  • 명시적 index / slice 이외에, 행렬 모든 원소를 access 하는 경우에는 iterator 사용 가능
  • numpy iterator는 next() 함수를 통해 데이터 값을 처음부터 끝까지 읽어 들이는 방법을 제공
A = np.array([[10, 20, 30, 40], [50, 60, 70, 80]])

print(A, "\n")
print("A.shape == ", A.shape, "\n")

# 행렬 A의 iterator 생성
# flags = ['multi_index'] : iterator 생성후 반복할때 행렬처럼 (row, column) 형태의 multi_index 형태로 동작
# op_flags = ['readwrite'] : iterator 를 R/W 형태로 생성
it = np.nditer(A, flags = ['multi_index'], op_flags = ['readwrite'])

while not it.finished:
    idx = it.multi_index
    print("current value => ", A[idx])
    it.iternext()
# 결과
[[10 20 30 40]
 [50 60 70 80]] 

A.shape ==  (2, 4) 

current value =>  10
current value =>  20
current value =>  30
current value =>  40
current value =>  50
current value =>  60
current value =>  70
current value =>  80

concatenate

  • 행렬에 행(row) 또는 열(column)을 추가하기 위해 사용하는 함수
  • 머신러닝의 회귀(regression) 코드 구현시 가중치(weight)와 바이어스(bias)를 별도로 구분하지 않고 하나의 행렬로 취급하기 위한 프로그래밍 구현 기술
# 행렬에 열과 행 추가

A = np.array([[10, 20, 30], [40, 50, 60]])

print(A.shape)

# A matrix에 행(row) 추가할 행렬, 1x3 reshape
# 행을 추가하기 때문에 우선 열을 3열로 만들어야 함
row_add = np.array([70, 80, 90]).reshape(1, 3)

# A matrix에 열(column) 추가할 행렬, 2x1 reshape
# 열을 추가하기 때문에 우선 행을 2행으로 만들어야 함
column_add = np.array([1000, 2000]).reshape(2, 1)

# numpy.concatenate 에서 axis = 0 (행 기준)
B = np.concatenate((A, row_add), axis = 0)

print(B)

# numpy.concatenate 에서 axis = 1 (열 기준)
C = np.concatenate((A, column_add), axis = 1)

print(C)
# 결과
(2, 3)
[[10 20 30]
 [40 50 60]
 [70 80 90]]
[[  10   20   30 1000]
 [  40   50   60 2000]]

useful functions (loadtxt(), rand(), argmax() ... )

loadtxt()

  • seperator로 구분된 파일에서 데이터를 읽기 위해 사용하는 함수
  • 리턴 값은 행렬이기 때문에 index / slice 이용하여 데이터를 분리할 수 있음
  • 머신러닝 코드에서 입력 데이터와 정답 데이터를 분리하는 프로그래밍 기법
# 데이터는 25x4 행렬이라고 가정
loaded_data = np.loadtxt('./data-01.csv', delimiter = ',', dtype = np.float32)

x_data = loaded_data[ :, 0:-1] # 모든 행, 3열까지의 데이터
t_data = loaded_data[ :, [-1]] # 모든 행, 4열의 데이터

print("x_data.ndim == ", x_data.ndim, ", x_data.shape == ", x_data.shape)
print("t_data.ndim == ", t_data.ndim, ", t_data.shape == ", t_data.shape)

# 결과
# x_data.ndim == 2, x_data.shape == (25, 3)
# t_data.ndim == 2, x_data.shape == (25, 1)

rand()

  • 0과 1 사이 임의의 실수 값을 리턴해주는 함수
  • 가중치(weight)나 바이어스(bias)를 임의로 설정할 때 자주 사용하는 함수
# 0 ~ 1 사이의 random number 발생

random_number1 = np.random.rand(3)
random_number2 = np.random.rand(1, 3)
random_number3 = np.random.rand(3, 1)

print("random_number1 == ", random_number1, ", random_number1.shape == ", random_number1.shape)
print("random_number2 == ", random_number2, ", random_number2.shape == ", random_number2.shape)
print("random_number3 == ", random_number3, ", random_number3.shape == ", random_number3.shape)
# 결과
random_number1 ==  [0.19724608 0.20855116 0.58046355] , random_number1.shape ==  (3,)
random_number2 ==  [[0.33375994 0.84088436 0.94006071]] , random_number2.shape ==  (1, 3)
random_number3 ==  [[0.8197173 ]
 [0.91239959]
 [0.30047281]] , random_number3.shape ==  (3, 1)

max(), min(), argmax(), argmin()

  • max(), min() : 최대 최소를 리턴
  • argmax(), argmin() : 최대 최소의 인덱스를 리턴
X = np.array([2, 4, 6, 8])

print("np.max(X) == ", np.max(X))
print("np.min(X) == ", np.min(X))
print("np.argmax(X) == ", np.argmax(X))
print("np.argmin(X) == ", np.argmin(X))
# 결과
np.max(X) ==  8
np.min(X) ==  2
np.argmax(X) ==  3
np.argmin(X) ==  0
X = np.array([[2, 4, 6], [1, 2, 3], [0, 5, 8]])

print("np.max(X) == ", np.max(X, axis = 0)) # 열 기준
print("np.min(X) == ", np.min(X, axis = 0)) # 열 기준

print("np.max(X) == ", np.max(X, axis = 1)) # 행 기준
print("np.min(X) == ", np.min(X, axis = 1)) # 행 기준

print("np.argmax(X) == ", np.argmax(X, axis = 0)) # 열 기준
print("np.argmin(X) == ", np.argmin(X, axis = 0)) # 열 기준

print("np.argmax(X) == ", np.argmax(X, axis = 1)) # 행 기준
print("np.argmin(X) == ", np.argmin(X, axis = 1)) # 행 기준
# 결과
np.max(X) ==  [2 5 8]
np.min(X) ==  [0 2 3]
np.max(X) ==  [6 3 8]
np.min(X) ==  [2 1 0]
np.argmax(X) ==  [0 2 2]
np.argmin(X) ==  [2 1 1]
np.argmax(X) ==  [2 2 2]
np.argmin(X) ==  [0 0 0]

ones(), zeros()

A = np.ones([3, 3])

print("A.shape == ", A.shape, ", A == ", A)

B = np.zeros([3, 2])

print("B.shape == ", B.shape, ", B == ", B)
# 결과
A.shape ==  (3, 3) , A ==  [[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
B.shape ==  (3, 2) , B ==  [[0. 0.]
 [0. 0.]
 [0. 0.]]

matplotlib

  • 실무에서는 머신러닝 코드를 구현하기 전에, 입력 데이터의 분포와 모양을 먼저 그래프로 그려보고, 데이터의 특성과 분포를 파악한 후, 어떤 알고리즘을 적용할 지 결정하고 있음
  • 데이터 시각화를 위해 matplotlib 라이브러리를 사용함
  • 일반적으로 line plot, scatter plot 등을 통해 데이터의 분포와 형태를 파악함
import matplotlib.pyplot as plt
import numpy as np

# 주피터 노트북을 사용하는 경우 노트북 내부에 그림 표시
%matplotlib inline

# x data, y data 생성
x_data = np.random.rand(100)
y_data = np.random.rand(100)

plt.title('scatter plot')
plt.grid()
plt.scatter(x_data, y_data, color = 'b', marker = 'o')
plt.show()

# x data, y data 생성
x_data = [x for x in range(-5, 5)]
y_data = [y*y for y in range(-5, 5)]

plt.title('line plot')
plt.grid()
plt.plot(x_data, y_data, color = 'b')
plt.show()

# x data, y data 생성
x_data = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
y_data = [-8, -13, 0, 3, 6, -1, -5, -7, 1, 8, 7, 12, 13]

plt.title('line plot')
plt.grid()
plt.plot(x_data, y_data, color = 'b')
plt.show()

 

 

 

참고

www.youtube.com/playlist?list=PLS8gIc2q83OjStGjdTF2LZtc0vefCAbnX

 

머신러닝/딥러닝 강의

머신러닝과 딥러닝의 동작원리와 이러한 알고리즘을 파이썬으로 구현한 강의 자료입니다.

www.youtube.com