본문 바로가기

Deep Learning/NLP

BERT

자료 출처

 

 

1. BERT의 개요

BERT(Bidirectional Encoder Representations from Transformers)는 트랜스포머를 이용하여 구현되었으며, 위키피디아(25억 단어)와 BooksCorpus(8억 단어)와 같은 레이블이 없는 텍스트 데이터로 사전 훈련된 언어 모델이다.

 

BERT가 높은 성능을 얻을 수 있었던 것은 레이블이 없는 방대한 데이터로 사전 훈련하고 레이블이 있는 다른 작업에서 Fine-tuning을 하였기 때문이다.

 

 

2. BERT의 크기

 

BERT의 기본 구조는 트랜스포머 인코더를 쌓아올린 구조이다. Base에서는 12개, Large에서는 24개를 쌓았다. 그 외에도 Large는 Base보다 $d_{model}$의 크기나 멀티 헤드 어텐션의 헤드 수가 더 크다. 트랜스포머 인코더 층의 수를 L, $d_{model}$의 크기를 D, 헤드 수를 A라고 하였을 때 각각의 크기는 다음과 같다.

 

  • Base : L=12, D=768, A=12 : 110M개의 파라미터
  • Large : L=24, D=1024, A=16 : 340M개의 파라미터

 

BERT-Base는 GPT-1과 하이퍼파라미터가 동일한데, 이는 BERT 연구진이 GPT-1과 성능을 비교하기 위해 동등한 크기로 설계하였기 때문이다. 반면, BERT-Large는 BERT의 최대 성능을 보여주기 위해 만들어진 모델이다.

 

편의를 위해 뒤에 이어지는 내용들은 Base를 기준으로 설명한다.

 

 

3. BERT의 문맥을 반영한 임베딩

BERT는 ELMo난 GPT-1과 마찬가지로 문맥을 반영한 임베딩(Contextual Embedding)을 사용한다.

 

 

$d_{model}$을 768로 정의하였으므로, 모든 단어들은 768차원의 임베딩 벡터가 되어 BERT의 입력으로 사용된다. BERT는 내부 연산을 거친후 동일하게 각 단어에 대해서 768차원의 벡터를 출력한다. 위 그림은 BERT가 768차원의 [CLS], I, love, you라는 4개의 벡터를 입력받아서(입력 임베딩) 동일하게 768차원의 4개의 벡터를 출력하는 모습(출력 임베딩)을 보여준다.

 

 

출력 임베딩은 문장의 문맥을 모두 참고한 문맥을 반영한 임베딩이 된다.

 

 

하나의 단어가 모든 단어를 참고하는 연산은 BERT의 12개의 층에서 전부 이루어지는 연산이다. BERT의 첫번째 층의 출력 임베딩은 두번째 층의 입력 임베딩으로 사용된다.

 

BERT가 모든 단어들을 참고하여 문맥을 반영한 출력 임베딩을 얻게 되는 이유는 당연히 셀프 어텐션이다.

 

 

4. BERT의 서브워드 토크나이저 : WordPiece

WordPiece토크나이저는 바이트 페어 인코딩과 유사한 방법으로 학습되지만 빈도 기반이 아닌 확률 기반으로 글자 쌍을 병합한다. 조금 더 자세한 내용은 이전 포스팅을 참고

2024.01.08 - [책/파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습] - 05 토큰화 - 하위 단어 토큰화

 

05 토큰화 - 하위 단어 토큰화

하위 단어 토큰화 현대 자연어 처리에서는 신조어의 발생, 오탈자, 축약어 등을 고려해야 하기 때문에 분석할 단어의 양이 많아져 어려움을 겪는다, 이를 해결하기 위한 방법 중 하나로 하위 단

ai-junha.tistory.com

 

transformers를 통해 BERT 토크나이저를 사용할 수 있다.

import pandas as pd
from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") # Bert-base의 토크나이저

 

'Here is the sentence I want embeddings for.'라는 문장을 어떻게 토큰화하는지 보자.

result = tokenizer.tokenize('Here is the sentence I want embeddings for.')
print(result)
['here', 'is', 'the', 'sentence', 'i', 'want', 'em', '##bed', '##ding', '##s', 'for', '.']

 

embeddings라는 단어는 단어 지합에 존재하지 않으므로 서브워드로 분리된다. 실제로 BERT의 단어 집합에 특정 단어가 있는지 조회하려면 토크나이저의 vocab에 접근한다.

print(tokenizer.vocab['embeddings'])
KeyError: 'embeddings'

 

그러나 'em'은 존재한다.

print(tokenizer.vocab['em'])
7861

 

참고로 BERT에서 사용되는 특수 토큰들과 매핑되는 정수는 다음과 같다.

 

  • [PAD] : 0
  • [UNK] : 100
  • [CLS] : 101
  • [SEP] : 102
  • [MASK] : 103

 

 

5. Position Embedding

트랜스포머에서는 포지셔널 인코딩으로 단어의 위치 정보를 표현하였다. BERT는 학습을 통해서 얻는 포지션 임베딩 방법을 사용한다.

 

