본문 바로가기
Data Science/머신러닝 기초

머신러닝 기초 (2) - NumPy 복습

by 싸코 2018. 1. 22.

가천대 최성철 교수님의 '밑바닥부터 시작하는 머신러닝 입문'을 수강하며 노트 필기 및 추가 내용 작성을 목적으로 포스팅합니다.


다른 포스팅에서 다루었던 NumPy에 대한 내용이지만 강의를 들으면서 참고할만한 팁(?) 같은 것들을 메모할 것들이 있었고 복습할 요량으로 되짚어 보기 위해서 정리하려고 합니다. NumPy는 딥러닝 프레임 워크에서도 데이터 타입의 표준으로 사용되고 있기 때문에 기본이 되는 것으로 꼭 알아야 합니다. NumPy에서 자주 사용하는 함수나 핵심내용에 대해서 아래 포스팅에서 정리해두었으니 참고하실 수 있습니다.


2017/10/09 - 파이썬으로 데이터 분석하자 (3) - NumPy 기본


Chapter 3. NumPy Section

머신러닝 기초(2) - NumPy 복습


왜 NumPy 인가? 파이썬 리스트와 무엇이 다른가?

Matrix와 Vector 같이 Array 연산을 지원해주기 위해서 사용하고 일반 파이썬 데이터 타입과 비교해서 빠르고 메모리 효율적이기 때문에 사용합니다. 일반 파이썬 List로 벡터 연산을 구현하기 위해서 for문을 사용해야 하지만 NumPy는 브로드캐스팅이라는 기능이 있어 이를 쉽게 할 수 있도록 해줍니다.


훨씬 빠르고 간편하기 때문에 사용합니다.



NumPy는 Dynamic typing을 지원하지 않습니다. 이는 파이썬 리스트 안에는 다양한 데이터 타입(str, int, float 등)이 섞여있을 수 있지만 NumPy의 array는 이를 지원하지 않습니다.


>> np.array( list, data_type )


으로 쉽게 파이썬 리스트를 넘파이의 array로 만들 수 있습니다.


파이썬 리스트는 리스트 안의 하나의 데이터(item)에 대해서 메모리 주소값이 부여되어 이를 참조하는 형식이라고 보면 됩니다. 이와 달리 넘파이의 array는 메모리를 할당 받아 차지하는 방식(?)이므로 훨씬 속도가 빨라지게 됩니다.



앞에서 말씀드린 대로 Dynamic Typing을 지원하지 않으므로 list 안에 문자 형태로 숫자를 넣어도 (ex. '7') 정해진 데이터 타입으로 변환하여 넘파이 array를 반환합니다.



NumPy는 쉽게 Tensor를 표현할 수 있다?

일반적으로 1차원의 넘파이 array를 벡터(vector), 2차원을 행렬(matrix), 3차원부터는 텐서(tensor)라고 합니다. 넘파이 array의 차원이나 데이터 크기(개수)를 보기위해서


>> np.ndim      #3부터는 tensor라고 보면 된다

>> np.size


를 사용합니다.




차원을 내 맘대로 만질 수 있는 마법 같은 reshape...

reshape이라는 함수를 사용해서 size(element의 개수)만 동일한다면 array의 shape을 변경할 수 있습니다. 무슨 말이나면 전체 사이즈가 48이라면 매트릭스를 12 x 4 의 형태를 8 x 6 의 형태로 변경할 수 있습니다.

이 때 -1을 사용한다면 해당 차원에 대해서는 자동으로 만들어달라는 의미입니다. 위의 48개의 넘파이 array를 예로 다시 달아보면, np.reshape(-1, 4)라는 것은 4를 고정하고 -1을 n으로 보아 전체 size 48에 맞추기 위해서는 n이 12가 되어야 하므로 해당 reshape에 따른 결과는 (12, 4)가 되는 것입니다.

이는 벡터를 행렬로, 벡터를 텐서로, 매트릭스를 텐서로 아니면 그 반대로도 reshape이 가능합니다. 최근에 데이터를 크롤링 했을 때 테이블을 긁어오게 되었는데 홀수행과 짝수행이 하나의 row/instance를 나타내는 형태였습니다. 홀수행과 짝수행에 사용되는 컬럼이 다르므로 데이터를 가졀올 때는 하나의 행으로 표현해야 했는데 이를 넘파이 array로 변환하여 reshape을 해주니 간단하게 처리할 수 있었습니다. (120, 8)의 형태였던 pandas dataframe이었는데 (Pandas에는 to_matrix라는 편리하게 numpy matrix로 변환해주는 함수가 있습니다) 이를 np.reshape(-1, 16)으로 해주니 짝수행 까지 하나의 컬럼으로 그리고 나머지는 -1이 계산해줘서 최종적으로 (60, 16) 형태가 되었고 골치 아플 뻔했던 문제가 쉽게 해결되었습니다.



