Naver Ai Boostcamp

[04/14] BERT 언어 모델 (1)

잡담연구소 2021. 4. 14. 22:31
강의 정리

 

개인적으로 오늘 강의 정말 진국;;

 

huggingface에서 제공하는 transforms 라이브러리가 너무 잘되어있어서 단! 3줄의 코드만으로도 bert를 가져와 쓸 수 있다고 한다. 

from transformers import AutoModel, AutoTokenizer
model = AutoModel.from_pretrained("<model-name>")
tokenizer = AutoTokenizer.from_pretrained("<model-name>")

AutoTokenizer는 model-name에 맞는 tokenizer를 자동으로 불러와주는 역할을 하는 듯 하다.

대표적인 multilingual 모델인 bert-base-multilingual-cased는  BertTokenizerFast class로 되어있다.

MODEL_NAME = "bert-base-multilingual-cased"
tokenizer_sent = tokenizer(user_question , return_tensors='pt')
for key , value in tokenizer_sent.items():
  print(f'{key} : {value}')

tokenizer에 text를 넣어주고, item을 출력해보면 

input_ids : input토큰에 대해 vocab dic의 key값을 출력 ex) {1: 안녕} 안녕 -> 1

token_type_ids : 0번째 문장인지, 1번째 문장인지에 대해 출력 

attention_mask : special token인지 아닌지를 출력해준다.

3가지가 출력된다. 

input_ids : tensor([[   101,  57790,   9004,  32537,  98199, 119088,  12965,    198,    106,
            102]])
token_type_ids : tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
attention_mask : tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])

지금은 단일문장이고 padding 처리를 하지 않았으므로 token_type_ids는 모두 0 , attention_mask는 모두 1이 나온다.

tokenizer(user_question , return_tensors='pt' , max_length=20,padding="max_length")

tokenizer에 option을 이렇게 다르게 주면 attention_mask의 뒷부분이 max_length에 맞춰 padding 되어 0으로 출력되는 걸 확인할 수 있다.

input_ids : tensor([[   101,   9521, 118741,   8996,  78199,   9580, 119437,  27654,    117,
          10233, 106249,  44359,   9654,   9365, 119335,  14523,    102,      0,
              0,      0]])
token_type_ids : tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
attention_mask : tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]])

Encoding / Decoding 작업 

tokenized_text = tokenizer.tokenize(user_question)
print(f"토큰 처리 : {tokenized_text}")

# encoding -> vocab의 ids로 표현하기기
tokenized_encoding = tokenizer.encode(user_question)
print(f"encoding : {tokenized_encoding}")

# encode된 정보를 decoding 처리해주기 -> cls, sep 토큰이 따라 나온다. 
tokenized_decoding = tokenizer.decode(tokenized_encoding)
print(f"decoding : {tokenized_decoding}")

# decode의 cls, sep 토큰이 안나오게 하고 싶으면?
tokenized_decoding = tokenizer.decode(tokenized_encoding , skip_special_tokens=True)
print(f"decoding w/o special token: {tokenized_decoding}")

# 아니면 encoding할 때 부터 special token을 안넣어주면 된다. 
tokenized_encoding = tokenizer.encode(user_question , add_special_tokens=False)
print(f"encoding w/o special token : {tokenized_encoding}")

tokenized_decoding = tokenizer.decode(tokenized_encoding)
print(f"decoding w/o special token: {tokenized_decoding}")
# 토큰 처리 : ['안', '##녕', '내', '이름은', '오', '##혜', '##린', ',', '24', '##살', '##이지', '잘', '부', '##탁', '##해']
# encoding : [101, 9521, 118741, 8996, 78199, 9580, 119437, 27654, 117, 10233, 106249, 44359, 9654, 9365, 119335, 14523, 102]
# decoding : [CLS] 안녕 내 이름은 오혜린, 24살이지 잘 부탁해 [SEP]
# decoding w/o special token: 안녕 내 이름은 오혜린, 24살이지 잘 부탁해
# encoding w/o special token : [9521, 118741, 8996, 78199, 9580, 119437, 27654, 117, 10233, 106249, 44359, 9654, 9365, 119335, 14523]
# decoding w/o special token: 안녕 내 이름은 오혜린, 24살이지 잘 부탁해

 

pretrained 된 model 과 tokenizer를 불러오게 되면 내가 풀어야 하는 task와 vocab이 안맞을 수가 있다. 

이런 에어비앤비 후기를 데이터셋으로 사용한다고 하면 

깟뻬뜨랑 우뤼갸 같은 건 vocab에 없는 단어이므로 UNK로 인코딩하게 되고, UNK가 많아질수록 의미가 희석된다. (안 좋다는 뜻!)

그래서 vocab에 새로 추가를 해줘야 한다! 

special token도 추가해줄 수 있는데 이때는 dictionary 형식으로 주어야 한다. 

added_token_num = tokenizer.add_tokens(["깟뻬뜨랑", "케쇽", "우뤼갸", "쳥쇼", "섀료"])
print(added_token_num)

added_token_num += tokenizer.add_special_tokens({"additional_special_tokens":["[SHKIM]", "[/SHKIM]"]})
print(added_token_num)

추가해준 token의 개수를 꼭꼭 알아야 하는데 vocab size가 바뀌면 token embedding을 resize해줘야하기 때문이다.

print(model.get_input_embeddings())
model.resize_token_embeddings(tokenizer.vocab_size + added_token_num)
print(model.get_input_embeddings())

