시작하며

이번 포스트에서는 2018년에 구글에서 공개한 사전 훈련된 모델인 BERT(Bidirectional Encoder Representations from Transformers)에 대해 자세히 알아보도록 하겠다. Bert는 Transformer의 인코더 구조를 이용해 만든 모델인 만큼 Transformer에 대한 기본 지식이 필요하다. 이 포스트는 Transformer에 대한 기본 지식을 알고 있다는 전제하에 포스팅 되므로 관련 기초 지식이 부족하다면 아래 링크를 통해 공부하고 보는 것을 추천한다.

트랜스포머 기초 개념 공부하러 가기
트랜스포머 인코더 ‘셀프 어텐션’ & ‘멀티 헤드 어텐션’ 공부하러 가기
트랜스포머 인코더 ‘FFNN’, ‘잔차 연결’, ‘층 정규화’ 공부하러 가기
트랜스포머 디코더 구조 공부하러 가기

1. BERT의 개요

image

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

BERT가 높은 성능을 얻을 수 있었던 것은, 레이블이 없는 방대한 데이터로 사전 훈련된 모델을 가지고, 레이블이 있는 다른 작업(Task)에서 추가 훈련과 함께 하이퍼파라미터를 재조정하여 이 모델을 사용하면 성능이 높게 나오는 기존의 사례들을 참고하였기 때문이다. 다른 작업에 대해서 파라미터 재조정을 위한 추가 훈련 과정을 파인 튜닝(Fine-tuning)이라고 한다.

위의 그림은 BERT의 파인 튜닝 사례를 보여준다. 우리가 하고 싶은 태스크가 스팸 메일 분류라고 하였을 때, 이미 위키피디아 등으로 사전 학습된 BERT 위에 분류를 위한 신경망을 한 층 추가한다. 이 경우, BERT가 언어 모델 사전 학습 과정에서 얻은 지식을 활용할 수 있으므로 스팸 메일 분류에서 보다 더 좋은 성능을 얻을 수 있다.

2. BERT의 크기

image

BERT의 기본 구조는 트랜스포머의 인코더를 쌓아올린 구조이다. Base 버전에서는 총 12개를 쌓았으며, Large 버전에서는 총 24개를 쌓았다. 그 외에도 Large 버전은 Base 버전보다 d_model의 크기나 셀프 어텐션 헤드(Self Attention Heads)의 수가 더 크다. 트랜스포머 인코더 층의 수를 L, d_model의 크기를 D, 셀프 어텐션 헤드의 수를 A라고 하였을 때 각각의 크기는 다음과 같다.

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

초기 트랜스포머 모델이 L=6, D=512, A=8이었다는 것과 비교하면 Base 또한 초기 트랜스포머보다는 큰 네트워크임을 알 수 있다. 여기서 BERT-base는 BERT보다 앞서 등장한 Open AI GPT-1과 하이퍼파라미터가 동일한데, 이는 BERT 연구진이 직접적으로 GPT-1과 성능을 비교하기 위해서 GPT-1과 동등한 크기로 BERT-Base를 설계하였기 때문이다. 반면, BERT-Large는 BERT의 최대 성능을 보여주기 위해 만들어진 모델이다. BERT가 세운 기록들은 대부분 BERT-Large를 통해 이루어졌다.

이 글에서는 앞으로 편의를 위해 BERT-BASE를 기준으로 설명한다.

3. BERT의 문맥을 반영한 임베딩(Contextual Embedding)

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

image

BERT의 입력은 앞서 배운 딥 러닝 모델들과 마찬가지로 임베딩 층(Embedding layer)를 지난 임베딩 벡터들이다. d_model을 768로 정의하였으므로, 모든 단어들은 768차원의 임베딩 벡터가 되어 BERT의 입력으로 사용됩니다. BERT는 내부적인 연산을 거친 후, 동일하게 각 단어에 대해서 768차원의 벡터를 출력한다. 위의 그림에서는 BERT가 각 768차원의 [CLS], I, love, you라는 4개의 벡터를 입력받아서(입력 임베딩) 동일하게 768차원의 4개의 벡터를 출력하는 모습(출력 임베딩)을 보여준다.

image

