MachineLearning

RNN(Recurrent Nueral Networks)

쌍쌍바나나 2017. 12. 10. 22:56
반응형

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]

참고

  • https://ratsgo.github.io/natural%20language%20processing/2017/03/09/rnnlstm/
  • https://ratsgo.github.io/deep%20learning/2017/04/03/recursive/
반응형