BERT에서 Mecab tokenization 코드
BERT에서 mecab tokenizer을 사용하여 pre-training와 fine-tuning을 실험했었다.
- 물론 제대로 하려면 코퍼스와 비용이 많이 필요하기 때문에 간단히 샘플위주로 학습했었다.
- 기존의 BERT은 BPE을 쓰기 때문에 sentence piece을 이용하여 한국어 코퍼스에 맞게 vocabulary와 tokenizer을 학습 및 구축해야한다.
- 근데 mecab은 이미 구축된 tokenizer이고 mecab을 이용한 사전이 구축되어 있었기 때문에 그냥 mecab을 쓴 것도 있다ㅎㅎ
아무튼 이때 mecab을 이용해서 입력을 tokenization하는 코드를 짜야한다.
- BERT의 tokenization.py을 다음과 같이 수정하는게 제일 쉽다. (물론 여러 방법이 있을 것이다. 이 부분은 고정하고 다른 부분을 수정하는 식 등)
class mecabTokenizer(object):def __init__(self, vocab_file):self.vocab = load_vocab(vocab_file)self.inv_vocab = {v: k for k, v in self.vocab.items()}self.joo_tokenizer=MeCab.Tagger('-d /usr/lib/mecab/dic/mecab-ko-dic/').parsedef tokenize(self, text):text = text.strip()split_tokens = []text_split = self.joo_tokenizer(text).split('\n')words = text.split(' ')word = ''word_position = 0pre_exist = 0for k in range(len(text_split)):token = text_split[k].split('\t')[0]if word_position == len(words):# print('ok')breakwhile words[word_position] == '':word_position += 1word += tokenif pre_exist == 0:split_tokens.append(token)if word == words[word_position]:pre_exist = 0word_position += 1word = ''else:pre_exist += 1else:split_tokens.append("##"+token)if word == words[word_position]:pre_exist = 0word_position += 1word = ''else:pre_exist += 1return split_tokensdef convert_tokens_to_ids(self, tokens):return convert_by_vocab2(self.vocab, tokens)def convert_ids_to_tokens(self, ids):return convert_by_vocab(self.inv_vocab, ids)
- 구축한 모델은 mecab tokenizer을 사용하고 pos tagging은 사용하지 않은 모델
- vocab은 128000개이다.
- 처음에 코드를 분석하면서 vocab_generation을 할 때, ##부분을 깜빡함을 인지하였다.
- 단순히 run_pretraining을 할 때는 ##을 고려안해도 상관없다.
- 실제로 이것을 fine_tunning할 때도 코드를 커스터마이징해서 쓸 수도 있다.
- 하지만 기본적으로 BERT의 vocab 구성은 ##을 이용한다. (## 이용하는게 일반적)
- 즉 "사과나무" → "사과" + "##나무" 와 같게 쪼개지는 단어는 ##이 붙어버린다.
- 따라서 BERT 모델을 사용할 때 코드에 이런 것을 처리하는 코딩들이 들어있는데 한글어 버전에서 ##을 안쓰면, 이 부분을 수정해야한다.
- 모델을 학습하기 위해서 데이터 전처리를 하는 과정이 필요하다.
- 즉 데이터로 미리 학습데이터를 tf_record로 만들어 둔다.
- 이것을 어떻게 만드냐에 따라서 방법이 여러가지가 있을 것이다.
- 위와 같이 tokenization.py을 수정하고 코드를 돌리면 된다.
- 돌리다보면, 가끔식 error가 발생을 하는데 발생이유를 찾아보면 데이터 문제다.
- 찾은 error로는 text에 다음과 같이 띄어쓰기가 2번 있는 경우다.
- "이 텍스트는 샘플입니다."에서 "텍스트"와 "샘플"사이의 띄어기가 2번 있는 경우
- 혹은 양끝에 공백이 있는 경우다.
- "이 텍스트는 샘플입니다. "에서 "샘플입니다." 뒤에 공백이 있는 경우
- 이런 자잘한 error을 찾아줘서 수정해주면 된다.
- 시행착오
- 맨처음에는 read_squad_examples 함수를 수정하는 방향으로 갔다.
- 왜냐하면, read_squad_examples을 영어버전으로 돌려보면, 띄어쓰기 단위로 token을 쪼개서 answer_start와 answer_end을 찾는다.
- 즉, 데이터에서 주어진 MRC에서 주어진 정답 answer은 character 단위의 위치이다.
- 예)"정답은 6번째 입니다."에서 answer_position 6라는 것은 "번"의 위치를 말다. (띄어쓰기 포함)
- 이것을 띄어쓰기 단위로 token을 쪼개서 이것과 align을 시키는 것이다.
- 하지만 우리가 모델을 학습할 때는 사용한 tokenizer의 token 단위의 position을 찾아야 한다.
- 따라서 meacb tokenizer에 맞게 수정코드를 짜면, 이 뒤에 사용되는 함수들을 수정해야한다.
- 하지만 뒷 부분을 수정하다보면 convert_examples_to_features 함수를 수정해줘야 한다.
- 이 함수를 분석해보면 띄어쓰기로 쪼개진 token을 실제 사용하는 tokenizer의 subtoken으로 쪼개서 align을 맞춘다.
- 하지만 위에서 이미 mecab_tokenizer로 align을 시켰으니 이 부분을 적절히 삭제, 수정하면 된다.
- 하지만 굳이 이렇게 전체적으로 코드를 수정안해도 학습이 되니 시행착오로 남겨둔다..
- 단, 만약에 BERT모델을 huggingFace와 같은 라이브러리를 이용하여 사용한다면
- fine_tunning을 실제로 코드를 밑바닥부터 짜야하는데
- 이럴 때는 시행착오에서 겪었던 tokenizer에 맞는 answer position align을 직접 짜줘야한다.
- 만약 tensorflow-BERT로 다른 task의 fine-tunning을 할 때, 구글의 텐서플로우식 코드를 짜거나 huggingface로 변화하여 사용해야한다.
- Google식 코드를 짠다면 def create_model 부분을 task 별로 수정하면 된다.
- 불가능한 것은 아니지만, 텐서플로우와 구글이 짜둔 BERT 코드들에 익숙해져야하므로 huggingface로 변환하여 사용하는 걸 추천.
댓글
댓글 쓰기