seq2seq 개선
seq2seq를 개선하기 위한 두가지 방법을 제안한다.
입력데이터 반전(Reverse)
입력데이터의 순서를 반전시킨다
(x_train, t_train), (x_test, t_test) = sequence.load_data('addition.txt')
char_to_id, id_to_char = sequence.get_vocab()
# Reverse input? =================================================
is_reverse = False # True
if is_reverse:
x_train, x_test = x_train[:, ::-1], x_test[:, ::-1]
x_test[:, ::-1]을 통해 배열의 행을 반전 시킨다.
입력 데이터를 반전시킨 것만으로는 학습 진행이 개선됨을 알 수 있다. 정답률이 50%까지 상승하였다.
입력데이터의 반전이 학습 진행이 빨라지고 정확도가 향상되는 이유는 기울기 전파가 원활해지기 때문이다.
1) 나는 고양이로소이다 -> I am a cat
2) 이다 로소 고양이 나 -> I am a cat
1의 방법으로 했을 때, 나에서 I까지 가려면 총 네단어 분량의 LSTM 계층을 거쳐야 한다. 2의 방법으로 하게 되면 나와 I의 거리가 가까워져서 기울기가 직접 전해지게 된다. 기울기가 잘 전해져 학습 효율이 더 좋아진다. 다만 입력 데이터를 반전해도 단어 사이의 평균적인 거리는 그대로이다.
엿보기 (Peeky)
기존 encoder는 입력문장을 고정길이 벡터 h로 변환한다. h안에는 decoder에게 필요한 정보가 모두 담겨 있다.
최초시각의 LSTM 계층만이 벡터h를 이용하고 있다.
제안된 개선방법은 encoder의 출력 h를 decoder의 다른 계층에게도 전해주는 것이다.
모든 시각의 affine 계층과 LSTM 계층에 encoder의 출력 h를 전해준다. (총 8개 계층에 공유)
LSTM 계층과 Affine 계층에 입력되는 벡터가 2개씩이다. 실제로는 두 벡터가 연결(concatenate) 된 것이다.
peeky decoder 클래스를 구현하면 다음과 같다.
class PeekyDecoder:
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')
lstm_Wx = (rn(H + D, 4 * H) / np.sqrt(H + 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 + H, V) / np.sqrt(H + H)).astype('f')
affine_b = np.zeros(V).astype('f')
self.embed = TimeEmbedding(embed_W)
self.lstm = TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=True)
self.affine = TimeAffine(affine_W, affine_b)
self.params, self.grads = [], []
for layer in (self.embed, self.lstm, self.affine):
self.params += layer.params
self.grads += layer.grads
self.cache = None
def forward(self, xs, h):
N, T = xs.shape
N, H = h.shape
self.lstm.set_state(h)
out = self.embed.forward(xs)
hs = np.repeat(h, T, axis=0).reshape(N, T, H) #시계열 만큼 복제하여 hs에 저장
out = np.concatenate((hs, out), axis=2) # hs와 embedding 계층 출력 연결
out = self.lstm.forward(out) #lstm에도 출력 연결
out = np.concatenate((hs, out), axis=2)
score = self.affine.forward(out) #affine 계층에도 출력 연결
self.cache = H
return score
peekySeq2seq를 구현하면 다음과 같다.
decoder 부분에서만 peekydecoder를 사용한다.
class PeekySeq2seq(Seq2seq):
def __init__(self, vocab_size, wordvec_size, hidden_size):
V, D, H = vocab_size, wordvec_size, hidden_size
self.encoder = Encoder(V, D, H)
self.decoder = PeekyDecoder(V, D, H)
self.softmax = TimeSoftmaxWithLoss()
self.params = self.encoder.params + self.decoder.params
self.grads = self.encoder.grads + self.decoder.grads
model = PeekySeq2seq(vocab_size,wordvec_size,hidden_size)
최종적으로 reverse+peekydecoder를 적용한 모델이 100%에 다다르는 성능이 나왔다. reverse와 peeky가 함께 효가적으로 작동함을 알 수 있었다.
'Deep Learning > from scratch II' 카테고리의 다른 글
[밑바닥부터 시작하는 딥러닝 2] - 8장 어텐션 II (0) | 2024.02.23 |
---|---|
[밑바닥부터 시작하는 딥러닝 2] - 8장 어텐션 I (0) | 2024.02.22 |
[밑바닥부터 시작하는 딥러닝 2] - 7장 RNN을 사용한 문장 생성 I (1) | 2024.02.21 |
[밑바닥부터 시작하는 딥러닝 2] - 6장 게이트가 추가된 RNN II (1) | 2024.02.20 |
[밑바닥부터 시작하는 딥러닝 2] - 6장 게이트가 추가된 RNN I (0) | 2024.02.20 |