Home BERT 사전학습
Post
Cancel

BERT 사전학습

이 절에서는 BERT를 사전학습 시키는 방법을 알아본다. 그런데 사전 학습이란 무엇일까? 모델을 하나 학습시켜야 된다고 가정해보자. 일단 특정 태스크에 대한 방대한 데이터셋으로 모델을 학습시키고 학습된 모델을 저장한다. 그 다음으로, 새 태스크가 주어지면 임의 가중치로 모델을 초기화하는 대신 이미 학습된 모델의 가중치로 모델을 초기화한다. 즉, 모델이 이미 대규모 데이터셋에서 학습되었으므로 새 태스크를 위해 새로운 모델로 처음부터 학습시키는 대신 사전 학습된 모델을 사용하고 새로운 태스크에 따라 가중치를 조정한다. 이런 방식이 사전학습의 대표적인 유형이다.

BERT는 MLM과 NSP라는 두 가지 재미있는 태스크를 이용해 거대한 말뭉치를 기반으로 사전 학습된다. 사전 학습 후 사전 학습된 BERT를 저장해두고, 새로운 태스크가 주어질 경우 BERT를 처음부터 학습시키는 대신 사전 학습된 BERT를 사용한다. 즉, 사전 학습된 BERT를 기반으로 새 태스크에 대한 가중치를 조정 (파인 튜닝)한다.

이제 BERT가 어떻게 사전 학습되는지 자세히 알아볼 것이다. 그전에 먼저 BERT가 허용하는 방식으로 입력 데이터를 구조화하는 방법부터 살펴보자.

2.4.1 BERT의 입력 표현

BERT에 데이터를 입력하기 전에 다음 세 가지 임베딩 레이어를 기반으로 입력 데이터를 임베딩으로 변환해야 한다.

  • 토큰 임베딩 (token embedding)
  • 세그먼트 임베딩 (segment embedding)
  • 위치 임베딩 (position embedding)

토큰 임베딩

먼저, 토큰 임베딩 레이어 차례다. 다음 두 문장으로 살펴보자.

A 문장: Paris is a beautiful city.

B 문장: I love Paris.

먼저 여기에 표시된 것처럼 두 문장 모두 토큰화해 토큰들을 추출한다. 이 예에서는 토큰을 소문자로 변환하지 않을 것이다.

다음으로, 첫 번째 문장의 시작 부분에만 [CLS] 토큰이라는 새 토큰을 추가한다.

1
tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "city", "I", "love", "Paris"]

그런 다음 모든 문장 끝에 [SEP]라는 새 토큰을 추가한다.

1
tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "city", "[SEP]",  "I", "love", "Paris", "[SEP]"]

[CLS] 토큰은 첫 번째 문장의 시작 부분에만 추가되고 [SEP] 토큰은 모든 문장의 끝에 추가한다. [CLS] 토큰은 분류 작업에 사용되며 [SEP] 토큰은 모든 문장의 끝을 나타내는 데 사용된다. 이 두 스페셜 토큰인 [CLS]와 [SEP]가 어떤 기능을 수행하는지 이 장에서 자세히 알아볼 것이다.

이제 모든 토큰을 BERT에 입력하기 전에 토큰 임베딩이라는 임베딩 레이어를 사용해 토큰을 임베딩으로 변환한다. 토큰 임베딩의 변수들은 사전학습이 진행되며 학습된다. [그림 2-8]에서 볼 수 있듯이 모든 토큰에 대한 임베딩이 있다. 즉, $E_{cls}$는 [CLS] 토큰의 임베딩을 나타내며, $E_{paris}$는 Paris 토큰의 임베딩을 나타낸다.

그림 2-8 토큰 임베딩

세그먼트 임베딩

세그먼트 임베딩은 주어진 두 문장을 구별하는 데 사용되낟. 앞에서 활용된 문장으로 세그먼트 임베딩을 살펴보자.

1
tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "city", "[SEP]",  "I", "love", "Paris", "[SEP]"]

이제 [SEP] 토큰과 별도로 두 문장을 구분하기 위해 모델에 일종의 지표를 제공해야 한다. 이를 위해 세그먼트 임베딩 레이어에 입력 토큰을 제공한다.

세그먼트 임베딩 레이어는 입력에 대한 출력으로 $E_A, E_B$만 반환한다. 입력 토큰이 A 문장에 속하면 $E_A$에 매핑되고, B 문장에 속하면 $E_B$에 매핑된다.

