ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [논문과 코드] Attention is all you need *line-by-line
    Deep-learning 2020. 7. 1. 15:04

    2017년 발표된 sequence to sequence modeling의 신세기를 연 "Attention is all you need"에 대한 article입니다. 3년이 지난 현재까지도 많은 Deep learning 관련 sequence to sequence 논문들이 Transformer model을 기반으로 설계되고 있습니다.

    Transformer model에 대해 공부하고 이 글을 작성하면서 https://nlpinkorean.github.io/illustrated-transformer/

     

    The Illustrated Transformer

    저번 글에서 다뤘던 attention seq2seq 모델에 이어, attention 을 활용한 또 다른 모델인 Transformer 모델에 대해 얘기해보려 합니다. 2017 NIPS에서 Google이 소개했던 Transformer는 NLP 학계에서 정말 큰 주목을

    nlpinkorean.github.io

    http://jalammar.github.io/illustrated-transformer/

     

    The Illustrated Transformer

    Discussions: Hacker News (65 points, 4 comments), Reddit r/MachineLearning (29 points, 3 comments) Translations: Chinese (Simplified), Japanese, Korean, Russian Watch: MIT’s Deep Learning State of the Art lecture referencing this post In the previous pos

    jalammar.github.io

    의 자료를 사용하였고, 위 링크의 자료를 통해 Transformer model이 구성에 대해 공부하시는 것도 도움이 되리라 생각됩니다.

     

    Transformer model이 우수한 점으로는 아래 performance 표를 참고하여 확인할 수 있습니다.

    Translation performance in Transformer model

    1. 번역(translate) Task에서 State-Of-The-Art 성능을 보임이 보고된 바 있습니다. (BLEU는 번역 task의 성능을 측정하는 지표로 대략 정답 번역 문장과의 유사도라고 생각하시면 됩니다. *높을수록 좋음* 자세한 설명은 여기를 클릭)

    2. Training cost (학습 시 비용 - FLOPs)가 획기적으로(10의 거듭제곱 규모로) 줄어들었습니다. (FLOPs란 FLoating point OPerations로 대략적으로는 연산 횟수라고 생각하시면 됩니다. 자세한 설명은 여기를 클릭)

    연산 횟수를 약 100분의 1, 1000분의 1 규모로 줄이면서 높은 성능을 보인다는 점에서 매우 의미가 큰 논문입니다. 그만큼 피인용 횟수도 높습니다.

     

    아래부터는 Transformer model 구조를 code와 함께 하나하나 살펴보도록 하겠습니다.

     1. Model Architecture

    Transformer network

    트랜스포머는 위와 같은 구조로 이루어져 있습니다. 트랜스포머 모델을 처음으로 본 분들은 어떤 점이 신박한 부분인지 잘 감이 안 올 수 있는데, 앞서 설명에서의 Transformer model의 장점과 연결시켜 설명드리면,

    1. Sequence-to-sequence 모델링은 말 그대로, 정보가 순서대로 나열된 수열(vector sequence)을 입력으로 받아 다른 종류의 수열을 출력으로 내보내게 되는데, 이를 정보가 순서대로 나열된 수열을 다루는 데에 사용되는 Recurrent Neural Network (RNN)을 사용하지 않고 최고 수준의 높은 성능이 나왔다.

    2. RNN을 사용하지 않았기 때문에 RNN 훈련에서의 단점인 순차적 학습으로 인한 학습 속도 저하가 없어 빠른 학습이 가능하다.

    로 설명할 수 있습니다.

     

    이러한 구조를 실제로 pytorch에서는 어떻게 구현이 되어있는지 코드와 대응시켜가면서 하나하나 알아보도록 하겠습니다.

    참고 code: official Pytorch Transformer (from torch.nn.Transformer) 

    https://pytorch.org/docs/master/generated/torch.nn.Transformer.html

     

    Transformer — PyTorch master documentation

    Shortcuts

    pytorch.org

    https://pytorch.org/docs/master/_modules/torch/nn/modules/transformer.html#Transformer

     

    torch.nn.modules.transformer — PyTorch master documentation

    Shortcuts

    pytorch.org

    위 두 링크 중 첫 번째는 Pytorch 안의 neural network 클래스들을 제공하는 torch.nn.Transformer 함수의 사용법이고, 두 번째 링크는 이 torch.nn.Transformer의 실제 source code입니다.

    공식적으로 제공되는 라이브러리는 여러가지 환경 및 상황에서 빠른 속도로 동작 가능해야 하므로 여러가지 예외 처리 및 최적화가 이루어져 있습니다. 본 글에서는 실제 계산이 수행되는 각 부분에 집중하여 Transformer model을 이해할 수 있도록 예외 처리 및 기타 최적화 코드는 설명을 생략하거나 매우 간단하게만 짚고 넘어가도록 하겠습니다.

    **한 article에 code를 포함하여 전체를 담아내기에는 분량이 너무 길어질 수 있어 3개의 article로 나누어 설명드리도록 하겠습니다.**

    1. Transformer 전체 구조 - model weight 초기화 및 거시적인 계산 과정(architecture) [현재 page]
    2. Encoder, Decoder 구조 - model 내부에서 Transformer의 각 block이 어떻게 작동하는지에 대한 계산 과정 [링크]
    3. Out of Encoder/Decoder - Encoder, decoder 외부의 block의 구현 및 계산 과정 [링크]

    1. class Transformer(torch.nn.Module):

    Transformer class - __init__() (initializer)

    원래 line1 - line2 사이에 class에 대한 설명과 각 input(argument)에 대한 설명이 포함되어 있으나 가독성을 위해 삭제하였습니다.

    행(line) 설명(Description)
    1 Transformer라는 이름의 클래스를 선언하면서, Module이라는 class로부터 상속을 받습니다.
    [optional]Module은 torch 라이브러리에서 딥러닝 프레임워크를 사용하기 위한 여러가지 기본적인 기능들을 사용할 수 있도록 구성되어 있습니다.
    2~4 Transformer의 class 변수의 실제 선언(instantiation)시 수행되는 생성자 함수를 정의합니다.
    d_model: Transformer model에서 attention에 사용되는 입력 및 출력의 dimension입니다.
    nhead: multi-head attention에서 몇 개의 head로 나눌지를 결정하는 입력입니다. **중요** d_model에서 nhead로 나눌 시 나머지 없이 나누어 떨어져야 합니다.
    num_encoder_layers: encoder layer를 몇 번 중첩하여 쌓을지를 결정하는 입력입니다. 위 그림에서 왼쪽의 "N×"에 해당하는 입력입니다.
    num_decoder_layers: decoder layer를 몇 번 중첩하여 쌓을지를 결정하는 입력입니다. 위 그림에서 오른쪽의 "N×"에 해당하는 입력입니다.
    dim_feedforward: Transformer model에서 Feed-Forward network의 내부 layer의 dimension입니다.
    dropout: attention과 Feed-Forward network를 지난 후 적용되는 dropout 비율입니다.
    activation: Feed-Forward network의 activation을 결정하는 입력으로 "relu"와 "gelu"중에 선택할 수 있고, 별도 지시 없을 시(default) "relu"로 사용됩니다.
    [optional]
    custom_encoder, custom_decoder : 전체 구조에서 encoder와 decoder만 별도로 직접 구성한 encoder/decoder block을 사용하고 싶을 때 선택적으로 사용할 수 있는 입력이며, 사용하지 않을 시 논문에서 제안하는 구조를 사용하게 됩니다.
    [/optional]
    5 부모 class인 torch.nn.Module class의 생성자(__init__)를 호출합니다.
    [optional]이 생성자에는 학습시 필요한 forward propagation, backpropagation에서의 weight 정보, optimizer 정보 및, Debugging을 도와주는 기능 등을 은연중에 사용할 수 있도록 도와주는 변수들을 선언합니다.[/optional]
    7~8 [optional]만일 custom_encoder를 별도로 입력할 경우 line 8이 수행됩니다.
    직접 구성한 encoder block이 입력으로 존재한다면, (if custom_encoder is not None):
     >> Transformer encoder로 custom_encoder를 사용합니다.(Transformer.encoder = custom_encoder)[/optional]
    9~12 만일 custom_encoder가 별도로 입력되지 않은 경우(일반적인 Transformer model 구조를 사용하고 싶을 경우)
    line 10: 중첩되는  encoder_layer로 TransformerEncoderLayer를 사용함
    line 11: LayerNormalization을 적용함
    [optional1] LayerNormalization이란 deep learning의 forward propagation 과정에서 hidden layer의 크기(variance)를 dimension마다 같도록 하는 과정이며, 이를 수행할 경우 안정적인 학습이 가능하다는 것이 알려져 있습니다.[/optional1]
    [optional2] line 11의 layer normalization은 encoder의 출력에 마지막으로 적용하게 되는데 이는 논문에서는 언급된 바 없는 부분입니다. 하지만 안정성을 위해서, 있더라도 나쁜 효과를 일으키지는 않습니다.[/optional2]
    line 12: line 10의 encoder_layer를 num_encoder_layers만큼 반복하여 중첩한 후 line 11의 LayerNormalization을 적용하는 TransformerEncoder를 선언합니다.
    14~15 [optional]만일 custom_decoder를 별도로 입력할 경우 line 8이 수행됩니다.
    직접 구성한 decoder block이 입력으로 존재한다면, (if custom_decoder is not None):
     >> Transformer decoder 로 custom_decoder를 사용합니다.(Transformer.decoder = custom_encoder)[/optional]
    16~19 만일 custom_decoder가 별도로 입력되지 않은 경우(일반적인 Transformer model 구조를 사용하고 싶을 경우)
    line 17: 중첩되는 decoder_layer의 한 유닛로 TransformerDecoderLayer를 사용함
    line 18: LayerNormalization을 적용함
    [optional] line 11의 layer normalization은 decoder의 출력에 마지막으로 적용하게 되는데 이는 논문에서는 언급된 바 없는 부분입니다. 하지만 안정성을 위해서, 있더라도 나쁜 효과를 일으키지는 않습니다.[/optional]
    line 19: line 17의 encoder_layer를 num_decoder_layers만큼 반복하여 중첩한 후 line 18의 LayerNormalization을 적용하는 TransformerEncoder를 선언합니다.
    21 Transformer class 내부 함수(method)로 선언된 _reset_parameter()를 호출합니다.
    복잡한 기능은 아니고 선언된 model의 weight를 초기화합니다.(xavier initializer)
    23 입력이 들어왔을 때 계산을 용이하게 하기 위해 class 내부 변수(item)으로 model의 dimension을 저장합니다.
    24 입력이 들어왔을 때 계산을 용이하게 하기 위해 class 내부 변수(item)으로 head 수를 저장합니다.
    d_model과 nhead를 알아야 multi-head attention 계산 시 각 head로 나누기 용이하기 때문에 저장합니다.

    Transformer - forward()

    위 그림은 Transformer class의 forward 함수로 encoder 입력과 decoder 입력을 받아서 predicted output 결과를 실제로 내보내는(return) 함수입니다.

    행(line) 설명(Description)
    27~29 forward 함수를 선언하면서 Transformer 결과를 계산하는 데에 필요한 입력(argument)을 받습니다.
    src: encoder 입력 sequence입니다. 영어→한국어 번역 task라면 영어 sequence일 것입니다.
    tgt: decoder 입력 sequence입니다. 영어→한국어 번역 task라면 한국어 sequence일 것입니다.
    src_mask: encoder의 입력 sequence 중 attention 계산을 수행하지 않고 무시할 sequence sample 정보를 제공하는 mask 값을 갖는 sequence입니다.
    tgt_mask: decoder의 입력 sequence 중 attention 계산을 수행하지 않고 무시할 sequence sample 정보를 제공하는 mask 값을 갖는 sequence입니다.
    memory_mask: decoder의 enc-dec attention에 입력되는 sequence 중 attention 계산을 수행하지 않고 무시할 sequence sample 정보를 제공하는 mask 값을 갖는 sequence입니다.
    src_key_padding_mask: encoder 입력에서 mini-batch 단위로 들어오게 되어 mini-batch 내 길이가 짧은 sequence에 padding을 수행하게 되는데, 이 padding sample에는 attention을 수행하지 않도록 padding sample의 index에는 mask를 수행하는 mask sequence입니다.
    tgt_key_padding_mask: decoder 입력의 padding sample에는 attention을 수행하지 않도록 padding sample의 index에는 mask를 수행하는 mask sequence입니다.
    memory_key_padding_mask: decoder의 enc-dec attention의 입력의 padding sample에는 attention을 수행하지 않도록 padding sample의 index에는 mask를 수행하는 mask sequence입니다.

    mask 입력에서 입력변수명(argument)에 src, tgt, memory가 포함된 mask는 각각 해당 위치의 입력 sequence에 대해서(memory는 decoder의 multi-head attention 입력으로 사용됨) masking을 수행하게 됩니다.
    [optional1]padding mask는 통상 [False, False, False, ..., False, True, True, ..., True]처럼 일관적으로 False였다가 padding이 아닌 sample이 끝나는 지점에서부터 True인 형태를 입력으로 받게 됩니다.[/optional1]
    [optional2]padding이 붙지 않은 mask는 주로 NLP분야에서 word 단위의 embedding을 거칠 때 unknown token으로 처리된 sample에 대해서 unknown token에 attention 가중치가 가지 않도록 하기 위해 사용되는 mask sequence이며, 그렇기 때문에 불규칙적으로 False, True가 나타나게 됩니다(unknown token이 있는 sample index에서 True).[/optional2]

    31~32 입력된 src와 tgt의 Batch size가 같지 않다면 에러를 발생시킵니다.
    34~35 입력된 src와 tgt의 sequence sample당 dimension이 d_model과 같지 않은 경우 에러를 발생시킵니다. [optional] __init__()에서 self.d_model을 따로 저장해둔 이유가 여기 있습니다. 만일 self.d_model을 따로 선언해두지 않았을 경우에는 Transformer model의 matrix의 weight를 복잡한 함수를 사용하여 호출해야 하였을 것이고, 코드가 복잡해졌을 것입니다. [/optional]
    37 Transformer Encoder 계산을 수행합니다. 모델 그림에서 왼쪽 큰 사각형 부분의 계산을 수행합니다.

    .
    38~40 Transformer Decoder 계산을 수행합니다. 모델 그림에서 오른쪽 큰 사각형 부분의 계산을 수행합니다.
    41 Decoder의 결과를 함수의 출력으로 내보냅니다(return)을 수행합니다.

    여기까지, Transformer model의 전체적인 선언에 대한 설명이었습니다. 이후의 글에서는 encoder와 decoder에서 세부적인 neural network block이 어떻게 구현되어 있는지 설명을 드리도록 하겠습니다.

    아직까지는 torch.nn 라이브러리의 요소는 거의 사용하지 않았습니다. 거시적인 느낌에서 Transformer model이 어떻게 선언되는지에 대해 감을 잡으셨으면 이 글을 충분히 이해한 것이라 스스로 생각해도 좋을 것 같습니다. 연결되는 articles에서는 조금 더 구체적으로 Transformer model을 파헤치도록 하겠습니다.

    "Attention is all you need"가 주장하는 굉장한 구조가 어떻게 line-by-line으로 간단하게 구현되는지 알게 되어 독자분들이 논문을 읽고 최신의 모델을 다루는 데에 조금 더 실제적인 도움이 되지 않을까 하는 바람이 있습니다 :)

    빠른 시일 내로 이후 내용의 글을 작성하도록 하겠습니다.

    댓글

Designed by Tistory.