BERT의 연산을 거친 후의 출력 임베딩은 문장의 문맥을 모두 참고한 문맥을 반영한 임베딩이 된다. 위의 좌측 그림에서 [CLS]라는 벡터는 BERT의 초기 입력으로 사용되었을 입력 임베딩 당시에는 단순히 임베딩 층(embedding layer)를 지난 임베딩 벡터였지만, BERT를 지나고 나서는 [CLS], I, love, you라는 모든 단어 벡터들을 모두 참고한 후에 문맥 정보를 가진 벡터가 된다. 위의 좌측 그림에서는 모든 단어를 참고하고 있다는 것을 점선의 화살표로 표현하였다.

이는 [CLS]라는 단어 벡터 뿐만 아니라 다른 벡터들도 전부 마찬가지이다. 가령, 우측의 그림에서 출력 임베딩 단계의 love를 보면 BERT의 입력이었던 모든 단어들인 [CLS], I, love, you를 참고하고 있다.

image

하나의 단어가 모든 단어를 참고하는 연산은 사실 BERT의 12개의 층에서 전부 이루어지는 연산이다. 그리고 이를 12개의 층을 지난 후에 최종적으로 출력 임베딩을 얻게되는 것이다. 가령, 위의 그림은 BERT의 첫번째 층에 입력된 각 단어가 모든 단어를 참고한 후에 출력되는 과정을 화살표로 표현하였다. BERT의 첫번째 층의 출력 임베딩은 BERT의 두번째 층에서는 입력 임베딩이 된다.

image

그렇다면 BERT는 어떻게 모든 단어들을 참고하여 문맥을 반영한 출력 임베딩을 얻게 되는 것일까? 바로 ‘셀프 어텐션’이다. BERT는 기본적으로 트랜스포머 인코더를 12번 쌓은 것이므로 내부적으로 각 층마다 멀티 헤드 셀프 어텐션과 포지션 와이즈 피드 포워드 신경망을 수행하고 있다.

4. 포지션 임베딩(Position Embedding)

트랜스포머에서는 포지셔널 인코딩(Positional Encoding)이라는 방법을 통해서 단어의 위치 정보를 표현했다. 포지셔널 인코딩은 사인 함수와 코사인 함수를 사용하여 위치에 따라 다른 값을 가지는 행렬을 만들어 이를 단어 벡터들과 더하는 방법이다. BERT에서는 이와 유사하지만, 위치 정보를 사인 함수와 코사인 함수로 만드는 것이 아닌 학습을 통해서 얻는 포지션 임베딩(Position Embedding)이라는 방법을 사용한다.

image

위의 그림은 포지션 임베딩을 사용하는 방법을 보여준다. 우선, 위의 그림에서 WordPiece Embedding은 우리가 이미 알고 있는 단어 임베딩으로 실질적인 입력이다. 그리고 이 입력에 포지션 임베딩을 통해서 위치 정보를 더해주어야 한다. 포지션 임베딩의 아이디어는 굉장히 간단한데, 위치 정보를 위한 임베딩 층(Embedding layer)을 하나 더 사용한다. 가령, 문장의 길이가 4라면 4개의 포지션 임베딩 벡터를 학습시킨다. 그리고 BERT의 입력마다 다음과 같이 포지션 임베딩 벡터를 더해주는 것이다.

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

실제 BERT에서는 문장의 최대 길이를 512로 하고 있으므로, 총 512개의 포지션 임베딩 벡터가 학습된다. 결론적으로 현재 설명한 내용을 기준으로는 BERT에서는 총 두 개의 임베딩 층이 사용된다. 단어 집합의 크기가 30,522개인 단어 벡터를 위한 임베딩 층과 문장의 최대 길이가 512이므로 512개의 포지션 벡터를 위한 임베딩 층이다.

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

5. BERT의 사전 훈련(Pre-training)

image

위의 그림은 BERT의 논문에 첨부된 그림으로 ELMo와 GPT-1, 그리고 BERT의 구조적인 차이를 보여준다. 가장 우측 그림의 ELMo는 정방향 LSTM과 역방향 LSTM을 각각 훈련시키는 방식으로 양방향 언어 모델을 만들었다. 가운데 그림의 GPT-1은 트랜스포머의 디코더를 이전 단어들로부터 다음 단어를 예측하는 방식으로 단방향 언어 모델을 만들었다. Trm은 트랜스포머를 의미한다. 단방향(→)으로 설계된 Open AI GPT와 달리 가장 좌측 그림의 BERT는 화살표가 양방향으로 뻗어나가는 모습을 보여준다. 이는 마스크드 언어 모델(Masked Language Model)을 통해 양방향성을 얻었기 때문이다. BERT의 사전 훈련 방법은 크게 두 가지로 나뉜다. 첫번째는 마스크드 언어 모델이고, 두번째는 다음 문장 예측(Next sentence prediction, NSP)이다.

