목차

  1. 쌍곡함수
  2. RNN의 계산 그래프
  3. RNNLM(RNN Language Model)
  4. BPTT
  5. Truncated BPTT
  6. RNN 구현

쌍곡함수(Hyperbolic function)


출처: 나무위키

  • 삼각함수는 원과 관련이 있다.

{x=costy=sint\begin{cases} x = cos t \\ y = sin t\end{cases}

x2+y2=cos2t+sin2t=1x^2 + y^2 = cos^2t + sin^2t = 1

  • 이와 유사하게 쌍곡선 함수를 만들 수 있다.

{x=coshty=sinht\begin{cases} x = cosh t \\ y = sinh t\end{cases}

x2y2=cosh2tsinh2t=1x^2 - y^2 = cosh^2t - sinh^2t = 1

정의

1) sinhx=exex2sinh x = \frac{e^x-e^{-x}}{2}

2) coshx=ex+ex2cosh x = \frac{e^x + e^{-x}}{2}

3) tanhx=exexex+extanh x = \frac{e^x-e^{-x}}{e^x + e^{-x}} = e2x1e2x+1\frac{e^{2x}-1}{e^{2x}+1}

공식

삼각함수 기본 공식
1) cos2x+sin2x=1cos^2x + sin^2x = 1
2) 1+tan2x=sec2x1+tan^2x = sec^2x

hyperbolic 삼각함수 기본식
1) cosh2xsinh2x=1cosh^2x - sinh^2x = 1
2) sech2x=1tanh2xsech^2x = 1- tanh^2x

도함수

1) ddxsinhx=coshx\frac{d}{dx}sinhx = coshx
2) ddxcoshx=sinhx\frac{d}{dx}coshx = sinhx
3) ddxtanhx=sech2x=1cosh2x=1tanh2x\frac{d}{dx}tanhx = sech^2x = \frac{1}{cosh^2x} = 1-tanh^2x

증명
(tanhx)(tanhx)'
.
=(sinhxcoshx)= (\frac{sinhx}{coshx})'
.
=cosh2xsinhxcosh2x= \frac{cosh^2x - sinh^x}{cosh^2x}
.
=1cosh2x= \frac{1}{cosh^2x}
.
=1sinh2xcosh2x= 1-\frac{sinh^2x}{cosh^2x}
.
=1tanh2x= 1-tanh^2x

RNN의 계산 그래프


출처: 포장빵의 IT

  • 위 계산그래프는 다음 식을 그래프로 나타낸 것이다.

    [출처:밑바닥부터 시작하는 딥러닝2 사이토 고기토 저]

  • 이처럼 계산식을 그래프로 나타내면 역전파 계산이 용이해진다.

계산그래프의 역전파 과정

  • δhnext\delta{h_{next}}: 이하 δht\delta{h}_{t}로 불리며 다음 시점의 은닉 상태 벡터의 미분. tanhtanh을 지나 다음 노드로 역전파된다.
  • δht\delta{h}_{t}tanhtanh를 지나 δhδt(1y2)\frac{\delta{h}}{\delta{t}}(1-y^2)가 된다.
    - 여기서 yy는 RNN 계산식의 일부인 tanh(ht1Wh+xtWx+b)tanh(h_{t-1}W_{h} + x_{t}W_{x} + b)을 의미한다.


출처: Kyung Hoon Han's Home
tanhtanh를 역전파 계산하면 δLδy\frac{\delta{L}}{\delta{y}}가 들어가서
tanhtanh의 미분인 (1y2)(1-y^2)를 곱하고 다음 층으로 전달된다.

역전파의 규칙

  • 1부터 시작한다.
  • 계산그래프상에서는 다음과 같이 쉽게 표현 가능하다.
  • 덧셈은 변화가 없다.
  • 곱셈은 흘러들어온 값에 xxyy 자리를 교차하여 곱해준다.
  • f(x,m,t)f(x,m,t)xx로 미분하는 것은 사과 가격에 따른 전체 가격의 변동량(변화량)을 의미한다.
  • 여기서 L은 Loss 즉 손실함수를 뜻하며 대체로 CEE(Cross Entropy Error)함수를 많이 사용한다.
    CEE=ttilog(yi)CEE = -\sum_{t} t_i log(y_i)
    CEECEE: 교차 엔트로피 오차
    ii : 데이터의 인덱스
    tit_i : 실제값 (참값)
    yiy_i : 모델의 예측값 (출력값)

