본문 바로가기

Programming/(Python)(Ubuntu)

BERT

출처 : http://docs.likejazz.com/bert/#fn:fn-2

 

BERT 알아보기

 

BERT 는 Bidirectional Encoder Representations from Transformers 의 약자로 2018년 10월에 발표되었습니다.

오픈소스화 까지 이루어진 구글의 새로운 Language Representation Model 입니다.

 

인간은 직접 혹은 간접 경험을 통해 특정 상황이나 문제에 대한 가장 효과적인 해결책을 찾을 수 있는 지혜를 얻을 수 있습니다.

BERT의 모델은 Transformer를 기반으로 합니다. 이 그림은 Transformer 논문에 있는 모델 아키텍처입니다. 인코더-디코더 모델은 언뜻 보기엔 상당히 복잡합니다. BERT는 이 중에서도 왼쪽, 인코더만 사용하는 모델입니다.

 

Positional Encoding

 

Transformer의 가장 큰 특징은 Convolution도, Recurrence도 사용하지 않는다는 것입니다. 토큰의 상대적 또는 절대적 위치에 대한 정보를 주입하는데 이를 Positional Encoding 이라 합니다. 이는 사인 함수(sinusoid function)를 이용한 수식 결과를 더해 네트워크가 토큰의 상대적 위치와 관련한 정보를 학습할 수 있게 합니다.

 

PE(pos,2i)=sin(pos/100002i/dmodel)

PE(pos,2i+1)=cos(pos/100002i/dmodel)

pos 는 문장의 순서를 나타내며 i는 임베딩 벡터 차원의 위치를 나타냅니다. 그런 다음 pos/i 행렬의 각 값은 위의 방정식을 사용하여 계산됩니다.

 

Positional Encoding은 이를 따로 이용할 수 있는 구현도 존재합니다. 만약 동일하게 사인 함수를 이용한 위치 정보를 주입하고 싶다면 PyTorch로 구현한 Positional Encoding 구현을 참고할 수 있습니다.

 

Input Embedding

 

BERT는 Transformer와 달리 Positional Encoding을 사용하지 않고 대신 Positional Embeddings를 사용합니다. 여기에 Segment Embeddings를 추가해 각각의 임베딩, 즉 3개의 임베딩을 합산한 결과를 취합니다.

 

BERT의 임베드 레이어 구현 방법은 아래에서 확인하실 수 있습니다.

https://medium.com/@_init_/why-bert-has-3-embedding-layers-and-their-implementation-details-9c261108e28a

 

다음은 BERT에서 각 포함 레이어의 기능을 적절하게 설명하는 논문의 다이어그램입니다.

이를 코드로 나타내면 아래와 같습니다.

e = self.tok_embed(x) + self.seg_embed(seg) + self.pos_embed(pos)

pos는 각 토큰의 위치 정보, 위치에 따라 차례대로 값이 부여되는 range(0, max_len)이며 seg는 토큰 타입 입니다.

입력 문장의 종류에 따라 각각 다른 값을 부여합니다. 여기서 두 문장을 [SEP]로 구분해 입력했고 첫 번째 문장 위치에는 0, 두번째 문장 위치에는 1을 시퀀스 길이만큼 부여했다. 이 값에 대한 각각 임베딩을 얻어 합산하고 여기에 LayerNorm&Dropout 한 최종 결과를 인코더 블럭의 입력값으로 한다.

 

NLP 관련 작업을 해결하기위한 대부분의 딥 러닝 모델과 마찬가지로 BERT는 각 입력 토큰(입력 텍스트의 단어)을 토큰 포함 레이어를 통해 전달하여 각 토큰이 벡터 표현으로 변환됩니다. 다른 딥 러닝 모델과 달리 BERT에는 세그먼트 임베딩 및 위치 임베딩 형식의 추가 임베드 레이어가 있습니다. 이 추가 임베드 레이어의 이유는 추후에 분명해집니다.

 

Encoder Block

BERT는 N개의 인코더 블럭을 지니고 있습니다. Base 모델은 12개, Large 모델은 24개로 구성되는데, 이는 입력 시퀀스 전체의 의미를 N번 만큼 반복적으로 구축하는 것을 의미합니다. 당연히 인코더 블럭의 수가 많을수록 단어 사이에 보다 복잡한 관계를 더 잘 포착할 수 있을 것입니다.

 

인코더 블럭은 이전 출력값을 현재의 입력값으로 하는 RNN과 유사한 특징을 지니고 있습니다. 따라서 이 부분은 병렬 처리가 아닌 Base 모델은 12번, Large 모델은 24번, 전체가 Recursive하게 반복 처리되는 형태로 구성되며 블럭내에서 각각의 입력과 처리 결과는 도식에서 보는 바와 같이 매 번 Residual connections로 처리하여 이미지 분야의 ResNet이 보여준 것 처럼 그래디언트가 non-linear activations를 거치지 않고 네트워크를 직접 흐르게 하여 Explode 또는 Vanishing Gradients 문제를 최소화 하고자 했습니다.

 

