A A
[DL] λ‹¨μˆœν•œ Layer κ΅¬ν˜„ν•΄λ³΄κΈ°

 

μ΄λ²ˆκΈ€μ—μ„œλŠ” λ‹¨μˆœν•œ Layer λΆ€ν„° ν•œλ²ˆ κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • μ•žμ˜ κΈ€μ—μ„œλ³Έ 계산 κ·Έλž˜ν”„μ˜ κ³±μ…ˆ λ…Έλ“œλ₯Ό 'MultiLayer', λ§μ…ˆ λ…Έλ“œλ₯Ό 'AddLayer'λΌλŠ” μ΄λ¦„μœΌλ‘œ κ΅¬ν˜„ν•©λ‹ˆλ‹€.

κ³±μ…ˆ 계측

  • λͺ¨λ“  계측은 forward()와 backward()λΌλŠ” κ³΅ν†΅μ˜ Method(μΈν„°νŽ˜μ΄μŠ€)λ₯Ό 갖도둝 κ΅¬ν˜„ν•©λ‹ˆλ‹€.
  • forward()λŠ” Forward Propagation(μˆœμ „νŒŒ), backward()λŠ” Back propagation(μ—­μ „νŒŒ)λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  • ν•œλ²ˆ κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.
# coding: utf-8

class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None

	# x와 yλ₯Ό 인수라 λ°›κ³  두 값을 κ³±ν•΄μ„œ λ°˜ν™˜
    def forward(self, x, y):
        self.x = x
        self.y = y                
        out = x * y

        return out

	# 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ λ―ΈλΆ„(dout)에 μˆœμ „νŒŒ λ•Œμ˜ 값을 
    # μ„œλ‘œ λ°”κΏ” κ³±ν•œ ν›„ ν•˜λ₯˜λ‘œ 흘렀쀌
    def backward(self, dout):
        dx = dout * self.y  # x와 yλ₯Ό λ°”κΎΌλ‹€.
        dy = dout * self.x

        return dx, dy
  • μΈμŠ€ν„΄μŠ€ λ³€μˆ˜μΈ x,yλ₯Ό μ΄ˆκΈ°ν™” ν•©λ‹ˆλ‹€. 이 두 λ³€μˆ˜λŠ” Forward PropagatIon(μˆœμ „νŒŒ)μ‹œ μž…λ ₯ 값을 μœ μ§€ν•˜κΈ° μœ„ν•΄μ„œ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • forward()μ—μ„œλŠ” x, yλ₯Ό 인수둜 λ°›κ³  두 값을 κ³±ν•΄μ„œ λ°˜ν™˜ ν•©λ‹ˆλ‹€.
  • 반면 backward()μ—μ„œλŠ” 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ λ―ΈλΆ„(dout)에 Forward PropagatIon(μˆœμ „νŒŒ) λ•Œμ˜ 값을 'μ„œλ‘œ 봐꿔' κ³±ν•œ ν›„ ν•˜λ₯˜λ‘œ ν˜λ¦½λ‹ˆλ‹€.

  • MulLayerλ₯Ό μ‚¬μš©ν•˜μ—¬ μˆœμ „νŒŒλ₯Ό λ‹€μŒκ³Ό 같이 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
# coding: utf-8
from layer_naive import *

apple = 100
apple_num = 2
tax = 1.1

mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)

print(price) # 200
  • 각 λ³€μˆ˜μ— λŒ€ν•œ 미뢄은 backward() μ—μ„œ ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.
# backward (μ—­μ „νŒŒ)

dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print(dapple, dapple_num, dtax) # 2.2 110 200
  • backward() μ—­μ „νŒŒμ˜ 호좜 μˆœμ„œλŠ” forward() μˆœμ „νŒŒ λ•Œμ™€λŠ” λ°˜λŒ€μž…λ‹ˆλ‹€.
  • 또, backward()κ°€ λ°›λŠ” μΈμˆ˜λŠ” 'μˆœμ „νŒŒμ˜ 좜λ ₯에 λŒ€ν•œ λ―ΈλΆ„'μž„μ— μ£Όμ˜ν•©λ‹ˆλ‹€.

λ§μ…ˆ 계측

class AddLayer:
	def __init__(self):
    	pass
        
	# μž…λ ₯받은 두 인수 x, yλ₯Ό λ”ν•΄μ„œ λ°˜ν™˜
    def forward(self, x, y):
    	out = x + y
        return out
        
	# 상λ₯˜μ—μ„œ λ‚΄λ €μ˜¨ λ―ΈλΆ„(dout)을 κ·ΈλŒ€λ‘œ ν•˜λ₯˜λ‘œ 흘렀쀌
    def backward(self, dout):
    	dx = dout * 1
        dy = dout * 1
        return dx, dy
  • λ§μ…ˆ κ³„μΈ΅μ—μ„œλŠ” μ΄ˆκΈ°ν™”κ°€ ν•„μš” μ—†κΈ° λ•Œλ¬Έμ— _init_()μ—μ„œλŠ” 아무 일도 ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
    • 즉, passλŠ” 아무것도 ν•˜μ§€ λ§λΌλŠ” λͺ…λ Ήμž…λ‹ˆλ‹€.
  • λ§μ…ˆ κ³„μΈ΅μ˜ forward()μ—μ„œλŠ” μž…λ ₯받은 두 인수 x, yλ₯Ό λ”ν•΄μ„œ λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • backward()μ—μ„œλŠ” 상λ₯˜μ—μ„œ λ‚΄λ €μ˜¨ λ―ΈλΆ„(dout)을 κ·ΈλŒ€λ‘œ ν•˜λ₯˜λ‘œ 흘릴 λΏμž…λ‹ˆλ‹€.
그러면 λ§μ…ˆ, κ³±μ…ˆ 계측을 μ‚¬μš©ν•˜μ—¬ 사과 2κ°œμ™€ κ·€ 3개λ₯Ό μ‚¬λŠ” 상황을 κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

# coding: utf-8
from layer_naive import *

apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)  # (1)
orange_price = mul_orange_layer.forward(orange, orange_num)  # (2)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)  # (3)
price = mul_tax_layer.forward(all_price, tax)  # (4)

# backward
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)  # (4)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)  # (3)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)  # (2)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)  # (1)

print("price:", int(price))
print("dApple:", dapple)
print("dApple_num:", int(dapple_num))
print("dOrange:", dorange)
print("dOrange_num:", int(dorange_num))
print("dTax:", dtax)
  • ν•˜λ‚˜ν•˜λ‚˜μ˜ λͺ…령은 λ‹¨μˆœν•©λ‹ˆλ‹€. ν•„μš”ν•œ 계측을 λ§Œλ“€μ–΄μ„œ Forward Propagation(μˆœμ „νŒŒ) Method인 forward()λ₯Ό μ μ ˆν•œ μˆœμ„œλ‘œ ν˜ΈμΆœν•©λ‹ˆλ‹€.
  • 그런 λ‹€μŒ Forward Propagation(μˆœμ „νŒŒ)와 λ°˜λŒ€ μˆœμ„œλ‘œ Back Propagation(μ—­μ „νŒŒ) Method인 Backward()λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ›ν•˜λŠ” 미뢄이 λ‚˜μ˜΅λ‹ˆλ‹€.

Model Code (by Python)

# κ³±μ…ˆ 계측 μ •μ˜
class MulLayer:
    def __init__(self):
        self.x = None  # μˆœμ „νŒŒ μž…λ ₯κ°’ μœ μ§€λ₯Ό μœ„ν•΄μ„œ μ΄ˆκΈ°ν™”
        self.y = None  # μˆœμ „νŒŒ μž…λ ₯κ°’ μœ μ§€λ₯Ό μœ„ν•΄μ„œ μ΄ˆκΈ°ν™”

    def forward(self, x, y):
        self.x = x  # μˆœμ „νŒŒ μ‹œ μž…λ ₯된 xκ°’ μ €μž₯
        self.y = y  # μˆœμ „νŒŒ μ‹œ μž…λ ₯된 yκ°’ μ €μž₯
        out = x * y  # μž…λ ₯된 두 κ°’μ˜ κ³± λ°˜ν™˜
        return out