[그림 2-9]에 표시된 대로 문장 A의 모든 토큰은 $E_A$에 매핑되고, B 문장의 모든 토큰은 $E_B$에 매핑된다.

그림 2-9 세그먼트 임베딩: 문장이 2개인 경우

그럼 문장이 하나만 있는 경우 세그먼트 임베딩은 어떻게 될까? “Paris is a beautiful city”라는 문장만 있다고 가정하면, 다음과 같이 문장의 모든 토큰이 $E_A$에 매핑된다.

그림 2-10 세그먼트 임베딩: 문장이 1개인 경우

위치 임베딩

다음으로 위치 임베딩이 있다. 전 장에서 트랜스포머가 어떤 반복 메커니즘도 사용하지 않고 모든 단어를 병렬로 처리하므로 단어 순서와 관련된 정보를 제공해야 한다는 것을 배웠다. 이때 위치 인코딩을 사용했다.

BERT는 본질적으로 트랜스포머의 인코더이므로 BERT에 데이터를 직접 입력하기 전에 문장에서 단어 (토큰)의 위치에 대한 정보를 제공해야 한다. 결국 우리는 위치 임베딩이라는 레이어를 사용해 문장의 각 토큰에 대한 위치 임베딩 출력을 얻게 된다.

[그림 2-11]에서 [CLS]의 위치 임베딩인 $E_0$와 Paris 토큰의 위치 임베딩인 $E_1$ 등을 확인할 수 있다.

그림 2-11 위치 임베딩

최종 입력 데이터 표현

이제 최종 입력 데이터 표현을 살펴보자. [그림 2-12]에 표시된 것처럼 먼저 주어진 입력 문장을 토큰으로 변환하고 토큰을 토큰 임베딩, 세그먼트 임베딩, 위치 임베딩 레이어에 공급하고 임베딩을 얻는다. 그 다음으로 모든 임베딩을 합산헤 BERT에 입력으로 제공한다.

그림 2-12 입력의 최종 표현

지금까지 세 가지 임베딩 레이어를 사용해 입력을 임베딩으로 변환하는 방법을 배웠다. 다음으로 BERT에서 사용하는 워드피스 토크나이저에 대해 알아보자.

워드피스 토크나이저

BERT는 워드피스 토크나이저라는 특별한 유형의 토크나이저를 사용하며, 이는 하위 단어 토큰화 알고리즘을 기반으로 한다. 예제를 통해 워드피스 토크나이저가 어떻게 작동하는지 이해해보자. 먼저 다음 문장이 주어졌다고 가정하자.

Let us start pretraining the model.

워드피스 토크나이저를 사용해 문장을 토큰화하면 다음과 같은 토큰을 얻을 수 있다.

1
tokens = ["let", "us", "start", "pre", "##train", "##ing", "the", "model"]

워드피스 토크나이저를 사용해 문장을 토큰화하면 개별 단어가 pre, ##train, ##ing와 같은 하위단어(subword)로 분할되는 것을 볼 수있따. 왜 하위 단어로 분할하는 것일까?

BERT는 워드피스 토크나이저를 사용해 토큰화할 때 단어가 어휘 사전에 있는지 확인한다. 단어가 어휘 사전에 있으면 그 단어를 토큰으로 사용하고, 단어가 어휘 사전에 없으면 그 단어를 하위 단어로 분할해 하위 단어가 어휘 사전에 있는지 확인한다. 하위 단어가 어휘 사전에 있으면 이를 토큰으로 사용한다. 만약 하위 단어가 어휘 사전에 없으면 다시 하위 단어로 분할하여 다시 하위 단어로 분할한다. 이런 식으로 개별 문자에 도달할 때 까지 어휘 사전을 기반으로 하위 단어를 계속 분할하고 확인한다. 이 방식은 어휘 사전 이외(out-of-vocabulary, OOV)의 단어를 처리하는데 효과적이다.

BERT 어휘 사전 크기는 3만 토큰이다. 입력 단어가 3만 토큰에 속하면 이를 토큰으로 사용하고 그렇지 않으면 하위 단어로 분할하여 하위 단어가 이 3만 토큰에 속하는지 확인한다. 알고리즘은 개별 문자에 도달할 때까지 어휘 사전(3만 토큰)으로 하위 단어를 계속 분할하고 확인한다.

