본문 바로가기
AI/자연어처리

BERT 의 MLM, NSP (35)

by 바다의 공간 2025. 1. 7.

1. 구글 BERT의 MLM

1.마스크드 언어 모델과 토크나이저

- 기본 임포트

!pip install transformers
from transformers import TFBertForMaskedLM
from transformers import AutoTokenizer
model = TFBertForMaskedLM.from_pretrained('bert-large-uncased')
tokenizer = AutoTokenizer.from_pretrained('bert-large-uncased')


https://huggingface.co/google-bert/bert-large-uncased
영어에 대한 사전 학습된 모델로, 마스크 언어 모델링(MLM) 목적을 사용합니다.
소개된 논문: https://arxiv.org/abs/1810.04805
이 저장소 에서 처음 공개되었습니다 .
이 모델은 대소문자 구분하지 않습니다.

 

 

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

We introduce a new language representation model called BERT, which stands for Bidirectional Encoder Representations from Transformers. Unlike recent language representation models, BERT is designed to pre-train deep bidirectional representations from unla

arxiv.org

 

 

google-bert/bert-large-uncased · Hugging Face

BERT large model (uncased) Pretrained model on English language using a masked language modeling (MLM) objective. It was introduced in this paper and first released in this repository. This model is uncased: it does not make a difference between english an

huggingface.co


2. BERT입력

inputs = tokenizer('Soccer is a really fun [MASK].', return_tensors='tf')