# 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ λ―ΈλΆ„(dout)μ—μ„œ μˆœμ „νŒŒ λ•Œμ˜ 값을 μ„œλ‘œ 봐꿔 κ³±ν•œ ν›„ ν•˜λ₯˜λ‘œ 흘림
    def backward(self, dout):
        dx = dout * self.y  # x에 λŒ€ν•œ λ―ΈλΆ„κ°’ 계산
        dy = dout * self.x  # y에 λŒ€ν•œ λ―ΈλΆ„κ°’ 계산
        return dx, dy  # λ―ΈλΆ„ κ²°κ³Ό λ°˜ν™˜


# λ§μ…ˆ 계측 μ •μ˜
class AddLayer:
    def __init__(self):
        pass  # λ§μ…ˆ 계측은 λ³„λ„μ˜ μ΄ˆκΈ°ν™” μž‘μ—…μ΄ ν•„μš” μ—†μŒ

    def forward(self, x, y):
        out = x + y  # μž…λ ₯된 두 κ°’μ˜ ν•© λ°˜ν™˜
        return out

# 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ λ―ΈλΆ„(dout)μ—μ„œ μˆœμ „νŒŒ λ•Œμ˜ 값을 μ„œλ‘œ 봐꿔 κ³±ν•œ ν›„ ν•˜λ₯˜λ‘œ 흘림
    def backward(self, dout):
        dx = dout * 1  # x에 λŒ€ν•œ λ―ΈλΆ„κ°’ 계산
        dy = dout * 1  # y에 λŒ€ν•œ λ―ΈλΆ„κ°’ 계산
        return dx, dy  # λ―ΈλΆ„ κ²°κ³Ό λ°˜ν™˜