이 예에서 pretraining이라는 단어는 BERT의 어휘사전에 없기 때문에 이 단어를 pre, ##train, ##ing와 같은 하위단어로 나눈다. ##train과 ##ing 토큰 앞의 해시 기호는 하위 단어임을 나타내고 앞에 다른 단어가 있음을 의미한다. 이제 어휘 사전에 ##train과 ##ing하위 단어가 있는지 확인하고, 이들은 어휘 사전에 존재하기 때문에 다시 나누지 않고 토큰으로 사용한다.

결국, 워드피스 토크나이저를 사용해 다음과 같은 토큰들을 얻게 된다.

1
tokens = ["let", "us", "start", "pre", "##train", "##ing", "the", "model"]

이후 문장 시작 부분에 [CLS] 토큰을 추가하고 문장 끝 부분에 [SEP] 토큰을 추가한다.

앞에서 배운 것처럼 입력 토큰을 토큰, 세그먼트, 위치 임베딩 레이어에 입력해 각 임베딩을 얻고 이들 임베딩을 합한 다음 BERT에 입력한다. 워드피스 토크나이저의 작동 방식과 어휘 사전 구축 방식은 2.5절 다른 토크나이저와 함께 자세히 다룬다.

2.4.2 학습 전략

BERT는 다음 두 가지 태스크에 대해 사전 학습된다.

  1. 마스크 언어 모델링 (Masked Language Modeling, MLM)
  2. 다음 문장 예측 (Next Sentence Prediction, NSP)

이 두 가지 학습 전략을 차례로 살펴 봄으로써 어떻게 작동하는지 이해해보자. MLM 태스크를 설명하기 전에 먼저 언어 모델링 태스크를 살펴본다.

언어 모델링

언어 모델링 (Language Modeling)은 일반적으로 임의의 문장이 주어지고 단어를 순서대로 보면서 다음 단어를 예측하도록 모델을 학습하는 것이다. 언어 모델링은 다음 두 가지로 분류할 수 있다.

  • 자동 회귀 언어 모델링 (auto-regressive language modeling)
  • 자동 인코딩 언어 모델링 (auto-encoding language modeling)

    ### 자동 회귀 언어 모델링

    자동 회구 언어 모델링은 다시 다음 두 가지 방식으로 구분할 수 있다.

    • 전방 (왼쪽에서 오른쪽으로) 예측 (forward(left-to-right) prediction)
    • 후방 (오른쪽에서 왼쪽으로) 예측 (backward(right-to-left)) prediction)

    이 두 가지 방법이 어떻게 동작하는지 예제로 살펴보자. “Paris is a beautiful city. I love Paris” 라는 문장이 주어졌을 때 다음과 같이 “city”라는 단어를 제거하고 공백을 추가해본다.

    Paris is a beautiful ___. I love Paris.

    이제 모델은 공백을 예측해야 한다. 전방 예측을 사용하는 경우 모델은 예측을 사용하기 위해 다음과 같이 왼쪽에서 오른쪽으로 공백까지의 모든 단어를 읽는다.

    Paris is a beautiful ___.

    후방 예측을 사용하면 예측을 수행하기 위해 모델은 다음과 같이 오른쪽에서 왼쪽으로 공백까지 모든 단어를 읽는다.

    ___. I love Paris.

    자동 회귀 모델은 원래 단방향이므로 한 방향으로만 문장을 읽는다.

마스크 언어 모델링 (MLM)

BERT는 자동 인코딩 언어 모델로, 예측을 위해 문장을 양방향으로 읽는다. 마스크 언어 모델링은 주어진 입력 문장에서 전체 단어의 15%를 무작위로 마스킹하고 마스크된 단어를 예측하도록 모델을 학습시키는 것이다. 마스크된 단어를 예측하기 위해 모델은 양방향으로 문장을 읽고 마스크된 단어를 예측하려 시도한다.

마스크 언어 모델링이 어떻게 작동하는지 예제를 통해 살펴보자. 앞에서 살펴본 문장 (Paris is a beautiful city, I love Paris)을 토큰화한다.

1
tokens = ["Paris", "is", "a", "beautiful", "city", "I", "love", "Paris"]

첫 번째 문장의 시작 부분에 [CLS] 토큰을 추가하고 문장 끝에 [SEP] 토큰을 추가한다.

