Numpy에서 np.sum 함수의 axis 이해
통계 및 데이터 분석, 딥러닝을 하다 보면 스칼라, 벡터, 행렬, 텐서와 같은 다양한 데이터 유형을 다루게 됩니다. 데이터 분석은 여러 유형의 데이터 합을 구하고 빈도수와 확률을 계산하는 반복적인 작업입니다. 다양한 데이터를 대상으로 선형대수(Linear Algebra) 연산에 numpy의 sum 함수을 사용하면 매우 편리합니다. 그러나 처음 numpy의 sum 함수를 접하면 axis 파라미터 때문에 굉장히 어렵게 느껴집니다. axis를 기준으로 합을 계산하는 의미를 이해하기 어렵습니다. 이제부터 numpy의 sum 함수에서 axis가 무엇을 의미하는지 알아보겠습니다.
numpy의 sum 함수 사용 예
다음 코드는 3차원 배열을 만들고, 3차원 배열의 합을 구하는 코드입니다.
>>> arr = np.arange(0, 4*2*4)
>>> len(arr)
32
>>> arr
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])
>>> v = arr.reshape([4,2,4]) ## 차원 변환 [4, 2, 4]; row: 4, column: 2, depth: 4
>>> v
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]]])
>>> v.ndim ## v의 차원
3
>>> v.sum() ## 모든 element의 합
496
>>> res01=v.sum(axis=0) ## axis=0 기준 합계
>>> res01.shape
(2, 4)
>>> res01
array([[48, 52, 56, 60],
[64, 68, 72, 76]])
>>> res02=v.sum(axis=1) ## axis=1 기준 합계
>>> res02.shape
(4, 4)
>>> res02
array([[ 4, 6, 8, 10],
[20, 22, 24, 26],
[36, 38, 40, 42],
[52, 54, 56, 58]])
>>> res03=v.sum(axis=2) ## axis=2 기준 합계
>>> res03.shape
(4, 2)
>>> res03
array([[ 6, 22],
[ 38, 54],
[ 70, 86],
[102, 118]])
>>>
sum 함수의 axis 설정에 따라 결과가 완전히 달라지는 것을 확인할 수 있습니다. 이제부터 axis 파라미터가 어떤 의미가 있는지 살펴보겠습니다.
선형 대수의 데이터의 유형
데이터 분석의 각종 계산을 돕는 학문이 바로 선형대수(linear algebra)입니다. 선형대수를 사용하면 대량의 데이터를 포함하는 복잡한 계산 과정을 간단한 수식으로 표현할 수 있습니다. 선형대수의 데이터 유형은 데이터의 수와 형태에 따라서 스칼라(scalar), 벡터(vector), 행렬(matrix), 텐서(Tensor)로 구분됩니다. 각 데이터 유형에 대하여 간단하게 살펴보겠습니다.
스칼라: scalar
스칼라는 하나의 숫자만으로 이루어진 데이터를 의미합니다. 스칼라는 보통 x 와 같이 알파벳 소문자로 표기하며 실수(real number)인 숫자 중의 하나이므로 실수 집합 “R“의 원소라는 의미에서 다음과 같이 표기한다.
$$ x \in {\Bbb R} $$
벡터: vector
벡터는 여러 숫자가 순서대로 모여 있는 것으로, 일반적인 일차원 배열이 벡터입니다.
$$ x = \begin{bmatrix}x_1 \\ x_2 \\ x_3 \\ x_4 \end{bmatrix} $$
하나의 벡터를 이루는 데이터의 개수를 차원(dimension)이라고 합니다. 위에서 예로든 벡터는 4개의 실수로 이루어져 있고, 4차원 벡터입니다. 이 벡터는 다음과 같이 표기할 수 있습니다.
$$ x \in {\Bbb R^4} $$
행렬: matrix
행렬은 복수의 차원을 가지는 데이터가 다시 여러 개 있는 경우의 데이터를 합쳐서 표기한 것이다. 일반적으로 2차원 배열이 행렬입니다. 특히 3차원 이상 배열은 텐서(tensor)라고 합니다.
다차원 배열의 축(axis)
다차원 배열의 경우 그림 1과 같은 축을 갖습니다.
벡터에 <그림 1>을 적용해 보면, 벡터는 x 축만을 갖는 자료형입니다. 1차원 배열에 해당하는 벡터의 각 요소(Element)는 그 자체가 Row입니다.
2차원 배열 형태의 행렬(matrix)은 x축의 행과 y축의 컬럼을 갖습니다. 2차원 배열 행렬은 depth가 1이라고 생각할 수 있습니다.1
3차원 배열 형태의 Tensor는 행과 열을 갖고 각 컬럼은 벡터 형태를 갖습니다. 이러한 벡터를 Depth로 표현합니다.
4차원 이상의 배열은 z축의 depth 요소가 스칼라가 아니라 벡터 이상의 자료형을 갖는 것을 의미합니다. 이러한 방식으로 데이터의 Dimension(차원)은 끝없이 확장될 수 있습니다.
실습 데이터
Numpy에서 sum 함수의 동작 방식을 이해하기 위해서 다음과 같은 자료형을 준비하였습니다.
>>> arr = np.arange(0, 32)
>>> len(arr)
32
>>> arr
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])
>>> v = arr.reshape([4,2,4])
32개의 요소를 이용하여 shape이 (4, 2, 4)인 텐서를 만들었습니다. 이 데이터는 4개의 row와 2개의 컬럼을 가지며, 각 컬럼의 depth는 4입니다. 이 자료형은 <그림 2>와 <그림 3>과 같이 표현할 수 있습니다.
Shape: (4, 2, 4) 데이터의 구조를 <그림 2>와 <그림 3>을 참조하여 이해하시기 바랍니다.
Numpy의 sum 함수 작동 방식
sum 함수의 axis 파라미터의 기본값은 “None”입니다. axis는 대상 데이터의 Dimension보다 작은 값을 설정합니다.
>>> v.ndim ## v의 차원
3
예제 데이터의 Dimension은 3이기 때문에 axis는 2까지 설정 가능합니다.
case 1: axis=None
axis를 기본값으로 실행하면 대상 데이터의 모든 요소의 합을 반환합니다.
>>> v.sum() ## 모든 element의 합
496
대상 데이터에 포함된 모든 요소를 단순하게 합산하는 연산을 수행합니다.
case 2: axis=0
axis=0는 x축을 기준으로 합을 구하는 방식입니다. x축 row를 합산입니다. shape가 (4, 2, 4)인 데이터를 x축을 기준으로 합산하면 결과 shape는 row를 제거한 (2, 4)가 반환됩니다.
>>> res01=v.sum(axis=0) ## axis=0 기준 합계
>>> res01.shape
(2, 4)
>>> res01
array([[48, 52, 56, 60],
[64, 68, 72, 76]])
axis=0의 합산 방향은 <그림 4>와 같습니다. 결과적으로 row를 합치는 과정입니다.
axis=0는 가장 외각의 괄호를 제거하는 이미지를 상상하시면 이해하기 편리합니다. <그림 5>과 같이 가장 외각의 괄호를 제거하고, 각 row에 데이터는 위치별로 합산합니다. 결과적으로 컬럼과 depth의 위치별 합이 계산됩니다.
<그림 5>는 shape (4, 2, 4)의 데이터에 sum(axis=0)을 적용하는 과정을 설명합니다. 각 요소에 객은 합산되는 그룹을 의미합니다. 총 8번의 sum이 계산되고 Shape(2, 4)가 반환됩니다. 모든 row에 동일 위치의 컬럼과 depth 요소들의 합이 계산됩니다.
case 3: axis=1
axis=1은 y축을 기준으로 합을 구하는 방식입니다. y축 즉 컬럼을 합치는 합산입니다. shape이 (4, 2, 4)인 데이터를 y축을 기준으로 합산하면 결과 shape은 컬럼을 제거한 (4, 4)가 반환 됩니다. 각 row의 2개의 컴럼이 1개로 축소됩니다.
>>> res02=v.sum(axis=1) ## axis=1 기준 합계
>>> res02.shape
(4, 4)
>>> res02
array([[ 4, 6, 8, 10],
[20, 22, 24, 26],
[36, 38, 40, 42],
[52, 54, 56, 58]])
>>>
axis=1의 합산 방향은 <그림 6>와 같습니다. 결과적으로 컬럼(column)을 합치는 과정입니다.
axis=1은 가장 외각에서 두 번째 괄호를 제거하는 이미지를 상상하시면 편리합니다. <그림 7>과 같이 각 row에서 컬럼을 구분하는 괄호를 제거하고, 각 컬럼의 데이터는 위치별로 합산합니다. 결과적으로 row 별로 컬럼들의 depth 위치별 합이 계산됩니다.
<그림 7>은 shape (4, 2, 4)의 데이터에 sum(axis=1)를 적용하는 과정을 설명합니다. 각 요소에 색은 합산되는 그룹을 의미합니다. row 별로 4번의 합이 계산되고 4개의 row에서 총 16번의 합이 계산됩니다. 결과로 Shape(4, 4)가 반환됩니다. 각 row 별로 컬럼들의 동등 depth 요소들의 합이 반환됩니다.
case 4: axis=2
axis=2는 z축을 기준으로 합을 구하는 방식입니다. z축 dpeth 요소를 합치는 연산입니다. shape이 (4, 2, 4)인 데이터를 z축을 기준으로 합산하면 결과 shape은 depth를 제거한 (4, 2)가 반환 됩니다. 각 컬럼의 depth는 1개의 스칼라값으로 변환됩니다.
>>> res03=v.sum(axis=2) ## axis=2 기준 합계
>>> res03.shape
(4, 2)
>>> res03
array([[ 6, 22],
[ 38, 54],
[ 70, 86],
[102, 118]])
>>>
axis=2의 합산 방향은 <그림 8>과 같습니다. 결과적으로 각 row에 위치하는 컬럼이 갖는 Depth는 1개의 스칼라값으로 변환됩니다.
axis=2는 depth를 묶는 괄호를 제거하는 이미지를 상상하시면 편리합니다. <그림 9>와 같이 각 column에서 Depth를 구분하는 괄호를 제거하고, 모든 Depth 요소를 합산합니다. 결과적으로 예제에서 각 row의 컬럼들은 1개의 스칼라값으로 변환됩니다.
<그림 9>는 shape (4, 2, 4)의 데이터에 sum(axis=2)를 적용하는 과정을 설명합니다. 각 요소에 색은 합산되는 그룹을 의미합니다. row 별로 2번의 합이 계산되고 4개의 row에서 총 8번의 합이 계산됩니다. 결과로 Shape(4, 2)가 반환됩니다. 각 row별로 각 컬럼은 depth 요소들의 합인 스칼라값을 갖습니다.
마치며
Numpy로 선형대수를 프로그래밍할 때 자료형의 Dimension과 자료형의 기준 축에 대하여 정리해 보았습니다. sum 함수의 매개변수를 기준으로 axis는 다음과 같은 의미가 있습니다.
- axis=None은 기본값으로 모든 요소의 값을 합산하여 1개의 스칼라값을 반환합니다.
- axis=0은 x축을 기준으로 여러 row를 한 개로 합치는 과정입니다.
- axis=1은 y축을 기준으로 row 별로 존재하는 column들의 값을 합쳐 1개로 축소하는 과정입니다.
- axis=2는 z축을 기준으로 column의 depth가 가진 값을 축소하는 과정입니다.
3차원 배열로 만들어진 Tensor의 경우 axis=2를 계산할 때 column은 스칼라값(Depth가 스칼라로 계산됨)이 됩니다. 4차원 이상의 배열로 확장될 경우 이와 같은 방식으로 개념을 적용하고 확장할 수 있습니다.
참고자료
- “2차원 배열(행렬)은 depth가 1”이라는 설명은 엄밀하게 말하면 잘못된 설명입니다. 행렬은 컬럼이 스칼라이고 텐서는 컬럼이 벡터 이상의 자료형이기 때문에 완전히 다른 것 입니다. 위 설명은 가능한 개념을 단순화하기 위한 설명입니다. [return]