RNNLM(RNN Language Model)

BPTT(Back Propagation Through Time)

Truncated BPTT

RNN 구현

RNN

class RNN:
    def __init__(self, Wx, Wh, b):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)

        self.cache = (x, h_prev, h_next)
        return h_next

    def backward(self, dh_next):
        Wx, Wh, b = self.params
        x, h_prev, h_next = self.cache

        dt = dh_next * (1 - h_next ** 2)
        db = np.sum(dt, axis=0)
        dWh = np.dot(h_prev.T, dt)
        dh_prev = np.dot(dt, Wh.T)
        dWx = np.dot(x.T, dt)
        dx = np.dot(dt, Wx.T)

        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db

        return dx, dh_prev
class RNN:
    def __init__(self, Wx, Wh, b):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

RNN을 구현하는 클래스이다.
__init__()함수는 Wx, Wh, b를 인자로 입력 받는다.
Wx: 입력벡터의 가중치 행렬
Wh: 은닉벡터의 가중치 행렬
b: 편향(입력벡터 편향 + 은닉벡터 편향)

	def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)
        self.cache = (x, h_prev, h_next)
        return h_next
    def backward(self, dh_next):
        Wx, Wh, b = self.params
        x, h_prev, h_next = self.cache
        dt = dh_next * (1 - h_next ** 2)
        db = np.sum(dt, axis=0)
        dWh = np.dot(h_prev.T, dt)
        dh_prev = np.dot(dt, Wh.T)
        dWx = np.dot(x.T, dt)
        dx = np.dot(dt, Wx.T)
        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db
        return dx, dh_prev

TimeRNN

class TimeRNN:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None

        self.h, self.dh = None, None
        self.stateful = stateful

    def forward(self, xs):
        Wx, Wh, b = self.params
        N, T, D = xs.shape
        D, H = Wx.shape

        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = RNN(*self.params)
            self.h = layer.forward(xs[:, t, :], self.h)
            hs[:, t, :] = self.h
            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        Wx, Wh, b = self.params
        N, T, H = dhs.shape
        D, H = Wx.shape

        dxs = np.empty((N, T, D), dtype='f')
        dh = 0
        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh = layer.backward(dhs[:, t, :] + dh)
            dxs[:, t, :] = dx

            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh

        return dxs

    def set_state(self, h):
        self.h = h

    def reset_state(self):
        self.h = None
   

TimeEmbedding

class TimeEmbedding:
    def __init__(self, W):
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.layers = None
        self.W = W

    def forward(self, xs):
        N, T = xs.shape
        V, D = self.W.shape

        out = np.empty((N, T, D), dtype='f')
        self.layers = []

        for t in range(T):
            layer = Embedding(self.W)
            out[:, t, :] = layer.forward(xs[:, t])
            self.layers.append(layer)

        return out

    def backward(self, dout):
        N, T, D = dout.shape

        grad = 0
        for t in range(T):
            layer = self.layers[t]
            layer.backward(dout[:, t, :])
            grad += layer.grads[0]

        self.grads[0][...] = grad
        return None
        

TimeAffine

class TimeAffine:
   def __init__(self, W, b):
       self.params = [W, b]
       self.grads = [np.zeros_like(W), np.zeros_like(b)]
       self.x = None

   def forward(self, x):
       N, T, D = x.shape
       W, b = self.params

       rx = x.reshape(N*T, -1)
       out = np.dot(rx, W) + b
       self.x = x
       return out.reshape(N, T, -1)

   def backward(self, dout):
       x = self.x
       N, T, D = x.shape
       W, b = self.params

       dout = dout.reshape(N*T, -1)
       rx = x.reshape(N*T, -1)

       db = np.sum(dout, axis=0)
       dW = np.dot(rx.T, dout)
       dx = np.dot(dout, W.T)
       dx = dx.reshape(*x.shape)

       self.grads[0][...] = dW
       self.grads[1][...] = db

       return dx
profile
聞一知十

0개의 댓글