목차
1. Seq2Seq의 문제점
2. Encoder의 개선
3-1. Decoder의 개선 (1)
3-1-1. 대응 관계를 찾는 방법
3-2. Decoder의 개선 (2)
3-3. Decoder의 개선 (3)
3-4. Time Attention 계층의 구현
4. Attention을 갖춘 Seq2Seq의 구현
5. 평가
1. Seq2Seq의 문제점
짧은 문장이든, 긴 문장이든 Encoder에서 같은 길이의 벡터만 출력한다.
2. Encoder의 개선
Encoder의 출력 길이는 입력 문장의 길이에 따라 바꿔준다.
LSTM 계층의 은닉 상태 벡터를 모두 이용한다.
지금까지는 LSTM 계층의 마지막 은닉 상태만을 Decoder에 전달했었다.
3-1. Decoder의 개선 (1)
지금까지와 똑같이 Encoder의 마지막 은닉 상태 벡터는 Decoder의 첫번째 LSTM 계층에 전달하되, '어떤 계산'을 추가 한다.
'어떤 계산'은 hs(각 LSTM 계층의 은닉벡터를 원소로 하는 행렬)과 시각별 LSTM 계층의 은닉 상태를 입력으로 받는다.
※ 시각별 LSTM 계층의 은닉 상태라 하면, Decoder의 입력된 단어와 대응 관계인 hs 행렬의 원소를 뜻한다.
하고자 하는건,
'입력과 출력의 단어 중 대응 관계(Alignment)'를 학습 시키는 것.
즉, '도착어 단어'와 대응관계 있는 '출발어 단어' 정보를 골라 내는 것. 그리고 해당 정보를 이용해 번역하는것.
3-1-1. 대응 관계를 찾는 방법
- 앞에서 표현한 '골라낸다'는 행위를 어떻게 수학적으로 나타낼 수 있을까?
- 방법 : hs에 가중치 벡터(a)를 둬서, 문맥 벡터(c)를 생성한다.
※ 가중치 벡터(a)는 hs의 각 벡터와 LSTM 블록으로 부터 나온 결과 h가 얼마나 비슷한지를 수치로 나타낸 것.
학습할 대상이다.
문맥 벡터를 나타낸 신경망
class WeightSum:
def __init__(self):
self.params, self.grads = [], []
self.cache = None
def forward(self, hs, a):
N, T, H = hs.shape
ar = a.reshape(N, T, 1).repeat(H, axis=2)
t = hs * ar
c = np.sum(t, axis=1) # T(5)축을 없엔다.
self.cache = (hs, ar)
return c
def backward(self, dc):
hs, ar = self.cache
N, T, H = hs.shape
# sum의 역전파 - repeat
dt = dc.reshape(N, 1, H).repeat(T, axis=1)
dar = dt * hs
dhs = dt * ar
#repeat의 역전파 - sum
da = np.sum(dar, axis=2)
return dhs, da
3-2. Decoder의 개선 (2)
- 앞에서는 가중치(a)를 둬서 맥락벡터를 구할 수 있었다.
- 이제는 가중치(a)를 구하는 방법을 알아본다.
※ 가중치 벡터(a)는 hs의 각 벡터와 LSTM 블록으로 부터 나온 결과 h가 얼마나 비슷한지를 수치로 나타낸 것.
학습할 대상이다. 비슷함의 척도는 두 벡터의 내적 값으로 판단한다.
※ 앞에서 a는 '가중치' 였고, 여기서 h는 LSTM의 은닉벡터이다. hs와 h의 유사도를 구하기 위해 내적하는 것이다.
class AttentionWeight:
def __init__(self):
self.params, s.grads = []. []
self.softmax = Softmax()
self.cache = None
def forward(self, hs, h):
N, T, H = 10, 5, 4
hr = h.reshape(N, 1, H).repeat(T, axis=1) # hs와 h의 유사도를 구하기 위해 내적하는 것
t = hs * hr
s = np.sum(t, axis=2) # H(4)축을 없엔다.
a = self.softmax.forward(s)
self.cache = (hs, hr)
return a
def backward(self, da):
hs, hr = self.cache
N, T, H = hs.shape
ds = self.softmax.backward(da)
dt = ds.reshape(N, T, 1).repeat(H, axis=2)
dhs = dt * hr
dhr = dt * hs
dh = np.sum(dhr, axis=1)
return dhs, dh
3-3. Decoder의 개선 (3)
이제 맥락벡터(c)를 구할 차례이다.
앞서구한 Attention Weight(3-2) 계층은 hs와 h의 유사도를 나타내는 수치값 이었다.
Weight Sum계층(3-1)은 hs와 a를 곱하여(Pointwise Muliplication) 맥락 벡터 c를 계산한다.
class Attention:
def __init__(self):
self.params, self.grads = [], []
self.attention_weight_layer = AttentionWeight()
self.weight_sum_layer = WeightSum()
self.attention_weight = None
def forward(self, hs, h):
a = self.attention_weight_layer.forward(hs, h)
out = self.weight_sum_layer.forward(hs, a)
self.attention_weight = a
return out
def backward(self, dout):
dhs0, da = self.weight_sum_layer.backward(dout)
dhs1, dh = self.attention_weight_layer.backward(da)
dhs = dhs0 + dhs1
return dhs, dh
※ Weight Sum + Attention Weight = Attention
기존의 Decoder 계층에서 Affine 계층은 LSTM 계층의 은닉벡터 만을 입력으로 받았으나, Attention 계층의 맥락벡터(c) 까지 같이 입력으로 받게 되었다.
3-4. Time Attention 계층의 구현
class TimeAttention:
def __init__(self):
self.params, self.grads = [], []
self.layers = None
self.attention_weight = None
def forward(self, hs_enc, hs_dec):
N, T, H = hs_dec.shape
out = np.empty_like(hs_enc)
self.layers = []
self.attention_weight = []
for t in range(T):
layer = Attention()
out[:, t, :] = layer.forward(hs_enc, hs_dec[:, t, :])
self.layers.append(layer)
self.attention_weight.append(layer.attention_weight)
return out
def backward(self, dout):
N, T, H = dout.shape
dhs_enc = 0
dhs_dec = np.empty_like(dout)
for t in range(T):
layer = self.layers[t]
dhs, dh = layer.backward(dout[:, t, :])
dhs_enc += dhs
dhs_dec[:, t, :] = dh
return dhs_enc, dhs_dec
4. Attention을 갖춘 Seq2Seq의 구현
이제 Seq2Seq를 Attention 계층을 넣어서 수정해 본다.
4-1. Encoder의 구현
class AttentionEncoder(Encoder):
def forward(self, xs):
xs = self.embed.forward(xs)
hs = self.lstm.forward(xs)
return hs
def backward(self, dhs):
dout = self.lstm.backward(dhs)
dout = self.embed.backward(dout)
return dout
4-2. Decoder의 구현
class AttentionDecoder:
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(D, 4 * H) / np.sqrt(D)).astype('f')
lstm_Wh = (rn(D, 4 * H) / np.sqrt(H)).astype('f')
lstm_b = np.zeros(4 * H).astype('f')
affine_W = (rn(2 * H, V) / np.sqrt(2 * 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.attention = TimeAttention() # 추가됨
self.affine = TimeAffine(affine_W, affine_b)
layers = [self.embed, self.lstm, self.attention, self.affine]
self.params, self.grads = [], []
for layer in layers:
self.params += layer.params
self.grads += layer.grads
def forward(self, xs, enc_hs):
h = enc_hs[:, -1]
self.lstm.set_state(h)
out = self.embed.forward(xs)
dec_hs = self.lstm.forward(out)
c = self.attention.forward(enc_hs, dec_hs)
out = np.concatenate((c, dec_hs), axis=2)
score = self.affine.forward(out)
return score
def backward(self, dscore):
dout = self.affine.backward(dscore)
N, T, H2 = dout.shape
H = H2 // 2
dc, ddec_hs0 = dout[:,:,:H], dout[:,:,H:]
denc_hs, ddec_hs1 = self.attention.backward(dc)
ddec_hs = ddec_hs0 + ddec_hs1
dout = self.lstm.backward(ddec_hs)
dh = self.lstm.dh
denc_hs[:, -1] += dh
self.embed.backward(dout)
return denc_hs
def generate(self, enc_hs, start_id, sample_size):
sampled = []
sample_id = start_id
h = enc_hs[:, -1]
self.lstm.set_state(h)
for _ in range(sample_size):
x = np.array([sample_id]).reshape((1, 1))
out = self.embed.forward(x)
dec_hs = self.lstm.forward(out)
c = self.attention.forward(enc_hs, dec_hs)
out = np.concatenate((c, dec_hs), axis=2)
score = self.affine.forward(out)
sample_id = np.argmax(score.flatten())
sampled.append(sample_id)
return sampled
4-3. Seq2Seq의 구현
class AttentionSeq2Seq(Seq2seq):
def __init__(self, vocab_size, wordvec_size, hidden_size):
args = vocab_size, wordvec_size, hidden_size
self.encoder - AttentionEncoder(*args) #추가됨
self.decoder = AttentionDecoder(*args) #추가됨
self.softmax - TimeSoftmaxWithLoss()
self.params = self.encoder.params + self.decoder.params
self.grads = self.encoder.grads + self.decoder.grads
'데이터 과학 > 딥러닝(Deep Learning)' 카테고리의 다른 글
PCA(주성분 분석) 정리 (0) | 2021.02.10 |
---|---|
Autoencoder 설명 (0) | 2021.02.06 |
Sigmoid 함수 vs Softmax 함수 (0) | 2021.02.01 |
Transformer 설명 (0) | 2021.01.14 |
RNN & LSTM 설명 및 구현 (2) | 2020.11.10 |