티스토리 뷰
아래 글에 이어 작성된 글입니다.
기본적인 Numpy의 사용법과 연습문제 풀이를 진행합니다.
Numpy
Numpy(넘파이)는 과학 및 공학 분야에서 널리 사용되는 파이썬의 오픈소스 라이브러리로 기존 파이썬에서 제공하는 리스트보다 빠른 계산 속도를 가진 ndarray(다차원 배열) 배열을 사용할 수 있도록 해줍니다. 인공지능의 반은 잘 정제된 데이터이기 때문에 좋은 품질의 데이터를 모으고 또 그 데이터를 이해하는 것이 굉장히 중요합니다. 넘파이는 이에 특화된 라이브러리입니다.
Numpy 적재하기
파이썬 코드에서 넘파이를 사용하기 위해서는 라이브러리를 적재해야 합니다. 보통 as명령어를 사용해 np로 별칭을 붙힌 후 사용합니다.
import numpy as np
Numpy 배열 생성하기
배열은 넘파이의 중심이 되는 데이터 구조로 array( ) 함수로 생성할 수 있습니다. 함수 안에 리스트를 넣으면 ndarray 객체로 변환해 줍니다. 이때 배열의 요소는 모두 같은 타입을 가져야 합니다.
각 요소는 인덱스로 접근할 수 있습니다. 다차원 배열의 경우 콤마(,) 를 사용해 인덱스를 표기할 수 있습니다.
import numpy as np
# 배열 선언하기
a = np.array([1,2,3]) # array([1,2,3])
a[0] # 1
# 다차원 배열 선언하기
b = np.array([[1,2,3],[4,5,6],[7,8,9]]) # array([[1,2,3],
# [4,5,6],
# [7,8,9]])
b[0, 2] # 3
b[0][2] # 3
Numpy 배열의 속성
넘파이 배열(ndarray)은 일반적으로 동일한 타입의 요소들이 저장되며 그 크기가 고정된 컨테이너입니다. 배열의 차원 및 항목 수는 형상(shape)에 의해 정의됩니다. 배열의 형상은 각 차원의 크기를 지정하는 정수 튜플 형태이며 이때 차원을 넘파이에서는 axes(축)이라고 부릅니다. ndarry 클래스는 다음과 같은 속성을 가집니다. (아래 속성에 대해 확실히 알고 넘어가야 후에 헷갈리지 않습니다.)
속성 | 설명 |
ndim | 축의 개수 ex. 2차원 배열인 경우 ndim은 2이다. |
shape | 배열의 형상, 형상은 정수 튜플로 나타낸다. ex. n개의 행과 m개의 열이있는 행렬의 경우 (n,m) |
size | 배열 안에 있는 요소들의 총 개수 |
dtype | 배열 요소들의 자료형 ex. numpy.int32, numpy.int16, numpy.float64 등 |
itmesize | 배열 요소들의 크기, 단위는 바이트 이다. ex. float64의 경우 8 |
data | 실제 데이터가 저장되는 메모리 블럭의 주소 |
import numpy as np
a = np.array([[0,1,2],[3,4,5],[6,7,8]])
a.shape # (3,3)
a.ndim # 2
a.dtype # dtype('int32')
a.itemsize # 8
a.size # 9
자료형 지정하기
지정하지 않은 디폴트로 np.float64 실수 타입으로 만들어 집니다. 명시적으로 np.int32, np.unicode_ 등을 지정할 수 있습니다.
c = np.array([[1, 2], [3, 4]], dtype=complex)
# array([[1.+0.j, 2.+0.j],
# [3.+0.j, 4.+0.j]])
1이나 0으로 채워진 배열 생성하기
인수로 배열의 형상을 주어 1이나 0으로 채워진 배열을 생성할 수 있습니다.
np.zeros((3, 4))
# array([[0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.]])
np.ones((2, 3, 4), dtype=np.int16)
# array([[[1, 1, 1, 1],
# [1, 1, 1, 1],
# [1, 1, 1, 1]],
#
# [[1, 1, 1, 1],
# [1, 1, 1, 1],
# [1, 1, 1, 1]]], dtype=int16)
연속되는 값으로 생성하기
arrage( ) 함수를 사용하면 연속되는 값을 가지는 배열을 쉽게 만들수 있습니다.
### arange(시작값, 종료값, 간격)
np.arange(5)
# array([0, 1, 2, 3, 4])
np.arange(1, 6)
# array([1, 2, 3, 4, 5])
np.arange(10, 30, 5)
# array([10, 15, 20, 25])
np.arange(0, 2, 0.3) # it accepts float arguments
# array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])
균일한 간격의 값으로 생성하기
linspace( )를 사용해 균일한 간격으로 지정된 개수만큼의 요소를 가지는 배열을 생성할 수 있습니다.
np.linspace(0, 2, 9) # 9 numbers from 0 to 2
# array([0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])
정렬하기
sort( )를 사용해 배열의 요소를 정렬할 수 있습니다. 이때 축의 종류 및 순서를 지정할 수 있습니다.
a = np.array([[1,4],[3,1]])
np.sort(a) # sort along the last axis
# array([[1, 4],
# [1, 3]])
np.sort(a, axis=None) # sort the flattened array
# array([1, 1, 3, 4])
np.sort(a, axis=0) # sort along the first axis
# array([[1, 1],
# [3, 4]])
형상(shape) 변경하기
reshape를 이용하면 행렬안의 데이터 개수는 유지한 채로 배열의 차원을 변경할 수 있습니다.
a = np.arange(6) # 1d array
# array([0, 1, 2, 3, 4, 5])
b = np.arange(12).reshape(4, 3) # 2d array
# array([[0, 1, 2],
# [3, 4, 5],
# [6, 7, 8],
# [9, 10, 11]])
c = np.arange(24).reshape(2, 3, 4) # 3d array
# array([[[0, 1, 2, 3],
# [4, 5, 6, 7],
# [8, 9, 10, 11]],
# [[12, 13, 14, 15],
# [16, 17, 18, 19],
# [20, 21, 22, 23]]])
이때 인수로 -1을 전달하면 데이터 개수에 맞춰 자동으로 배열의 형태가 결정됩니다.
a = np.arange(12)
a.reshape(6, -1)
# array([[0, 1],
# [2, 3],
# [4, 5],
# [6, 7],
# [8, 9],
# [10, 11]])
평탄화
flatten( ) 를 사용하면 다차원 배열을 1차원 배열로 변환(평탄화)할 수 있습니다. ravel( )도 같은 기능을 가지고 있습니다! 다만 부모 배열에도 영향을 미치기 때문에 주의해서 사용해야 합니다.
# a = np.arange(12)
# a.reshape(6, -1)
a.flatten() # array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
슬라이싱
파이썬의 리스트에서와 같이 넘파이 배열에서도 인덱싱과 슬라이싱을 사용할 수 있습니다.
import numpy as np
a = np.arange(10)
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
생략 시 각각 첫 인덱스와 마지막 인덱스로 지정됩니다.
# 슬라이싱
a[1:3] # 인덱스 1에서 인덱스 2까지
# array([1, 2])
a[:3] # 인덱스 0에서 인덱스 3까지
# array([0, 1, 2])
a[1:] # 인덱스 1에서 마지막 인덱스 까지
# array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# 마이너스의 경우 뒤에서 부터 센다
# -1 = (마지막 인덱스) -1
# -2 = (마지막 인덱스) -2
# ...
a[:-1] # 인덱스 0에서 인덱스 -1까지
# array([0, 1, 2, 3, 4, 5, 6, 7, 8])
a[3:-2] # 인덱스 3에서 인덱스 -2까지
# array([3, 4, 5, 6, 7])
2차원 배열의 경우 다음과 같이 활용할 수 있습니다.
data = np.arange(16).reshape(-1,4)
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
data[0] # array([0, 1, 2, 3])
data[1,:] # array([4, 5, 6, 7])
data[:,2] # array([ 2, 6, 10, 14])
data[0:2, 0:2] # array([[0, 1],
# [4, 5]])
data[0:2, 2:4] # array([[2, 3],
# [6, 7]])
# ::2의 의미는 시작 인덱스 부터 2씩 건너 뛰며 요소를 선택하라는 의미이다.
data[::2, ::2] # array([[ 0, 2],
# [ 8, 10]])
data[1::2, 1::2] # array([[ 5, 7],
# [13, 15]])
논리적인 인덱싱(Logical indexing)
논리적인 인덱싱이란 배열에 특정 조건을 주어 값을 추려내는 것을 말합니다. 이를 잘 이용하면 원하는 값을 쉽게 뽑아낼 수 있습니다.
# 논리적인 인덱싱
ages = np.array([18, 19, 25, 30, 28])
# 나이가 20 보다 큰 사람 확인하기
ages > 20
# array([False, False, True, True, True])
# 20 보다 큰 요소 뽑아내기
ages[ages > 20]
# array([25, 30, 28])
# 20 보다 크고 30 작은 요소 뽑아내기
ages[(ages > 20) & (ages < 30)]
기본 연산
넘파이 배열에는 + 연산자나 * 연산자 등 수학적인 연산자를 적용할 수 있습니다.
arr1 = np.arange(1,4) # array([1, 2, 3])
arr2 = np.arange(4,7) # array([4, 5, 6])
arr1 + arr2 # array([5, 7, 9])
arr1 * arr2 # array([ 4, 10, 18])
arr1 - arr2 # array([-3, -3, -3])
arr1 ** arr2 # array([ 1, 32, 729])
여러 계산 함수들
sum( )는 요소들의 합을, min( )은 요소들 중 최소값을, max( ) 눈 요소들 중 최대 값을, mean은 평균값을 계산해 줍니다. 이때 axis 속성을 지정하여 각 행이나, 열에 대해서도 쉽게 계산할 수 있습니다.
a = np.array([[1,2,3],[4,5,6],[7,8,9]]) # array([[1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]])
a.sum() # 45
a.min() # 1
a.max() # 9
a.mean() # 5.0
# 각 열의 합
a.sum(axis=0) # array([12, 15, 18])
# 각 열의 평균값
a.mean(axis=0) # array([4., 5., 6.])
# 각 행의 평균값
a.mean(axis=1) # array([2., 5., 8.])
+ 브로드 캐스팅
배열과 단일 숫자 사이에서 연산을 하는 경우(벡터와 스칼라 사이의 연산하려고 할때) 넘파이는 자동으로 스칼라를 적절한 벡터로 확장시켜 연산을 진행합니다.
np.arange(5) * 2 # array([0, 2, 4, 6, 8])
행렬 곱셈
행렬 곱셈(내적)을 하기 위해서는 *연산자가 아닌 @나 dot( )를 사용하여야 합니다.
arr1 = np.arange(1,4) # array([1, 2, 3])
arr2 = np.arange(4,7) # array([4, 5, 6])
arr1 * arr2 # array([ 4, 10, 18])
arr1 @ arr2 # 32
arr1.dot(arr2) # 32
전치 행렬 구하기
속성 T나 transpose( ) 함수를 이용해 쉽게 전치 행렬을 계산할 수 있습니다.
a = np.array([[1,2,3],[4,5,6],[7,8,9]]) #array([[1, 2, 3],
# [4, 5, 6],
# [7, 8, 9]])
a.T # array([[1, 4, 7],
# [2, 5, 8],
# [3, 6, 9]])
# a.transpose()
난수 생성하기
넘파이의 random 함수들을 이용하면 다양한 분포의 난수 배열을 쉽게 생성할 수 있습니다.
동일한 값을 계속 얻고자 한다면 seed를 설정해주면 됩니다.
# 시드 100으로 설정하기
np.random.seed(100)
균일 분포 난수 생성하기
rand( ) 함수를 사용하면 입력한 행, 열을 가지는 0.0과 1.0 사이의 난수 배열을 얻을 수 있습니다.
# np.random.seed(100)
np.random.rand(5) # array([0.54340494, 0.27836939, 0.42451759, 0.84477613, 0.00471886])
np.random.rand(2,5) # array([[0.54340494, 0.27836939, 0.42451759, 0.84477613, 0.00471886],
# [0.12156912, 0.67074908, 0.82585276, 0.13670659, 0.57509333]])
특정 범위에 있는 난수를 생성하려면 수식을 조금 추가하면 됩니다.
# a과 b사이의 난수 생성하기
# (b-a) * np.random.rand(3) + a
# 10과 20사이의 난수 생성하기
(20-10) * np.random.rand(3) + 10
정수 값을 가지는 난수
randint() 함수를 사용하면 정수 값을 가지는 난수 배열을 얻을 수 있습니다.
# np.random.randint(시작값, 종료값, size=크기)
np.random.randint(1,7, size=10) # 1부터 6까지의 값을 가지는 크기 10 배열 구하기
정규분포 난수 생성
randn( ) 함수를 사용하면 정규 분포를 따르는 난수 배열을 생성할 수 있습니다.
# 크기 5의 정규분포 난수 배열 생성하기
np.random.randn(5)
normal( ) 함수 또한 정규 분포 난수를 생성하는데 사용되며, 정규분포 평균과 표준편차를 인수로 지정할 수 있습니다.
# 크기 5의 평균 0, 표준편차를 0.1로 가지는 정규분포 난수 배열 생성하기
np.random.normal(loc=0.0, scale = 0.1, size = 5)
+ 고유 항목 및 개수 얻기
unique( )를 사용하면 배열에서 고유한 요소를 쉽게 추출할 수 있습니다. return_counts를 True로 설정하면 빈도수를 확인할 수 있습니다.
# 1부터 6까지의 값을 가지는 크기 100 배열 생성
a = np.random.randint(1,7, size=100)
np.unique(a) # 고유값 추출
np.unique(a, return_counts = True) # 빈도수 출력
연습문제
위 내용을 바탕으로 [딥러닝 express]의 연습문제 2장을 풀어보았습니다. (개인적인 풀이기에 오류가 있을 수 있습니다!)
01. 다음 코드의 최종 결과를 쓰시오.
import numpy as np
my_vector = np.array([1, 2, 3, 4, 5, 6])
selection = my_vector % 2 == 0
my_vector[selection]
답 : [2, 4, 6]
논리적인 인덱싱 my_vector %2 == 0 (짝수이면 Ture)에 의해 selection은 [Fasle, True, Fasle, True, Fasle, True,]를 가진 boolean형 배열이 된다. 이를 인덱스로 지정하여 my_vector에 보냈으니 출력은 [2, 4, 6]이다.
02. 다음 코드의 최종 결과를 쓰시오.
import numpy as np
first_matrix = np.array([[1, 2, 3], [4, 5, 6]])
second_matrix = np.array([1, 2, 3])
first_matrix + second_matrix
답 : [[2, 4, 6], [5, 7, 9]]
넘파이는 다른 크기, 차원을 가진 행렬에도 산술 연산이 가능하도록 자동으로 스칼라를 확장하는 기능(브로드캐스팅)을 가지고 있기에 second_matrix가 [[1, 2, 3], [1, 2, 3]]로 확장되어 연산이 진행된다. 따라서 정답은 [[1+1, 2+2, 3+3], [4+1, 5+2, 6+3]] = : [[2, 4, 6], [5, 7, 9]]이다.
03. 크기가 10의 널 벡터를 생성하고 다섯 번째 요소는 1로 설정하는 코드를 작성하라.
import numpy as np
array = np.zeros(10) # np.zeros를 사용하면 크기가 10인 널벡터를 생성할 수 있다.
array[4] = 1 # 원하는 요소를 1로 설정한다.
print(array) # [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
04. 10에서 19까지의 값을 가지는 1차원 배열을 생성하라.
import numpy as np
# arange를 사용하면 원하는 범위의 연속되는 값을 가진 배열을 생성할 수 있다.
# arange(시작값, 종료값, 간격)
array = np.arange(10, 20)
print(array) # [10 11 12 13 14 15 16 17 18 19]
05. 0부터 9까지의 값으로 넘파이 1차원 배열을 채우고, 이 배열을 거꾸로 하는 문장을 작성하라.
import numpy as np
array = np.arange(0, 10) # arange 를 이용해 0~9 값을 가지는 1차원 배열 생성
array = array[::-1] # 슬라이싱 : 처음부터 끝까지 -1칸 간격으로 (역순으로) 변환
print(array) # [9 8 7 6 5 4 3 2 1 0]
06. 0부터 8까지의 값을 가지고 크기가 3x3인 행렬을 생성하라.
import numpy as np
# arange를 통해 0~8 값을 가지는 1차원 리스트 생성
# reshape를 통해 3x3 배열로 형태 변경
array = np.arange(9).reshape(3, 3)
print(array) ###[[0 1 2]
### [3 4 5]
### [6 7 8]]
07. 난수로 채워진 3x3 넘파이 배열을 생성해 보자.
import numpy as np
# random.rand(행, 열)을 이용한 난수(0.0~1.0)로 이루어진
# 2차원 넘파이 배열을 생성할 수 있다.
array = np.random.rand(3, 3)
print(array)
08. 임의의 값으로 10x10 배열을 만들고 최소값과 최대값을 찾아보자.
import numpy as np
# random.rand(행, 열)을 이용하 난수(0.0~1.0)로 이루어진
# 2차원 넘파이 배열을 생성할 수 있다.
array = np.random.rand(10, 10)
# min와 max 메소드로 최소, 최대 값을 찾을 수 있다.
print(array.min());
print(array.max());
09. 배열의 테두리에 1, 내부에 0을 가진 3x3 크기의 2차원 배열을 생성해보자. 슬라이싱을 이용한다.
import numpy as np
array = np.ones((3,3)) # 1로 가득 채워진 3x3 넘파이 배열을 생성한다.
array[1:-1,1:-1] = 0 # 1부터 -1(끝부터 1개)까지, 즉 [1][1] 요소를 0으로 바꾼다.
print(array) #[[1. 1. 1.]
# [1. 0. 1.]
# [1. 1. 1.]]
10. 5x5 행렬을 만들어 체스 보드 패턴으로 채워보자.
import numpy as np
array = np.zeros((5,5)) # 1로 가득 채워진 5x5 넘파이 배열을 생성한다.
array[1::2, ::2] = 1 # 행은 1부터 2간격 씩, 열은 0부터 2간격 씩 선택해 1로 변경
array[::2, 1::2] = 1 # 행은 0부터 2간격 씩, 열은 1부터 2간격 씩 선택해 1로 변경
print(array) #[[0. 1. 0. 1. 0.]
# [1. 0. 1. 0. 1.]
# [0. 1. 0. 1. 0.]
# [1. 0. 1. 0. 1.]
# [0. 1. 0. 1. 0.]]
11. 3x3 난수로 행렬을 만들고 평균값과 표준 편차로 행렬을 정규화하여 보자.
import numpy as np
# random.rand(행, 열)을 이용한 난수(0.0~1.0)로 이루어진
# 2차원 넘파이 배열을 생성할 수 있다.
array = np.random.rand(3, 3)
mean = np.mean(array) # mean을 사용해 평균 구하기
std = np.std(array) # std를 사용해 표준 편차 구하기
array = (array - mean)/std # 정규화
print(array)
12. 넘파이를 사용하여 0에서 9까지의 값을 가진 벡터를 만들고 5에서 8 사이의 숫자 부호를 반전시켜보자.
import numpy as np
array = np.arange(10) # arange를 통해 0~9 값을 가지는 1차원 리스트 생성
# 논리적인 인덱싱으로 5~8값을 선택후 *-1로 부호 반전
array[(array >= 5) & (array <= 8)] *= -1
print(array) # [ 0 1 2 3 4 -5 -6 -7 -8 9]
13. 넘파이로 3x3 크기의 2차원 배열을 생성하고, 모든 요소의 합, 각열의 합, 각 행의 합을 계산해보자.
import numpy as np
# arange를 통해 0~8 값을 가지는 1차원 리스트 생성
# reshape를 통해 3x3 배열로 형태 변경
array = np.arange(9).reshape(3, 3)
print("원본배열 :")
print(array)
print("모든 요소의 합 : ", array.sum()) # sum으로 모든 요소의 합 출력
print("각 열의 합 : ",array.sum(axis = 0)) # axis을 0으로 지정하여 열 지정
print("각 행의 합 : ",array.sum(axis = 1)) # axis을 0으로 지정하여 행 지정
원본배열 :
[[0 1 2]
[3 4 5]
[6 7 8]]
모든 요소의 합 : 36
각 열의 합 : [ 9 12 15]
각 행의 합 : [ 3 12 21]
14. 주어진 두 벡터의 내적을 계산하기 위해 넘파이 프로그램을 작성해보자.
import numpy as np
# 넘파이 배열 선언
arr1 = np.array([4, 5])
arr2 = np.array([7, 10])
print(arr1)
print(arr2)
print("백터의 내적", np.dot(arr1,arr2)) # dot메소드로 내적 계산
print("백터의 내적", arr1 @ arr2) # @ 연산자로 내적 계산
[ 4 5]
[ 7 10]
백터의 내적 78
백터의 내적 78
15. [2, 0, 3, 6, 8, 12, 10, 9, 18, 20, 22]와 같은 데이터를 이용하여 다음과 같은 선 그래프를 그려보자.
import numpy as np
import matplotlib.pyplot as plt # 맵플롯립 탑재
# 넘파이 배열 선언
x = np.arange(13) # x축 데이터 삽입
y = np.array([2, 0, 3, 6, 4, 6, 8, 12, 10, 9, 18, 20, 22]) # y축 데이터 삽입
plt.plot(x, y) # 그래프 그리기
plt.show() # 그래프 보이기
감사합니다.
공부한 내용을 복습/기록하기 위해 작성한 글이므로 내용에 오류가 있을 수 있습니다.
'인공지능' 카테고리의 다른 글
[인공지능] Sklearn + Numpy만으로 퍼셉트론 구현하기 (1) | 2022.09.07 |
---|---|
[인공지능] Numpy로 경사하강법 구현하기 (0) | 2022.09.07 |
[인공지능] 딥러닝과 Python 라이브러리 (0) | 2022.08.30 |
[인공지능] 하이퍼 파라미터 (에포크/배치/학습률/옵티마이저/손실함수/활성화함수) (0) | 2022.08.09 |
[인공지능] 다층 퍼셉트론 (MLP)과 역전파 알고리즘 (1) | 2022.08.08 |