BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

[접기]

목차

  • 1. 개요
  • 2. BERT가 하는 일
    • 2.1. 입력과 출력
    • 2.2. 사용 예시
  • 3. 모델 구조
    • 3.1. 전체 구조
    • 3.2. 레이어 사이의 연산
    • 3.3. Multi-Head란?
  • 4. 성능
  • 5. 읽을거리


BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
- NAACL 2019. 06. -
https://arxiv.org/pdf/1810.04805.pdf

1. 개요


Google에서 2019년도 NAACL[1]에 발표한 언어 모델로 RNN이나 CNN 구조 없이 Attention을 적극 활용하여 SOTA를 달성하였다. 특히, Task마다 처음부터 학습해서 성능을 측정하는 것이 아니라, 미리 큰 Corpus에 대해서 Pre-training을 시키고 각 Task에 대해서 Fine-tuning을 하여 SOTA를 달성하였다. 이에 저자들 또한 BERT를 Pre-trained Representation으로 소개하고 있다. 한동안 이후에 나온 많은 자연어 처리 관련 논문들도 BERT를 응용하긴 하지만 학습시키는 과정은 전반적으로 "① Pre-trained BERT에서 출발 → ② 원하는 Task의 입력과 출력을 BERT에 어떠한 형태로 넣을지 고민 → ③ BERT의 출력값들을 잘 조합해서 결과 생성 → ④ SOTA 달성"으로 비슷했다. 물론, 이 글을 쓰는 시점에서는 이미 많은 개선된 모델들이 나왔고 모델 분석도 많이 이루어졌으나, 그 시작이 된 논문을 짚고 넘어가는 게 당연하다고 생각하기에 간단히 정리해보고자 한다. 그리고 처음 보는 사람에게는 아무래도 생소한 형태의 모델이기 때문에 자세한 분석보다는 모델이 어떻게 생겼는지에 대해 소개하는 느낌으로 진행하고자 한다.

2. BERT가 하는 일


2.1. 입력과 출력


먼저, BERT는 입력으로 토큰들로 구성된 배열을 받으며, 각각은 양의 정수로 이루어져야 한다. 실제로는 apple은 3번, banana는 5번과 같이 각 토큰마다 Vocabulary에 번호가 정해져 있고 이를 그대로 사용하면 된다.
여기에서 [CLS][SEP]라는 처음 보는 토큰들이 등장한다. [CLS]는 항상 맨 처음에 넣으며 해당 위치의 출력값-즉, 출력값들 중 맨 처음 값-을 분류 문제에 활용하고자 BERT에서 고유하게 사용하는 토큰이다. [SEP]는 두 문서-또는 문장이나 문단- AB의 경계와 맨 뒤에 넣으며 문서의 경계임을 표시하기 위해 BERT에서 고유하게 사용하는 토큰이다. 단, [SEP]는 입력 맨 뒤에 꼭 넣진 않아도 되며, 넣는지 안 넣는지에 따라 성능 차이가 있으므로 잘 살펴보아야한다. 이 두 토큰 또한 Vocabulary 내에 고유한 번호를 가지고 있다.

그럼 BERT의 출력은 어떻게 생겼을까? BERT는 입력과 똑같은 수의 텐서를 출력으로 낸다. 좀 더 자세히 설명하자면, 입력이 10개의 토큰으로 구성되었다면 출력 또한 10개의 텐서로 구성되고, 각 텐서는 768차원(BERT 버전마다 다름)의 실숫값을 가진다. 때문에, 일반적으로 각각의 출력값은 해당 위치의 입력 토큰과 대응되는 존재로 생각된다. 예를 들어, 주어진 Task가 POS Tagging이었다면, 각 토큰의 Tag는 출력값에서 각 토큰 위치에 해당하는 텐서들을 적당히 Tag 종류만큼의 크기를 갖는 텐서로 만들어 Softmax를 취해서 구할 수 있다.

2.2. 사용 예시


여기까지만 들어서는 입력을 정확히 어떻게 넣고 출력을 어떻게 쓰는지(예를 들어, {{{A}}}랑 {{{B}}}는 무엇인지, 출력에서 [CLS][SEP]는 어떻게 쓰이는지 등) 궁금할텐데, 논문에서 소개하는 방법들은 다음과 같다. 읽는 순서는 "① 이미지 → ② Task 종류 → ③ 설명"이다.

두 문장이 관련이 있는지 없는지, 두번째 문장이 첫번째 문장의 다음 문장으로 오기에 자연스러운지와 같이 두 문장 사이의 관계를 결정할 때 쓰이는 입력과 출력의 형태이다. [CLS]가 하나의 768차원 텐서로 나오면 이를 적당한 과정을 거쳐 Class 수만큼의 크기를 갖는 텐서로 만들고 Softmax를 취하여 사용한다.

