Colab에서 TPU로 BERT 처음부터 학습시키기 - Tensorflow/Google ver.
2018년말부터 현재까지 NLP 연구에서 BERT는 여전히 압도적인 위치를 차지하고 있다.
한편, BERT모델을 사용하는 이유 중 가장 큰 것 하나가 바로 한국어로 Pretrained된 모델이 있다는 점이다. Google에서 논문을 처음 공개했을 때 Multilingual pretrained model을 공개해 Fine-tuning만으로도 우리가 필요한 데이터셋에 맞춰 분류기를 만드는 등의 여러 응용이 가능하고, 동시에 높은 성능을 보여주었기 때문에 BERT 자체를 학습시키는 것에 대해서는 크게 신경쓰지 않은 것이 사실이다.
한편 작년 ETRI의 한국어 BERT 언어모델, 그리고 SKTBrain의 KoBERT 등 한국어 데이터셋으로 학습시킨 모델들이 등장했고, 이런 모델들을 Fine-tuning할 경우 기존 구글의 다국어 모델을 사용한 것보다 성능이 조금이라도 더 잘 나오기도 한다. (특히 정제되지 않은 글에 대해 좀 더 나은 성능을 보여줬다. OOV문제가 덜한 편이었다.)
다만 이런 모델들 역시 굉장히 ‘보편적’ 글로 학습된 것이라 도메인 특화된 분야에 있어서는 성능이 잘 나오지 않을 수도 있다. 따라서 특수한 경우의 특수한 도메인에 최적화된 Pretrained model을 만든다면 우리의 NLP 모델도 좀 더 성능이 좋아질 수 있다!
이번 글에서는 BERT 모델을 TPU와 Tensorflow를 이용해 처음부터 학습시켜보는 과정을 다뤄본다.
# create formatter and add it to the handlers formatter = logging.Formatter('%(asctime)s : %(message)s') sh = logging.StreamHandler() sh.setLevel(logging.INFO) sh.setFormatter(formatter) log.handlers = [sh]
with tf.Session(TPU_ADDRESS) as session: log.info('TPU address is ' + TPU_ADDRESS) # Upload credentials to TPU. with open('/content/adc.json', 'r') as f: auth_info = json.load(f) tf.contrib.cloud.configure_gcs(session, credentials=auth_info) else: log.warning('Not connected to TPU runtime') USE_TPU = False
위에서 설정한 과정은 /content/adc.json 파일에 저장되고, 이 파일 설정으로 TPU를 사용하게 된다.
텍스트 데이터에서 많이 쓰는 문장부호나 기타 이모티콘🤩등을 학습에서 제거할지, 제거하지 않을지는 우리가 학습시키는 모델이 어떤 목적이냐에 따라 달라진다.
위와 같은 이모티콘은 사용 빈도가 낮은 편이기 때문에 위 이모티콘을 임베딩에 포함시킬 경우 Vocab의 용량이 굉장히 커지게 되어 보편적인 Language Model을 만들기 위해서는 보통 특수문자나 이모티콘 등을 제거해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
regex_tokenizer = nltk.RegexpTokenizer("\w+")
defnormalize_text(text): # lowercase text text = str(text).lower() # remove non-UTF text = text.encode("utf-8", "ignore").decode() # remove punktuation symbols text = " ".join(regex_tokenizer.tokenize(text)) return text
defcount_lines(filename): count = 0 with open(filename) as fi: for line in fi: count += 1 return count
# apply normalization to the dataset # this will take a minute or two
total_lines = count_lines(RAW_DATA_FPATH) bar = Progbar(total_lines)
with open(RAW_DATA_FPATH,encoding="utf-8") as fi: with open(PRC_DATA_FPATH, "w",encoding="utf-8") as fo: for l in fi: fo.write(normalize_text(l)+"\n") bar.add(1)
데이터셋 텍스트 토크나이징하기
BERT등 NLP 모델을 학습시킬때는 토크나이징한 Vocab의 크기를 적절히 제한하는 것이 모델의 성능을 높이는데 도움이 된다. (용량도 역시)
큰 모델일수록 Vocab의 크기도 커지지만, 보통의 경우는 3만개 내외의 Vocab을 만드는 것으로 보인다.
위 코드를 실행하면 SentencePiece에서 해당 모델을 열심히 잘라가며 Vocab을 생성하고, 이후 텍스트를 자르기 위한 Tokenizer를 학습한다.
학습된 Sentencepiece Vocab을 로딩해주자.
1 2 3 4 5 6 7 8 9 10 11 12
defread_sentencepiece_vocab(filepath): voc = [] with open(filepath, encoding='utf-8') as fi: for line in fi: voc.append(line.split("\t")[0]) # skip the first <unk> token voc = voc[1:] return voc
위 SentencePiece를 통해 학습한 Vocab을 BERT가 이해하는 형태로 바꿔주기 위해서는 _로 시작한 토큰들을 ## 으로 시작하도록 바꿔주면 되고, ["[PAD]","[UNK]","[CLS]","[SEP]","[MASK]"]의 경우는 BERT에서 사용하는 특수 토큰이기 때문에 해당 토큰에 대한 정보들을 추가해 최종적인 bert_vocab을 만들어준다.
1 2 3 4 5 6 7 8 9
defparse_sentencepiece_token(token): if token.startswith("▁"): return token[1:] else: return"##" + token
with open("{}/bert_config.json".format(MODEL_DIR), "w") as fo: json.dump(bert_base_config, fo, indent=2) with open("{}/{}".format(MODEL_DIR, VOC_FNAME), "w") as fo: for token in bert_vocab: fo.write(token+"\n")
그리고 앞서 만들어준 모델, 프리트레이닝 데이터를 GCS 버킷에 업로드한다.
1 2
if BUCKET_NAME: !gsutil -m cp -r $MODEL_DIR $PRETRAINING_DIR gs://$BUCKET_NAME
모델 학습 Hyper Parameters 설정하기
GCS 버킷에 데이터와 모델을 모두 업로드해준 뒤, 실제 TPU에서 학습을 진행하도록 명령을 넘겨줘야 한다.
첫번째 줄의 BUCKET_NAME만 위와 동일하게 설정해주면 된다.
중간의 BATCH_SIZE, LEARNING_RATE, TRAIN_STEPS, NUM_TPU_CORES 등의 변수를 조절해 모델의 학습 속도를 결정할 수 있다.