RNN(Recurrent Nueral Networks)

2021. 5. 3. 03:41 AI, 머신러닝/머신러닝

RNN

RNN은 Recurrent Neural Networks와 더불어 자연어처리 분야에서 각광받고 있는 모델.
Recursive, Recurrent Neural Neworks

두 모델은 음성, 문자 등 순차적 데이터 처리에 강점을 지니고 있음
이름이 유사하지만, 조금은 차이가 있다.

 

Recurrent Nueral Networks

RNN의 기본 구조

  • RNN은 히든 노드가 방향을 가진 엣지로 연결돼 순환구조를 이루는(directed cycle) 인공신경망의 한 종류
  • 시퀀스 길이에 관계없이 인풋과 아웃풋을 받아들일 수 있는 네트워크 구조 > 다양하고 유연하게 구조를 만들 수 있다는 점 RNN 큰 특징
  • ht는 직전 시점의 히든 state h(t-1)를 받아 갱신
  • hidden state의 activation function은 tanh

 

RNN의 기본 동작

  • 글자가 주어졌을때 바로 다음 글자를 예측하는 Chracater-level-model을 만든다고 칠때
    • h0는 random하게 주어지고, hidden layer를 fowrard propagation을 하면서 모두 갱신
    • 정답셋을 바탕으로 backpropagation을 수행해 parameters의 값들을 갱신
  • RNN이 학습하는 parameter는 무엇일까?
    • Wxh, Whh, W_hy의 parameters
    • x > hidden, hidden > hidden, hidden > y

 

LSTM

  • RNN은 관련 정보와 그 정보를 사용하는 지점 사이거리가 멀 경우 vanishing gradient problem이 발생
    • vanishing gradient problem: backpropagation시 gradient가 점차 줄어들어 학습 능력 저하
  • 위 문제를 극복하기 위해서 바로 LSTM을 사용 (https://ratsgo.github.io/natural%20language%20processing/2017/03/09/rnnlstm/)
    • LSTM은 RNN의 hidden state에 cell_state를 추가
    • cell state는 일종의 컨베이어 벨트 역할
      • state가 꽤 오래 경과하더라도 gradient가 비교적 전파가 잘 된다.
    • forget gate
      • '과거 정보 잊기'
      • sigmoid를 하기 때문에 0이라면 이전 상태의 정보를 잊고, 1이라면 이전 상태의 정보를 온전히 기억
    • input gate
      • '현재 정보를 기억하기 위한'
      • i(t)의 범위는 0~1, g(t)의 범위는 -1~1, 각각 강도와 방향을 나타낸다.
    • H(t)에서 행을 기준으로 4등분해, i,f,o,g 각각에 해당하는 activation 함수를 적용

 

Python 코드

def lossFun(inputs, targets, hprev, cprev):
    xs, hs, cs, is_, fs, os, gs, ys, ps= {}, {}, {}, {}, {}, {}, {}, {}, {}
    hs[-1] = np.copy(hprev) # t=0일때 t-1 시점의 hidden state가 필요하므로
    cs[-1] = np.copy(cprev)
    loss = 0
    H = hidden_size
    # forward pass
    for t in range(len(inputs)):
        xs[t] = np.zeros((vocab_size, 1))
        xs[t][inputs[t]] = 1
        tmp = np.dot(Wxh, xs[t]) + np.dot(Whh, hs[t - 1]) + bh  # hidden state
        is_[t] = sigmoid(tmp[:H])
        fs[t] = sigmoid(tmp[H:2 * H])
        os[t] = sigmoid(tmp[2 * H: 3 * H])
        gs[t] = np.tanh(tmp[3 * H:])
        cs[t] = fs[t] * cs[t-1] + is_[t] * gs[t]
        hs[t] = os[t] * np.tanh(cs[t])

    # compute loss
    for i in range(len(targets)):
        idx = len(inputs) - len(targets) + i
        ys[idx] = np.dot(Why, hs[idx]) + by  # unnormalized log probabilities for next chars
        ps[idx] = np.exp(ys[idx]) / np.sum(np.exp(ys[idx]))  # probabilities for next chars
        loss += -np.log(ps[idx][targets[i], 0])  # softmax (cross-entropy loss)

    # backward pass: compute gradients going backwards
    dWxh, dWhh, dWhy = np.zeros_like(Wxh), np.zeros_like(Whh), np.zeros_like(Why)
    dbh, dby = np.zeros_like(bh), np.zeros_like(by)
    dhnext, dcnext = np.zeros_like(hs[0]), np.zeros_like(cs[0])
    n = 1
    a = len(targets) - 1
    for t in reversed(range(len(inputs))):
        if n > len(targets):
            continue
        dy = np.copy(ps[t])
        dy[targets[a]] -= 1  # backprop into y
        dWhy += np.dot(dy, hs[t].T)
        dby += dy
        dh = np.dot(Why.T, dy) + dhnext  # backprop into h
        dc = dcnext + (1 - np.tanh(cs[t]) * np.tanh(cs[t])) * dh * os[t]  # backprop through tanh nonlinearity
        dcnext = dc * fs[t]
        di = dc * gs[t]
        df = dc * cs[t-1]
        do = dh * np.tanh(cs[t])
        dg = dc * is_[t]
        ddi = (1 - is_[t]) * is_[t] * di
        ddf = (1 - fs[t]) * fs[t] * df
        ddo = (1 - os[t]) * os[t] * do
        ddg = (1 - np.tanh(gs[t]) * np.tanh(gs[t])) * dg
        da = np.hstack((ddi.ravel(),ddf.ravel(),ddo.ravel(),ddg.ravel()))
        dWxh += np.dot(da[:,np.newaxis],xs[t].T)
        dWhh += np.dot(da[:,np.newaxis],hs[t-1].T)
        dbh += da[:, np.newaxis]
        dhnext = np.dot(Whh.T, da[:, np.newaxis])
        n += 1
        a -= 1
    for dparam in [dWxh, dWhh, dWhy, dbh, dby]:
        np.clip(dparam, -5, 5, out=dparam)  # clip to mitigate exploding gradients
    return loss, dWxh, dWhh, dWhy, dbh, dby, hs[len(inputs) - 1], cs[len(inputs) - 1]

 

참고

출처 : ourcstory.tistory.com/400?category=646926