Attention을 이용하여 seq2seq가 가진 문제점을 해결할 수 있다.
한계점 : Encoder에서 context vector (= 고정 길이 벡터)를 출력한다.
입력 문장의 길이에 관계없이 항상 같은 길이의 벡터로 변환한다. 긴문장일 경우, 필요한 정보가 벡터에 다 담기지 못한다.
개선점 : Encoder의 출력 벡터는 입력 문장의 길이에 따라 바뀌어야한다.
seq2seq : LSTM 계층의 마지막 hidden state의 벡터만을 Decoder에 전달한다.
Attention : LSTM 계층의 모든 hidden state의 벡터를 Decoder에 전달한다.
각 단어의 hidden state vector를 모두 이용하면 입력된 단어와 같은 수의 vector를 얻을 수 있다. 따라서, 더이상 고정 길이의 vector가 아니다.
개선점 : Encoder에서 각 단어에 대응하는 LSTM의 hidden state vector들을 hs 행렬로 모아 출력한다.
seq2seq : Encoder의 마지막 hidden state vector만 Decoder의 첫 hidden state에 전달한다.
Attention : Encoder에서 각 단어에 대응하는 hs 행렬을 Decoder에 전달하여 시계열 변환한다.
hs 행렬의 모든 정보를 활용할 수 있도록 Decoder를 개선했다.
Input
Affine
Softmax with loss
Context vector
import numpy as np
N, T, H = 10, 5, 4
hs = np.random.randn(N, T, H)
a = np.random.randn(N, T)
ar = np.reshape(N, T, 1).repeat(H, axis=2)
# np.broadcast를 사용하는 경우
# ar = a.reshape(N, T, 1)
# weighted 구하는 방법
t = hs * ar
print(t.shape)
# (10, 5, 4)
# weighted sum 구하는 방법
c = np.sum(t, axis=1)
print(c.shape)
# (10, 4)
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(T, axis=1)
t = hs * ar
c = np.sum(t, axis=1)
self.cache = (hs, ar)
return c
def backward(self, dc):
hs, ar = self.cache
N, T, H = hs.shape
dt = dc.reshape(N, 1, H).repeat(T, axis=1) # sum의 역전파
dar = dt * hs
dhs = dt * ar
da = np.sum(dar, axis=2) # repeat의 역전파
return dhs, da
각 단어의 중요도를 나타내는 가중치를 이용하여 가중합을 구하고 가중합을 이용해 context vector를 구한다. Context vector를 구하는 방법은 알게되었는데, 가중치를 구하는 방법은?
각 단어의 중요도를 데이터로부터 자동으로 학습할 수 있도록 설계해야한다.
hs의 각 단어 벡터와 h 벡터 간의 벡터의 내적을 사용하여 두 벡터의 유사도를 계산한다.
class AttentionWeight:
def __init__(self):
self.params, self.grads = [], []
self.softmax = Softmax()
self.cache = None
def forward(self, hs, h):
N, T, H = hs.shape
hr = h.reshape(N, 1, H)#.repeat(T, axis=1)
t = hs * hr
s = np.sum(t, axis=2)
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
h와 hs의 각 단어 벡터와의 유사도를 이용하여 데이터로부터 가중치를 자동으로 계산할 수 있다.