본문 바로가기
Study 공부할레나

[스터디/밑시딥3] 제 2고지 자연스러운 코드로_15. 복잡한 계산 그래프(이론 편)

by LENA-cfg 2023. 8. 22.

지금까지는 변수와 함수가 위의 이미지처럼 한 줄로 연결된 계산 그래프로 되어있습니다.

그러나 같은 변수를 반복해서 사용하거나 여러 변수를 입력 받는 함수를 사용하는 계산을 할 수 있기 때문에

아래 이미지처럼 더 복잡한 연결을 만들 수 있습니다.

현재 DeZero는 이런 복잡한 연결의 역전파를 진행할 수가 없습니다.

*그래프의 '연결된 형태'는 위상이라고 하는데 다양한 위상의 계산 그래프에 대응하여 제대로 미분하는 것이 목표

 

15.1 역전파의 올바른 순서

중간에 분기했다가 다시 합류하는 위의 계산 그래프에서 주목할 부분은 변수 'a'입니다.

이전 단계(Add 함수를 사용하여 역전파를 구현했던)처럼 같은 변수를 반복해서 사용하면 역전파 때 출력 쪽에서 전파되는 각각의 미분값들을 더해야합니다.

그래서, a를 계산하려면 2개의 미분값을 전해줘야 a에서 x로 미분값을 전파 할 수 있습니다.

 

역전파의 순서

위의 이미지 순서대로 보면 a에 2개의 미분값이 모두 전파된 후 a에서 x로 미분값을 전파합니다.

 

15.2 현재의 DeZero

 

class Variable:

    def backward(self):
        if self.grad is None:
            self.grad = np.ones_like(self.data)

        funcs = [self.creator]
        while funcs:
            f = funcs.pop() # 마지막에 담긴 함수를 꺼내줌
            gys = [output.grad for output in f.outputs]
            gxs = f.backward(*gys)
            print('gxs:', gxs)
            if not isinstance(gxs, tuple):
                gxs = (gxs,)

            for x, gx in zip(f.inputs, gxs):
                if x.grad is None:
                    x.grad = gx
                    # print('x.grad:', x.grad)
                else:
                    x.grad = x.grad + gx

                if x.creator is not None:
                    funcs.append(x.creator) # 처리할 함수 후보 추가

현재 DeZero의 코드인데 while 블록의 마지막 줄을 보면 처리할 함수의 후보를 funcs 리스트의 끝에 추가하고 있는데

그 후에 pop으로 마지막에 담긴 함수를 꺼내줍니다. 

 

이렇게 될경우 D, C, A, B, A가 되고 아래의 2개의 문제가 발생합니다.

1) C다음에 A가 오는 문제

2) 함수 A의 역전파가 두 번 일어나는 문제

 

실제 코드 돌렸을때 

x = Variable(np.array(3.0))  # or x.cleargrad()

a = exp(x)
b = square(a)
c = square(a)
y = add(b,c)
y.backward()

Exp가 나중에 한번 나와야하는데 2개의 Square함수 뒤에 나오는걸 알 수 있습니다.

* 책에 없는 내용이고 그래프 따라서 개인적으로 예시로 만든거라 참고만 하시면 좋을 것 같아요.

15.3 함수 우선순위

funcs 리스트에 다음에 처리할 후보들이 있으나 지금까지는 마지막 원소(.pop)만 꺼내서 사용했습니다.

마지막 원소가 아닌 출력 쪽에 가까운 함수를 꺼낼 수 있도록 함수의 '우선순위'를 줘야합니다.

 

우선순위 주는 방법

1) 주어진 계산 그래프를 '분석'하여 알아내는 방법인 '위상 정렬' 알고리즘을 사용하여 노드의 연결 방법을 기초로 노드들을 정렬할 수 있습니다.

2) [더 쉬운 방법] 순전파 때 만들어내는 관계인 '창조자-피조물 관계', '부모-자식 관계'를 기준으로 함수와 변수의 세대를 기록할 수 있습니다.

 

역전파 시 세대가 큰 쪽부터(=우선순위) 처리하여 부모보다 자식이 먼저 처리 됨을 보장 할 수 있고

B(1세대)와 A(0세대)중 선택해야할때 B를 먼저 꺼낼 수 있습니다.