if __name__ == '__main__':
    # 문제1: 사과 가격 계산 μ˜ˆμ‹œ
    apple = 100  # 사과 ν•œ 개 가격
    apple_num = 2  # 사과 개수
    tax = 1.1  # μ„ΈκΈˆ

    # 계측 생성
    mul_apple_layer = MulLayer()  # 사과 가격 계산을 μœ„ν•œ κ³±μ…ˆ 계측
    mul_tax_layer = MulLayer()  # μ„ΈκΈˆ 계산을 μœ„ν•œ κ³±μ…ˆ 계측

    # μˆœμ „νŒŒ
    apple_price = mul_apple_layer.forward(apple, apple_num)  # 사과 가격 계산
    price = mul_tax_layer.forward(apple_price, tax)  # μ΅œμ’… 가격 계산

    print(price)  # μ΅œμ’… 가격 좜λ ₯

    # μ—­μ „νŒŒ
    dprice = 1  # 가격에 λŒ€ν•œ λ―ΈλΆ„κ°’ μ΄ˆκΈ°ν™”
    dapple_price, dtax = mul_tax_layer.backward(dprice)  # μ„ΈκΈˆ 계산 μ—­μ „νŒŒ
    dapple, dapple_num = mul_apple_layer.backward(dapple_price)  # 사과 가격 계산 μ—­μ „νŒŒ

    print(dapple, dapple_num, dtax)  # λ―ΈλΆ„ κ²°κ³Ό 좜λ ₯

    # 문제2: 사과와 μ˜€λ Œμ§€ 가격 계산 μ˜ˆμ‹œ
    orange = 150  # μ˜€λ Œμ§€ ν•œ 개 가격
    orange_num = 3  # μ˜€λ Œμ§€ 개수

    # 계측 μž¬μ‚¬μš© 및 μƒˆλ‘œμš΄ 계측 생성
    mul_apple_layer = MulLayer()  # 사과 가격 계산을 μœ„ν•œ κ³±μ…ˆ 계측 (μž¬μ‚¬μš©)
    mul_orange_layer = MulLayer()  # μ˜€λ Œμ§€ 가격 계산을 μœ„ν•œ κ³±μ…ˆ 계측
    add_apple_orange_layer = AddLayer()  # 사과와 μ˜€λ Œμ§€ 가격 합산을 μœ„ν•œ λ§μ…ˆ 계측
    mul_tax_layer = MulLayer()  # μ„ΈκΈˆ 계산을 μœ„ν•œ κ³±μ…ˆ 계측 (μž¬μ‚¬μš©)

    # μˆœμ „νŒŒ
    apple_price = mul_apple_layer.forward(apple, apple_num)  # 사과 가격 계산
    orange_price = mul_orange_layer.forward(orange, orange_num)  # μ˜€λ Œμ§€ 가격 계산
    all_price = add_apple_orange_layer.forward(apple_price,

Activation Function Layer (ν™œμ„±ν™” ν•¨μˆ˜ 계측) κ΅¬ν˜„ν•˜κΈ°

계산 κ·Έλž˜ν”„λ₯Ό 신경망에 μ μš©ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. 신경망을 κ΅¬μ„±ν•˜λŠ” Layer(계측)을 각각의 클래슀 ν•˜λ‚˜λ‘œ κ΅¬ν˜„ν•©λ‹ˆλ‹€.

ReLU 계측

  • ν™œμ„±ν™” ν•¨μˆ˜λ‘œ μ‚¬μš©λ˜λŠ” ReLU의 μˆ˜μ‹μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • x에 λŒ€ν•œ y의 미뢄은 μ•„λž˜μ˜ μ‹μ²˜λŸΌ κ΅¬ν•©λ‹ˆλ‹€.

  • μœ„μ˜ μˆ˜μ‹κ³Ό 같이, Forward Propagation(μˆœμ „νŒŒ) λ•Œμ˜ μž…λ ₯인 xκ°€ 0보닀 크면 Back Propagation(μ—­μ „νŒŒ)λŠ” 상λ₯˜μ˜ 값을 κ·ΈλŒ€λ‘œ ν•˜λ₯˜λ‘œ ν˜λ¦½λ‹ˆλ‹€.
  • λ‹€λ§Œ, Forward Propagation(μˆœμ „νŒŒ) λ•Œ xκ°€ 0 μ΄ν•˜λ©΄ Back Propagation(μ—­μ „νŒŒ) λ•ŒλŠ” ν•˜λ₯˜λ‘œ μ‹ ν˜Έλ₯Ό 보내지 μ•ŠμŠ΅λ‹ˆλ‹€. (0을 λ³΄λƒ…λ‹ˆλ‹€.) 계산 κ·Έλž˜ν”„λ‘œλŠ” μ•„λž˜μ˜ 그림처럼 κ·Έλ¦½λ‹ˆλ‹€.

  • 이제 ν•œλ²ˆ ReLU 계츨을 κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.
class Relu:
    def __init__(self):
        self.mask = None  # μž…λ ₯값이 0 μ΄ν•˜μΈμ§€ μ—¬λΆ€λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ λ³€μˆ˜λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

    def forward(self, x):
        self.mask = (x <= 0)  # x의 값이 0 μ΄ν•˜μΈ μ›μ†ŒλŠ” True, κ·Έ μ™ΈλŠ” False둜 ν•˜λŠ” 배열을 μƒμ„±ν•©λ‹ˆλ‹€.
        out = x.copy()  # μž…λ ₯κ°’ x의 볡사본을 μƒμ„±ν•©λ‹ˆλ‹€.
        out[self.mask] = 0  # maskκ°€ True인 μœ„μΉ˜, 즉 x의 값이 0 μ΄ν•˜μΈ μœ„μΉ˜μ˜ μ›μ†Œλ₯Ό 0으둜 μ„€μ •ν•©λ‹ˆλ‹€.
        
        return out  # ν™œμ„±ν™” ν•¨μˆ˜λ₯Ό μ μš©ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
    def backward(self, dout):
        dout[self.mask] = 0  # μˆœμ „νŒŒ λ•Œ 0 μ΄ν•˜μ˜€λ˜ μ›μ†Œμ— λŒ€μ‘ν•˜λŠ” μ—­μ „νŒŒ 값은 0으둜 μ„€μ •ν•©λ‹ˆλ‹€.
        dx = dout  # λ‚˜λ¨Έμ§€ μ›μ†ŒλŠ” κ·ΈλŒ€λ‘œ dout을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
        return dx  # μž…λ ₯값에 λŒ€ν•œ 미뢄값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • ReLU ν΄λž˜μŠ€λŠ” maskλΌλŠ” μΈμŠ€ν„΄μŠ€ λ³€μˆ˜λ₯Ό κ°€μ§‘λ‹ˆλ‹€.
  • maskλŠ” True/False둜 κ΅¬μ„±λœ Numpy Array(λ°°μ—΄)둜, Forward Propagation(μˆœμ „νŒŒ)의 μž…λ ₯인 x의 μ›μ†Œ 값이 0μ΄ν•˜μΈ indexλŠ” True, κ·Έ μ™Έ(0보닀 큰 μ›μ†Œ)λŠ” False둜 μœ μ§€ν•©λ‹ˆλ‹€.
  • μ˜ˆμ»¨λŒ€ mask λ³€μˆ˜λŠ” True/False둜 κ΅¬μ„±λœ Numpy 배열을 μœ μ§€ν•©λ‹ˆλ‹€.

Sigmoid 계측

  • Sigmoid ν•¨μˆ˜λŠ” λ‹€μŒ 식을 μ˜λ―Έν•©λ‹ˆλ‹€.

  • μœ„μ˜ 식을 계산 κ·Έλž˜ν”„λ‘œ 그리면 μ•„λž˜μ˜ 그림처럼 λ©λ‹ˆλ‹€.

Sigmoid κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„ (Forward Propagation - μˆœμ „νŒŒ)

  • 'x'와 '+' λ…Έλ“œλ§κ³ λ„, 'exp' ,'/' λ…Έλ“œκ°€ μžˆλŠ”λ°, 'exp' λ…Έλ“œλŠ” y = exp(x) 계산을 μˆ˜ν–‰ν•˜κ³  '/' λ…Έλ“œλŠ” y = 1/x 계산을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.
  • 계산은 'κ΅­μ†Œμ  계산'의 μ „νŒŒλ‘œ μ΄λ€„μ§‘λ‹ˆλ‹€. 이제 μœ„μ˜ 계산 κ·Έλž˜ν”„μ˜ Back Propagation(μ—­μ „νŒŒ)의 흐름을 였λ₯Έμͺ½μ—μ„œ μ™Όμͺ½μœΌλ‘œ ν•œ 단계씩 μ‹Άμ–΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

1단계

  • '/' λ…Έλ“œ, y = 1/x 을 λ―ΈλΆ„ν•˜λ©΄ λ‹€μŒ 식이 λ©λ‹ˆλ‹€.

  • Back Propagation(μ—­μ „νŒŒ) λ•ŒλŠ” 상λ₯˜μ—μ„œ 흘러온 값에 -y**2 (μˆœμ „νŒŒμ˜ 좜λ ₯을 μ œκ³±ν•œ ν›„ λ§ˆμ΄λ„ˆμŠ€λ₯Ό 뢙인 κ°’)을 κ³±ν•΄μ„œ ν•˜λ₯˜λ‘œ μ „λ‹¬ν•©λ‹ˆλ‹€.
  • 계산 κ·Έλž˜ν”„μ—μ„œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

2단계

  • '+'λ…Έλ“œλŠ” 상λ₯˜μ˜ 값을 μ—¬κ³Ό 없이 ν•˜λ₯˜λ‘œ λ‚΄λ³΄λ‚΄μ§€λŠ”κ²Œ λ‹€μž…λ‹ˆλ‹€.

3단계

  • 'exp' λ…Έλ“œλŠ” y = exp(x) 연산을 μˆ˜ν–‰ν•˜λ©°, κ·Έ 미뢄은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • 계산 κ·Έλž˜ν”„μ—μ„œλŠ” 상λ₯˜μ˜ 값에 Forward Propagation(μˆœμ „νŒŒ) λ•Œμ˜ 좜랡(이 μ˜ˆμ—μ„œλŠ” exp(-x))을 κ³±ν•΄ ν•˜λ₯˜λ‘œ μ „νŒŒν•©λ‹ˆλ‹€.

4단계

  • 'x'λ…Έλ“œλŠ” Forward Propagation(μˆœμ „νŒŒ) λ•Œμ˜ 값을 'μ„œλ‘œ 봐꿔' κ³±ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œλŠ” -1λ₯Ό κ³±ν•©λ‹ˆλ‹€.

  • μœ„μ— 계산 κ·Έλž˜ν”„λ₯Ό 보면, Sigmoid κ³„μΈ΅μ˜ Back Propagation(μ—­μ „νŒŒ)λ₯Ό 계산 κ·Έλž˜ν”„λ₯Ό μ™„μ„±ν–ˆμŠ΅λ‹ˆλ‹€.
  • μ΄λŸ¬ν•œ 계산 κ·Έλž˜ν”„μ˜ 쀑간 과정을 λͺ¨λ‘ λ¬Άμ–΄ λ‹¨μˆœν•œ 'Sigmoid' λ…Έλ“œ ν•˜λ‚˜λ‘œ λŒ€μ²΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Sigmoid κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„ (κ°„μ†Œν™” 버전)

  • 계산 κ·Έλž˜ν”„μ™€, κ°„μ†Œν™” κ·Έλž˜ν”„μ˜ κ°„μ†Œν™” λ²„μ „μ˜ κ²°κ³ΌλŠ” κ°™μŠ΅λ‹ˆλ‹€.
  • κ·Έλ ‡μ§€λ§Œ, κ°„μ†Œν™” 버전은 Back Propagation(μ—­μ „νŒŒ) κ³Όμ •μ˜ 쀑간 과정을 μƒλž΅ν•˜μ—¬ 더 효율적인 계산이라고 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ˜ν•œ Nodeλ₯Ό κ·Έλ£Ήν™” ν•˜μ—¬ Sigmoid κ³„μΈ΅μ˜ μ„Έμ„Έν•œ λ‚΄μš©μ„ λ…ΈμΆœν•˜μ§€ μ•Šκ³  μž…λ ₯, 좜λ ₯μ—λ§Œ 집쀑 ν•  수 μžˆλ‹€λŠ” 것도 쒋은 포인트 μž…λ‹ˆλ‹€.

λ―ΈλΆ„ μˆ˜μ‹ 정리

  • 이처럼 Sigmoid κ³„μΈ΅μ˜ Back Propagation(μ—­μ „νŒŒ)λŠ” Forward Progagation(μˆœμ „νŒŒ)의 좜λ ₯(y)만으둜 계산할 수 μžˆμŠ΅λ‹ˆλ‹€.

Sigmoid κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„: μˆœμ „νŒŒμ˜ 좜λ ₯ y만으둜 μ—­μ „νŒŒλ₯Ό 계산할 수 μžˆλ‹€.

  • 그러면 ν•œλ²ˆ Sigmoid 계측을 Python으둜 κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • μ—¬κΈ°μ„œλŠ” Forward Progagation(μˆœμ „νŒŒ)의 좜λ ₯을 μΈμŠ€ν„΄μŠ€ λ³€μˆ˜ out에 λ³΄κ΄€ν–ˆλ‹€κ°€, Back Propagation(μ—­μ „νŒŒ)λ•Œ κ·Έ 값을 μ‚¬μš©ν•©λ‹ˆλ‹€.
class Sigmoid:
    def __init__(self):
        self.out = None  # μˆœμ „νŒŒμ˜ 좜λ ₯값을 μ €μž₯ν•˜κΈ° μœ„ν•œ λ³€μˆ˜λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

    def forward(self, x):
        out = 1 / (1 + np.exp(-x))  # μ‹œκ·Έλͺ¨μ΄λ“œ ν•¨μˆ˜λ₯Ό μ μš©ν•©λ‹ˆλ‹€.
        self.out = out  # μˆœμ „νŒŒμ˜ κ²°κ³Όλ₯Ό μ €μž₯ν•©λ‹ˆλ‹€. 이 값은 μ—­μ „νŒŒ λ•Œ μ‚¬μš©λ©λ‹ˆλ‹€.
        return out  # ν™œμ„±ν™” ν•¨μˆ˜λ₯Ό μ μš©ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out  # μ‹œκ·Έλͺ¨μ΄λ“œ ν•¨μˆ˜μ˜ 미뢄을 μ μš©ν•©λ‹ˆλ‹€.
        return dx  # μž…λ ₯값에 λŒ€ν•œ 미뢄값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
# dout은 상λ₯˜(λ‹€μŒ 계측)μ—μ„œ λ„˜μ–΄μ˜¨ λ―ΈλΆ„κ°’μž…λ‹ˆλ‹€.
# μ‹œκ·Έλͺ¨μ΄λ“œ ν•¨μˆ˜μ˜ 미뢄은 y(1-y)이며, μ—¬κΈ°μ„œ yλŠ” μ‹œκ·Έλͺ¨μ΄λ“œ ν•¨μˆ˜μ˜ 좜λ ₯κ°’μž…λ‹ˆλ‹€.
# λ”°λΌμ„œ, self.out이 y에 ν•΄λ‹Ήν•˜κ³ , (1.0 - self.out) * self.out이 y(1-y)에 ν•΄λ‹Ήν•©λ‹ˆλ‹€.
# 이λ₯Ό 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ λ―ΈλΆ„κ°’κ³Ό κ³±ν•˜μ—¬ 이 계측을 톡과할 λ•Œμ˜ 미뢄값을 κ΅¬ν•©λ‹ˆλ‹€.

Affine 계측

μ‹ κ²½λ§μ˜ Forward Propagation(μˆœμ „νŒŒ)μ—μ„œλŠ” Weight(κ°€μ€‘μΉ˜) μ‹ ν˜Έμ˜ 총합을 κ³„μ‚°ν•˜κΈ° λ•Œλ¬Έμ— ν–‰λ ¬μ˜ κ³±(Numpyμ—μ„œλŠ” np.dot())을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.
  • Neuron(λ‰΄λŸ°)의 Weight(κ°€μ€‘μΉ˜) 합은 Y = np.dot(X, W) + B처럼 κ³„μ‚°ν•©λ‹ˆλ‹€.
  • 그리고 이 Yλ₯Ό Activation Function(ν™œμ„±ν™” ν•¨μˆ˜)둜 λ³€ν™˜ν•΄ λ‹€μŒ Layer(μΈ΅)으둜 μ „νŒŒν•˜λŠ” 것이 신경망 Forward Propagation(μˆœμ „νŒŒ)의 νλ¦„μ΄μ˜€μŠ΅λ‹ˆλ‹€.
  • ν–‰λ ¬μ˜ κ³± 계산은 λŒ€μ‘ν•˜λŠ” Dimension(차원)의 μ›μ†Œ 수λ₯Ό μΌμΉ˜μ‹œν‚€λŠ”κ²Œ ν•΅μ‹¬μž…λ‹ˆλ‹€. ν–‰λ ¬μ˜ ν˜•μƒμ„ (2, 3)처럼 κ΄„ν˜Έλ‘œ ν‘œκΈ°ν•˜λŠ” μ΄λ‰΄λŠ” Numpy shapeν•¨μˆ˜μ˜ Output(좜λ ₯) & ν˜•νƒœλ₯Ό ν†΅μΌν•˜κΈ° μœ„ν•΄μ„œ μž…λ‹ˆλ‹€.

μ‹ κ²½λ§μ˜ Forward Propagation(μˆœμ „νŒŒ) λ•Œ μˆ˜ν–‰ν•˜λŠ” ν–‰λ ¬μ˜ 곱은 κΈ°ν•˜ν•™μ—μ„œ Affine Transformation(μ–΄νŒŒμΈ λ³€ν™˜)이라고 ν•©λ‹ˆλ‹€.
  • 그러면 ν–‰λ ¬μ˜ κ³±κ³Ό Bias(편ν–₯)의 합을 계산 κ·Έλž˜ν”„λ‘œ ν•œλ²ˆ κ·Έλ €λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • 곱을 κ³„μ‚°ν•˜λŠ” λ…Έλ“œλ₯Ό 'dot'이라 ν•˜λ©΄ np.dot(X, W) + B 계산은 μ•„λž˜μ˜ κ·Έλž˜ν”„μ²˜λŸΌ κ·Έλ €μ§‘λ‹ˆλ‹€.
  • 참고둜 μ§€κΈˆκΉŒμ§€μ˜ 계산 κ·Έλž˜ν”„λŠ” λ…Έλ“œ 사이에 '슀칼라 κ°’'이 ν˜λ €λŠ”λ° λ°˜ν•΄, 이 μ˜ˆμ—μ„œλŠ” 'ν–‰λ ¬'이 흐λ₯΄κ³  μžˆμŠ΅λ‹ˆλ‹€.

Affine κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„: λ³€μˆ˜κ°€ ν–‰λ ¬μž„μ— 주의, 각 λ³€μˆ˜μ˜ ν˜•μƒμ„ λ³€μˆ˜λͺ… μœ„μ— ν‘œκΈ°

  • 그러면 μ΄λ²ˆμ—λŠ” Back propagation(μ—­μ „νŒŒ)에 λŒ€ν•΄ 생각해 λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • 행렬을 μ‚¬μš©ν•œ Back propagation(μ—­μ „νŒŒ)도 ν–‰λ ¬μ˜ μ›μ†Œλ§ˆλ‹€ μ „κ°œν•΄λ³΄λ©΄ 슀칼라 값을 μ‚¬μš©ν•œ μ§€κΈˆκΉŒμ§€μ˜ 계산 κ·Έλž˜ν”„μ™€ 같은 μˆœμ„œλ‘œ 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • WT의 TλŠ” μ „μΉ˜ν–‰λ ¬μ„ λœ»ν•˜λ©°, W의 (i, j) μœ„μΉ˜μ˜ μ›μ†Œλ₯Ό (j,i) μœ„μΉ˜λ‘œ 봐꾼것을 λ§ν•©λ‹ˆλ‹€.

Affine κ³„μΈ΅μ˜ μ—­μ „νŒŒ, λ³€μˆ˜κ°€ 닀차원 λ°°μ—΄μž„μ— 주의.

  • κ³„μ‚°κ·Έλž˜ν”„μ—μ„œ 각 λ³€μˆ˜μ˜ ν˜•μƒμ— μ£Όμ˜ν•΄μ„œ μ‚΄νŽ΄ 봐야 ν•©λ‹ˆλ‹€.
  • 특히 X와 δL/δX은 같은 ν˜•μƒμ΄κ³ , W와 δL/δW도 같은 ν˜•μƒμ΄λΌλŠ” 것을 κΈ°μ–΅ν•˜μ„Έμš”.

  • 근데, μ—¬κΈ°μ„œ 의문이 λ“œλŠ”κ²Œ μžˆμŠ΅λ‹ˆλ‹€. μ™œ ν–‰λ ¬μ˜ ν˜•μƒμ— 주의λ₯Ό ν•΄μ•Ό ν• κΉŒμš”?
  • ν–‰λ ¬μ˜ κ³±μ—μ„œ λŒ€μ‘ν•˜λŠ” μ°¨μ›μ˜ μ›μ†Œ 수λ₯Ό μΌμΉ˜μ‹œμΌœμ•Ό ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

ν–‰λ ¬ κ³±('dot' λ…Έλ“œ)의 μ—­μ „νŒŒλŠ” 행렬에 λŒ€μ‘ν•˜λŠ” μ°¨μ›μ˜ μ›μ†Œμˆ˜κ°€ μΌμΉ˜ν•˜λ„λ‘ 곱을 μ‘°λ¦½ν•˜μ—¬ ꡬ할 수 μžˆλ‹€.


배치용 Affine 계측

  • Affine 계측은 μž…λ ₯ λ°μ΄ν„°λ‘œ X ν•˜λ‚˜λ§Œμ„ κ³ λ €ν•œ κ²ƒμ΄μ˜€μŠ΅λ‹ˆλ‹€.
  • 이번 μ ˆμ—μ„œλŠ” 데이터 N개λ₯Ό λ¬Άμ–΄ Forward Propagation(μˆœμ „νŒŒ)ν•˜λŠ” 경우, 즉, 배치용 Affine 계측을 생각해 λ³΄κ² μŠ΅λ‹ˆλ‹€.
  • μ—¬κΈ°μ„œ 묢은 데이터λ₯Ό '배치'라고 λΆ€λ¦…λ‹ˆλ‹€.

배치용 Affine κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„

  • κΈ°μ‘΄κ³Ό λ‹€λ₯Έ 뢀뢄은 input(μž…λ ₯)인 X의 ν˜•μƒμ΄ (N, 2)κ°€ 된 것 λΏμž…λ‹ˆλ‹€. κ·Έ λ’€λ‘œλŠ” μ§€κΈˆκΉŒμ§€μ™€ 같이 계산 κ·Έλž˜ν”„μ˜ μˆœμ„œλ₯Ό 따라 순순히 ν–‰λ ¬ 계산을 ν•©λ‹ˆλ‹€.
  • λ˜ν•œ Back propagation(μ—­μ „νŒŒ) λ•ŒλŠ” ν–‰λ ¬μ˜ ν˜•μƒμ— μ£Όμ˜ν•˜λ©΄ δL/δXκ³Ό δL/δW은 이전과 같이 λ„μΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • νŽΈν•­μ„ λ”ν• λ•Œλ„ μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. Forward Propagation(μˆœμ „νŒŒ)의 Bias(편ν–₯) λ§μ…ˆμ€ X, W에 λŒ€ν•œ Bias(편ν–₯)이 각각의 데이터에 λ”ν•΄μ§‘λ‹ˆλ‹€.
  • κ·Έλž˜μ„œ Back propagation(μ—­μ „νŒŒ) λ•ŒλŠ” 각 λ°μ΄ν„°μ˜ Back propagation(μ—­μ „νŒŒ)값이 Bias(편ν–₯)의 μ›μ†Œμ— λͺ¨μ—¬μ•Ό ν•©λ‹ˆλ‹€.
  • μ΄λ ‡κ²Œ Affine 계측은 μ΄λ ‡κ²Œ κ΅¬ν˜„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
class Affine:
    def __init__(self, W, b):
        self.W = W  # κ°€μ€‘μΉ˜(weight)λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
        self.b = b  # 편ν–₯(bias)을 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
        self.x = None  # μž…λ ₯ 데이터λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ λ³€μˆ˜λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
        self.dW = None  # κ°€μ€‘μΉ˜μ˜ 기울기λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ λ³€μˆ˜λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
        self.db = None  # 편ν–₯의 기울기λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ λ³€μˆ˜λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

    def forward(self, x):
        self.x = x  # μž…λ ₯ 데이터λ₯Ό μ €μž₯ν•©λ‹ˆλ‹€.
        out = np.dot(x, self.W) + self.b  # μž…λ ₯ 데이터와 κ°€μ€‘μΉ˜μ˜ 내적을 κ³„μ‚°ν•˜κ³  편ν–₯을 λ”ν•©λ‹ˆλ‹€.
        return out  # κ³„μ‚°λœ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)  # 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ 미뢄값에 κ°€μ€‘μΉ˜μ˜ μ „μΉ˜λ₯Ό κ³±ν•˜μ—¬ μž…λ ₯ 데이터에 λŒ€ν•œ 미뢄값을 κ³„μ‚°ν•©λ‹ˆλ‹€. (μ˜€νƒ€ μˆ˜μ •: np.dout -> np.dot, xelf.W.T -> self.W.T)
        self.dW = np.dot(self.x.T, dout)  # μž…λ ₯ λ°μ΄ν„°μ˜ μ „μΉ˜μ™€ 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ 미뢄값을 κ³±ν•˜μ—¬ κ°€μ€‘μΉ˜μ— λŒ€ν•œ 미뢄값을 κ³„μ‚°ν•©λ‹ˆλ‹€.
        self.db = np.sum(dout, axis=0)  # 상λ₯˜μ—μ„œ λ„˜μ–΄μ˜¨ 미뢄값을 μΆ•(axis) 0을 따라 ν•©μ‚°ν•˜μ—¬ 편ν–₯에 λŒ€ν•œ 미뢄값을 κ³„μ‚°ν•©λ‹ˆλ‹€.
        return dx  # μž…λ ₯ 데이터에 λŒ€ν•œ 미뢄값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

Softmax-with-Loss 계측

좜λ ₯μΈ΅μ—μ„œ μ‚¬μš©ν•˜λŠ” Softmax ν•¨μˆ˜μ— κ΄€ν•΄ μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.
  • Softmax ν•¨μˆ˜λŠ” μž…λ ₯ 값을 μ •κ·œν™”ν•˜μ—¬ 좜λ ₯ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄μ„œ Mnist 데이터셋을 ν™œμš©ν•œ 손글씨 숫자 μΈμ‹μ—μ„œμ˜ Softmax κ³„μΈ΅μ˜ 좜λ ₯은 μ•„λž˜μ˜ 그림처럼 λ©λ‹ˆλ‹€.
    • Input Imageκ°€ Affine 계측, ReLU 계측을 ν†΅κ³Όν•˜μ—¬ λ³€ν™˜λ˜κ³ , λ§ˆμ§€λ§‰ Softmax 계측에 μ˜ν•΄μ„œ 10개의 μž…λ ₯이 μ •κ·œν™” λ©λ‹ˆλ‹€.
    • 이 κ·Έλ¦Όμ—μ„œλŠ” 숫자 '0'의 μ μˆ˜λŠ” 5.3이며, 이것이 Softmax 계측에 μ˜ν•΄μ„œ 0.008(0.8%)둜 λ³€ν™˜λ©λ‹ˆλ‹€.
    • λ˜ν•œ '2'의 μ •μˆ˜λŠ” 10.1μ—μ„œ 0.991(99.1%)둜 λ³€ν™˜λ©λ‹ˆλ‹€.

  • Softmax 계측은 μž…λ ₯ 값을 μ •κ·œν™”(좜λ ₯의 합이 1이 되둜둝 λ³€ν˜•)ν•˜μ—¬ 좜λ ₯ν•©λ‹ˆλ‹€.
  • 그리고 손글씨 μˆ«μžλŠ” 10개(Class 10개둜 λΆ„λ₯˜)μ΄λ―€λ‘œ Softmax κ³„μΈ΅μ˜ μž…λ ₯은 10κ°œκ°€ λ©λ‹ˆλ‹€.
μ‹ κ²½λ§μ—μ„œ μˆ˜ν–‰ν•˜λŠ” μž‘μ—…μ€ ν•™μŠ΅, μΆ”λ‘  2κ°€μ§€ 인데, μΆ”λ‘ ν• λ•Œμ˜ 일반적으둜 Softmax 계측을 μ‚¬μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
신경망은 μΆ”λ‘ ν• λ•Œ λ§ˆμ§€λ§‰ Affine κ³„μΈ΅μ˜ 좜λ ₯을 인식 결과둜 μ΄μš©ν•©λ‹ˆλ‹€.
그리고 μ‹ κ²½λ§μ—μ„œ μ •κ·œν™” ν•˜μ§€ μ•ŠλŠ” 좜λ ₯ κ²°κ³Όλ₯Ό Score(점수)라고 ν•©λ‹ˆλ‹€.
즉, 신경망 μΆ”λ‘ μ—μ„œ 닡을 ν•˜λ‚˜λ§Œ λ‚΄λŠ” κ²½μš°μ—λŠ” κ°€μž₯ 높은 Score(점수)만 μ•Œλ©΄ λ˜λ‹ˆ, Softmax 계측이 ν•„μš” μ—†μŠ΅λ‹ˆλ‹€.
λ‹€λ§Œ, 신경망을 ν•™μŠ΅ν• λ•Œμ—λŠ” Softmax 계측이 ν•„μš”ν•©λ‹ˆλ‹€.
  • 그러면 이제 Softmax 계측을 ν•œλ²ˆ λ³΄κ² μŠ΅λ‹ˆλ‹€. Loss Function(손싀 ν•¨μˆ˜)인 Cross-Entropy Error(ꡐ체 μ—”νŠΈλ‘œν”Ό 였차)도 ν¬ν•¨ν•˜μ—¬, 'Softmax-with-Loss 계측'μ΄λΌλŠ” μ΄λ¦„μœΌλ‘œ κ΅¬ν˜„ν•©λ‹ˆλ‹€.

Softmax-with-Loss κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„

  • λ³΄μ‹œλ‹€ μ‹œν”Ό, Softmax-with-Loss 계측은 λ³΅μž‘ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œλŠ” 결과만 ν•œλ²ˆ λ³΄κ² μŠ΅λ‹ˆλ‹€.

κ°„μ†Œν™”λœ Softmax-with-Loss κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„

  • μœ„μ˜ 계산 κ·Έλž˜ν”„μ—μ„œ μ†Œν”„νŠΈλ§₯수 ν•¨μˆ˜λŠ” 'Softmax" κ³„μΈ΅μœΌλ‘œ, Cross-Entropy Error'κ³„μΈ΅μœΌλ‘œ ν‘œκΈ°ν–ˆμŠ΅λ‹ˆλ‹€.
  • μ—¬κΈ°μ„œ 3개의 클래슀 λΆ„λ₯˜λ₯Ό κ°€μ •ν•˜κ³  이전 Layer(계측)μ—μ„œ 3개의 μž…λ ₯(Score)λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
  • κ·Έλ¦Όκ³Ό 같이 Softmax 계측은 μž…λ ₯ (a1, a2, a3)λ₯Ό μ •κ·œν™”ν•˜μ—¬ (y1, y2, y3)λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€.
  • Cross-Entropy Error 계측은 Softmax κ³„μΈ΅μ˜ 좜λ ₯ (y1, y2, y3)와 μ •λ‹΅ λ ˆμ΄λΈ” (t1, t2, t3)λ₯Ό λ°›κ³ , 이 λ°μ΄ν„°λ‘œλΆ€ν„° Loss(손싀) L을 좜λ ₯ν•©λ‹ˆλ‹€.
  • κ°„μ†Œν™”λœ Softmax-with-Loss κ³„μΈ΅μ˜ 계산 κ·Έλž˜ν”„μ—μ„œ μ£Όλͺ©ν• κ±΄ Back propagation(μ—­μ „νŒŒ)의 κ²°κ³Όμž…λ‹ˆλ‹€.
  • Softmax κ³„μΈ΅μ˜ Back propagation(μ—­μ „νŒŒ)λŠ” (y1 - t1, y2- t2, y3 - t3)λΌλŠ” 'λ§λ”ν•œ' κ²°κ³Όλ₯Ό 내놓고 μžˆμŠ΅λ‹ˆλ‹€.
  • (y1 ~ y3)λŠ” Softmax κ³„μΈ΅μ˜ 좜λ ₯이고, (t1 ~ t3)λŠ” μ •λ‹΅ λ ˆμ΄λΈ” μ΄λ―€λ‘œ, (y1 - t1, y2- t2, y3 - t3)λŠ” Softmax κ³„μΈ΅μ˜ 좜λ ₯κ³Ό μ •λ‹΅ λ ˆμ΄λΈ”μ˜ μ°¨λΆ„μΈκ²ƒμž…λ‹ˆλ‹€.
  • μ‹ κ²½λ§μ˜ Back propagation(μ—­μ „νŒŒ)λŠ” 이 차이인 μ˜€μ°¨κ°€ μ•ž 계측에 μ „ν•΄μ§€λŠ” κ²ƒμž…λ‹ˆλ‹€.
  • 이것이 신경망 ν•™μŠ΅μ˜ μ€‘μš”ν•œ μ„±μ§ˆμž…λ‹ˆλ‹€.
  • 그러면 Softmax-with-Loss 계측을 κ΅¬ν˜„ν•œ μ½”λ“œλ₯Ό λ³΄κ² μŠ΅λ‹ˆλ‹€.

Softmax-with-Loss Example Code (by Python)

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None # 손싀
        self.y = None # softmax의 좜λ ₯
        self.t = None # μ •λ‹΅ λ ˆμ΄λΈ”(원-ν•« 벑터)
        
    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)  # μž…λ ₯ x에 λŒ€ν•΄ softmax ν•¨μˆ˜λ₯Ό μ μš©ν•©λ‹ˆλ‹€.
        self.loss = cross_entropy_error(self.y, self.t
        # softmax의 좜λ ₯κ³Ό μ •λ‹΅ λ ˆμ΄λΈ”μ„ μ΄μš©ν•΄ 크둜슀 μ—”νŠΈλ‘œν”Ό 였차λ₯Ό κ³„μ‚°ν•©λ‹ˆλ‹€.
        return self.loss  # κ³„μ‚°λœ 손싀을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
    def backward(self, dout=1):
        batch_size = self.t.shape[0]  # 배치 크기λ₯Ό κ΅¬ν•©λ‹ˆλ‹€.
        dx = (self.y - self.t) / batch_size
        # 손싀 ν•¨μˆ˜μ˜ 미뢄을 κ³„μ‚°ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ dout=1은 손싀 ν•¨μˆ˜μ˜ 미뢄값이 1이라고 κ°€μ •ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.
        return dx  # μž…λ ₯값에 λŒ€ν•œ 미뢄값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • μ£Όμ˜ν•΄μ•Ό ν•˜λŠ”μ μ€ Back propagation(μ—­μ „νŒŒ) λ•ŒλŠ” μ „νŒŒν•˜λŠ” 값을 Batch_size둜 λ‚˜λˆ μ„œ 데이터 1κ°œλ‹Ή 였차λ₯Ό μ•ž Layer(계측)으둜 μ „νŒŒν•©λ‹ˆλ‹€.

Backprogagation(μ˜€μ°¨μ—­μ „νŒŒ)법 κ΅¬ν˜„ν•˜κΈ°

신경망 ν•™μŠ΅μ˜ 전체적인 Flow

λ‹€μ‹œ ν•œλ²ˆ 신경망 ν•™μŠ΅μ˜ μˆœμ„œλ₯Ό ν•œλ²ˆ λ³΄κ² μŠ΅λ‹ˆλ‹€.

μ „μ œ

  • μ‹ κ²½λ§μ—λŠ” 적응 κ°€λŠ₯ν•œ Weight(κ°€μ€‘μΉ˜)와 Bias(편ν–₯)이 있고, 이 Weight(κ°€μ€‘μΉ˜)와 Bias(편ν–₯)을 Training Data(ν›ˆλ ¨ 데이터)에 μ μ‘ν•˜λ„λ‘ μ‘°μ •ν•˜λŠ” 과정을 Training(ν•™μŠ΅)이라고 ν•©λ‹ˆλ‹€.
  • 그리고 Neural Network Training(신경망 ν•™μŠ΅)은 4λ‹¨κ³„λ‘œ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1단계 - Mini-Batch

  • ν›ˆλ ¨ 데이터 쀑 일뢀λ₯Ό λ¬΄μž‘μœ„λ‘œ κ°€μ Έμ˜΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ μ„ λ³„ν•œ 데이터λ₯Ό Mini-Batch(λ―Έλ‹ˆλ°°μΉ˜) 라고 ν•©λ‹ˆλ‹€.
  • 그리고 κ·Έ Mini-Batch(λ―Έλ‹ˆλ°°μΉ˜)의 Loss Function Value(손싀 ν•¨μˆ˜ κ°’)을 μ€„μ΄λŠ” 것이 λͺ©ν‘œμž…λ‹ˆλ‹€.

2단계 - Gradient(기울기) μ‚°μΆœ

  • Mini-Batch의 Loss Function 값을 쀄이기 μœ„ν•΄μ„œ 각 Weight Paraemter(κ°€μ€‘μΉ˜ λ§€κ°œλ³€μˆ˜)의 Gradient(기울기)λ₯Ό κ΅¬ν•©λ‹ˆλ‹€.
  • Gradient(기울기)λŠ” Loss Function Value(손싀 ν•¨μˆ˜ κ°’)을 κ°€μž₯ μž‘κ²Œ ν•˜λŠ” λ°©ν–₯을 μ œμ‹œν•©λ‹ˆλ‹€.

3단계 - Parameter(λ§€κ°œλ³€μˆ˜) κ°±μ‹ 

  • Weight Paraemter(κ°€μ€‘μΉ˜ λ§€κ°œλ³€μˆ˜)λ₯Ό Gradient(기울기) λ°©ν–₯으둜 μ•„μ£Ό 쑰금 κ°±μ‹ ν•©λ‹ˆλ‹€.

4단계 - 반볡

  • 1~3단계λ₯Ό λ°˜λ³΅ν•©λ‹ˆλ‹€.

  • μ—¬κΈ°μ„œ μ˜€μ°¨μ—­μ „νŒŒλ²•μ΄ λ“±μž₯ν•˜λŠ” λ‹¨κ³„λŠ” 2단계인 'Gradient(기울기)μ‚°μΆœ' μž…λ‹ˆλ‹€.
  • μ•žμ—μ„œλŠ” 이 Gradient(기울기)λ₯Ό κ΅¬ν•˜κΈ° μœ„ν•΄μ„œ 수치 미뢄을 μ‚¬μš©ν–ˆμ§€λ§Œ, μ˜€μ°¨μ—­μ „νŒŒλ²•μ„ μ΄μš©ν•˜λ©΄ Gradient(기울기)λ₯Ό 효율적, λΉ λ₯΄κ²Œ ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜€μ°¨μ—­μ „νŒŒλ²•μ„ μ΄μš©ν•œ 신경망 κ΅¬ν˜„ν•˜κΈ°

μ—¬κΈ°μ„œ 2μΈ΅ 신경망은 TwoLayerNet 클래슀둜 κ΅¬ν˜„ν•©λ‹ˆλ‹€. ν•œλ²ˆ 클래슀 & μΈμŠ€ν„΄μŠ€ λ³€μˆ˜ 및 Methodλ₯Ό μ •μ˜ν•œ ν‘œλ“€μ„ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

TwoLayerNet 클래슀 & μΈμŠ€ν„΄μŠ€ λ³€μˆ˜
TwoLayerNet 클래슀 & Method

  • μ•Œμ•„μ•Ό 할점은, Layer(계측)을 μ‚¬μš©ν•œλ‹€λŠ” μ μž…λ‹ˆλ‹€.
  • Layer(계측)을 μ‚¬μš©ν•¨μœΌλ‘œμ¨ 인식 κ²°κ³Όλ₯Ό μ–»λŠ” 처리(predict())와 Gradient(기울기)λ₯Ό κ΅¬ν•˜λŠ” 처리 (gradient()) κ³„μΈ΅μ˜ μ „νŒŒλ§ŒμœΌλ‘œ λ™μž‘μ΄ 이루어 μ§‘λ‹ˆλ‹€.
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # λΆ€λͺ¨ λ””λ ‰ν„°λ¦¬μ˜ νŒŒμΌμ„ κ°€μ Έμ˜¬ 수 μžˆλ„λ‘ μ„€μ •
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collections import OrderedDict


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # κ°€μ€‘μΉ˜ μ΄ˆκΈ°ν™”
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 
        self.params['b2'] = np.zeros(output_size)

        # 계측 생성
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()
        
    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        
        return x
        
    # x : μž…λ ₯ 데이터, t : μ •λ‹΅ λ ˆμ΄λΈ”
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)
    
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
        
    # x : μž…λ ₯ 데이터, t : μ •λ‹΅ λ ˆμ΄λΈ”
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        
        return grads
        
    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # κ²°κ³Ό μ €μž₯
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads
  • μΈμˆ˜λŠ” μ°¨λ‘€λŒ€λ‘œ μž…λ ₯μΈ΅ λ‰΄λŸ° μˆ˜, μ€λ‹‰μΈ΅ λ‰΄λŸ° μˆ˜, μΆœλ ₯μΈ΅ λ‰΄λŸ° μˆ˜, κ°€μ€‘μΉ˜ μ΄ˆκΈ°ν™” μ‹œ μ •κ·œλΆ„ν¬μ˜ μŠ€μΌ€μΌμž…λ‹ˆλ‹€.
  • OrderedDictλŠ” μˆœμ„œκ°€ μžˆλŠ” λ”•μ…”λ„ˆλŸ¬ μž…λ‹ˆλ‹€. 'μˆœμ„œκ°€ μžˆλŠ”' λ”•μ…”λ„ˆλ¦¬μ— μΆ”κ°€ν•œ μˆœμ„œλ₯Ό κΈ°μ–΅ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.
  • κ·Έλž˜μ„œ Forward Propagation(μˆœμ „νŒŒ) λ•ŒλŠ” μΆ”κ°€ν•œ μˆœμ„œλŒ€λ‘œ 각 Layer(계측)의 forward() Methodλ₯Ό ν˜ΈμΆœν•˜λ©΄ μ²˜λ¦¬κ°€ λ©λ‹ˆλ‹€.
  • Back Propagation(μ—­μ „νŒŒ)λ•Œμ—λŠ” Layer(계측)을 λ°˜λŒ€ μˆœμ„œλ‘œ 호좜 ν•˜κΈ°λ§Œ ν•˜λ©΄ λ©λ‹ˆλ‹€.
  • Affine, ReLU 계측이 각자의 λ‚΄λΆ€μ—μ„œ Forward Propagation(μˆœμ „νŒŒ), Back Propagation(μ—­μ „νŒŒ)λ₯Ό μ²˜λ¦¬ν•˜κ³  μžˆμœΌλ‹ˆκΉŒ, κ·Έλƒ₯ 계측을 μ˜¬λ°”λ₯Έ μˆœμ„œλ‘œ μ—°κ²°ν•œ ν›„ ν˜ΈμΆœν•΄μ£Όλ©΄ λμž…λ‹ˆλ‹€.

