ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [밑시딥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)

Designed by Tistory.