DonHurry

step39. 합계 ν•¨μˆ˜ λ³Έλ¬Έ

DeZero/πŸ—»μ œ4κ³ μ§€

step39. 합계 ν•¨μˆ˜

_도녁 2023. 2. 14. 00:01

πŸ“’ λ³Έ ν¬μŠ€νŒ…μ€ λ°‘λ°”λ‹₯λΆ€ν„° μ‹œμž‘ν•˜λŠ” λ”₯λŸ¬λ‹3을 기반으둜 μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 배운 λ‚΄μš©μ„ κΈ°λ‘ν•˜κ³ , 개인적인 곡뢀λ₯Ό μœ„ν•΄ μž‘μ„±ν•˜λŠ” ν¬μŠ€νŒ…μž…λ‹ˆλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ ꡐ재 ꡬ맀λ₯Ό κ°•λ ₯ μΆ”μ²œλ“œλ¦½λ‹ˆλ‹€.

 

 

이번 λ‹¨κ³„μ—μ„œλŠ” 합계λ₯Ό κ΅¬ν•˜λŠ” ν•¨μˆ˜ sum을 μΆ”κ°€ν•˜κ² μŠ΅λ‹ˆλ‹€. [κ·Έλ¦Ό 39-3]을 보면 sum ν•¨μˆ˜κ°€ μ–΄λ– ν•œ 역할을 ν•˜λŠ”μ§€ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. μ€‘μš”ν•œ 것은 μ—­μ‹œ μ—­μ „νŒŒμž…λ‹ˆλ‹€. 기울기λ₯Ό μž…λ ₯ λ³€μˆ˜μ˜ ν˜•μƒκ³Ό 같아지도둝 볡사해야 ν•©λ‹ˆλ‹€. 이전 단계듀과 λ§ˆμ°¬κ°€μ§€λ‘œ μ—­μ „νŒŒμ—μ„œλŠ” Variable μΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— 볡사 μž‘μ—…λ„ DeZero ν•¨μˆ˜λ‘œ ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λ•Œ ν˜•μƒμ— 맞게 μ›μ†Œλ₯Ό λ³΅μ‚¬ν•˜λŠ” μž‘μ—…μ€ λ„˜νŒŒμ΄μ˜ λΈŒλ‘œλ“œμΊμŠ€νŠΈλΌλŠ” 것과 같은 κΈ°λŠ₯μž…λ‹ˆλ‹€. λΈŒλ‘œλ“œμΊμŠ€νŠΈλŠ” λ‹€μŒ λ‹¨κ³„μ—μ„œ κ΅¬ν˜„ν•  μ˜ˆμ •μ΄μ§€λ§Œ, broadcast_toλΌλŠ” ν•¨μˆ˜λŠ” sum ν•¨μˆ˜ κ΅¬ν˜„μ—λ„ 쓰이기 λ•Œλ¬Έμ— 미리 가져와 μ‚¬μš©ν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

 

μš°μ„  Sum ν΄λž˜μŠ€μ™€ sum ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

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)

 

ν…ŒμŠ€νŠΈν•΄λ³΄λ©΄ μ •μƒμ μœΌλ‘œ λ™μž‘ν•˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

import numpy as np
from dezero import Variable
import dezero.functions as F

x = Variable(np.array([1, 2, 3, 4, 5, 6]))
y = F.sum(x)
y.backward()
print(y)
print(x.grad)

x = Variable(np.array([[1, 2, 3], [4, 5, 6]]))
y = F.sum(x)
y.backward()
print(y)
print(x.grad)

variable(21)
variable([1 1 1 1 1 1])
variable(21)
variable([[1 1 1]
            [1 1 1]])

 

DeZero ν•¨μˆ˜λ“€μ˜ μˆœμ „νŒŒλŠ” λ„˜νŒŒμ΄μ˜ ν•¨μˆ˜λ“€μ„ ν™œμš©ν•˜μ—¬ κ΅¬ν˜„ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λ„˜νŒŒμ΄μ˜ np.sum은 방금 κ΅¬ν˜„ν•œ 것보닀 더 λ‹€μ–‘ν•œ κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. λŒ€ν‘œμ μœΌλ‘œ axis와 keepdimsλΌλŠ” κΈ°λŠ₯이 μžˆμŠ΅λ‹ˆλ‹€. axisλŠ” 좕을 μ§€μ •ν•˜μ—¬ μ›ν•˜λŠ” λ°©ν–₯으둜 합계λ₯Ό ꡬ할 수 있고, keepdimsλŠ” μž…λ ₯κ³Ό 좜λ ₯의 차원 수(μΆ• 수)λ₯Ό λ˜‘κ°™κ²Œ μœ μ§€ν•  수 μžˆλ„λ‘ ν•˜λŠ” ν”Œλž˜κ·Έμž…λ‹ˆλ‹€. μžμ„Έν•œ μ„€λͺ…은 μƒλž΅ν•˜κ² μŠ΅λ‹ˆλ‹€. 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)

 

Sum 클래슀λ₯Ό μ΄ˆκΈ°ν™”ν•  λ•Œ axis와 keepdimsλ₯Ό μ†μ„±μœΌλ‘œ λ°›κ²Œ λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€. μ—­μ „νŒŒλŠ” κΈ°μ‘΄κ³Ό λ™μΌν•©λ‹ˆλ‹€. λ‹€λ§Œ, 주석 처리된 뢀뢄이 μΆ”κ°€λ˜μ—ˆλŠ”λ° 이 ν•¨μˆ˜λŠ” gy의 ν˜•μƒμ„ λ―Έμ„Έν•˜κ²Œ μ‘°μ •ν•©λ‹ˆλ‹€. axis와 keepdims둜 인해 기울기의 ν˜•μƒμ„ λ³€ν™˜ν•˜λŠ” κ²½μš°κ°€ 생기기 λ•Œλ¬Έμ— 이λ₯Ό μœ„ν•œ ν•¨μˆ˜μž…λ‹ˆλ‹€. κ΅μž¬μ—μ„œλ„ λ³Έμ§ˆμ—μ„œ λ²—μ–΄λ‚˜κΈ° λ•Œλ¬Έμ— μ„€λͺ…을 μƒλž΅ν•œλ‹€κ³  ν•˜λ‹ˆ, λ„˜μ–΄κ°€λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€. κΆκΈˆν•˜μ‹  뢄듀은 κΉƒν—ˆλΈŒμ— μ½”λ“œκ°€ μžˆμœΌλ‹ˆ μ°Ύμ•„λ³΄μ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.

 

λ§ˆμ§€λ§‰μœΌλ‘œ Variable의 λ©”μ„œλ“œλ‘œ μ‚¬μš©ν•  수 μžˆλ„λ‘ λ‹€μŒ μ½”λ“œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

class Variable:
	...
        def sum(self, axis=None, keepdims=False):
            return dezero.functions.sum(self, axis, keepdims)

 

ν…ŒμŠ€νŠΈλŠ” λ‹€μŒκ³Ό 같이 μ§„ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

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)

variable([5 7 9])
variable([[1 1 1]
            [1 1 1]])
(1, 1, 1, 1)