기계학습의 빠른 연산을 위해서 파이썬의 리스트보다는, NumPy의 배열을 자주 사용한다.
본격적으로 기계학습에 대해서 배워보기 전, NumPy에 익숙해져보자 :>
프로그래밍을 공부하는 학생이 지식 정리 및 공유를 위해 작성한 글으로, 정확하지 않은 내용이 있을 수 있습니다. 지적과 지식 공유는 환영합니다!
np_arr = np.array(arr)
# Python 리스트
# 각각의 원소마다 연산을 진행해줘야 함
for i in range(len(arr_x)):
gradient.append(arr_y[i] / arr_x[i])
[1, 2, 3] + [4, 5, 6] == [1, 2, 3, 4, 5, 6]
# NumPy 배열
# 같은 인덱스를 가진 원소끼리의 연산을 진행해줌
gradient = np_arr_y / np_arr_x
[1, 2, 3] + [4, 5, 6] == [5, 7, 9]
# 원소의 값이 10 이상인 값만 추릴 때
np_arr[np_arr >= 10]
np_arr = np.array([1, 2, 3, 4])
np_arr.shape # (4,) 1차원, 길이 4
np_arr.size # 4, 전체 원소의 개수
np_arr.reshape(4, 1)
'''
[[1]
[2]
[3]
[4]] 2차원 (4 * 1)
'''
np_arr.reshape(2, 2)
'''
[[1, 2]
[3, 4]] 2차원 (2 * 2)
'''
np_arr.reshape(1, 4)
'''
[[1, 2, 3, 4]] 2차원 (1 * 4)
'''
np_d_arr = np.array([[1, 2], [3, 4]])
np_f_arr = np_d_arr.flatten()
np_f_arr == np_arr # TRUE
위의 1차원 배열을 reshape()
함수를 통해서 다차원 배열로 만들 수 있다. 마찬가지로, 다차원 배열을 flatten()
을 통해 1차원 배열로 만들 수 있다. 값은 동일하지만, 이를 어떻게 해석할 지를 결정하는 것이다.
(컴퓨터에 저장될 때에는 어차피 1차원으로 메모리에 저장되기 때문에 이를 어떻게 해석할 지는 우리의 몫이다.)
empty_arr = np.empty((2, 2)) # 초기화되지 않은 값. 예상치 못한 값이 들어있음
zeros_arr = np.zeros((2, 3)) # 0으로 초기화된 값.
ones_arr = np.ones((4, 2))
tens_arr = np.full((2, 2), 10)
zeros_like_arr = np.zeros_like(ones_arr) # ones_arr와 같은 모양, 0으로 초기화
ones_like_arr = np.ones_like(zeros_arr) # zeros_arr와 같은 모양, 1로 초기화
full_like_arr = np.full_like(zeros_arr, 0.1)
range_arr = np.arange(0, 10, 1) # [0, 10) 범위, 스텝 1
linspace_arr = np.linspace(2, 3, 5) #[2, 3] 범위에서 5개
위 두 함수의 차이점
np.arange
- 간격을 정해준다, np.linspace
- 개수를 정해준다
np.arange에서 일어날 수 있는 문제; 정확성 문제가 일어날 수 있음!
Another stability issue is due to the internal implementation of numpy.arange. The actual step value used to populate the array is
dtype(start + step) - dtype(start)
and not step. Precision loss can occur here, due to casting or due to using floating points when start is much larger than step. This can lead to unexpected behaviour.> np.arange(0, 5, 0.5, dtype=int) array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) > np.arange(-3, 3, 0.5, dtype=int) array([-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
In such cases, the use of numpy.linspace should be preferred.
출처 : https://numpy.org/doc/stable/reference/generated/numpy.arange.html
uniform_rand_arr = np.random.rand(3, 2) # [0, 1) 범위 실수, size=(3,2)
range_rand_int_arr = np.random.randint(5, size=(2, 4)) # [0, 5) 범위 정수
np.random.randn(size) # size 크기로 표준정규분포(평균 0, 표준편차 1)에서 random으로 추출
# 정규분표를 따르는 데이터를 만드는 데 적합하다.
위에서 np.random.randn
의 경우 단순히 함수명만 보았을 때 이해하기 힘들었는데 어떤 함수냐 하면, 정규분포를 따르는 데이터를 생성할 때
평균 + 표준편차 * np.random.randn( )
위 식을 통해서 간편하게 사용할 수 있도록, 표준정규분포에서 random으로 값을 추출하는 것이다.
표준정규분포는 평균이 0이고, 표준편차가 1이기 때문에, np.random.randn()
에서 0이 나올 확률이 제일 많고, 0에서 먼 값일 수록 나올 확률이 적어진다. 그 값에 표준편차를 곱하고, 평균을 더하면 내가 원하는 평균과 표준편차를 가지는 정규분포 상의 데이터를 만들 수 있다!
조금 더 깊게, 이 함수가 만들어진 이유에 대해서 생각해보자.
중심 극한 정리
표본의 크기가 커질수록 표본 평균의 분포는 모집단의 분포 모양과는 관계없이 정규분포에 가까워진다.
위 정리에 의하여, 정규분포와는 전혀 다른 집단에서 무작위로 충분히 많은 수를 추출하면, 표본평균의 분포가 정규분포에 가깝게 되기 때문에 이 함수를 자주 사용할 수 있는 것이다.
3차원 배열에서 첫 번째 묶음 가져오기 : np_arr[0, :, :] == np_arr[0]
3차원 배열에서 짝수 번째 묶음 가져오기 : np_arr[0::2], 앞의 문자열 슬라이싱과 같음
깊은 복사 : np_copy = np.copy(np_arr)
얕은 복사( 같은 id를 가짐) : np_copy = np_arr
3차원 배열에서, 2번째 묶음 3번째 행의 값을 모두 1로 바꾸기 : np_arr[1, 2] = 1
array1 = np.array([[1, 2], [3, 4]]) # shape (2, 2)
array2 = np.array([[5, 6]]) # shape (1, 2)
두 배열을 붙일 때에는, 기준이 되는 축 이외의 값이 같아야 한다.
즉, 위 예제에서는 열의 모양이 같기 때문에, 행을 추가하는 식으로 붙여야 한다.
concatenated_array1 = np.concatenate((array1, array2), axis=0)
# (2, 2) (1, 2)을 0번째 인덱스로 더한다. -> (3, 2)
'''
[[1, 2]
[3, 4]
[5, 6]]
'''
concatenated_array2 = np.concatenate((array1, array2), axis=1) # error!
'''
[[1, 2] [5, 6]
[3, 4] ??????] <-ERROR
'''