말뭉치 데이터(Corpus)를 바탕으로 언어모델을 만드려고 할때, 말뭉치 데이터를 언어모델에 학습시키기 위해서는 우리가 사용하는 자연어를 벡터로 변환하는 과정이 필요하다.
자연어를 벡터로 변환하기 위해서는 자연어를 토큰화 하고, 토큰화된 단어들에 인덱스를 부여해서, 부여된 인덱스를 바탕으로 원핫 벡터
나 학습을 통해밀집된 벡터
들을 만들 수 있다.
원 핫 벡터
의 경우 단어가 많아질 수록 정보를 표현하는데 많은 공간이 필요하며, 이러한 벡터들이 공간상에서 의미가 있다고 볼 수 없다. 따라서 밀집된 벡터
를 만들기 위해 Word2Vec과 같은 다양한 방법들이 사용된다. 이와 같이 단어를 벡터로 표현하는 것을 워드임베딩(word embedding)이라 한다.
위와 같이 단어를 벡터로 만들기 위해서는, 우리가 가진 말뭉치, 문장 데이터를 토큰으로 나누어 주어야한다. 이때 말뭉치나 입력으로 들어온 문장을 토큰로 나누어 주는것을 토큰화한다고 하며, Tokenizer
가 문장을 토큰으로 나누어 준다. 이 때 주의할 점은 토큰은 단어 일 수도 있으며, 단어가 아닐 수도 있다.
언어모델을 학습하기 위한 토크나이저로 wordpiece tokenizer를 사용하기로 한다.
자연어처리에서 vocab은 vocabulary의 약자로 단어들의 사전을 의미한다.
사전이라는 단어가 생소하다면, 텍스트 파일에 토큰화 된 단어들이 나열 되어 있는 파일로 보면 된다. 아래 vocab.txt은 생성된 vocab 파일의 예시이다.
vocab.txt
[PAD]
[UNK]
[CLS]
[SEP]
[MASK]
지시
##훈련
##기구
##물에
런던
##해지는
늘어난
상황이다
...생략...
토크나이저란 위에 설명한 바와 같이 입력으로 들어온 문장들에 대해 토큰으로 나누어 주는 역할을 한다.
토크나이저는 크게 Word Tokenizer
와 Subword Tokenizer
으로 나뉜다.
Word Tokenizer
의 경우 단어를 기준으로 토큰화를 하는 토크나이저를 말하며,
subword tokenizer
의 경우 단어를 나누어 단어 안에 단어들로 토큰화를 하는것을 말한다. 예를들어 경찰차, 경찰관, 경찰복과 같은 단어들이 있는 경우, 아래와 같이 단어의 분절들로 나누어질 수 있다.
경찰
##차
##관
##복
subword tokenizer은 vocab에 없는 단어들에 대해서도 좋은 성능을 보인다는 장점을 가진다.
wordpiece tokenizer는 위에 설명한 subword tokenizer의 종류 중 하나이다. subword tokenizer에서 대표적으로 사용되는 방법으로 BPE(Byte Pair Encoding) 방법이 있다.
일반적으로 많이 사용하는 Sentencepiece의 경우 빈도수를 기반으로 BPE를 수행하며, Wordpiece의 경우 likelihood를 기반으로 BPE를 수행한 알고리즘이다.
BERT의 경우 Wordpiece를 이용한 tokenizer를 사용하였고, sentencepiece를 사용한 모델 또한 많다. 선택에 따라 필요한 tokenizer를 활용할 수 있다.
pip install tokenizers
import argparse
from tokenizers import BertWordPieceTokenizer
parser = argparse.ArgumentParser()
parser.add_argument("--corpus_file", type=str)
parser.add_argument("--vocab_size", type=int, default=22000) # 만들 Vocab의 숫자
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-{}-pretty".format(args.limit_alphabet, args.vocab_size),True)
import json # import json module
vocab_path = "../vocab/ch-6000-wpm-22000-pretty"
vocab_file = '../data/wpm-vocab-all.txt'
f = open(vocab_file,'w',encoding='utf-8')
with open(vocab_path) as json_file:
json_data = json.load(json_file)
for item in json_data["model"]["vocab"].keys():
f.write(item+'\n')
f.close()
huggingface transformers 패키지 필요
from transformers.tokenization_bert import BertTokenizer
vocab_path = "../data/wpm-vocab-all.txt"
tokenizer = BertTokenizer(vocab_file=vocab_path, do_lower_case=False)
test_str = ' [CLS] 나는 워드피스 토크나이저를 써요. 성능이 좋은지 테스트 해보려 합니다. [SEP]'
print('테스트 문장: ',test_str)
encoded_str = tokenizer.encode(test_str,add_special_tokens=False)
print('문장 인코딩: ',encoded_str)
decoded_str = tokenizer.decode(encoded_str)
print('문장 디코딩: ',decoded_str)
"""
테스트 문장: [CLS] 나는 워드피스 토크나이저를 써요. 성능이 좋은지 테스트 해보려 합니다. [SEP]
문장 인코딩: [2, 9310, 4868, 6071, 12467, 21732, 12200, 6126, 6014, 4689, 6100, 18, 11612, 6037, 9389, 6073, 16784, 17316, 6070, 10316, 18, 3]
문장 디코딩: [CLS] 나는 워드피스 토크나이저를 써요. 성능이 좋은지 테스트 해보려 합니다. [SEP]
"""
언어모델 학습하기 위한 Vocab 및 Tokenizer 생성 완료