# 토크나이저로 변환된 결과에서 문장을 구분하는 세그먼트 인코딩 결과확인
inputs['token_type_ids']
<tf.Tensor: shape=(1, 9), dtype=int32, numpy=array([[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)>

마스크를 주고 리턴타입은 기본적으로 파이토치지만 텐서객체로 리턴할수도 있습니다.

그래서 tf로 주었습니다.

 

텐서객체로 토큰화된 객체값이 나오게되는것을 확인할 수 있습니다.

 

현재의 입력은 문장이 두 개가 아니라 한 개이므로 여기서는 문장길이만큼의 0 시퀀스를 얻습니다.

두번째 문장이 시작되는 구간부터는 1의 시퀀스가 나오게되지만, 여기서는 해당되지않겠죠.

 

토크나이저로 변환된 결과에서 attention_mask를 통해서 실제 단어와 패딩 토큰을 구분하는 용도인 어텐션 마스크 확인이 가능합니다.

inputs = ['attention_mask]

#결과
<tf.Tensor: shape=(1, 9), dtype=int32, numpy=array([[1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=int32)>

 

현재의 입력에서는 패딩이 없으니 여기서는 문장길이만큼의 1시퀀스를 얻습니다.

그런데 만약에 뒤에 패딩이 있었다면 패딩이 시작되는 구간부터 0의 시퀀스가 나오게됩니다.

여기서는 역시나 해당되지 않습니다.


[MASK] 토큰 예측하기

from transformers import FillMaskPipeline
pip = FillMaskPipeline(model=model, tokenizer=tokenizer)
pip('Soccer is a really fun [MASK].')

의 결과가 나타난것을 볼 수 있습니다. 

마스크 위치에는 sport, game, thing, activity이다 라는것을 예측하고있습니다. 꽤 그럴싸하죠~

pip('The Avengers is a really fun [MASK].')

Avengers라는 영화를 말했는데도 예측을 하는지 궁금했는데 

사전학습된 모델은 진짜 대단하다는것을 알 수 있습니다. 영화로 제대로 인식했다는것을 알 수 있습니다.

pip('I went to the [MASK] this morning.')

 

여기서 잘 보면 정관사 THE를 붙였는데 아래는 the를 붙이지 않은것에 대해서 또 해보겠습니다.

현재는 장소를 잘 예측하고 있는것을 확인할 수 있습니다.

 

pip('I went to [MASK] this morning.')

 

the가 안붙는것들에 대해서 예측을 하고있다는것을 알 수 있습니다.

 

wow자연어의 매력은 진짜 어마무시하네요.


한국어 BERT의 MLM

마스크드 언어 모델과 토크나이저

model = TFBertForMaskedLM.from_pretrained('klue/bert-base')
tokenizer = AutoTokenizer.from_pretrained('klue/bert-base')

# https://huggingface.co/klue/bert-base

klue는 한국어에 대해서 사전학습된 모델입니다.

BERT입력

inputs = tokenizer('축구는 정말 재미있는 [MASK]다.', return_tensors='tf')

여기서 INPUT을 하면 토크나이저가 잘 된 것을 볼 수 있습니다 

2, 4713, 2259...으로 쭉 나열되어있는것을 확인할 수 잇습니다. 정수토큰화 완료!

 

그리고 한문장이기때문에

inputs['token_type_ids']
# numpy=array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

inputs['attention_mask']
# numpy=array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

가 되는것도 확인할 수 있습니다.


한국어 [MASK] 토큰 예측하기

pip = FillMaskPipeline(model=model, tokenizer=tokenizer)
pip('축구는 정말 재미있는 [MASK]다.')

오 제가 생각한것보다 더 다양하게 나오는것을 볼 수 있었습니다.

물론 스포츠가 좀 압도적으로 높다는것을 확인할 수 있습니다.

 

 

pip('어벤져스는 정말 재미있는 [MASK]다.')


구글 BERT의 NSP

1. 다음문장 예측 모델과 토크나이저

from transformers import TFBertForNextSentencePrediction

# https://huggingface.co/transformers/v3.0.2/model_doc/bert.html#tfbertfornextsentenceprediction

model = TFBertForNextSentencePrediction.from_pretrained('bert-base-uncased')
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')

# https://huggingface.co/google-bert/bert-base-uncased

 

2. BERT의 입력

# 다음문장예측 을 위해 문맥상으로 실제 이어지는 두개의 문장
prompt = "In Italy, pizza served in formal settings, such as at a restaurant, is presented unsliced."
next_sentence = "pizza is eaten with the use of a knife and fork. In casual settings, however, it is cut into wedges to be eaten while held in the hand."

# 두개의 문장, 정수 인코딩
encoding = tokenizer(prompt, next_sentence, return_tensors='tf')

encoding
''''
{'input_ids': <tf.Tensor: shape=(1, 58), dtype=int32, numpy=
array([[  101,  1999,  3304,  1010, 10733,  2366,  1999,  5337, 10906,
         1010,  2107,  2004,  2012,  1037,  4825,  1010,  2003,  3591,
         4895, 14540,  6610,  2094,  1012,   102, 10733,  2003,  8828,
         2007,  1996,  2224,  1997,  1037,  5442,  1998,  9292,  1012,
         1999, 10017, 10906,  1010,  2174,  1010,  2009,  2003,  3013,
         2046, 17632,  2015,  2000,  2022,  8828,  2096,  2218,  1999,
         1996,  2192,  1012,   102]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(1, 58), dtype=int32, numpy=
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(1, 58), dtype=int32, numpy=
array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=int32)>}
'''        

# 정수 인코딩된 결과
encoding['input_ids']
'''
<tf.Tensor: shape=(1, 58), dtype=int32, numpy=
array([[  101,  1999,  3304,  1010, 10733,  2366,  1999,  5337, 10906,
         1010,  2107,  2004,  2012,  1037,  4825,  1010,  2003,  3591,
         4895, 14540,  6610,  2094,  1012,   102, 10733,  2003,  8828,
         2007,  1996,  2224,  1997,  1037,  5442,  1998,  9292,  1012,
         1999, 10017, 10906,  1010,  2174,  1010,  2009,  2003,  3013,
         2046, 17632,  2015,  2000,  2022,  8828,  2096,  2218,  1999,
         1996,  2192,  1012,   102]], dtype=int32)>
'''

여기서 101과 102는 특별한 토큰입니다. [CLS]토큰 그리고 [SEP]토큰입니다.

print(tokenizer.cls_token, ':', tokenizer.cls_token_id)
print(tokenizer.sep_token, ':', tokenizer.sep_token_id)

확인을 해보자면 

[CLS]는 101 [SEP]는 102라는것을 알 수 있습니다.

 

인코딩 결과를 다시 디코딩해서 입력을 확인해보면

# 인코딩 결과를 다시 디코딩하여 입력 확인 가능.
tokenizer.decode(encoding['input_ids'][0])

[CLS] in italy, pizza served in formal settings, such as at a restaurant, is presented unsliced. [SEP] pizza is eaten with the use of a knife and fork. in casual settings, however, it is cut into wedges to be eaten while held in the hand. [SEP]

라는 결과가 나옵니다.

 

BERT에서 두개의 문장이 입력으로 들어갈 경우에는 맨 앞에는 [CLS]토큰이 존재하고 

첫번째 문장이 끝내면 [SEP] 토큰 그리고 두번째 문장이 종료되었을때 다시 추가적으로 [SEP]토큰이 추가됩니다.

 

문장을 구분하는 세그먼트 임베딩 결과도 확인할 수 있습니다.

 

encoding['token_type_ids']

'''
<tf.Tensor: shape=(1, 58), dtype=int32, numpy=
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=int32)>
'''

 

이제 0과 1이 구분되는것을 확인할 수 있었습니다.

 


다음 문장 예측하기

import tensorflow as tf
logits = model(encoding['input_ids'], token_type_ids=encoding['token_type_ids'])[0]
softmax = tf.keras.layers.Softmax()
probs = softmax(logits)
print(probs)

#결과값
tf.Tensor([[9.9999714e-01 2.8381912e-06]], shape=(1, 2), dtype=float32)

모델입력으로 넣고 logit으로 받고 softmax를 입력하게되면 텐서플로우형으로결과가 나오는것을 확인할 수 있습니다.

인덱스0 [0]은 문장이 하나이기때문에 추가했습니다. 또한 probs는 확률값을 의미합니다.

print('최종 예측 레이블 : ', tf.math.argmax(probs, axis=-1).numpy())

결과값
최종 예측 레이블 :  [0]

 

 실질적으로 이어지는 문장의 레이블은 0이고 이어지지 않는 문장의 레이블은 1입니다.

 

이어지지 않는 두개의 문장으로 테스트를해보겠습니다.

prompt = "In Italy, pizza served in formal settings, such as at a restaurant, is presented unsliced."
next_sentence = "The sky is blue due to the shorter wavelength of blue light."
encoding = tokenizer(prompt, next_sentence, return_tensors='tf')
logits = model(encoding['input_ids'], token_type_ids=encoding['token_type_ids'])[0]
softmax = tf.keras.layers.Softmax()
probs = softmax(logits)
print('최종 예측 레이블 : ', tf.math.argmax(probs, axis=-1).numpy())

그러면 최종예측 레이블은 [1]이 나옵니다.

앞뒤가 이어지지 않는 문장이니 당연한 결과라고 볼 수 있겠죠?


한국어 BERT의 NSP

다음 문장 예측모델과 토크나이저

model = TFBertForNextSentencePrediction.from_pretrained('klue/bert-base', from_pt=True)
tokenizer = AutoTokenizer.from_pretrained('klue/bert-base')

사전학습된 모델을 로딩해오려고 합니다.

 

 

다음 문장 예측하기

두개의 문장을 작성해보겠습니다. 

아래 문장은 이어지는 문장이 있는데 이 문장의 레이블은 어떤것인지 예측해보려고 합니다.

# 이어지는 두 개의 문장
prompt = "2002년 월드컵 축구대회는 일본과 공동으로 개최되었던 세계적인 큰 잔치입니다."
next_sentence = "여행을 가보니 한국의 2002년 월드컵 축구대회의 준비는 완벽했습니다."

 

encoding = tokenizer(prompt, next_sentence, return_tensors='tf')

logits = model(encoding['input_ids'], token_type_ids=encoding['token_type_ids'])[0]

softmax = tf.keras.layers.Softmax()
probs = softmax(logits)
print('최종 예측 레이블 :', tf.math.argmax(probs, axis=-1).numpy())

 

최종 예측 레이블은 0이 나왔습니다.

 

또한 상관 없는 두개의 문장을 한국어로 보자면

# 상관없는 두 개의 문장
prompt = "2002년 월드컵 축구대회는 일본과 공동으로 개최되었던 세계적인 큰 잔치입니다."
next_sentence = "극장가서 로맨스 영화를 보고싶어요"

 

encoding = tokenizer(prompt, next_sentence, return_tensors='tf')

logits = model(encoding['input_ids'], token_type_ids=encoding['token_type_ids'])[0]

softmax = tf.keras.layers.Softmax()
probs = softmax(logits)
print('최종 예측 레이블 :', tf.math.argmax(probs, axis=-1).numpy())

 

이것은 최종예측레이블이 1이되는것을 확인할 수 있습니다.