누적 합 Cumulative Sum cumsum 구현에 대하여

안녕하십니까.

이번 포스팅에서는 누적합 cumsum에 대하여 다루려고 합니다.

Keras 오픈소스 Contributing

케라스의 오픈소스를 공부하는 도중에 contribute를 하고 싶어서 issue들을 보고 있었습니다.
그중 어렵지 않아 보이는 이슈인 https://github.com/keras-team/keras/issues/12163 를 발견 하였었고 해당 이슈를 처리해보기 위해서 코드를 보고 있었으나 안타깝게도 이미 누군가 처리하신 흔적이 있어서 해당 코드를 알아보았습니다.

요구 사항

요구 사항은 다음과 같이 keras에 CNTK backend에서 사용할 수 있는 cumsum과 cumprod를 구현해 달라는 것입니다.

cumsum과 cumprod가 cntk 위에 돌아가게 구현해주세요.

keras는 시중 딥러닝 오픈소스를 사용하기 쉽게 감싼 오픈소스 이며 그 대상중 하나가 마이크로 소프트의 CNTK 입니다.
다른 backend 오픈소스인 텐서플로우, Theano 보다는 CNTK를 사용하는 사람이 적기 때문에 해당 이슈가 아직도 open 된것으로 보입니다.

cumsum

cumsum은 누적합(Cumulative Sum) 입니다. 이는 행렬 같은 구조체에 대하여 각 엘리먼트에 대하여 iteration 하면서 누적 합을 한 새로운 행렬을 리턴하는 간단한 수식 입니다.

예를 들어서
(1 2)
(3 4)
와 같은 행렬이 있으면 결과값은
(1 2)
(1+3 2+4)
가 되는 간단한 수식 입니다.

만약 내가 구현한다면?

만약 제가 구현한다면 for문을 통해서 각 row element를 iteration 하면서 더할것 같습니다.  대부분의 사람이 그렇게 할듯 합니다.

실제 구현된 방식은?

아래 코드가 다른분이 keras에 구현하신 코드 입니다.

def cumsum(x, axis=0):
    dim = x.shape[axis]
    U = C.constant(np.triu(np.ones((dim, dim))).astype(x.dtype))
    if axis != -1:
        x = C.swapaxes(x, -1, axis)
    out = C.times(x, U)
    if axis != -1:
        out = C.swapaxes(out, -1, axis)
    return out

동작 원리는?

코드로 보면 눈에 익숙치 않습니다.
x는 tensor 라는 데이터를 받는데 간단하게 행렬 입니다.
예를 들어서
X를 다음과 같은
(a b)
(c d)
X = (e f)      
(g h)
와 같은 4*2 행렬이라고 합시다.
dim = x.shape[axis]
dim는 측정 축에 대한 차원을 말하는데 위의 예시에서는 
axis=0이면 4이고 axis=1이면 2 입니다. 저는 4일 경우를 가정하겠습니다.
U = C.constant(np.triu(np.ones((dim, dim))).astype(x.dtype))
U는 아마 Upper를 말하는듯 합니다. np.ones는 1로 초기화 하는 함수이고 dim = 4이니 
4x4이면서 1로 채워진 함수 입니다.
그 바깥은 triu로 하였는데 이는 오른쪽 위만 남겨두는 함수이며 결국은 아래와 같이 됩니다.
(1 1 1 1)
(0 1 1 1)
U = (0 0 1 1)      
(0 0 0 1)

x = C.swapaxes(x, -1, axis)
그 다음 수식은 아래와 같은데 C는 cntk 오픈소스를 호출한것이고 swapaxes는 해당 축을 기준으로 행렬을 변환 합니다.
X의 축이 변환되므로 다음과 같이 됩니다.
(a c e g)
X = (b d f h)      
out = C.times(x, U)
결과값은 위의 X와 U를 곱합니다.
X * U
=
             (1 1 1 1)
(a c e g) (0 1 1 1)
(b d f h) (0 0 1 1) 
            (0 0 0 1)

=
(a a+c a+c+e a+c+e+g)
(b b+d b+d+f b+d+f+h)
out = C.swapaxes(out, -1, axis)
위의 결과값의 축을 다시 변환 합니다.
(a b)
(a+c b+d)
(a+c+e b+d+f)
(a+c+e+g b+d+f+h)
이 결과값을 return 하며 이는 cumsum이 구현하는 Cumulative Sum의 수식과 일치 합니다.

느낀점

간단하게 for문을 통해 구현할생각이었는데 행렬 곱셉으로 구현한것이 신기하였습니다.
잘 찾아보면 선형대수에서 배웠을것 같긴한데 저는 기억나지 않았고 이런 발상 자체가 신기 하네요.
성능이 잘 나올지는 모르겠습니다. numpy 연산이 최적화가 되어 있을듯 하긴 하네요.
for문으로도 numpy 연산이 가능할것 같긴 한데 이렇게 구현한 이유가 파이썬 쓰는 사람들은 논문 쓰시는사람분들이다 보니 for문보다는 행렬 수식이 익숙하신듯 합니다.

댓글

이 블로그의 인기 게시물

고려대학교 야간대학원 중간 후기

포켓몬 고 17셀 확인 포고맵 사용 방법

HTTP 오류 500.19 - Internal Server Error 에러 처리법