# Embedding(119547, 768, padding_idx=0)
# Embedding(119554, 768)

 


마스터님의 말대로 5일차부터 진행을 해볼 계획인데, 오늘 수업을 듣고 몇 가지 해볼만한 일들을 생각해봤다. 

 

1. 형태소로 분리한 후 wordpiece를 사용

2. entity embedding을 추가해서 entity1, entity2에 적용해보기 

3. entity embedding을 추가해서 주요 entity에 적용해보기 -> 이거 좀 어려울 거 같은데?

4. UNKNOWN 으로 인식되는 토큰들을 vocab에 추가해보기

4. UNKNOWN 으로 인식되는 entity들만 vocab에 추가해보기

 

오늘의 Mission

 

챗봇 만들기! 단순 cosine similarty로만 비교하니까 말도 안되는 답변을 해준다...

 

그리고 너무 느려서 loader를 만들어서 batch로 줬는데 더 느려졌다,,,, 어디가 문제인 것인가

 

 

오늘의 회고

아까 진정한 회고를 하면서 글을 썼는데 날라갔다😢

오늘 Notice 채널에 김상훈 마스터님이 이런 글을 올려주셨다. 

 

안녕하세요! 캐글 그랜드마스터 김상훈입니다.

랩업 리포트와 피어세션을 열심히 참여 하시면 좋은 이유를 10년 이상 현업에서 머신러닝 엔지니어 활동하면서 얻은 경험을 바탕으로 말씀 드려 봅니다. :)

1. PPT 발표와 랩업 리포트 작성을 열심히 하시면 좋은 이유!

- 현실에서 여러분은 주기적으로 자신이 개발하는 있는 모델을 (여러 분이 무엇을 하고 있는지 많이 궁금해 할 팀장 또는 상위 리더들에게) 발표 형태로 공유해야 합니다. 그 동안 어떤 ML문제를 풀어왔고 어떤 AI모델을 개발했는지 등을 PPT 녹여내서 발표합니다. 그래서 랩업 리포트를 열심히 만드시고 기회가 된다면 PPT 발표를 망설이지 마세요. 다 현업에서 필요한 일들입니다. 특히 취업하려면 발표와 커뮤니케이션을 잘 하셔야 한다는 것도 잊지 마세요.

2. 피어세션에서 나서서 말씀을 하시면 좋은 이유!

- 사업팀에서 요구하는 AI모델을 개발하기 위해서는 그들이 필요로 하는 것이 어떤 AI모델인지 알아낼 수 있어야 합니다. 사업팀 또는 고객의 요구에 의해 AI모델을 개발하는 경우가 굉장히 많습니다. 예를 들어 e커머스 회사에서는 고객이탈을 방지하는 모델을 함께 개발해 달라는 요청이 올 수 있습니다. 그 때, 네? 하고 반문하는 것이 아닌 어떤 경우를 고객이탈로 생각하시는 지(예:몇 개월 동안 구매가 없으면), 이탈예측은 할 수 있지만 이탈방지는 AI모델로 어려울 것 같은데 기존에 어떤 것을 생각하고 계시는지 등등을 이야기 해볼 수 있습니다. 근데 보통 이런 이야기는 처음 보는 사업부 팀 2~4명과 미팅 시간에 하게 됩니다. 가끔은 필요한 데이터셋을 얻기 위해서 다양한 부서와 함께 미팅을 해야 할 수도 있습니다. 그러니 피어세션에서 기회가 된다면 어떻게든 말씀을 꺼내보려 노력해 보세요. 참고로 미팅은 무엇을 말할지 충분히 준비한 뒤에 들어가는 것입니다. 피어세션도 마찬가지로 무엇을 이야기할지 준비하여 들어가 봅시다.

이야기를 해보고 기대한 반응과 다르다면, 주제가 문제인지 방법이 문제인지 등을 살펴보고 다음번에 다르게 시도해보실 수도 있습니다. 피어세션을 오히려 자기계발을 위한 기회로 활용하시면 좋겠습니다. 

 

특히 피어세션에 대해서 많은 생각을 하게 되었다.

랜덤 피어세션을 진행하면서 매일 새로운 사람들을 만나다 보니, 어색함을 항상 풀어야 했고 주로 먼저 나서서 어색함을 푸려고 하다보니 피로감이 쌓여만 갔었다. "처음 본 사람들이랑 무슨 얘기를 나누라는 거야 대체 어색해서 나올 말도 없겠네;'라고 종종 생각했었다. 엔지니어로 일하게 되면 처음 보는 고객, 처음보는 사업부 팀원들과 미팅을 가지게 된다고 한다. 그때도 똑같이 새로운 사람들이랑 뭘 하겠냐 이러면서 탓만 하고 있을 순 없을 것이다. 피어세션을 그런 미팅의 연습장으로 삼고 처음 보는 사람과 대화를 나누며 내가 원하는 내용을 이끌어내고, 원하는 방향으로 주도하는 방향으로 도전해봐야겠다. 

또, 미팅과 같이 피어세션에도 무엇을 이야기할지 준비하며 들어가보라는 마스터님의 말에 자기 반성이 좀 됐다.

항상 아무 생각없이 피어세션에 들어가서 떠오르는 말들을 하곤 했는데, 좀 더 주도적으로 준비해보라는 뜻이신거 같다.

내일부터는 피어세션에 들어가기 전 아래 두가지를 정리해서 들어가보면 어떨까?

1. 어떤 정보를 얘기해주고싶은지

2. 어떤 정보를 얻고 싶은지