728x90
반응형
RNNLM 구현
SimpleRnnlm의 계층 구성 : RNN 계층의 상태는 클래스 내부에서 관리
SimpleRnnlm
import sys
sys.path.append('..')
import numpy as np
from time_layers import *
class SimpleRnnlm:
def __init__(self, vocab_size, wordvec_size, hidden_size):
V, D, H = vocab_size, wordvec_size, hidden_size
rn = np.random.randn
# 가중치 초기화
embed_W = (rn(V, D) / 100).astype('f')
rnn_Wx = (rn(D, H) / np.sqrt(D)).astype('f')
rnn_Wh = (rn(H, H) / np.sqrt(H)).astype('f')
rnn_b = np.zeros(H).astype('f')
affine_W = (rn(H, V) / np.sqrt(H)).astype('f')
affine_b = np.zeros(V).astype('f')
# 계층 생성
self.layers = [
TimeEmbedding(embed_W),
TimeRNN(rnn_Wx, rnn_Wh, rnn_b, stateful=True),
TimeAffine(affine_W, affine_b)
]
self.loss_layer = TimeSoftmaxWithLoss()
self.rnn_layer = self.layers[1]
# 모든 가중치와 기울기를 리스트에 모은다.
self.params, self.grads = [], []
for layer in self.layers:
self.params += layer.params
self.grads += layer.grads
def forward(self, xs, ts):
for layer in self.layers:
xs = layer.forward(xs)
loss = self.loss_layer.forward(xs, ts)
return loss
def backward(self, dout=1):
dout = self.loss_layer.backward(dout)
for layer in reversed(self.layers):
dout = layer.backward(dout)
return dout
def reset_state(self):
self.rnn_layer.reset_state()
언어 모델의 평가
- 언어 모델은 주어진 과거 단어(정보)로부터 출현할 단어의 확률분포를 출력한다.
- 이때 언어 모델의 예측 성능을 평가하는 척도로 퍼블렉서티(perplexity, 훈련도)를 자주 이용한다.
- 퍼블렉서티 - 확률의 역수
- 데이터 수가 하나일 때에 정확히 일치한다.
- 퍼블렉서티는 작을수록 좋다.
퍼블렉서티
- 입력 데이터가 여러 개일때의 공식
- N은 데이터의 총개수
- tn은 원핫 벡터로 정답을 나타낸 레이블
- tnk는 n개째 데이터의 k번째 값을 의미한다.
- ynk는 확률분포를 나타낸다. (신경망에서는 Softmax의 출력)
- L은 신경망의 손실
- L을 이용해 e^L을 계산한 값이 곧 퍼블렉서티다.
RNNLM의 학습 코드
- PTB 데이터셋(훈련 데이터) 전부를 대상으로 학습하면 좋은 결과를 낼 수 없기 때문에, 처음 1,000개의 단어만 이용
# RNNLM의 학습 코드
import sys
sys.path.append('..')
import matplotlib.pyplot as plt
import numpy as np
from optimizer import SGD
import pbt
#하이퍼파라미터 설정
batch_size = 10
wordvec_size = 100
hidden_size = 100 # RNN 은닉 벡터 원소 수
time_size = 5 # Truncated BPTT가 한 번에 펼치는 시간 크기
lr = 0.1
max_epoch = 100
# 학습 데이터 읽기(전체 중 1000개만)
corpus, word_to_id, id_to_word = pbt.load_data('train')
corpus_size = 1000
corpus = corpus[:corpus_size]
vocab_size = int(max(corpus) + 1)
xs = corpus[:-1] # 입력
ts = corpus[1:] # 출력 (정답 레이블
data_size = len(xs)
print('말뭉치 크기 : %d, 어휘 수 : %d ' %(corpus_size, vocab_size) )
# 학습 시 사용하는 변수
max_iters = data_size # (batch_size * time_size)
time_idx = 0
total_loss = 0
loss_count = 0
ppl_list = []
# 모델 생성
model = SimpleRnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)
# 각 미니배치에서 샘플을 읽기 시작 위치를 계산
jump = (corpus_size - 1) # batch_size
offsets = [i*jump for i in range(batch_size)]
for epoch in range(max_epoch):
for iter in range(max_iters):
# 미니배치 흭득
batch_x = np.empty((batch_size, time_size), dtype='i')
batch_t = np.empty((batch_size, time_size), dtype='i')
for t in range(time_size):
for i, offset in enumerate(offsets):
batch_x[i, t] = xs[(offset + time_idx) %data_size]
batch_t[i, t] = ts[(offset + time_idx) %data_size]
time_idx += 1
# 기울기를 구하여 매개변수 갱신
loss = model.forward(batch_x, batch_t)
model.backward()
optimizer.update(model.params, model.grads)
total_loss += loss
loss_count +=1
# 에폭마다 퍼블렉서티 평가
ppl = np.exp(total_loss / loss_count)
print('|| 에폭 %d || 퍼블렉서티 %.2f' %(epoch+1, ppl))
ppl_list.append(float(ppl))
total_loss, loss_count = 0,0
과정
- 미니배치를 '순차적으로' 만들기
- 모델의 순전파와 역전파를 호출
- 옵티마이저로 가중치 갱신
- 퍼블렉서티를 구하기
정리
RNN(순환 신경망)
- RNN은 데이터를 순환시킴으로써 과거에서 현재, 그리고 미래로 데이터를 계속해서 흘려보낸다.
- RNN 계층 내부에는 '은닉 상태'를 기억하는 능력이 추가
- 언어 모델은 단어 시퀀스에 확률을 부여한다.
- 조건부 언어 모델은 지금까지의 단어 시퀀스로부터 다음에 출현할 단어의 확률을 계산해준다.
- RNN을 대체하는 새로운 계층들(LSTM, GRU 계층)
출처 : 밑바닥부터 시작하는 딥러닝2
https://www.hanbit.co.kr/store/books/look.php?p_code=B8950212853
728x90
반응형
최근댓글