Multi-Head Attention

인코더 블럭의 가장 핵심적인 부분은 Multi-Head Attention 입니다. 말 그대로 헤드가 여러개인 어텐션을 뜻하는데, 서로 다른 가중치 행렬을 이용해 어텐션을 h번 계산한 다음 이를 서로 연결(Concatenates)한 결과를 갖습니다.

BERT-Base 모델의 경우 각각의 토큰 벡터 768차원을 헤드 수 만큼인 12등분 하여 64개씩 12조각으로 차례대로 분리합니다. 여기에 Scaled Dot-Product Attention을 적용하고 다시 768차원으로 합칩니다. 그렇게 되면 768차원 벡터는 각각 부위별로 12번 Attention 받은 결과가 됩니다. Softmax는 e의 n승으로 계산하므로 변동폭이 매우 크며, 작은 차이에도 쏠림이 두드러지게 나타납니다. 즉, 값이 큰 스칼라는 살아남고, 작은 쪽은 거의 0에 가까운 값을 multiply하게 되어 배제되는 결과를 낳습니다.

 

Scaled Dot-Product Attention

 

Multi-Head Attention은 Scaled Dot-Product Attention을 h번 계산한 결과의 concatenates 입니다.

Sclaed Dot-Product Attention은 입력값으로 Q, K, V 세 개를 받습니다. 이는 입력값에 대한 플레이스 홀더로 맨 처음에는 임베딩의 fully-connected 결과, 두 번째 부터는 RNN과 유사하게 이전 인코더 블럭의 결과를 다음 인코더 블럭의 입력으로 사용합니다. 원래 Transformer에서 Q는 주로 디코더의 히든 스테이트, K는 주로 인코더의 히든 스테이트, V는 K에 어텐션을 부여 받은 Normalized Weights가 되며, 초기값은 V와 K가 동일합니다. 그러나 BERT는 디코더를 사용하지 않으며 Q, K, V의 초기값이 모두 동일합니다. 물론 저마다 각각 다른 초기화로 인해 실제로는 서로 다른 값에서 출발하지만 입력값의 구성은 동일합니다. BERT는 이처럼 동일한 토큰이 문장내의 다른 토큰에 대한 Self-Attention 효과를 갖습니다.

 

Q, K, V에 대한 Scaled Dot-Product Attention 수식은 아래와 같습니다.

이를 도식화 하면 아래와 같습니다.

Masked Attention 의 경우 위 도식에 Mask는 Optional로 되어 있으며, BERT 공식 구현을 살펴보면 다음과 같습니다.

# Since attention_mask is 1.0 for positions we want to attend and 0.0 for
# masked positions, this operation will create a tensor which is 0.0 for
# positions we want to attend and -10000.0 for masked positions.
adder = (1.0 - tf.cast(attention_mask, tf.float32)) * -10000.0

BERT의 경우 Inference시에는 제로 패딩으로 입력된 토큰에 대해서는 항상 마스킹 처리를 하며 이 토큰에 대해서는 페널티를 부과해 어텐션 점수를 받지 못하도록 구현되어 있습니다.

 

Position-wise Feed-Forward Network

마지막으로 어텐션의 결과를 Position-wise Feed-forward Network로 통과합니다.

 

학습

 

BERT 학습 방식의 가장 큰 특징은 Bidirectional 하다는 점입니다. 이는 OpenAI GPT와 구분되는 뚜렷한 차이점으로, 원래 Transformer는 Bidirectional 합니다. 하지만 이후 출현하는 단어의 예측 확률을 계산해야 하는 Statistical Language Model은 Bidirectional 하게 구축할 수 없습니다.

 

따라서 BERT는 이 문제를 다른 형태의 문제로 전환해 Bidirectional이 가능하게 했습니다. 여기에는 두 가지 방식이 사용되었는데, Masked Language Model과 Next Sentence Prediction입니다. 이를 위해 BERT는 Input Embeddings에 특별한 식별자를 추가했습니다. 앞서 Input Embeddings 항목에서도 확인할 수 있는 [CLS]와 [SEP]이 그것입니다.

 

[SEP]는 문장의 끝을 나타내는 식별자로 두 문장을 구분하는 역할로도 쓰입니다. 이를 통해 QA등의 문제 해결과 Pre-train시 Next Sentence Prediction 문제를 해결하는데 사용합니다. 또한 문장의 맨 앞에는 클래스를 뜻하는 [CLS]를 추가했습니다. 이를 통해 분류 문제(Classification Tasks)를 해결하는데 사용하며 아래 성능 항목에서 볼 수 있는 두 문장의 유사도를 판별하는데도 이 레이어의 벡터를 사용합니다.

 