Gradient(기울기) κ²€μ¦ν•˜κΈ°

  • 크게 2κ°€μ§€ 방법이 μžˆμŠ΅λ‹ˆλ‹€.
  • 수치 미뢄을 μ¨μ„œ Gradient(기울기)λ₯Ό κ΅¬ν•˜λŠ” 방법, ν•΄μ„μ μœΌλ‘œ μˆ˜μ‹μ„ ν’€μ–΄μ„œ Gradient(기울기)λ₯Ό κ΅¬ν•˜λŠ” 방법 2κ°€μ§€κ°€ μžˆμŠ΅λ‹ˆλ‹€.
    • μˆ˜μ‹μ„ ν’€μ–΄μ„œ κ΅¬ν•˜λŠ” 방법은 μ˜€μ°¨μ—­μ „νŒŒλ²•μ„ μ‚¬μš©ν•΄μ„œ λ§€κ°œλ³€μˆ˜κ°€ λ§Žμ•„λ„ 효율적으둜 계산이 κ°€λŠ₯ν•©λ‹ˆλ‹€.
  • μ—¬κΈ°μ„œ 수치 미뢄은 μ˜€μ°¨μ—­μ „νŒŒλ²•μ˜ κ²°κ³Όλ₯Ό λΉ„κ΅ν•˜μ—¬ μ œλŒ€λ‘œ κ΅¬ν˜„ν–ˆλŠ”μ§€ κ²€μ¦ν•˜λŠ” 과정을 κ±°μΉ©λ‹ˆλ‹€.
  • 이 κ²€μ¦ν•˜λŠ” μž‘μ—…μ„ '기울기 확인(Gradient Check)'라고 ν•©λ‹ˆλ‹€.
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # λΆ€λͺ¨ λ””λ ‰ν„°λ¦¬μ˜ νŒŒμΌμ„ κ°€μ Έμ˜¬ 수 μžˆλ„λ‘ μ„€μ •
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 데이터 읽기
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

