Transformer -1-

항생제투여자·2022년 2월 7일
0

자연어처리

목록 보기
2/2

Seq2seq 와 Attention

seq2seq

정의 : 어떤 Sequence(연속된 데이터)들을 다른 Sequence로 Mapping하는 알고리즘

구성 : 정보 압축을 위한 Encoder->새로운 데이터를 생성해내는 Decoder

문제 :

  • Encoder에서 Decoder로 들어가는 과정에서 손실되는 정보들이 생김
  • RNN의 고질적인 문제인 기울기 소실(vanishing gradient)문제 존재

Attention

정의 : Decoder에서 출력 단어를 예측하는 매 시점(time step)마다, Encoder에서의 전체 입력 문장을 다시 한 번 참고하는 알고리즘.

Attention(Q,K,V) = Attention Value

Q = Query : t 시점의 디코더 셀에서의 은닉 상태
K = Keys : 모든 시점의 인코더 셀의 은닉 상태들
V = Values : 모든 시점의 인코더 셀의 은닉 상태들

이해

Dot product Attention의 예)


(출처 : https://wikidocs.net/22893)

  • Dot product Attention을 위해서는 Encoder의 Hidden state(h)와 Decoder의 Hidden state함수(s)가 얼마나 유사한지를 표현하기 위한 내적으로 시작
  • 이렇게 내적으로 얻은 Attention score들을 Sotfmax 함수에 넣어서 Attention Distribution을 얻음.
  • 얻은 Attention Distibution값과 Encoder의 Hidden state와 가중합을 해서, Attention Value값을 구하여 Decoder의 Hidden state 차원 밑에 그대로 붙이고(Concatenate)
  • 이를 output 계산의 input으로 넣는다.

Transformer의 정의

말 그대로 번역기
A국가의 문장 혹은 단어를 입력받으면 인코더와 디코더를 지나 B국가의 문장 혹은 단어로 출력해주는 것

기존 seq2seq 모델과 transformer의 차이점

  • 기존 Seq2seq 모델 : 기존 seq2seq 모델에 Attention을 보조로 사용

  • transformer : Attention으로만 구성되어있다.

정리

A. Encoder

RNN, LSTM의 약점

  • 단어를 순차적으로 입력받아서 계산하기 때문에, 병렬 처리가 어렵다.

Transformer

  • 순차적으로 Data를 넣는 것이 아니라, Sequence를 한 번에 넣음으로써 병렬처리 가능
  • Attention 등의 구조를 통해 어떤 부분이 중요한지 전달하여 위치 정보를 반영

  • Embedding : Data를 임의의 N-dimension Data로 만들어주는 단계.
    (N-dimension : 자연어 처리에서 단어 등의 단위)
  • Positional Encoding : 'Sequence 내 해당 정보의 위치 정보'와 'Embedding된 데이터'를 사인함수와 코사인함수 형태로 만들어 다음 Layer의 Input으로 전달
    positional Encoding으로 Transformer는 해당 Input의 위치를 반영한 pre-processing을 할 수 있게 된다.

Encoder의 구성

Multi-head Self Attention / Add & Normalize / Position-wise FFNN라는 모듈들로 구성

Multi-head Self Attention과 Attention의 차이점

  • Attention은 Encoder의 Hidden state와 Decoder의 Hidden state를 연산해서 Attention score값을 계산
  • Self-Attention은 Encoder로 들어간 벡터와 Encoder로 들어간 모든 벡터를 연산해서 Attention score값을 계산


(출처 : https://engineering-ladder.tistory.com/73)

B. Decoder

  • Decoder 와 Encoder는 Postional Encoding과 Multi-head Self Attention을 하는 것은 비슷하지만 두 가지의 차이점이 있다.

1. Decoder에는 Multi-head Self Attention에 Masked가 들어가있다.

  • Decoder는 현재까지 들어온 데이터 보다 미래의 데이터를 보면서 학습하면 안되기 때문에 미래에 해당하는 Attention score들에는 매우 작은 음수값(-1e9)를 넣어서 이 부분을 Masking해버린다.

한 번 더 Attention을 진행

  • 이 때는 Self-Attention이 아니라, 일반적인 Attention을 사용하여 Output을 추측하는데 사용.
  • 이 때의 input으로는 Decoder에서 사용한 Masked Attention Matrix와 Encoder에서 만든 Matrix를 Attention에 넣게 된다.

Transformer의 주요 하이퍼파라미터

  • d_model = 512
    트랜스포머의 Encoder와 Decoder에서의 정해진 입력과 출력의 크기를 의미.
    Embedding 벡터의 차원 또한 d_model이며, 각 Encoder와 Decoder가 다음 층의 Encoder와 Decoder로 값을 보낼 때에도 이 차원을 유지.
    논문에서는 512로 설정.

  • num_layers= 6
    Transformer에서 하나의 Encoder와 Decoder를 층으로 생각하였을 때, Transformer 모델에서 Encoder와 Decoder가 총 몇 층으로 구성되었는지 의미
    논문에서는 Encoder와 Decoder를 각각 총 6층으로 설정.

  • num_heads = 8
    Transformer에서는 Attention을 사용할 때, 한 번 하는 것보다 여러 개로 분할해서 병렬로 Attention을 수행하고, 결과값을 다시 하나로 합치는 방식을 채택
    num_heads는 이때 이 병렬의 개수를 의미.

  • d_ff
    Transformer 내부에는 feed forward 신경망이 존재하며 해당 신경망의 은닉층의 크기를 의미.
    feed forward 신경망의 input layers와 output layers의 크기는 d_model

(feed forward : input layer로 데이터가 입력되고, 1개 이상으로 구성되는 hidden layer를 거쳐서 마지막에 있는 output layer로 output data를 내보내는 과정)

Transformer


(출처 : https://wikidocs.net/31379)

  • Transformer는 RNN을 사용하지 않지만, 기존의 seq2seq처럼 encoder에서 입력 시퀀스를 입력받고, Decoder에서 출력 시퀀스를 출력하는 Encoder-Decoder 구조를 유지하고 있다.
  • 이전 seq2seq 구조에서는 Encoder와 Decoder에서 각각 하나의 RNN이 t개의 시점(time step)을 가지는 구조였다면, 이번에는 Encoder와 Decoder라는 단위가 N개로 구성되는 구조이다.
  • 논문에서는 Encoder와 Decoder의 개수를 각각 6개 사용.


(출처 : https://wikidocs.net/31379)

Encoder와 Decoder가 6개씩 존재하는 Transformer의 구조.


(출처 : https://wikidocs.net/31379)

  • Encoder로부터 정보를 전달받아 Decoder가 출력 결과를 만들어내는 Transformer 구조.
  • Decoder는 마치 기존의 seq2seq 구조처럼 시작 심볼 <sos>를 입력으로 받아 종료 심볼 <eos>가 나올 때까지 연산을 진행
  • 이는 RNN은 사용되지 않지만 여전히 Encoder-Decoder의 구조는 유지되고 있음을 보여준다.

Transformer의 입력

  • Transformer의 Encoder와 Decoder는 단순히 각 단어의 Embedding 벡터들을 입력받는 것이 아니라 Embedding 벡터에서 조정된 값을 입력받음.

포지셔널 인코딩(Positional Encoding)

  • RNN이 유리했던 이유는 단어의 위치에 따라 단어를 순차적으로 입력받아서 처리하는 RNN의 특성으로 인해 각 단어의 위치 정보(position information)를 가질 수 있었기 때문이다.

  • Transformer는 단어의 위치 정보를 얻기 위해서 각 단어의 Embedding 벡터에 위치 정보들을 더하여 모델의 입력으로 사용하는데, 이것을 포지셔널 인코딩(positional encoding)이라고 한다.


(출처 : https://wikidocs.net/31379)

입력으로 사용되는 Embedding 벡터들이 Transformer의 입력으로 사용되기 전에 포지셔널 인코딩의 값이 더해지는 것을 보여준다.

  • Embedding 벡터가 Encoder의 입력으로 사용되기 전 포지셔널 인코딩값이 더해지는 과정의 시각화

    (출처 : https://wikidocs.net/31379)

  • Transformer의 위치 정보를 가진 값을 만들기 위한 함수 공식

    (출처 : https://wikidocs.net/31379)

    pos = 입력 문장에서의 Embedding 벡터의 위치
    i = Embedding 벡터 내의 차원의 인덱스
    d_model = 트랜스포머의 인코더와 디코더에서의 정해진 입력과 출력의 크기

  • 사인 함수와 코사인 함수의 그래프를 상기해보면 요동치는 값의 형태를 생각해볼 수 있는데, Transformer는 사인 함수와 코사인 함수의 값을 Embedding 벡터에 더해주므로서 단어의 순서 정보를 더하여 준다.

  • Embedding 벡터와 포지셔널 인코딩의 덧셈은 Embedding 벡터가 모여 만들어진 문장 행렬과 포지셔널 인코딩 행렬의 덧셈 연산을 통해 이루어진다.

  • Embedding 벡터 내의 각 차원의 인덱스가 짝수인 경우 (pos,2i) -> 사인 함수

  • Embedding 벡터 내의 각 차원의 인덱스가 홀수인 경우 (pos,2i+1) -> 코사인 함수

  • Embedding 벡터는 d_model의 차원을 가진다.

위와 같은 포지셔널 인코딩 방법을 사용하면 순서 정보가 보존되는데, 각 임베딩에 포지셔널 인코딩의 값을 더하면 같은 단어라고 하더라도 문장 내의 위치에 따라서 Transformerr의 입력으로 들어가는 Embedding 벡터의 값이 달라진다.
이에 따라 Transformer의 입력은 순서 정보가 고려된 Embedding 벡터가 된다.

class PositionalEncoding(tf.keras.layers.Layer):
  def __init__(self, position, d_model):
    super(PositionalEncoding, self).__init__()
    self.pos_encoding = self.positional_encoding(position, d_model)

  def get_angles(self, position, i, d_model):
    angles = 1 / tf.pow(10000, (2 * (i // 2)) / tf.cast(d_model, tf.float32))
    return position * angles

  def positional_encoding(self, position, d_model):
    angle_rads = self.get_angles(
        position=tf.range(position, dtype=tf.float32)[:, tf.newaxis],
        i=tf.range(d_model, dtype=tf.float32)[tf.newaxis, :],
        d_model=d_model)

    # 배열의 짝수 인덱스(2i)에는 사인 함수 적용
    sines = tf.math.sin(angle_rads[:, 0::2])

    # 배열의 홀수 인덱스(2i+1)에는 코사인 함수 적용
    cosines = tf.math.cos(angle_rads[:, 1::2])

    angle_rads = np.zeros(angle_rads.shape)
    angle_rads[:, 0::2] = sines
    angle_rads[:, 1::2] = cosines
    pos_encoding = tf.constant(angle_rads)
    pos_encoding = pos_encoding[tf.newaxis, ...]

    print(pos_encoding.shape)
    return tf.cast(pos_encoding, tf.float32)

  def call(self, inputs):
    return inputs + self.pos_encoding[:, :tf.shape(inputs)[1], :]

50 x 128의 크기를 가지는 포지셔널 인코딩 행렬을 시각화

# 문장의 길이 50, 임베딩 벡터의 차원 128
sample_pos_encoding = PositionalEncoding(50, 128)

plt.pcolormesh(sample_pos_encoding.pos_encoding.numpy()[0], cmap='RdBu')
plt.xlabel('Depth')
plt.xlim((0, 128))
plt.ylabel('Position')
plt.colorbar()
plt.show()
(1, 50, 128)

========
도움받은 사이트 :
https://wikidocs.net/31379
https://wikidocs.net/22893
https://engineering-ladder.tistory.com/73
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=beyondlegend&logNo=221373971859

profile
인공지능(머신러닝, 딥러닝)공부 중인 사람입니다.

0개의 댓글