-
[밑시딥2] 어텐션A.I/Study 2023. 7. 30. 03:35
어텐션의 구조
seq2seq의 문제점
Encoder의 출력은 '고정 길이의 벡터'인데, 이는 어떤 길이의 시퀀스가 들어와도 항상 같은 길이로 변환함을 의미한다.
아무리 긴 문장이여도 항상 같은 길이의 벡터에 집어 넣다보니 필요한 정보를 다 담지 못하는 문제가 발생한다.
Encoder 개선
이번 단계에서 개선할 핵심 사항은 출력의 길이를 입력 문장의 길이에 따라 바꿔주는 것이다.
구체적으로는 각 시각별 LSTM 계층의 모든 은닉 상태 벡터를 모두 이용하는 것이다.
이렇게 쌓은 은닉 상태 벡터를 모두 이용하면 입력된 단어와 같은 수의 벡터를 얻을 수 있다.
또한 그 각각의 은닉 상태 벡터는 각각 그 시각 직전에 입력된 단어에 대한 정보가 가장 많이 포함되어있다는 사실도 기억해야한다.
Decoder 개선
Encoder의 개선을 통해 Decoder는 모든 시각에 대한 은닉 상태 벡터를 전달 받는다.
이를 잘 사용하게끔 하는 것이 개선의 첫 목표이다.
사람이 문장을 번역할때는 특정 단어에 주목하여 그 단어의 변환을 수시로 하게된다.
이를 다른게 표현하면 입력과 출력의 여러 단어 중 어떤 단어끼리 서로 관련되어 있는가 라는 대응 관계를 의미한다.
도착어 단어와 대응 관계에 있는 출발어 단어의 정보를 골라내는 것, 즉, 플요한 정보에만 주목하여 그 정보로부터 시계열 변환을 수행하는 것을 어텐션이라고 부르며 이것이 목표다.
주목한 단어를 선택하는 것을 미분가능한 연산으로 표현하기 위해 가중치합을 사용한다.
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) dar = dt * hs dhs = dt * ar da = np.sum(dar, axis=2) return dhs, da
각 단어의 중요도를 나타내는 가중치 a가 있다면, 가중합을 이용해 맥락 벡터를 얻을 수 있습니다.
각 단어의 가중치 a를 구하는 방법은 Encoder로 받은 hs와 Decoder의 LSTM 계층의 은닉상태 벡터 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
마지막으로 이 두가지를 결합하여 Attention이라는 계층을 만들 수 있다.
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
Encoder가 건네주는 정보 hs에서 중요한 원소에 주목하여, 그것을 밭앙으로 맥락 벡터를 구해 위쪽 계층으로 전파한는 것이 Attention 기술의 핵심이다.
레퍼런스
밑바닥부터 시작하는 딥러닝 2 (사이토 고키 지음, 개앞맵시 옮김)
개앞맵시님의 밑시딥2 저장소: WegraLee/deep-learning-from-scratch-2: 『밑바닥부터 시작하는 딥러닝 ❷』(한빛미디어, 2019) (github.com)
'A.I > Study' 카테고리의 다른 글
[밑시딥2] RNN을 사용한 문장 생성 (0) 2023.07.25 [Math for Deeplearning] More Probability (0) 2023.07.21 [밑시딥2] 게이트가 추가된 RNN (0) 2023.07.14 [밑시딥2] 순환 신경망 (RNN) (0) 2023.07.13 [Math for Deeplearning] Linear Algebra (0) 2023.07.08