바로 앞에서 언급한 두 문장 사이의 관계를 판단하는 문제와 비슷하지만, 이번에는 문장이 하나이다. 즉, 문장 자체에 어떠한 의미의 점수를 매길 때 사용한다.

질문이 주어지면 이에 대한 답이 문서 내의 어느 부분에 있는지(예: 5번째 토큰부터 12번째 토큰) 판단하는 문제이다. 대부분 답이 토큰들 안에서 연속된 구간으로 나타나기 때문에, 그림과 같이 각 토큰 위치에 해당하는 출력 텐서들을 이용하여 각 토큰이 답을 나타내는 구간의 Start 또는 End 토큰일 확률을 구한다.

POS Tagging 같은 것을 떠올리면 된다. 주어지는 문장은 하나이고, 각 토큰에 대한 Tag는 질문 답변 문제와 비슷하게 각 토큰 위치에 해당하는 출력 텐서로부터 구한다.

3. 모델 구조


3.1. 전체 구조


그렇다면 BERT는 내부에서 어떤 과정을 거쳐 이러한 계산들을 하는 것일까? 먼저, 모델 구조를 전체적으로 보면 다음 그림과 같다. 보는 방향은 "아래 → 위"이다. (단, 제시하는 수치들은 BERT 버전에 따라 다르며 간단하게 설명하기 위하여 일부 과정은 생략하거나 단순화하였음)

그림에서 빨간 네모들은 입력 토큰(정수)들을 의미하고 노란 네모들은 각 토큰에 대한 Embedding 텐서(768차원의 실숫값)들을 의미한다. 그다음부터는 "이전 레이어 텐서들 → 다음 레이어 텐서들" 형태의 연산이 12번 이루어지며, 모두 같은 과정이지만 파라미터는 공유하지 않는다.

3.2. 레이어 사이의 연산


그럼 "이전 레이어 텐서들 → 다음 레이어 텐서들" 형태의 연산이 어떻게 이루어지는지 살펴보자. 읽는 순서는 "이미지 → 설명"이다.

먼저, 이전 레이어의 텐서들로부터 N2 크기의 Self Attention을 구한다. 이를 이용하여 다음 레이어가 될 텐서들을 만드는데, 예를 들면, [CLS] 자리의 텐서는 L1[CLS] * [CLS]-[CLS] + L1A1 * [CLS]-A1 + ... + L1Bm * [CLS]-Bm이 된다. 자세한 과정은 Self Attention에 대해 더 찾아보길 권장한다.

그 뒤로 선형 변환과 활성 함수를 적용하고 배치 정규화까지 적용하면 비로소 다음 레이어의 텐서가 완성된다. 일반적으로 여기까지 해야 Transformer를 적용했다고 본다.

3.3. Multi-Head란?



그런데 BERT는 여기에서 조금 더 나아가 Multi-Head Transformer를 사용하므로 위 과정 자체를 여러 번 거친다. 앞선 설명에서는 각각의 토큰 위치에 해당하는 텐서가 768차원이라고 이야기하였지만, 실제로는 64차원이다. 그 이유는 일련의 과정들을 거치기 전에 먼저 768차원의 텐서를 12개의 64차원 텐서로 나누고, 각각에 대해 앞서 설명한 연산들을 수행하고, 최종적으로 이들을 다시 합쳐 768차원의 텐서로 만들기 때문이다. 이는 하나의 Attention이 모든 종류의 정보에 대해 유동적인 중요도를 나타내기 힘들기 때문인데, 예를 들어, 문장에서 고유명사에 높은 가중치를 주는 것과 조사에 높은 가중치를 주는 것과 긍정적인 표현에 높은 가중치를 주는 작업 등 Attention에게 기대하는 다양한 기능들이 있는데 이들을 하나의 Attention이 모두 잘해내기는 어렵다. 이에 저자들은 입력을 12개의 Head로 나누고 각각에 대해 Attention을 따로 구하여 모델이 최대한 다양한 방향의 Attention을 활용할 수 있도록 하였다. 물론, 비슷한 이유로 CNN에서도 filter를 여러 개 사용하는데 이때는 차원을 쪼개지는 않는다는 점에서 BERT의 방식에 의문이 들 수 있다. 내가 추측하기로는 아마 Parameter가 과도하게 많아져 모델이 너무 무거워지는 건 아닌가 하는 걱정과 768차원이 이미 너무 커서 64차원씩 쪼개도 충분히 의미가 있다는 믿음을 기반으로 하지 않았나 싶다.

4. 성능



언어 이해에 관련된 여러가지 Task를 수행해야한다.

질문이 오면 문서 내에서 어느 부분에 정답이 있는지 구간의 시작과 끝을 찾아야한다.

v1.1에 비해 데이터도 늘어났으며, 답이 없는 경우 답이 없다는 출력 또한 내야한다.

주어진 문장 뒤에 이어지기 자연스러운 문장을 선택지 중에 하나 고르는 Task이다.

5. 읽을거리



[1] North American Chapter of the Association for Computational Linguistics