Slicing, Indexing, Arange는 파이썬의 그것과 동일한가요?

대부분의 슬라이싱, 인덱싱, range는 파이썬의 리스트에서 하는 방식과 동일합니다. 하지만 몇가지 부분에서 약간의 차이점이 있습니다. 
넘파이는 arange에서 간격을 float으로도 가능합니다.
>> np.arange(0,5, 0.5)



np.ones, np.zeros, np.empty는 어떤 때 사용하나요?

세 함수는 argument로 shape, dytpe을 설정합니다. 딥러닝에서 초기값을 설정할 때 미리 차원을 만들어 놓기 위한 것으로 또는 초기값으로 설정할 때 위의 세 함수를 사용합니다. np.empty는 재미있는 것이 메모리 영역에 값이 남아있으면 그 값이 empty 차원 크기 안에 있다면 np.empty로 새롭게 불러와도 함께 있다는 것이다. 이는 넘파이의 array가 메모리 영역을 직접 사용하기 때문에 그런 것입니다.

np.zeros_like 같이 뒤에 like가 붙으면 기존 array의 차원만 가져와서 ones나 zeros array를 만들어 주는 것입니다.
np.eye는 대각행렬을 반환하여 줍니다. np.diag는 대각행렬의 값을 뽑아 반환합니다.

그 외에도 아래와 같은 균등분포, 정규분포 등을 만들어서 인공신경망에서 가중치 초기화를 설정할 때 분포를 고려하기도 합니다. 매우매우 중요하니 꼭 알고 있는 것이 좋습니다. 더 궁금하면 아래 포스트를 참고해주시기 바랍니다.

>> np.random.uniform   #균등분포
>> np.random.normal    #정규분포



차원(축)을 기준으로 array 내 연산이 가능한가요?

np.sum 같은 합 연산을 수행 할 때 축을 기준으로 합 연산이 가능합니다. 벡터 연산을 제공한다고 했는데 벡터는 차원에서 같은 자리를 가진 것들끼리만 계산할 수 있도록 해줍니다. 이를 element-wise 방식이라고 하는데 신경망에서도 자주 다루어지는 내용입니다. 이러한 내용들은 선형대수학에서 주로 다루는 내용이므로 현재 해당 내용에 대해서 정리중이니 기회가 되면 향후 포스트에서 이를 보일 수 있을 것 같습니다.

기준이 되는 dimension 축으로 axis를 설정할 때 axis는 차원의 순서대로 index를 가지고 있습니다. 예를 들어서 (a, b, c)라는 차원을 가진 텐서의 axis는 a는 axis가 0, b는 axis가 1, c는 axis가 2 입니다.

머리 속에 matrix와 tensor가 그려지고 이를 통해 차원 형태도 바꿔보고 연산도 해보고 할 수 있어야 앞으로 딥러닝을 공부할 때 도움이 됩니다. 쉽지 않지만 노력하고 있습니다.

자세한 넘파이 기반의 연산 방법은 맨 위에서 소개한 포스트를 참조하시면 될 것 같습니다.



축을 기준으로 Concatenate!!!

vstack, hstack을 사용해서 수직으로 수평으로 NumPy의 array들을 합칠 수 있습니다. 합칠 때는 차원이 동일해야 됩니다. 최근 딥러닝 코드를 짤 때 이 함수들을 사용하였습니다. input 데이터를 만들거나 또는 전처리 할 때(? 둘다 전처리인가..) 여하튼 배치를 만들기도 하고 여러 이유로 array들을 합쳐야 할 때가 있습니다.

vstack은 열은 고정시키고 행을 늘리는 방식이므로 np.concatenate 관점에서 봤을 때는 차원의 축이 column이기 때문에 axis가 1입니다. 반대로 hstack은 행을 고정시키고 열을 늘리는 방식이므로 axis가 0입니다.