위 그림은 포지션 임베딩을 사용하는 방법을 보여준다. 포지션 임베딩의 아이디어는 위치 정보를 위한 Embedding Layer를 하나 더 사용한다. 가령, 문장의 길이가 4개라면 4개의 포지션 임베딩 벡터를 학습시킨다. 그리고 BERT의 입력마다 다음과 같이 포지션 임베딩 벡터를 더해주는 것이다.

 

  • 첫번째 단어의 임베딩 벡터 + 0번 포지션 임베딩 벡터
  • 두번째 단어의 임베딩 벡터 + 1번 포지션 임베딩 벡터
  • 세번째 단어의 임베딩 벡터 + 2번 포지션 임베딩 벡터
  • 네번째 단어의 임베딩 벡터 + 3번 포지션 임베딩 벡터

 

BERT에서는 문장의 최대 길이를 512로 하고 있으므로, 총 512개의 포지션 임베딩 벡터가 학습된다. 

 

BERT는 세그먼트 임베딩(Segment Embedding)이라는 1개의 임베딩 층을 더 사용하는데, 이는 뒤에서 언급한다.

 

 

6. BERT의 사전 훈련

 

위의 그림은 BERT, GPT-1, ELMo의 구조적인 차이를 보여준다. GPT의 경우 트랜스포머의 디코더를 이전 단어들로부터 다음 단어를 예측하는 방식으로 단방향 언어 모델을 만들었다. 반면에 BERT는 화살표가 양방향으로 뻗어나가는 모습을 보여준다. 이는 마스크드 언어 모델(Masked LM)을 통해 양방향성을 얻었기 때문이다.

 

BERT의 사전 훈련 방법은 마스크드 언어 모델다음 문장 예측(Nest Sentence Prediction, NSP)로 2가지이다.

 

6.1 Masked Language Model

BERT는 사전 훈련을 위해 입력으로 들어가는 입력 텍스트의 15%의 단어를 랜덤으로 마스킹한다. 그리고 모델이 마스킹된 단어들을 예측하도록 한다. 예를 들어 '나는 [MASK]에 가서 빵과 [MASK]를 샀다'를 입력으로 하고 '슈퍼'와 '우유'를 맞추게 한다.

 

더 정확히는 전부 [MASK]로 변경하지는 않고, 선택된 15%의 단어들은 다시 다음과 같은 비율로 규칙이 적용된다.

 

  • 80%의 단어들은 [MASK]로 변경.
  • 10%의 단어들은 랜덤으로 단어가 변경된다.
  • 10%의 단어들은 동일하게 둔다.

 

이렇게 하는 이유는 [MASK]만 사용할 경우에는 [MASK] 토큰이 파인 튜닝 단계에서는 나타나지 않으므로 사전 학습 단계와 파인 튜닝 단계에서의 불일치가 발생하는 문제가 있다. 이 문제를 완화하기 위해서 모든 토큰을 [MASK]로 사용하지 않는다. 전체 단어 관점으로 보면 다음과 같다.

 

예시를 통해 이해해보자. 'My dog is cute. he likes playing'이라는 문장에 대해서 마스크드 언어 모델을 학습하고자 한다. BERT 토크나이저에 의해 이 문장은 ['my', 'dog', 'is', 'cute', 'he', 'likes', 'play', '##ing']로 토큰화된어 BERT의 입력으로 사용된다.

 

  • 'dog' 토큰이 [MASK]로 변경되었다.

 

위 그림은 'dog' 토큰이  [MASK]로 변경되어서 BERT 모델이 원래 단어를 맞추려고 하는 모습을 보여준다. 손실 함수에서 'dog' 토큰의 위치가 아닌 다른 위치에서의 예측은 무시한다. 출력층에서는 예측 단어 집합의 크기만큼의 밀집층에 소프트맥스 함수가 사용된 1개의 층을 사용하여 원래 단어가 무엇인지를 맞추게 된다.

 

  • 'dog' 토큰이 [MASK]로 변경되었다.
  • 'he'는 랜던 단어 'king'으로 변경되었다.
  • 'play'는 변경되진 않았지만 예측에 사용된다.

 

 

BERT는 'king'으로 변경된 토큰에 대해서도 원래 단어가 무엇인지, 변경되지 않은 단어 'play'에 대해서도 원래 단어가 무엇인지를 예측해야 한다. 이는 손실함수에 'king'과 'play'의 위치에 대한 예측이 포함된다는 의미이다. 'play'는 변경되지 않았지만 BERT 입장에서는 이것이 변경된 것인지 아닌지 모르기 때문에 원래 단어를 예측해야 한다.

 

6.2 NSP

BERT는 두 개의 문장을 준 후에 이 문장이 이어지는 문장인지 아닌지를 맞추는 방식으로 훈련시킨다. 이를 위해서 1:1 비율로 실제 이어지는 두 개의 문장과 랜덤으로 이어붙인 두 개의 문장을 주고 훈련시킨다. 두 개의 문장을 Sentence A와 Sentence B라고 하였을 때 예시는 다음과 같다.

 

  • 이어지는 문장의 경우

A : The man went to the store.

