39.1 sum 함수의 역전파
덧셈의 미분은 y = x0+x1일 때 ∂y/∂x0 = 1, ∂y/∂x1 = 1입니다.
역전파는 출력 쪽에서 전해지는 기울기를 그대로 입력 쪽으로 흘려보내주면 됩니다.

덧셈을 수행한 후 변수 y로 부터 역전파를 수행하는데
x0, x1에 출력 쪽에서 전해준 기울기 1을 두개로 '복사'해서 전달해줍니다.

원소가 두개인 sum 함수의 계산 그래프입니다.
[1, 2]라는 2개의 원소로 구성된 벡터에 sum 함수를 통과해 [3]이라는 스칼라를 출력합니다. => forward
출력 쪽에서 전해준 값 1을 [1, 1]로 벡터(1차원 배열)로 확장해 전파합니다. => backward

원소가 2개 이상인 벡터 일땐?
기울기 벡터 원소 수 만큼 '복사'해줍니다. 이러한 원리가 입력 변수가 2차원 이상의 배열일때 동리하게 적용됩니다.
39.2 sum 함수 구현
현재 DeZero의 sum 함수 역전파에서는 입력 변수의 형상과 같아지도록 기울기의 원소를 복사하는데
역전파에선 Variable의 인스턴스를 입력하기 때문에 복사 작업을 DeZero 함수로 합니다.
사용할 함수: broadcast_to
class Sum(Function):
def forward(self, x):
self.x_shape = x.shape
y = x.sum()
return y
def backward(self, gy):
gx = broadcast_to(gy, self.x_shape)
return gx
def sum(x):
return Sum()(x)
forward 메서드에서 x.shape을 저장하고
backward에서 broadcast_to 함수를 사용하여
입력 변수의 형상(x.shape)이 같아지게 gy의 원소를 복사합니다.
1차원 배열 실행 코드
x = Variable(np.array([1, 2, 3, 4, 5, 6]))
y = F.sum(x)
y.backward()
print(y)
print(x.grad)

2차원 배열(=행렬) 실행 코드
x = Variable(np.array([[1, 2, 3], [4, 5, 6]]))
y = F.sum(x)
y.backward()
print(y)
print(x.grad)

39.3 axis와 keepdims
1. axis

axis는 축이며 다차원 배열에서 화살표 방향을 의미합니다.
np.sum 함수는 합계를 구할 때 '축'을 지정할 수 있습니다.
x = np.array([[1, 2, 3], [4, 5, 6]])
y = np.sum(x, axis=0)
print(y)
print(x.shape, '->', y.shape)

x의 쉐입이 (2, 3)이고 출력 쉐입이 (3,)입니다.
위의 코드에서 axis=0으로 지정했습니다.
즉, 축에 따라 합계를 구할 방향을 정할 수 있습니다.

axis=0이면
1+4 = 5, 2+5=7, 3+6=9 해서 [5, 7, 9] 가 됩니다.
axis=1이면
1+2+3 = 6, 4+5+6=15 해서 [6, 15]가 됩니다.
(Caution) 인수 axis는 int 외에 None과 튜플도 받습니다.
None이면 모든 원소를 다 더한 값 하나(스칼라)를 출력합니다.
그리고 (0, 2)처럼 튜플로 지정하면 해당 튜플에서 지정한 축 모두 0, 2번 축 모두에 합계를 계산합니다.
2. keepdims
keepdims이란 입력과 출력의 차원 수(축 수)를 똑같게 유지할지 정하는 플래그입니다.
x = np.array([[1, 2, 3], [4, 5, 6]])
y = np.sum(x, keepdims=True)
print(y)
print(y.shape)

keepdims = True로 지정해서 축의 수가 유지되고 False로 지정했다면 스칼라여서 ()로 출력 될 것입니다.
3. DeZero sum함수에 두 인자를 사용할 수 있도록 코드 구현
class Sum(Function):
def __init__(self, axis, keepdims):
self.axis = axis
self.keepdims = keepdims
def forward(self, x):
self.x_shape = x.shape
y = x.sum(axis=self.axis, keepdims=self.keepdims)
return y
def backward(self, gy):
gy = utils.reshape_sum_backward(gy, self.x_shape, self.axis,
self.keepdims)
gx = broadcast_to(gy, self.x_shape)
return gx
def sum(x, axis=None, keepdims=False):
return Sum(axis, keepdims)(x)
우선, __init__ 생성자 메서드를 만들어줍니다.
이 메서드를 호출하여 sum 클래스를 초기화 할때 axis와 keepdims를 입력받아 속성으로 설정할 수 있게 합니다.
forward에서 y = x.sum(axis=self.axis, keepdims=self.keepdims) 부분에서 속성들을 사용해 합계를 구합니다.
backward 메서드에서
gy = utils.reshape_sum_backward(gy, self.x_shape, self.axis, self.keepdims)
reshape_sum_backward 함수가 추가되었는데 해당 함수는 sum 연산의 그래디언트의 shape을 변경해줍니다.
gx = broadcast_to(gy, self.x_shape)
gy와 인풋 값의 형상을 받아 broadcast_to함수를 사용해 입력 변수의 형상과 같아지도록 기울기 원소를 복사합니다.
class Variable:
@property
def sum(self, axis=None, keepdims=False):
return dezero.functions.sum(self, axis, keepdims)
@property 데코레이터를 사용해서 sum 메서드를 속성처럼 접근할 수 있게되고
해당 부분을 구현함으로써 sum 함수를 Variable의 메서드로 사용할 수 있게 됩니다.
x = Variable(np.array([[1, 2, 3], [4, 5, 6]]))
y = F.sum(x, axis=0)
y.backward()
print(y)
print(x.grad)
x = Variable(np.random.randn(2, 3, 4, 5))
y = x.sum(keepdims=True)
print(y.shape)


'Study 공부할레나' 카테고리의 다른 글
| [스터디/밑시딥3] 제 4고지 신경망 만들기_38. 형상 변환 함수 (1) | 2023.10.08 |
|---|---|
| [스터디/밑시딥3] 제 4고지 신경망 만들기_37. 텐서를 다루다 (1) | 2023.10.08 |
| [스터디/밑시딥3] 제 3고지 고차미분계산_36. 고차 미분 이외의 용도 (0) | 2023.09.11 |
| [스터디/밑시딥3] 제 3고지 고차미분계산_35. 고차 미분 계산 그래프 (0) | 2023.09.11 |
| [스터디/밑시딥3] 제 3고지 고차미분계산_34. sin 함수 고차 미분 (0) | 2023.09.11 |