논문에 따르면 BERT는 BookCorpus(8억 단어)와 위키피디아(25억 단어)로 학습되었습다.

1) 마스크드 언어 모델(Masked Language Model, MLM)


BERT는 사전 훈련을 위해서 인공 신경망의 입력으로 들어가는 입력 텍스트의 15%의 단어를 랜덤으로 마스킹(Masking)한다. 그리고 인공 신경망에게 이 가려진 단어들을(Masked words) 예측하도록 한다. 중간에 단어들에 구멍을 뚫어놓고, 구멍에 들어갈 단어들을 예측하게 하는 식이다. 예를 들어 ‘나는 [MASK]에 가서 그곳에서 빵과 [MASK]를 샀다’를 주고 ‘슈퍼’와 ‘우유’를 맞추게 한다.

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

  • 80%의 단어들은 [MASK]로 변경한다.
    Ex) The man went to the store → The man went to the [MASK]

  • 10%의 단어들은 랜덤으로 단어가 변경된다.
    Ex) The man went to the store → The man went to the dog

  • 10%의 단어들은 동일하게 둔다.
    Ex) The man went to the store → The man went to the store

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

image

전체 단어의 85%는 마스크드 언어 모델의 학습에 사용되지 않는다. 마스크드 언어 모델의 학습에 사용되는 단어는 전체 단어의 15%이다. 학습에 사용되는 12%는 [MASK]로 변경 후에 원래 단어를 예측한다. 1.5%는 랜덤으로 단어가 변경된 후에 원래 단어를 예측한다. 1.5%는 단어가 변경되지는 않았지만, BERT는 이 단어가 변경된 단어인지 원래 단어가 맞는지는 알 수 없다. 이 경우에도 BERT는 원래 단어가 무엇인지를 예측하도록 한다.

예시를 통해 이해해보자. ‘My dog is cute. he likes playing’이라는 문장에 대해서 마스크드 언어 모델을 학습하고자 한다. 약간의 전처리와 BERT의 서브워드 토크나이저에 의해 이 문장은 [‘my’, ‘dog’, ‘is’ ‘cute’, ‘he’, ‘likes’, ‘play’, ‘##ing’]로 토큰화가 되어 BERT의 입력으로 사용된다. 그리고 언어 모델 학습을 위해서 다음과 같이 데이터가 변경되었다고 가정해보자.

  • ‘dog’ 토큰은 [MASK]로 변경되었습니다.

image

위 그림은 ‘dog’ 토큰이 [MASK]로 변경되어서 BERT 모델이 원래 단어를 맞추려고 하는 모습을 보여준다. 여기서 출력층에 있는 다른 위치의 벡터들은 예측과 학습에 사용되지 않고, 오직 ‘dog’ 위치의 출력층의 벡터만이 사용된다. 구체적으로는 BERT의 손실 함수에서 다른 위치에서의 예측은 무시한다. 출력층에서는 예측을 위해 단어 집합의 크기만큼의 밀집층(Dense layer)에 소프트맥스 함수가 사용된 1개의 층을 사용하여 원래 단어가 무엇인지를 맞추게 된다. 그런데 만약 ‘dog’만 변경된 것이 아니라 다음과 같이 데이터셋이 변경되었다면 어떨까? 이번에는 세 가지 유형 모두에 대해서 가정해보자.

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

image

BERT는 랜덤 단어 ‘king’으로 변경된 토큰에 대해서도 원래 단어가 무엇인지, 변경되지 않은 단어 ‘play’에 대해서도 원래 단어가 무엇인지를 예측해야 한다. ‘play’는 변경되지 않았지만 BERT 입장에서는 이것이 변경된 단어인지 아닌지 모르므로 마찬가지로 원래 단어를 예측해야 한다. BERT는 마스크드 언어 모델 외에도 다음 문장 예측이라는 또 다른 태스크를 학습한다.

2) 다음 문장 예측(Next Sentence Prediction, NSP)