x_batch = x_train[:3]
t_batch = t_train[:3]

grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)

# 각 κ°€μ€‘μΉ˜μ˜ 차이의 μ ˆλŒ“κ°’μ„ κ΅¬ν•œ ν›„, κ·Έ μ ˆλŒ“κ°’λ“€μ˜ 평균을 λ‚Έλ‹€.
for key in grad_numerical.keys():
    diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )
    print(key + ":" + str(diff))
# Result

        W2:9.71260696544e-13
        b2:1.20570232964e-10
        W1:2.86152966578e-13
        b1:1.19419626098e-12
        수치 λ―ΈλΆ„κ³Ό μ˜€μ°¨μ—­μ „νŒŒλ²•μœΌλ‘œ κ΅¬ν•œ 기울기의 차이가 맀우 μž‘λ‹€.
        μ‹€μˆ˜ 없이 κ΅¬ν˜„λ˜μ—ˆμ„ ν™•λ₯ μ΄ λ†’λ‹€.
        정밀도가 μœ ν•œν•˜κΈ° λ•Œλ¬Έμ— μ˜€μ°¨κ°€ 0이 λ˜μ§€λŠ” μ•ŠλŠ”λ‹€.

μ˜€μ°¨μ—­μ „νŒŒλ²•μ„ μ΄μš©ν•œ ν•™μŠ΅ κ΅¬ν˜„ν•˜κΈ°