1
tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "city", "[SEP]",  "I", "love", "Paris", "[SEP]"]

다음으로, 토큰의 15%를 무작위로 마스킹한다. 다음과 같이 city라는 단어를 마스킹한 다음 [MASK] 토큰으로 바꾼다.

1
tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "[MASK]", "[SEP]",  "I", "love", "Paris", "[SEP]"]

“city”라는 단어를 [MASK] 토큰으로 대체했다. 이젠 마스크된 토큰을 예측하기 위한 BERT를 학습시킨다.

여기에 작은 문제가 있다. 위와 같은 방식으로 토큰을 마스킹하면 사전학습과 파인튜닝 사이에 불일치가 생기게 된다. [MASK] 토큰을 예측해 BERT를 사전학습시키고, 학습 시킨 후에는 감정 분석과 같은 다운스트림 태스크를 위해 사전 학습된 BERT를 파인 튜닝한다. 그런데 파인 튜닝에는 입력에 [MASK] 토큰이 없다. 이 때문에 BERT가 사전 학습되는 방식과 파인 튜닝에 사용되는 방식 간에 불일치가 발생한다.

이 문제를 극복하기 위해 80-10-10% 규칙을 사용한다. 문장에서 토큰의 15%를 무작위로 마스킹한다는 것을 확인했다. 그럼 15% 토큰에 대해 다음을 수행한다.

  • 15% 중 80%의 토큰(실제 단어)를 [MASK] 토큰으로 교체한다. 적용 결과는 다음과 같다.

    1
    
      tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "[MASK]", "[SEP]",  "I", "love", "Paris", "[SEP]"]
    
  • 15% 증 10%의 토큰(실제 단어)을 임의의 토큰(임의 단어)로 교체한다. 적용 결과는 다음과 같다.

    1
    
      tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "love", "[SEP]",  "I", "love", "Paris", "[SEP]"]
    
  • 15% 중 나머지 10%의 토큰은 어떤 변경도 하지 않는다. 적용결과는 다음과 같다.

    1
    
      tokens = ["[CLS]", "Paris", "is", "a", "beautiful", "city", "[SEP]",  "I", "love", "Paris", "[SEP]"]
    

토큰화 및 마스킹 후에 입력 토큰을 토큰, 세그먼트, 위치 임베딩 레이어에 입력해 입력 임베딩을 얻는다.

이제 이 입력 임베딩을 BERT에 제공한다.

[그림 2-13]과 같이 BERT는 입력을 받은 다음 각 토큰의 표현 벡터를 출력으로 반환한다. $R_{CLS}$는 [CLS]토큰의 표현 벡터를 의미하고, $R_{Paris}$는 Paris토큰의 표현 벡터를 의미한다. 이 예에서는 12개의 인코더 레이어, 12개의 어텐션 헤드, 768개의 은닉 유닛이 있는 BERT-base를 사용한다. BERT-base 모델을 사용하기 때문에 각 토큰의 표현 벡터 크기는 768이 된다.

그림 2-13 BERT

[그림 2-13]에서 각 토큰의 표현 $R$을 얻었다. 이제 이러한 표현으로 마스크된 토큰을 어떻게 예측하게 될까?

마스크된 토큰을 예측하기 위해 BERT에서 반환된 마스크된 토큰 $R_{MASK}$의 표현을 소프트맥스 활성화를 통해 피드포워드 네트워크에 입력한다. 피드포워드 네트워크는 다음그림과 같이 $R_{MASK}$ 단어가 마스크된 단어가 될 확률을 반환한다. 여기서는 복잡함을 줄이기 위해 입력 임베딩 레이어 (토큰, 세그먼트, 위치)를 표시하지 않았다.

그림 2-14 마스크된 토큰 예측

[그림 2-14]에서 볼 수 있듯이 “city”라는 단어가 마스크된 단어일 확률이 높다. 이 경우 마스크된 단어는 “city”로 예측된다.

학습 초기에는 BERT의 피드포워드 네트워크 및 인코더 계층의 가중치가 최적이 아니므로 모델이 올바른 확률을 반환하지 않는다. 그러나 역전파를 통한 일련의 반복 과정을 거치며 BERT 피드포워드 네트워크 및 인코더 계층의 가중치 업데이트가 반복되면서 최적의 가중치를 학습하게 된다.

