모두야
CH6 ) 게이트가 추가된 RNN - (3) 본문
728x90
반응형
언어 모델 구현하기
# coding: utf-8
import sys
sys.path.append('..')
from common.time_layers import *
from common.base_model import BaseModel
class Rnnlm(BaseModel):
def __init__(self, vocab_size=10000, wordvec_size=100, hidden_size=100):
V, D, H = vocab_size, wordvec_size, hidden_size
rn = np.random.randn
# 가중치 초기화
embed_W = (rn(V, D) / 100).astype('f')
lstm_Wx = (rn(D, 4 * H) / np.sqrt(D)).astype('f')
lstm_Wh = (rn(H, 4 * H) / np.sqrt(H)).astype('f')
lstm_b = np.zeros(4 * 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),
TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=True),
TimeAffine(affine_W, affine_b)
]
self.loss_layer = TimeSoftmaxWithLoss()
self.lstm_layer = self.layers[1]
# 모든 가중치와 기울기를 리스트에 모은다.
self.params, self.grads = [], []
for layer in self.layers:
self.params += layer.params
self.grads += layer.grads
def predict(self, xs):
for layer in self.layers:
xs = layer.forward(xs)
return xs
def forward(self, xs, ts):
score = self.predict(xs)
loss = self.loss_layer.forward(score, 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.lstm_layer.reset_state()
# 매개변수 읽기/쓰기
def save_params(self,file_name='Rnnlm.pkl'):
with open(file_name,'wb') as f:
pickle.dump(self.params,f)
def load_params(self,file_name='Rnnlm.pkl'):
with open(file_name,'rb') as f:
self.params = pickle.load(f)
신경망 학습 - PTB 데이터셋
# coding: utf-8
import sys
sys.path.append('..')
from common.optimizer import SGD
from common.trainer import RnnlmTrainer
from common.util import eval_perplexity
from dataset import ptb
#from rnnlm import Rnnlm
# 하이퍼파라미터 설정
batch_size = 20
wordvec_size = 100
hidden_size = 100 # RNN의 은닉 상태 벡터의 원소 수
time_size = 35 # RNN을 펼치는 크기
lr = 20.0
max_epoch = 4
max_grad = 0.25
# 학습 데이터 읽기
corpus, word_to_id, id_to_word = ptb.load_data('train')
corpus_test, _, _ = ptb.load_data('test')
vocab_size = len(word_to_id)
xs = corpus[:-1]
ts = corpus[1:]
# 모델 생성
model = Rnnlm(vocab_size, wordvec_size, hidden_size) # Rnnlm 사용
optimizer = SGD(lr)
trainer = RnnlmTrainer(model, optimizer) # RnnlmTrainer사용
# 기울기 클리핑을 적용하여 학습
trainer.fit(xs, ts, max_epoch, batch_size, time_size, max_grad, # 모델의 기울기를 구해 매개변수 갱신한다.
eval_interval=20) #20번째 반복마다 퍼플렉서티를 평가하라 # max_grad : 기울기 클리핑 적용
trainer.plot(ylim=(0, 500))
# 테스트 데이터로 평가
model.reset_state()
ppl_test = eval_perplexity(model, corpus_test)
print('테스트 퍼플렉서티: ', ppl_test)
# 매개변수 저장
model.save_params()
퍼플렉서티 평가 중 ...
234 / 235
테스트 퍼플렉서티: 133.41907499405525
- 데이터셋 어휘 수 10,000개 중 후보 단어를 133개로 줄였다.
- 더 줄여야 성능이 좋다고 할 수 있다.
RNNLM 추가 개선
1. LSTM 계층 다층화
LSTM을 여러겹 쌓아 모델 정확도를 향상시키자.
첫번째 LSTM 계층의 은닉상태가 두번째 LSTM 계층에 입력된다.
몇 층 정도로 쌓을까? - 하이퍼파라미터
2~4층 정도가 적당하긴함
2. 드롭아웃에 의한 과적합 억제
층을 깊게 쌒을 수록 표현력이 풍부하지만, 과적합을 일으킬 수 있다.
과적합 억제 방법
- 훈련 데이터양 늘리기
- 모델 복잡도 줄이기
- 모델 복잡도에 페널티 주는 정규화(normalization)
- 드롭아웃 : 훈련 시 계층 내의 뉴런 몇개를 무시하며 학습한다.
RNN에 드롭아웃을 계층의 깊이 방향(상하 방향)으로 삽입하는 것이 좋다.
=> RNN의 시간 방향 정규화(변형 드롭아웃)
3. 가중치 공유(weight tying)
Embedding 계층의 가중치와 Affine 계층의 가중치를 공유한다.
- 학습하는 매개변수 수가 크게 줄어든다.
(과적합억제) - 정확도도 향상된다.
개선점을 추가한 RNNLM 구현
- LSTM 계층 다층화
- 드롭아웃 사용 (깊이 방향)
- 가중치 공유 (Embedding 계층과 Affine 계층에서 가중치 공유)
# coding: utf-8
import sys
sys.path.append('..')
from common import config
# GPU에서 실행하려면 아래 주석을 해제하세요(CuPy 필요).
# ==============================================
# config.GPU = True
# ==============================================
from common.optimizer import SGD
from common.trainer import RnnlmTrainer
from common.util import eval_perplexity, to_gpu
from dataset import ptb
#from better_rnnlm import BetterRnnlm
# 하이퍼파라미터 설정
batch_size = 20
wordvec_size = 650
hidden_size = 650
time_size = 35
lr = 20.0
max_epoch = 40
max_grad = 0.25
dropout = 0.5
# 학습 데이터 읽기
corpus, word_to_id, id_to_word = ptb.load_data('train')
corpus_val, _, _ = ptb.load_data('val')
corpus_test, _, _ = ptb.load_data('test')
if config.GPU:
corpus = to_gpu(corpus)
corpus_val = to_gpu(corpus_val)
corpus_test = to_gpu(corpus_test)
vocab_size = len(word_to_id)
xs = corpus[:-1]
ts = corpus[1:]
model = BetterRnnlm(vocab_size, wordvec_size, hidden_size, dropout)
optimizer = SGD(lr)
trainer = RnnlmTrainer(model, optimizer)
best_ppl = float('inf')
for epoch in range(max_epoch):
trainer.fit(xs, ts, max_epoch=1, batch_size=batch_size,
time_size=time_size, max_grad=max_grad)
model.reset_state()
ppl = eval_perplexity(model, corpus_val)
print('검증 퍼플렉서티: ', ppl) # 매 에폭마다 검증데이터로 퍼플렉서티 평가
if best_ppl > ppl:
best_ppl = ppl
model.save_params()
else:
lr /= 4.0 # 기존 퍼플렉서티보다 낮으면 학습률을 1/4로 줄인다.
optimizer.lr = lr
model.reset_state()
print('-' * 50)
# 테스트 데이터로 평가
model.reset_state()
ppl_test = eval_perplexity(model, corpus_test)
print('테스트 퍼플렉서티: ', ppl_test)
정리
- 단순한 RNN에서는 기울기 폭발/소실 문제가 있었다.
- 이를 해결하기 위해 게이트가 추가된 RNN(LSTM,GRU)를 사용하였다.
- LSTM 계층을 사용한 언어 모델을 만들고, PTB 데이터셋을 학습하여 퍼플렉서티를 구하였다.
- LSTM 다층화, 드롭아웃, 가중치 공유 기법을 적용하여 정확도를 향상시켰다.
- 다음 장에서는 언어 모델을 사용해 문장을 생성해보자.
728x90
반응형
'밑.시.딥 > 2권' 카테고리의 다른 글
CH7) RNN을 사용한 문장 생성 - (2) (0) | 2021.09.28 |
---|---|
CH7) RNN을 사용한 문장 생성 - (1) (0) | 2021.09.28 |
CH6) 게이트가 추가된 RNN (기울기 문제점 대책) (0) | 2021.09.26 |
CH6) 게이트가 추가된 RNN(기울기 폭발/ 기울기 손실) (0) | 2021.09.26 |
CH5) 순환 신경망(RNN) -(2) (0) | 2021.09.24 |