BERT는 두 개의 문장을 준 후에 이 문장이 이어지는 문장인지 아닌지를 맞추는 방식으로 훈련시킨다. 이를 위해서 50:50 비율로 실제 이어지는 두 개의 문장과 랜덤으로 이어붙인 두 개의 문장을 주고 훈련시킨다. 이를 각각 Sentence A와 Sentence B라고 하였을 때, 다음의 예는 문장의 연속성을 확인한 경우와 그렇지 않은 경우를 보여준다.

  • 이어지는 문장의 경우
    Sentence A : The man went to the store.
    Sentence B : He bought a gallon of milk.
    Label = IsNextSentence

  • 이어지는 문장이 아닌 경우 경우
    Sentence A : The man went to the store.
    Sentence B : dogs are so cute.
    Label = NotNextSentence

image

BERT의 입력으로 넣을 때에는 [SEP]라는 특별 토큰을 사용해서 문장을 구분한다. 첫번째 문장의 끝에 [SEP] 토큰을 넣고, 두번째 문장이 끝나면 역시 [SEP] 토큰을 붙여준다. 그리고 이 두 문장이 실제 이어지는 문장인지 아닌지를 [CLS] 토큰의 위치의 출력층에서 이진 분류 문제를 풀도록 하자. [CLS] 토큰은 BERT가 분류 문제를 풀기 위해 추가된 특별 토큰이다. 그리고 위의 그림에서 나타난 것과 같이 마스크드 언어 모델과 다음 문장 예측은 따로 학습하는 것이 아닌 loss를 합하여 학습이 동시에 이루어진다.

BERT가 언어 모델 외에도 다음 문장 예측이라는 태스크를 학습하는 이유는 BERT가 풀고자 하는 태스크 중에서는 QA(Question Answering)나 NLI(Natural Language Inference)와 같이 두 문장의 관계를 이해하는 것이 중요한 태스크들이 있기 때문이다.

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

image

앞서 언급했듯이 BERT는 QA 등과 같은 두 개의 문장 입력이 필요한 태스크를 풀기도 합니다. 문장 구분을 위해서 BERT는 세그먼트 임베딩이라는 또 다른 임베딩 층(Embedding layer)을 사용한다. 첫번째 문장에는 Sentence 0 임베딩, 두번째 문장에는 Sentence 1 임베딩을 더해주는 방식이며 임베딩 벡터는 두 개만 사용된다.

결론적으로 BERT는 총 3개의 임베딩 층이 사용된다.

  • WordPiece Embedding : 실질적인 입력이 되는 워드 임베딩. 임베딩 벡터의 종류는 단어 집합의 크기로 30,522개.

  • Position Embedding : 위치 정보를 학습하기 위한 임베딩. 임베딩 벡터의 종류는 문장의 최대 길이인 512개.

  • Segment Embedding : 두 개의 문장을 구분하기 위한 임베딩. 임베딩 벡터의 종류는 문장의 최대 개수인 2개.

주의할 점은 많은 문헌에서 BERT가 문장 중간의 [SEP] 토큰과 두 종류의 세그먼트 임베딩을 통해서 두 개의 문장을 구분하여 입력받을 수 있다고 설명하고 있지만, 여기서 BERT에 두 개의 문장이 들어간다는 표현에서의 문장이라는 것은 실제 우리가 알고 있는 문장의 단위는 아니다. 예를 들어 QA 문제를 푸는 경우에는 [SEP]와 세그먼트 임베딩을 기준으로 구분되는 [질문(Question), 본문(Paragraph)] 두 종류의 텍스트를 입력받지만, Paragraph 1개는 실제로는 다수의 문장으로 구성될수 있습니다. 다시 말해 [SEP]와 세그먼트 임베딩으로 구분되는 BERT의 입력에서의 두 개의 문장은 실제로는 두 종류의 텍스트, 두 개의 문서일 수 있다.

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

7. BERT를 파인 튜닝(Fine-tuning)하기

이번에는 사전 학습 된 BERT에 우리가 풀고자 하는 태스크의 데이터를 추가로 학습 시켜서 테스트하는 단계인 파인 튜닝 단계에 대해서 알아보자. 실질적으로 태스크에 BERT를 사용하는 단계에 해당된다.

1) 하나의 텍스트에 대한 텍스트 분류 유형(Single Text Classification)


image