차원을 고치고 만지고 할 때 많이 사용하기 때문에 concatenate에 대해서 이해할 수 있어야 합니다. 2차원 형식의 matrix는 R이나 Python dataframe에서 또는 RDB의 테이블에서 concatenate하는 방식과 개념적으로는 동일하기 때문에 쉽게 이해할 수 있습니다. 3차원부터는 이게 조금 복잡해지는 것 같습니다.



Array 간 연산은 Element-wise 방식만 있나요?

다른 포스트에서도 다루었지만 numpy에는 dot product를 지원합니다. 행렬의 내적을 구하는 것과 같습니다. Array간 연산을 위해서 transpose를 사용하여 행과 열의 위치를 변경하기도 합니다. dot product를 위해서는 차원의 형식이 곱셈이 가능할 수 있도록 해야 하는데 헹렬의 내적과 동일함게 다음과 같은 방식으로  (a, b) * (c, d) 일 때 b=c, 결과는 (a, d)가 됩니다. 그리고 행렬의 곱셉 성질에 따라서 array간 계산 순서가 달라지면 결과는 달라지는데 b와 c가 안맞아 계산 자체가 안될 경우도 있습니다. 곱셈 계산이 되도록 차원을 맞춰주는 역할을 transpose가 해줍니다.

브로드캐스팅이라는 방식도 있습니다. 기본적으로 element-wise 방식을 기반으로 하지만 조금 다른 특성이 있으니 반드시 알고 넘어갈 수 있어야 합니다. 해당 내용을 아래에서 다루고 있습니다.




Array가 조건문과 비교 연산도 지원하나요?

넘파이 array의 장점 중 하나는 map 함수에서 하는 것과 비슷하게 모든 element에 대해서 한 번에 comparison 연산을 할 수 있습니다. 예를 들어서 a = np.array([1,2,3,4]) 가 있을 때 sum(a>10)  이라고 한다면 0이 나옵니다. False(0)가 4개가 반환되기 때문임. 해당 조건(a>10)에 대해서 각각에 대해서 비교 연산이 되었기 때문입니다.

np.any, np.all, np.logical_and, np.logical_or   같은 비교 연산도 있습니다.

np.where를 많이 사용하게 됩니다. 조건문을 파라미터로 넣을 수 있는데 해당 조건을 만족하는 np.array의 인덱스 값을 반환합니다. 또는 if( condition, a, b)와 동일하게 where( condition, a, b)라고 한다면 condition을 만족하면 a로 만족하지 않으면 b로 값을 변환합니다.
데이터 전처리에서 많이 사용하기 때문에 기억해야 합니다.

np.isnan 도 많이 사용합니다. NaN 값이냐 아니냐 논리 연산을 하는 것입니다. 데이터 탐색할 때 NULL 값이 있는지 있다면 얼마나 있는지 확인할 때 사용합니다.

np.argmax 라는 함수 역시 많이 사용되는데 자주 들어본 image classification의 결정함수로 사용되는 softmax에서 armax를 찾아볼 수 있습니다. 간단하게 리스트 값에서 가장 큰 값이 있는 인덱스(index)를 반환하는 것입니다. MNIST에서는 argmax로 반환되는 index 값 자체가 target의 값과 동일하기 때문에 argmax 값을 그대로 사용합니다.

array[ condition ]  형태를 사용하면 condition을 만족하는 인덱스의 값들만을 추출할 수 있습니다. 이는 R의 데이터프레임에 익숙하신 분들에게는 익숙한 방법입니다. condition 자체가 불리언 인덱스로 나타나기 때문에 ( np.array > 0 을 할 때 True 또는 False로 구성된 동일한 차원을 가진 np.array가 반환) 해당 불리언 인덱스만 만족하는 array를 반환합니다.

array[ index array ] 또는 array.take( index array ) 를 할 때는 array의 인덱스 값을 가지고 있는 array를 입력해서 해당 인덱스의 값으로 구성된 값을 반환해주는데 일반적으로 y의 값을 추출할 때 주로 사용하는 방법입니다.



NumPy도 데이터를 읽고(load) 쓸(write) 수 있나요?

np.loadtxt로 파일을 불러올 수 있고, np.savetxt로 파일로 저장할 수 있습니다.

파일의 형태는 파이썬 pickle 형태 (binary)로 데이터를 저장하고 불러올 수도 있습니다. 확장자는 .npy 입니다.





댓글