μ§€κΈˆκΉŒμ§€μ™€ λ‹€λ₯Έ 뢀뢄은 Grdient(기울기)λ₯Ό μ˜€μ°¨μ—­μ „νŒŒλ²•μœΌλ‘œ κ΅¬ν•œλ‹€λŠ” 점 λΏμž…λ‹ˆλ‹€.
# coding: utf-8
import sys, os
sys.path.append(os.pardir)

import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 데이터 읽기
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

# hyperparameter
iters_num = 10000 # 반볡횟수
train_size = x_train.shape[0]
batch_size = 100 # λ―Έλ‹ˆλ°°μΉ˜ 크기
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

# 1 epochλ‹Ή λ°˜λ³΅ν•˜λŠ” 횟수
iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
	# λ―Έλ‹ˆλ°°μΉ˜ νšλ“
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # μ˜€μ°¨μ—­μ „νŒŒλ²•μœΌλ‘œ 기울기 계산 (λ³€κ²½ν•œ λΆ€λΆ„)
    #grad = network.numerical_gradient(x_batch, t_batch) # 수치 λ―ΈλΆ„ 방식
    grad = network.gradient(x_batch, t_batch) # μ˜€μ°¨μ—­μ „νŒŒλ²• 방식(훨씬 λΉ λ₯΄λ‹€)
    
    # λ§€κ°œλ³€μˆ˜ κ°±μ‹ 
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    # ν•™μŠ΅ κ²½κ³Ό 기둝
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    # 1epoch λ‹Ή accuray 계산
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print(train_acc, test_acc)
# Result

