티스토리 뷰

딥러닝

기울기 클리핑 / 기본적인 LSTM

4567은 소수 2021. 1. 29. 23:03

기본적인 RNN 구조의 문제점

 

1. 기울기 손실 / 기울기 폭발

 

장기 기억의 문제점 : 예를 들어 "Tom was watching TV in his room. Mary came into the room. Mary said hi to ?" 라는 문장에서 ?=Tom 이지만, Tom은 첫 단어 이외에 등장하지 않습니다. 순차적으로 진행해나가면서 Tom이라는 정답레이블이 주어질 때 역전파 과정에서 의미 있는 기울기를 전달해야하지만, 전달 과정에서 기울기 가중치가 0이 될 수 있습니다. 이를 기울기 손실이라 합니다. 그리고 역전파 과정에서의 행렬 곱에서는 기울기 폭발이 일어납니다.

(자세한 내용 책 참고)

 

2. 기울기 폭발 대책 - 기울기 클리핑

식은 다음과 같습니다.

hat(g) = 신경망에서 사용되는 매개변수의 기울기를 모은 것

threshold = 문턱값

 

이는 다음과 같이 구현할 수 있습니다.

# 기울기 폭발 대체
import numpy as np

dW1 = np.random.rand(3,3) * 10
dW2 = np.random.rand(3,3) * 10
grads = [dW1, dW2]
max_norm = 5.0

def clip_grads(grads, max_norm):
    total_norm = 0
    for grad in grads:
        total_norm += np.sum(grad**2)
    total_norm = np.sqrt(total_norm)
    
    rate = max_norm / (total_norm + 1e-6)
    if rate < 1:
        for grad in grads:
            grad *= rate

print('before:', dW1.flatten())
clip_grads(grads, max_norm)
print('after:', dW1.flatten())

3. LSTM

LSTM의 기본 구조는 다음과 같습니다. (n_t 는 h_t가 잘린 것)

c_t = 시각 t에서의 LSTM의 기억이 저장, 과거로부터 t까지 필요한 정보가 저장되도록 학습 수행

h_t = 은닉 상태

x_t = 입력 값

sigma = sigmoid 함수 

f = forget게이트, c_(t-1)에서 불필요한 기억 제거하는 역할

( f = sigma(x_t * W_x + h_(t-1) * W_h + b) ( * : 행렬곱 ) => c_t = f * c_(t-1) ( * : 원소별 곱)

g = 새로 기억해야할 정보 추가 

( g = tanh(x_t * W_x + h_(t-1) * W_h + b )

i = input 게이트, g의 각 원소가 새로 추가되는 정보로써의 가치가 얼마나 큰지 판단

( i = sigma(x_t * W_x + h_(t-1) * W_h + b) )

o = output 게이트, 다음 은닉 상태 h_t를 출력

( o = sigma(x_t * W_x + h_(t-1) * W_h + b) )

 

=> c_t = f * c_(t-1) + g * i  , h_t = o * tanh(c_t) ( * : 원소 별 곱)

 

실제 구현시 겹치는 부분이 많으므로 공통 부분 (x_t, h_(t-1), b) 를 묶어 Affine 변환을 이용해서 계산합니다.

from common.np import *  # import numpy as np (or import cupy as np)
from common.layers import *
from common.functions import sigmoid

class LSTM:
    def __init__(self, Wx, Wh, b):
        '''

        Parameters
        ----------
        Wx: 입력 x에 대한 가중치 매개변수(4개분의 가중치가 담겨 있음)
        Wh: 은닉 상태 h에 대한 가장추 매개변수(4개분의 가중치가 담겨 있음)
        b: 편향(4개분의 편향이 담겨 있음)
        '''
        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, c_prev):
        Wx, Wh, b = self.params
        N, H = h_prev.shape

        A = np.dot(x, Wx) + np.dot(h_prev, Wh) + b

        f = A[:, :H]
        g = A[:, H:2*H]
        i = A[:, 2*H:3*H]
        o = A[:, 3*H:]

        f = sigmoid(f)
        g = np.tanh(g)
        i = sigmoid(i)
        o = sigmoid(o)

        c_next = f * c_prev + g * i
        h_next = o * np.tanh(c_next)

        self.cache = (x, h_prev, c_prev, i, f, g, o, c_next)
        return h_next, c_next

    def backward(self, dh_next, dc_next):
        Wx, Wh, b = self.params
        x, h_prev, c_prev, i, f, g, o, c_next = self.cache

        tanh_c_next = np.tanh(c_next)

        ds = dc_next + (dh_next * o) * (1 - tanh_c_next ** 2)

        dc_prev = ds * f

        di = ds * g
        df = ds * c_prev
        do = dh_next * tanh_c_next
        dg = ds * i

        di *= i * (1 - i)
        df *= f * (1 - f)
        do *= o * (1 - o)
        dg *= (1 - g ** 2)

        dA = np.hstack((df, dg, di, do))

        dWh = np.dot(h_prev.T, dA)
        dWx = np.dot(x.T, dA)
        db = dA.sum(axis=0)

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

        dx = np.dot(dA, Wx.T)
        dh_prev = np.dot(dA, Wh.T)

        return dx, dh_prev, dc_prev

4. TimeLSTM

LSTM T개를 묶은 TimeLSTM (책에서 명명한 것)은 다음과 같습니다.

from common.np import *  # import numpy as np (or import cupy as np)
from common.layers import *
from common.functions import sigmoid

class TimeLSTM:
    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.c = None, None
        self.dh = None
        self.stateful = stateful

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

        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')
        if not self.stateful or self.c is None:
            self.c = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = LSTM(*self.params)
            self.h, self.c = layer.forward(xs[:, t, :], self.h, self.c)
            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 = Wx.shape[0]

        dxs = np.empty((N, T, D), dtype='f')
        dh, dc = 0, 0

        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh, dc = layer.backward(dhs[:, t, :] + dh, dc)
            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, c=None):
        self.h, self.c = h, c

    def reset_state(self):
        self.h, self.c = None, None

 

'딥러닝' 카테고리의 다른 글

keras를 활용한 MNIST 모델 훈련시키기 (기본)  (0) 2021.03.09
밑바닥부터 시작하는 딥러닝 2권 후기 및 앞으로 계획  (0) 2021.02.19
BPTT  (0) 2021.01.29
네거티브 샘플링  (0) 2021.01.27
CBOW 1  (0) 2021.01.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함