B : He bought a gallon of milk.

Label : IsNextSentence

 

  • 이어지는 문장이 아닌 경우

A : The man went to the store.

B : Dogs are so cute..

Label : NotNextSentence

 

 

BERT는 특수 토큰 [SEP]을 사용해 문장을 구분한다. 첫 번째 문장의 끝에 [SEP] 토큰을 넣고 두 번째 문장의 끝에 [SEP] 토큰을 붙여준다. 그리고 두 문장의 연속성을 판단하기 위해 [CLS] 토큰의 위치의 출력층에서 이진 분류 문제를 풀도록 한다.

 

위의 그림처럼 MLM과 NSP는 따로 학습하는 것이 아닌 손실을 합하여 학습이 동시에 이루어진다.

 

 

7. 세그먼트 임베딩(Segment Embedding)

 

BERT는 문장 구분을 위해 세그먼트 임베딩이라는 또 다른 임베딩 층을 사용한다. [CLS]부터 첫 번째 문장 끝의 [SEP]까지는 Sentence 0 임베딩, 두 번째 문장 시작부터 [SEP]까지는 Sentence 1 임베딩을 더해주는 방식으로 두 개만 사용한다. 

 

두 번째 문장 부분에는 실제로는 다수의 문장으로 구성될 수 있다. 예를 들어 QA 문제를 푸는 경우에는 [Question, Paragraph]으로 입력을 받지만 Paragraph 부분이 다수의 문장으로 구성될 수 있다.

 

또한 BERT가 두 개의 문장을 입력받을 필요가 없는 경우도 있다. 예를 들어 영화 리뷰 분류 같은 경우 한 개의 문서에 대해서만 분류하는 것이므로 이 경우에는 전체 입력에 Sentence 0 임베딩만을 더해준다.

 

 

8. BERT Fine-tuning

8.1 하나의 텍스트에 대한 텍스트 분류

 

텍스트 분류 문제를 풀기 위해서 [CLS] 토큰의 위치의 출력층에서 밀집층이나 완전 연결층 등을 추가하여 분류에 대한 예측을 하게 된다.

 

8.2 하나의 텍스트에 대한 태깅 작업

 

대표적으로 문장의 각 단어에 품사를 태깅하는 품사 태깅 작업과 개체를 태깅하는 개체명 인식 작업이 있다. 위 그림은 개체명 인식 작업을 나타내며, 출력층에서는 입력 텍스트의 각 토큰의 위치에 밀집층을 사용하여 분류에 대한 예측을 한다.

 

8.3 텍스트의 쌍에 대한 분류 또는 회귀 문제

 

텍스트의 쌍을 입력으로 받는 대표적인 작업으로 자연어 추론이 있다. 이는 두 문장이 논리적으로 어떤 관계에 있는지를 분류하는 것이다. 관계 유형으로는 모순(contradiction), 함의(entailment), 중립(neutral)이 있다.

 

8.4 QA

 

BERT로 QA를 풀기 위해서 질문과 본문 두 개의 텍스트 쌍을 입력한다. 이 작업의 대표적인 데이터셋으로 SQuAD(Stanford Question Answering Dataset)이 있다. 이 데이터셋을 푸는 방법은 질문과 본문을 입력받으면, 본문의 일부분을 추출해서 질문에 답변하는 것이다.

 

예를 들어 "강우가 떨어지도록 영향을 주는 것은?" 라는 질문이 주어지고, "기상학에서 강우는 대기 수증기가 응결되어 중력의 영향을 받고 떨어지는 것을 의미합니다. 강우의 주요 형태는 이슬비, 비, 진눈깨비, 눈, 싸락눈 및 우박이 있습니다." 라는 본문이 주어졌다고 해본다. 이 경우 정답은 "중력"이 되어야 한다.

 

9. 그 외 기타

  • 최대 입력 길이 : 512
  • 100만 step 훈련 (총 합 33억 단어 말뭉치에 대해 40 에포크 학습)
  • 옵티마이저 : Adam
  • 학습률 : 1e-4
  • 가중치 감소 : L2 정규화로 0.01
  • 드롭 아웃 :  모든 레이어에 대해 0.1
  • 활성화 함수 : gelu
  • 배치 크기 : 256

 

10. 어텐션 마스크

 

BERT를 실습하게 되면 어텐션 마스크라는 시퀀스 입력이 추가로 필요하다. 이는 어텐션 연산을 할때 불필요하게 패딩 토큰에 대해서 어텐션을 하지 않도록 실제 단어와 패딩 토큰을 구분할 수 있도록 알려주는 입력이다. 이 값은 0과 1을 가지는데, 1은 해당 토큰이 실제 단어이므로 마스킹을 하지 않는다는 의미이고, 0은 해당 토큰이 패딩 토큰으로 마스킹을 한다는 의미이다. 

'Deep Learning > NLP' 카테고리의 다른 글

ELMo(Embeddings from Language Model)  (0) 2024.02.06
트랜스포머  (0) 2024.01.11
어텐션 메커니즘  (0) 2024.01.11
서브워드 토크나이저 (Subword Tokenizer)  (0) 2023.10.05