BERT를 사용하는 첫번째 유형은 하나의 문서에 대한 텍스트 분류 유형이다. 이 유형은 영화 리뷰 감성 분류, 로이터 뉴스 분류 등과 같이 입력된 문서에 대해서 분류를 하는 유형으로 문서의 시작에 [CLS] 라는 토큰을 입력한다. 앞서 사전 훈련 단계에서 다음 문장 예측을 설명할 때, [CLS] 토큰은 BERT가 분류 문제를 풀기위한 특별 토큰이라고 언급한 바 있다. 이는 BERT를 실질적으로 사용하는 단계인 파인 튜닝 단계에서도 마찬가지이다. 텍스트 분류 문제를 풀기 위해서 [CLS] 토큰의 위치의 출력층에서 밀집층(Dense layer) 또는 같은 이름으로는 완전 연결층(fully-connected layer)이라고 불리는 층들을 추가하여 분류에 대한 예측을 하게된다.

2) 하나의 텍스트에 대한 태깅 작업(Tagging)


image

BERT를 사용하는 두번째 유형은 태깅 작업이다. 대표적으로 문장의 각 단어에 품사를 태깅하는 품사 태깅 작업과 개체를 태깅하는 개체명 인식 작업이 있다. 출력층에서는 입력 텍스트의 각 토큰의 위치에 밀집층을 사용하여 분류에 대한 예측을 하게 된다.

3) 텍스트의 쌍에 대한 분류 또는 회귀 문제(Text Pair Classification or Regression)


image

BERT는 텍스트의 쌍을 입력으로 받는 태스크도 풀 수 있다. 텍스트의 쌍을 입력으로 받는 대표적인 태스크로 자연어 추론(Natural language inference)이 있다. 자연어 추론 문제란, 두 문장이 주어졌을 때, 하나의 문장이 다른 문장과 논리적으로 어떤 관계에 있는지를 분류하는 것이다. 유형으로는 모순 관계(contradiction), 함의 관계(entailment), 중립 관계(neutral)가 있다.

텍스트의 쌍을 입력받는 이러한 태스크의 경우에는 입력 텍스트가 1개가 아니므로, 텍스트 사이에 [SEP] 토큰을 집어넣고, Sentence 0 임베딩과 Sentence 1 임베딩이라는 두 종류의 세그먼트 임베딩을 모두 사용하여 문서를 구분한다.

4) 질의 응답(Question Answering)


image

텍스트의 쌍을 입력으로 받는 또 다른 태스크로 QA(Question Answering)가 있다. BERT로 QA를 풀기 위해서 질문과 본문이라는 두 개의 텍스트의 쌍을 입력한다. 이 태스크의 대표적인 데이터셋으로 SQuAD(Stanford Question Answering Dataset) v1.1이 있습니다. 이 데이터셋을 푸는 방법은 질문과 본문을 입력받으면, 본문의 일부분을 추출해서 질문에 답변하는 것이다. 실제로 이 데이터셋은 영어로 되어있지만 한국어로 예시를 들어보겠습다. “강우가 떨어지도록 영향을 주는 것은?” 라는 질문이 주어지고, “기상학에서 강우는 대기 수증기가 응결되어 중력의 영향을 받고 떨어지는 것을 의미합니다. 강우의 주요 형태는 이슬비, 비, 진눈깨비, 눈, 싸락눈 및 우박이 있습니다.” 라는 본문이 주어졌다고 해보자. 이 경우, 정답은 “중력”이 되어야 한다.

8. 어텐션 마스크(Attention Mask)

image

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

마치며

이번 포스팅에선 Transformer의 인코더 기반으로 만들어진 Bert모델에 대해 알아보았다. 최근 Bert는 GPT와 함께 자연어 처리 분야를 이끌어가는 쌍두마차라고 볼 수 있다. 일반적으로 Bert는 문장 분류나 개체명 인식과 같은 작업에, GPT는 자연어 생성과 같은 작업에 적합하다. 앞에서 Bert는 양방향으로, GPT는 단방향으로 사전학습 된다고 언급한 바 있는데, 이는 Bert는 Transformer의 인코더 기반 모델이고 GPT는 디코더 기반 모델이기 때문이다. 앞선 Transformer의 디코더 부분 포스팅에서 디코더의 첫 번째 서브층에서 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 도입했다는 설명을 한 적 있다. 디코더에서는 현재 시점 이후의 단어들은 현재 예측에 영향을 미치지 못하므로, 이 디코더 기반으로 이루어진 GPT는 당연히 왼쪽에서 오른쪽(현재 시점 이전의 단어들 기반)으로 학습할 수 밖에 없는 것이다.

참고 서적

딥 러닝을 이용한 자연어 처리 입문

태그: ,

카테고리:

업데이트:

댓글남기기