마스크 언어 모델링 태스크는 빈칸채우기 태스크 (cloze task)라고도 한다. 지금까지 마스크 언어 모델링 태스크가 작동하는 방식과 마스크 언어 모델링 태스크를 사용해 BERT를 학습시키는 방법을 배웠다. 이제 좀 더 어려운 전체 단어 마스킹 방법을 알아보자.

전체 단어 마스킹(WWM)

전체 단어 마스킹 (Whole Word Masking, WWM)이 어떻게 동작하는지 예제를 통해 이해해보자. “Let us start pretraining the model” 이라는 문장을 예로 들어보자. BERT는 워드피스 토크나이저를 사용하므로 워드피스 토크나이저를 통해 문장을 토큰화하면 다음과 같은 토큰을 얻게 된다.

1
tokens = ["let", "us", "start", "pre", "##train", "##ing", "the", "model"]

문장 시작 부분에 [CLS] 토큰을 추가하고 문장 끝부분에 [SEP] 토큰을 추가한다.

1
tokens = ["[CLS]", "let", "us", "start", "pre", "##train", "##ing", "the", "model", "[SEP]"]

마지막으로 단어의 15%를 무작위로 마스킹하는데, 마스킹 결과가 다음과 같다고 가정해보자.

1
tokens = ["[CLS]", "[MASK]", "us", "start", "pre", "[MASK]", "##ing", "the", "model", "[SEP]"]

let과 ##train이라는 단어를 마스킹했다. ##train이라는 단어는 하위 단어로 사전 학습이라는 단어의 일부다. WWM 방법에서는 하위 단어가 마스킹되면 해당 하위 단어와 관련된 모든 단어를 마스킹한다. 따라서 이제 토큰 리스트는 다음과 같아진다.

1
tokens = ["[CLS]", "[MASK]", "us", "start", "[MASK]", "[MASK]", "[MASK]", "the", "model", "[SEP]"]

##train 하위 단어와 관련된 모든 단어들이 마스킹되었다. WWM의 경우 하위 단어가 마스킹되면 하위 단어와 관련된 모든 단어를 마스킹하면서 마스크 비율(15%)을 유지하려 한다. 따라서 하위 단어와 관련된 모든 단어를 마스킹하는 동안 마스킹 비율이 15%를 초과하면 다른 단어의 마스킹을 무시한다. 다음과 같이 마스킹 비율을 유지하기 위해 let의 마스킹을 무시했다.

1
tokens = ["[CLS]", "let", "us", "start", "[MASK]", "[MASK]", "[MASK]", "the", "model", "[SEP]"]

이러한 방식으로 WWM을 기반으로 토큰을 마스킹한다. 마스킹한 후 토큰을 BERT에 입력하고 앞서 배운 것처럼 마스크된 토큰을 예측하도록 모델을 학습시킨다.

다음 문장 예측(NSP)

NSP는 BERT학습에 사용되는 또 다른 흥미로운 태스크로, 이진 분류 태스크이다. NSP 태스크에서는 BERT에 두 문장을 입력하고 두 번째 문장이 첫 번째 문장의 다음 문장인지 예측한다. 예제를 통해 NSP 태스크를 이해해보자.

다음 두 문장이 주어졌다고 가정해보자.

A: She cooked pasta.

B: It was delicious.

이 문장 쌍에서 B 문장은 A 문장의 후속 문장이다. 즉, A 문장에서 이어지는 문장이다. 따라서 이 문장 쌍을 isNext로 표시해 B 문장이 A 문장의 다음 문장임을 알 수 있게 한다.

다시 다음 문장이 주어졌다고 가정해보자.

A: Turn the radio on.

B: She bought a new hat.

이 문장 쌍에서 B 문장은 A 문장의 후속 문장이 아니다. 즉, A 문장에 이어지는 문장이 아니다. 따라서 이 문장 쌍을 notNext로 표시해 B 문장이 A 문장의 다음 문장이 아님을 알 수 있게 한다.

NSP 태스크에서 모델의 목표는 문장 쌍이 isNext 범주에 속하는지 여부를 예측하는 것이다. 문장 쌍 (문장 A 및 B)을 BERT에 입력하고 B 문장이 A 문장 다음에 오는지 여부를 예측하도록 학습시킨다. 모델은 B문장이 A문장에 이어지면 isNext를 반환하고, 그렇지 않으면 notNext를 반환한다. 따라서 NSP는 본질적으로 이진 분류 태스크이다.