Masked Language Model

 

Masked Language Model은 문장의 다음 단어를 예측하는 것이 아니라 문장내 랜덤한 단어를 마스킹하고 이를 예측하도록 하는 방식입니다. Context 토큰을 마스킹된 토큰을 맞추도록 학습한 결과를 직접 벡터로 갖기 때문에 보다 직관적인 방식으로 볼 수 있습니다. 마스킹은 전체 단어의 15%정도만 진행하며, 모든 토큰을 마스킹하는게 아니라 80%정도만 <Mask>로 처리하고 10%는 랜덤한 단어, 나머지는 10%는 정상적인 단어를 그대로 둡니다.

 

<Mask> 토큰에 대해서만 학습한다면 Fine-tuning시 이 토큰을 보지 못할 것이고 아무것도 예측할 필요가 없다고 생각해 성능에 영향을 끼칠 것 입니다. 때문에 <Mask> 토큰이 아닌 것도 예측하도록 학습하여 문장의 모든 단어에 대한 문맥 표현(Contextual Representation)이 학습되도록 합니다.

 

BERT는 전체 Vocab Size에 대한 Softmax를 모두 계산합니다. 구글에서 공개한 영문 학습 모델의 Vocab size는 30,522개로, Output Size는 Vocab Size와 동일한 갯수의 Linear Transformation 결과의 Softmax를 정답으로 합니다. 따라서 Loss는 정답 벡터 위치와 Vocab Size 만큼의 Softmax 차이가 됩니다. 한편 한글 모델의 경우에는 형태소 분석 결과가 10만개를 넘어가는 경우가 흔하므로 학습에 더욱 오랜 시간이 걸립니다.

 

Next Sentence Prediction

 

Next Sentence Prediction은 두 문장을 주고 두 번째 문장이 코퍼스 내에서 첫 번째 문장의 바로 다음에 오는지 여부를 예측하도록 하는 방식입니다. 이 방식을 사용하는 이유는 BERT는 Transfer Learning으로 사용되고 QA와 Natural Language Inference(NLI)등의 태스크에서는 Masked Language Model로 학습하는 것 만으로는 충분하지 않았기 때문입니다. 두 문장이 실제로 이어지는지 여부는 50% 비율로 참인 문장과 랜덤하게 추출되어 거짓인 문장의 비율로 구성되며, [CLS] 벡터의 Binary Classification 결과를 맞추도록 학습합니다.


인용 요약) BERT의 Embedding Layer의 구현 세부 사항

Token Embedding / Segment Embedding / Position Embedding

 

Token Embedding(토큰의 의미 표현)

 

Token Embedding Layer의 역할은 단어를 고정 차원의 벡터 표현으로 변환하는 것입니다. BERT의 경우 각 단어는 768차원 벡터로 표시됩니다.

 

입력 텍스트가 "I like watermerlon"라고 가정합니다. 다음은 토큰 임베드 계층의 역할을 설명하는 다이어 그램입니다.