"""
train acc, test acc | 0.0992833333333, 0.1032
train acc, test acc | 0.898, 0.9026
train acc, test acc | 0.92135, 0.9216
train acc, test acc | 0.936016666667, 0.9337
train acc, test acc | 0.945316666667, 0.9431
train acc, test acc | 0.94675, 0.9427
train acc, test acc | 0.954766666667, 0.9521
train acc, test acc | 0.9602, 0.9551
train acc, test acc | 0.9634, 0.9581
train acc, test acc | 0.9656, 0.9597
train acc, test acc | 0.9683, 0.9615
train acc, test acc | 0.970516666667, 0.9629
train acc, test acc | 0.97305, 0.9649
train acc, test acc | 0.9731, 0.9661
train acc, test acc | 0.975916666667, 0.9659
train acc, test acc | 0.976383333333, 0.9666
train acc, test acc | 0.977916666667, 0.969
[Finished in 45.5s]
"""

Summary

- 계산 κ·Έλž˜ν”„λ₯Ό μ΄μš©ν•˜λ©΄ 계산 과정을 μ‹œκ°μ μœΌλ‘œ νŒŒμ•…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
- 계산 κ·Έλž˜ν”„μ˜ λ…Έλ“œλŠ” κ΅­μ†Œμ  κ³„μ‚°μœΌλ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€. κ΅­μ†Œμ  계산을 μ‘°ν•©ν•΄ 전체 계산을 κ΅¬μ„±ν•©λ‹ˆλ‹€.
- 계산 κ·Έλž˜ν”„μ˜ μˆœμ „νŒŒλŠ” ν†΅μƒμ˜ 계산을 μˆ˜ν–‰ν•©λ‹ˆλ‹€. ν•œνŽΈ, 계산 κ·Έλž˜ν”„μ˜ μ—­μ „νŒŒλ‘œλŠ” 각 λ…Έλ“œμ˜ 미뢄을 ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€.
- μ‹ κ²½λ§μ˜ ꡬ성 μš”μ†Œλ₯Ό κ³„μΈ΅μœΌλ‘œ κ΅¬ν˜„ν•˜μ—¬ 기울기λ₯Ό 효율적으둜 계산할 수 μžˆμŠ΅λ‹ˆλ‹€(μ˜€μ°¨μ—­μ „νŒŒλ²•).
- 수치 λ―ΈλΆ„κ³Ό μ˜€μ°¨μ—­μ „νŒŒλ²•μ˜ κ²°κ³Όλ₯Ό λΉ„κ΅ν•˜λ©΄ μ˜€μ°¨μ—­μ „νŒŒλ²•μ˜ κ΅¬ν˜„μ— 잘λͺ»μ΄ μ—†λŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€(기울기 확인).