NSP 태스크의 목적은 무엇일까? NSP 태스크를 수행함으로써 모델은 두 문장 사이의 관계를 파악할 수 있다. 두 문장 간의 관계를 이해하는 것은 질문-응답 및 유사문장탐지와 같은 다운스트림 태스크에서 유용하다.

그럼, NSP 태스크를 위한 데이터셋을 어떻게 얻을 수 있을까? 어떠한 말뭉치에서도 데이터셋을 확보할 수 있다. 2개의 문서가 있다고 가정해보자. isNext 클래스의 경우 한 문서에서 연속된 두 문장을 isNext로 표시하고 notNext 클래스의 경우 한 문서에서 한 문장을, 임의의 문서에서 다른 문장을 가져와 notNext로 표시하면 된다. isNext 클래스를 전체의 50% 비율로 유지하고 notNext 클래스에서 나머지 50%를 유지해 클래스가 균형을 이룰 수 있도록 한다.

이제 NSP 태스크가 무엇인지 알았으니, NSP 태스크를 수행하기 위해 BERT를 학습시키는 방법을 살펴보겠다. 데이터셋이 [그림 2-15]와 같이 나타난다고 가정해보자.

그림 2-15 간단한 데이터셋

[그림 2-15]에 제시된 첫 번째 데이터를 살펴보자. 먼저 다음과 같이 문장 쌍을 토큰화한다.

1
tokens = ["She", "cooked", "pasta", "It", "was", "delicious"]

첫 번째 문장의 시작 부분에 [CLS] 토큰을 추가하고 모든 문장의 끝에 [SEP] 토큰을 추가한다.

1
tokens = ["[CLS]", "She", "cooked", "past", "[SEP]", "it", "was", "delicious", "[SEP]"]

이 토큰들을 토큰, 세그먼트, 위치 임베딩 레이어에 입력하고 입력 임베딩을 반환받는다. 그런 다음 입력 임베딩을 BERT에 넣어 각 토큰의 표현을 얻는다. [그림 2-16]에서 볼 수 있듯 토큰 $R_{CLS}$는 [CLS]의 표현을 나타내고 $R_{she}$는 She 토큰의 표현을 나타낸다.

그림 2-16 BERT

좀 전에 NSP가 이진 분류 작업이라고 배웠는데, 지금 우리는 문장 쌍에서 각 토큰의 표현만 가지고 있다. 이러한 표현을 기반으로 문장 쌍을 어떻게 분류할 수 있을까?

분류를 수행하려면 간단히 [CLS] 토큰 표현을 가져와 소프트맥스 함수를 사용해 피드포워드 네트워크에 입력한다. 그러면 문장쌍이 isNext인지, notNext인지에 대한 확률값이 반환된다. 그렇다면 왜 [CLS] 토큰만 포함시켜야 할까? 다른 토큰의 임베딩이 아닌 이유는 무엇일까?

[CLS] 토큰은 기본적으로 모든 토큰의 집계 표현을 보유하고 있으므로 문장 전체에 대한 표현을 담고 있다. 따라서 다른 모든 토큰의 표현을 무시하고 [CLS] 토큰 표현 $R_{CLS}$를 가져와 확률을 반환하는 소프트맥스 함수를 사용해 피드포워드 네트워크에 공급할 수 있다. 이러한 내용이 [그림 2-17]에 포현되어 있다. 여기서는 복잡함을 줄이기 위해 입력 임베딩 레이어(토큰, 세그먼트, 임베딩 레이어)를 표시하지 않았다.

그림 2-17 NSP 태스크

[그림 2-17]의 피드포워드 네트워크는 입력 문장이 isNext 클래스에 속할 확률이 높다는 것을 보여준다.

학습 초기에는 피드포워드 네트워크 및 인코더 계층의 가중치가 최적이 아니기 때문에 모델이 올바른 확률을 반환하지 못할 것이다. 그러나 역전파를 기반으로 한 일련의 반복학습을 통해 피드포워드 네트워크의 가중치와 BERT 인코더 계층의 가중치를 업데이트하고 최적의 가중치를 학습하게 된다.

2.4.3 사전 학습 절차

BERT의 사전 학습에는 토론토 책 말뭉치 및 위키피디아 데이터셋을 사용한다. 앞서 BERT는 MLM 및 NSP 태스크를 사용해 사전 학습된다는 것을 배웠다. 그럼, 이 두 태스크를 사용해 BERT를 학습시키기 위한 데이터셋을 어떻게 준비할까?

