나만의 BERT Wordpiece Vocab 만들기

나만의 BERT Wordpiece Vocab 만들기

개인적으로 Pretrained Language Model 성능에 큰 영향을 주는 것 중 하나로 Vocab quality라고 생각한다.

이번 포스트에서는 tokenization의 방법 중 하나인 Wordpiece를 이용하여 어떻게 vocab을 만드는지 알아보려 한다:)

Introduction

한국어 Tokenizer의 대안으로는 크게 Sentencepiece, Mecab, Wordpiece가 있다. (여기서의 wordpiece는 Google의 BERT에서 사용된 wordpiece로 가정한다.)

BERT, ELECTRA 등은 기본적으로 Wordpiece를 사용하기에 공식 코드에서 기본적으로 제공되는 Tokenizer 역시 이에 호환되게 코드가 작성되었다. 즉, SentencepieceMecab을 사용하려면 별도의 Tokenizer를 직접 만들어야 하고, 이렇게 되면 transformers 등의 라이브러리에서 모델을 곧바로 사용하는데 불편함이 생기게 된다.

Original wordpiece code is NOT available!

공식 BERT에서 사용된 Wordpiece Builder는 제공되지 않고 있다. BERT 공식 Github에서 다른 대안들을 제시해줬지만, 완전히 동일한 Wordpiece Vocab이 나오지 않았다.

몇몇 오픈소스들이 Wordpiece vocab builder를 구현하였지만 input file이 매우 클 시 메모리, 속도 등의 이슈가 종종 발생한다ㅠ

Huggingface Tokenizers

최종적으로, 최근 Huggingface에서 발표한 Tokenizers 라이브러리를 이용하여 Wordpiece Vocabulary를 만드는게 제일 좋았다.

해당 라이브러리를 사용하면 Corpus가 매우 커도 메모리 이슈가 발생하지 않으며, Rust로 구현이 되어있어 속도 또한 Python보다 빠르다😃

Code for building Wordpiece vocab

(tokenizer v0.7.0 기준으로 작성하였다. 현재도 라이브러리가 업데이트 중이어서 api가 달라질 수도…)

import argparse
from tokenizers import BertWordPieceTokenizer

parser = argparse.ArgumentParser()

parser.add_argument("--corpus_file", type=str)
parser.add_argument("--vocab_size", type=int, default=32000)
parser.add_argument("--limit_alphabet", type=int, default=6000)

args = parser.parse_args()

tokenizer = BertWordPieceTokenizer(
vocab_file=None,
clean_text=True,
handle_chinese_chars=True,
strip_accents=False, # Must be False if cased model
lowercase=False,
wordpieces_prefix="##"
)

tokenizer.train(
files=[args.corpus_file],
limit_alphabet=args.limit_alphabet,
vocab_size=args.vocab_size
)

tokenizer.save("./", "ch-{}-wpm-{}".format(args.limit_alphabet, args.vocab_size))
  • 주의해야할 점은 lowercase=False로 할 시 strip_accent=False로 해줘야 한다는 것!

  • [UNK]의 비중을 최대한 줄이기 위해 모든 character를 커버할 수 있도록 처리하였다. (limit_alphabet)

  • Corpus의 전처리가 완료되었다는 전제하에 sentencepiece와 비교했을 때 UNK Ratio가 훨씬 낮았다.

Reference