꺼내먹는지식 준
Faster R CNN 에 나오는 numpy 활용법 본문
Faster R CNN 소스 코드를 읽으면서 numpy 의 용례를 많이 찾아볼 수 있다.
본 글은 numpy 에 익숙하지 않은 사람 대상으로, Faster R CNN 소스코드를 읽을 때 만날 수 있는 여러 시행착오를 줄이기 위해 작성
Numpy Where
np.where 의 official doc
https://numpy.org/doc/stable/reference/generated/numpy.where.html
numpy.where — NumPy v1.22 Manual
Return elements chosen from x or y depending on condition. Note When only condition is provided, this function is a shorthand for np.asarray(condition).nonzero(). Using nonzero directly should be preferred, as it behaves correctly for subclasses. The rest
numpy.org
official doc 을 읽는 것도 좋지만, 직관적으로 내용을 설명해보자면 이렇다.
np.where 자체는 주어진 np.array에서 True index 를 반환한다.
예를 들면,
np.where(np.array([True, False, False]))
(array([0]),)
활용
np.where(a==b)
직관적으로 설명해보자면 a==b를 먼저 수행 한 후, 내부에서 나온 결과에 따라 True False 를 원소로 갖는 np array를 반환
다음과 같은 경우가 있다고 하자.
a = np.arange(200).reshape(20,10)
b = np.range(10,20,-1)
a.shape
(20, 10)
b.shape
(10,)
각 np array의 원소를 출력해보면 다음과 같다.
print(a)
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, 24, 25, 26, 27, 28, 29],
[ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99],
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
[110, 111, 112, 113, 114, 115, 116, 117, 118, 119],
[120, 121, 122, 123, 124, 125, 126, 127, 128, 129],
[130, 131, 132, 133, 134, 135, 136, 137, 138, 139],
[140, 141, 142, 143, 144, 145, 146, 147, 148, 149],
[150, 151, 152, 153, 154, 155, 156, 157, 158, 159],
[160, 161, 162, 163, 164, 165, 166, 167, 168, 169],
[170, 171, 172, 173, 174, 175, 176, 177, 178, 179],
[180, 181, 182, 183, 184, 185, 186, 187, 188, 189],
[190, 191, 192, 193, 194, 195, 196, 197, 198, 199]])
print(b)
array([20, 19, 18, 17, 16, 15, 14, 13, 12, 11])
이 부분에서 numpy array 에 익숙하지 않은 사람들은 a == b 가 크기가 맞지 않아 애초에 성립이 불가능하다고 생각하기 쉽다.
하지만, 이 부분에서 numpy 는 자동으로
for i in range(a):
a == b
를 수행한다.
이에 따라 a 와 b를 비교하면,
a == b
array([[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, True, False, False, False,
False],
[ True, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False]])
다음과 같은 결과를 얻을 수 있다.
그 결과 np.array(a==b) 는
np.where( a == b
array([[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, True, False, False, False,
False],
[ True, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False],
[False, False, False, False, False, False, False, False, False,
False]]))
가 된다고 할 수 있다.
이에 따라 True 인 항의 index를 반환하면
(array([1, 2]), array([5, 0]))
다음의 결과를 얻는다.
※추가 정보
np.where(condition, if True: , else: )
직관적으로 작성해보았다.
np.where의 결과가 True 이면 두번째 항의 값으로, False이면 3번째 항의 값으로 설정할 수도 있다.
이 경우 index 가 아닌 value 를 return 한다.
np.where(a==b, a, -1)
array([[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, 15, -1, -1, -1, -1],
[20, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1]])
다음의 결과로 관측할 수 있는 건 True 인 경우 a 로, False 경우 -1 로 원소를 치환 한 것을 알 수 있다.
numpy meshgrid
numpy meshgrid 를 처음 사용해보고 황당했다.
a = np.arange(5)
b = np.arange(20,15,-1)
print(a)
array([0, 1, 2, 3, 4])
print(b)
array([20, 19, 18, 17, 16])
다음의 간단한 두 numpy array로 np.meshgrid 를 수행하면,
np.meshgrid(a,b)
[array([[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]]),
array([[20, 20, 20, 20, 20],
[19, 19, 19, 19, 19],
[18, 18, 18, 18, 18],
[17, 17, 17, 17, 17],
[16, 16, 16, 16, 16]])]
다음의 황당한 결과를 얻는다.
즉 np.meshgrid 의 첫번째 파라미터의 numpy array는 원소 개수만큼 numpy array를 단순 복사해주고,
두번째 파라미터의 numpy array는 원소개수만큼, numpy array의 각 원소를 복사하여, 각 원소별로 하나의 numpy array를 생성한다.
작성해놓고도 말이 어렵다. 위의 예제를 통해 이해하자.
결론적으로 이걸 왜 할까에 대해 직관적인 이해가 바로 되지 않아서 고민했었다.
그러던 중 이게 얼마나 편리한 기능인지 깨달았다.
x = np. meshgrid(a,b)[0]
y = np. meshgrid(a,b)[1]
우리는 아주 종종
$[(x_1,y_1), (x_2,y_1), (x_3, y_1)] \\ [(x_1,y_2), (x_2,y_2), (x_3, y_2)] \\ [(x_1,y_3), (x_2,y_3), (x_3, y_3)]$
다음의 형태로 tensor 를 생성해야 할 때가 있다.
이때 for 문을 돌리며 생성하는 과정이 좀 짜친다는 생각을 늘 했었다.
이를 mesh.grid 를 사용하면 간단하게 처리가 가능하다.
stack_result = np.stack((x.ravel(), y.ravel(),
), axis=1)
· .ravel() 은 1차원으로 tensor 를 평탄화
np.stack((x.ravel(),y.ravel()), axis=1)
array([[ 0, 20],
[ 1, 20],
[ 2, 20],
[ 3, 20],
[ 4, 20],
[ 0, 19],
[ 1, 19],
[ 2, 19],
[ 3, 19],
[ 4, 19],
[ 0, 18],
[ 1, 18],
[ 2, 18],
[ 3, 18],
[ 4, 18],
[ 0, 17],
[ 1, 17],
[ 2, 17],
[ 3, 17],
[ 4, 17],
[ 0, 16],
[ 1, 16],
[ 2, 16],
[ 3, 16],
[ 4, 16]])
다음과 같이 사용할 수 있다. 너무 편리한 기능이다. 하지만 이 정도 경지에 오르려면 numpy 를 수족처럼 사용해야 할 듯 싶다.
numpy clip
np.clip(array, min, max)
np clip을 어디에 사용할까? 엄청난 용례가 있다.
faster R CNN 논문에서 bounding box 가 사진 이미지 크기를 벗어나는 일은 자주 발생한다.
이 때, clip을 통해 img 크기를 벗어난 tensor 의 크기를 clip해준다.
코드내에서 출력값이 너무 길어서 간단한 예제로 대채해보자.
a = np.arange(20).reshape(2,5,2)
np.clip(a,2,8)
array([[[2, 2],
[2, 3],
[4, 5],
[6, 7],
[8, 8]],
[[8, 8],
[8, 8],
[8, 8],
[8, 8],
[8, 8]]])
다음과 같이 사용될 수 있다.
numpy Maximum
간단하게는 앞뒤 list를 비교하고 더 큰 값을 return 해준다.
※numpy 함수들은 parameter로 list 를 주면 자동으로 numpy array로 치환해서 계산
np.maximum([2, 3, 4], [1, 5, 2])
array([2, 5, 4])
np.maximum([2, 3, 4], [[1, 5, 2], [1,1,9]])
array([[2, 5, 4],
[2, 3, 9]])
모든 과정을 통해 살펴본 numpy 는 마지막 차원만 동일하면 연산 수행이 가능하다.
np.maximum([2, 3, 4], [[[1, 5, 2], [1,1,9]], [[1, 5, 2], [1,1,9]]] )
array([[[2, 5, 4],
[2, 3, 9]],
[[2, 5, 4],
[2, 3, 9]]])
※항상 실수하기 좋은 추가 정보
Numpy shape[0] vs [0]
a.shape
(1, 2, 5, 2)
다음 형태의 numpy array가 있다고 할 때,
a.shape[0]
a[0]
두 결과는 당연히 다르다.
첫번째는 단순히 첫번째 차원이 크기를 받아오는 거고,
두번째는 첫번째 차원을 기준으로 index 중 1개만 선택하여 차원수를 줄이는 것이다.
a[0].shape
(2, 5, 2)
차원수를 줄일 때 다음과 같이 많이 활용된다.
np.squeeze(a) == a[0]
array([[[ True, True],
[ True, True],
[ True, True],
[ True, True],
[ True, True]],
[[ True, True],
[ True, True],
[ True, True],
[ True, True],
[ True, True]]])
squeeze와 동일한 기능이다.
사실 readability를 위해 squeeze를 사용하는 것이 더 나아보인다.