먼저 말뭉치에서 두 문장을 샘플링한다. A와 B문장을 샘플링했다고 가정해보자. A와 B 문장의 총 토큰 수의 합은 512보다 작거나 같아야 한다. 두 문장을 샘플링할 때 전체의 50%는 B 문장이 A 문장의 후속 문장이 되도록 샘플링하고, 나머지 50%는 B 문장을 A 문장의 후속 문장이 아닌 것으로 샘플링한다.

다음 두 문장을 샘플링했다고 가정하자.

A 문장: We enjoyed the game.

B 문장: Turn the radio on.

먼저 워드피스 토크나이저를 통해 문장을 토큰화하고 첫 번째 문장 시작 부분에 [CLS] 토큰을 추가한 다음 모든 문장의 끝에 [SEP] 토큰을 추가해, 다음과 같은 토큰 리스트를 얻는다.

1
tokens = ["[CLS]", "we", "enjoyed", "the", "game", "[SEP]", "turn", "the", "radio", "on", "[SEP]"]

다음 과정으로, 80-10-10% 규칙에 따라 토큰의 15%를 무작위로 마스킹한다. game 토큰을 마스킹했다고 가정하면 다음과 같다.

1
tokens = ["[CLS]", "we", "enjoyed", "the", "[MASK]", "[SEP]", "turn", "the", "radio", "on", "[SEP]"]

이제 토큰을 BERT에 입력하고 마스크된 토큰을 예측하기 위해 모델을 학습시키며 동시에 B 문장이 A 문장의 후속 문장인지 여부를 분류하게 한다. 즉, MLM과 NSP 작업을 동시에 사용해 BERT를 학습시킨다.

BERT는 총 100만 스텝을 학습시키고, 각 스텝당 크기 256 입력 시퀀스에 대해 학습시킨다. 학습률은 $lr=1e-4, \beta_1=0.9, \beta_2=0.999$로 설정하고 아담 옵티마이저를 사용하며, 웜업은 1만 스텝으로 학습을 진행한다. 여기서 웜업 스텝은 무엇일까?

학습이 진행되면, 높은 학습률을 설정해 학습 초기에 모델의 큰 변화를 유도하고 학습 후반에는 낮은 학습률을 설정해 모델에 작은 변화를 주어 최적화한다. 학습 초기에는 수렴과 거리가 멀기 때문에 모델에 과감한 변화를 주지만 이후에는 수렴에 가까워지기 때문에 큰 변화보다 작은 변화를 주어 모델을 최적화 하는 것이다. 이와 같이 학습 초기에 학습률 값을 높게 설정한 다음 학습이 진행되면서 학습률을 감소시키는 것을 학습률 스케줄링이라고 한다.

웜업 스텝은 학습률 스케줄링의 일부다. 학습률이 $lr=1e-4$고 웜업 스텝이 총 1만 스텝이라고 가정하면, 초기 1만 스텝은 학습률이 0에서 $1e-4$로 선형적으로 증가한다는 것을 의미한다. 1만 스텝 후에는 수렴에 가까워짐에 따라 학습률을 선형적으로 감소시키게 된다.

또한 드롭아웃확률이 0.1인 모든 레이어에 드롭아웃을 적용한다. BERT에서는 GELU라는 활성화 함수를 사용하는데, 이는 가우시안 오차 선형 유닛(Gaussian Error Linear Unit)을 의미한다.

GELU 함수는 다음과 같다.

\[GELU(x)=x\phi(x)\]

$\phi(x)$는 표준 가우시안 누적 분포(standard Gaussian cummulative distribution)함수이며, GELU 함수는 다음 수식의 근사치다.

\[GELU(x)=0.5x(1+\tanh(\sqrt{2\over\pi}(x+0.044715x^3)))\]

[그림 2-18]은 GELU 함수를 도식화 한 것이다.

그림 2-18 GELU 활성화 함수

이게 전부다. 이러한 방식으로 MLM 및 NSP 태스크를 사용해 BERT를 사전 학습시킬 수 있다. 사전 학습된 BERT는 다양한 태스크에 이용할 수 있다.

This post is licensed under CC BY 4.0 by the author.

BERT의 구조

Google AI4Code