입력 텍스트는 토큰 임베딩 레이어로 전달되기 전에 먼저 토큰화 됩니다. 또한 토큰화 된 문장의 시작([CLS] 및 끝([SEP])에 토큰이 추가됩니다. 이 토큰의 목적은 분류 작업을 위한 입력 표현 역할을 하고 입력 텍스트 쌍을 각각 분리하는 것입니다. 분류 문제가 아닌 경우에 이 벡터는 사용되지 않습니다. [SEP] 토큰으로 문장들을 구분합니다.

 

토큰화는 WordPiece 토큰화라는 방법을 사용합니다. WordPiece는 단어 중심의 단어와 단어 외 단어 사이의 군형을 이루기 위한 데이터 기반 토큰화 방법입니다. 이것은 "멜론"가 "멜론"와 "멜론"로 분리 된 방식입니다.

WordPiece 토큰화를 사용하면 BERT는 어휘에 30,522개의 "단어"만 저장할 수 있으며 영어 텍스트를 토큰화할 때 어휘에서 단어 외 단어가 거의 발생하지 않습니다.

 

Token Embedding Layer는 각 WordPiece 토큰을 768차원 벡터 표현으로 변환합니다. 따라서 배치 축을 포함시키면 6개의 입력 토큰이 (6, 768)의 배열 또는 (1, 6, 768)텐서로 변환됩니다.

 

Segment Embedding(문장과 문장을 이어주는 용도로 표현)

 

BERT는 한 쌍의 입력 텍스트가 주어지면 텍스트 분류와 관련된 NLP 작업을 해결할 수 있습니다. 이러한 문제의 예는 두개의 텍스트가 의미적으로 유사한지 여부를 분류하는 것입니다. 입력 텍스트 쌍은 단순히 연결되어 모델에 제공됩니다. BERT는 주어진 쌍의 입력을 어떻게 구별할까요? 답은 Segment Embedding 입니다.

 

입력 테스트 쌍이 "고양이를 좋아한다", "개를 좋아한다"고 가정하면 다음은 Segment Embedding이 BERT가 이 입력 쌍에서 토큰을 구별하는데 도움이 되는 방법입니다.

Segment Embedding Layer는 2개의 벡터 표현만 있습니다. 첫 번째 벡터(인덱스 0)는 입력 1에 속하는 모든 토큰에 할당되고 마지막 벡터(인덱스 1)는 input 2에 속하는 모든 토큰에 할당됩니다. 입력이 하나의 입력문장만으로 구성된 경우 Segment Embedding 테이블의 인덱스 0에 해당하는 벡터일 뿐입니다.

 

Position embedding(토큰의 sequential한 의미를 보유하기 위해 표현)

-최대 512의 token까지 표현할 수 있다.

 

BERT는 Transformer 스택으로 구성되며 광범위하게 말해 Transformer는 입력의 순차적 특성을 인코딩하지 않습니다. 

다음 블로그 게시물을 통해 알 수 있듯이,위치 임베딩을 사용하면 BERT가 다음과 같은 입력 텍스트가 있음을 이해할 수 있습니다.

 

+내용추가


추가설명

 

BERT-base 자체가 GPT 모델이랑 하이퍼파라미터가 같습니다. 근런데 GPT는 uniDirection 모델이고 BERT는 biDirection모델입니다. 그래서 "더 성능이 좋은건 bidirection 때문이다."를 이야기하기 위해서 base 모델을 구축한 것입니다.

 

input을 어떻게 구성을 할 것이냐?, sentence - 문단 이상이 될 수 있습니다.

[Question, Answer]에서 Answer 자체가 물어보는 내용자체가 들어갑니다. 해당 지문이 들어갑니다. 해당 지문에서 정답이 어디있는지를 start포인트 end포인트를 찾아냅니다. Answer가 길어질 수 있는데 길어질수있습니다.

 

input 자체는 wordpiece embeddings을 이용합니다. w2v와 비슷..?

+알아보기

positional embedding - transformer이랑 똑같습니다.

 

segment embedding : 해당 Q, A가 정해져 있는데 그 Q가 A를 어떻게 구분할 것인가?

Q가 첫번째 문장, A가 2번째 문장으로 지정되어있는데 seperate 토큰으로만 나눌 수 없기 때문에 segment embedding을 습니다.

 

Q,A를 할 때 두문장만 넣어서 SEP로 구분할 수 있는데 근데 Q,A 같은 경우에는 2번째문장 자체가 여러가지 문장으로 되어 있기 때문에 SEP만 넣으면 어디에 속해있는지 알 수 없기 때문에 segment embedding을 넣어줍니다.

 

wordpiece : 언어와 원만하게 작용할 수 있는 것

 

BERT pre-training task 방법론 2가지

1. MASKED LM

my dog is hairy - 전체문장에서 15%를 마스크 시킵니다. 전체를 마스크를 씌우는게 아니고 80%는 마스크를 씌우고 10%는 엉뚱한 단어로 바꾸고 10%는 그대로 씁니다. 이는 실제 representation의 bias를 시켜주기 위해 해줍니다.

Transformer 인코더에서는 어떤단어가 mask 되는건지 어떤단어를 predict하라고 물어보는건지 아니면 random words로 바뀌었는지 모릅니다. 이는 모든 토큰에 대해서 predict 하는것인거 같고 random word로 바뀌는건 15%에서 10%기 때문에 1.5%밖에 안되기 때문에 language understanding 능력에는 해를 끼치지 않는다고 합니다.

MLM은 각각 단어를 prediction을 하는데 LM은 15%만 학습을 합니다.

 

2. Next sentence prediction

두 문장을 줘놓고 뒤에문장이 앞에문장이 뒷문장이냐 아니냐 맞추는것입니다. - fine tuning을 할 때 특정 테스크들은 해당 단어만 맞추는걸로 해결할 수 없는 문제가 있습니다. 전체 문장이 흘러가는 것을 알아야 풀 수 있는 문제 를 해결하기 위해 추가합니다.

 IsNext와 NotNext를 50 대 50 으로 넣어놓습니다. 트레이닝을 시키는데 97~98% 가 되도록 학습이 됩니다.

다음 문장을 예측하는게 QA와 NLI에서 beneficial 했습니다. 

 

pretraining procedure : 읽어보기

4개의 TPU로 학습시켰고

16개의 TPU로 학습

 

fine tuning procedure

softmax 달아서 classification 등 

classification 라벨 수만큼 K(W) 풀리컨넥티드 구성하는데 임베딩 H자체가

 NER내용

SQuAD v1.1

반응형