2019년 1월 10일 목요일

Bert-Multilingual model을 이용해 KorQuAD 수행해보기 Part 1 훈련 및 평가

지난 2018년 10월 구글에서 기존 SQuAD 1.1 task에서 human performance를 넘어서는 BERT 모델에 대한 논문을 발표했다. SQuAD는 현재는 SQuAD dataset이 2.0으로 업그레이드( 더 어렵게) 되었고, 구글이 BERT로 SQuAD 2.0에서도 1등을 한 이후(2018. 11/15), 여러 팀들이 BERT에 여러 아이디어를 추가해 구글을 넘어서는 결과를 만들어냈다(아직까지 human performance를 넘지는 못했다). 이처럼 BERT 모델은 현재 NLP 분야에서 기본적으로 응용해야 하는, 꼭 알아야 되는 모델이 되었다.

그리고 지난 12월 LG CNS에서 한국어로 된 SQuAD 1.0과 같은(2.0와 같은 형식은 아니다.) 데이터셋 KorQuAD1.0을 공개했다. https://korquad.github.io/

그래서 BERT 모델과 KorQuAD 데이터셋을 이용해 QA task를 수행하는 과정을 정리해보려고 한다. 그리고 과연 BERT를 한국어에 적용했을 때 성능은 얼마나 좋을 지도 확인할 수 있는 좋은 기회이기도 하다.

모델 준비

*유의 사항
BERT 모델을 train, inference(predict)에 이용하기 위해선 최소한 8G의 gpu 메모리가 필요한 것 같다. 1060 6g 모델에서는 out of memory가 뜨고 codalab의 Tesla m60 8g gpu를 썼을 때는 괜찮았다.

영어 모델을 한국어에 적용하는 과정은 얼핏 생각하기에 어려울 수도 있겠지만 구글이 친절하게(무섭게도) Multilingual BERT 모델을 공개해 놓았다. 이 모델은 100개 이상의 언어에 적용할 수 있다. ㄷㄷㄷ...

https://github.com/google-research/bert
에 가면 Readme 파일 제일 윗부분에 아래 그림처럼 링크가 걸려있다.

파란 글씨 BERT-Base, Multilingual Cased를 누르면 압축파일이 다운받아지고, 압축을 풀면 된다.


데이터셋 다운로드

위에 써 있는대로 https://korquad.github.io/ 에서 데이터셋을 다운받을 수 있다.
train, dev 데이터와 evaluate script까지 다운로드 받는다.

Fine-tuning 

이제 Pre-trained 모델을 KorQuAD task에 맞게 fine-tuning하기 위해 https://github.com/google-research/bert repository를 clone하거나 다운로드 받는다.

이제 준비가 다 끝났다.
argument를 맞춰서 실행해주면 된다.

python run_squad.py
--bert_config_file "pretrained 모델 폴더의 bert_config.json"
--vocab_file "pretrained 모델 폴더의 vocab.txt"
--output_dir "훈련된 모델이 저장될 폴더" (prediction 결과도 이 폴더에 저장된다.)
--do_train (훈련을 하겠다는 옵션)
--train_file "KorQuAD  데이터셋 폴더의 KorQuAD_v1.0_train.json"
--do_predict (predict 하겠다는 옵션)
--predict_file "KorQuAD 데이터셋 폴더의 KorQuAD_v1.0_dev.json"
--do_lower_case=false (현재 다운받은 Cased 모델은 이 옵션을 적용하지 않는다.)
--max_seq_length 적당히
--train_batch_size 적당히
--init_checkpoint "pretrained 모델 폴더"

max_seq_length와 train_batch_size 옵션은 메모리 사용량과 관계가 있다.
아래와 같이 구글에서 12G 메모리 기준으로 max_seq_length와 train_batch_size를 얼마로 해야 좋은지 실험한 결과를 공개했다.
이를 참고하여 쓰는 gpu의 메모리에 맞게 값을 조정하면 되겠다.

그 외 메모리와 관련된 추가적인 사항은 https://github.com/google-research/bert#out-of-memory-issues 를 참고하면 된다.

gpu의 성능에 따라 다르겠지만 훈련에는 대략 2~5시간 정도 소요될 것으로 예상된다.
epoch는 기본으로 3으로 설정되어 있다.

점수 평가하기

훈련이 잘 끝났다면 output_dir에 훈련된 모델 파일과 prediction.json 파일이 생겼을 것이다.
점수를 평가하기 위한 명령을 실행하여 점수를 확인해 본다.

python evaluate-v1.0.py KorQuAD_v1.0_dev.json predictions.json
evaluate-v1.0.py는 KorQuAD 홈페이지에서 받을 수 있으며 3가지 파일의 위치를 잘 지정해서 실행하면 내가 훈련한 모델이 낸 답들을 평가할 수 있다.

꽤 높이 나와서 놀랐다.
내가 한 건 단순 다운로드 & 실행 뿐인데...


이제 내가 훈련한 모델을 KorQuAD leaderboard에 등록할 차례이다.
이 부분에 대한 포스트는 다음에 작성하도록 하겠다.

여기까지 수고 많으셨습니다.

댓글 50개:

  1. 똑같이 해봤는데, 각각 Exact matching 1퍼... F1 score 5퍼 내외가 뜨네요; 근데 애초에 vocab이 거의 다 없던데... 성능이 어떻게 나오죠??

    답글삭제
    답글
    1. 음... 똑같이 했는데 저렇게 훈련이 안됐다면 이유가 잘 생각이 안나네요.
      Cased 모델 받은 게 맞나요? 아니면 3 epoch를 다 돈 건가요?
      저는 그냥 제가 쓴 명령어대로 훈련 + 평가만 하면 거의 f1 90 가까이 나오던데요...
      파이썬3을 썼는데 파이썬 버전도 별 문제가 될 거 같진 않고요. ..

      vocab이 없어 보이는 이유는 byte-pair 인코딩을 써서 vocab 내의 단어가 거의 글자 단위로 쪼개져서 그럴 거에요. 물론 그렇다고 하더라도 성능이 잘 나오는 게 신기하긴 합니다.

      삭제
    2. F1 score 5퍼 정도면 거의 숫자만 맞춘 거 같기도 한데
      https://github.com/google-research/bert/pull/228
      이걸 참조해서 한 번 시도해보세요.
      저는 저렇게 수정하나 안하나 차이가 없긴 했어요.

      삭제
    3. 계속 요청만 하게 되어 죄송한데요. byte-pair 인코딩은 글자가 기호로 표기되는 것 같네요. 한글로 변환하려면 어떻게 해야 할까요?

      삭제
    4. 실제 사용되는 vocabulary는 다운 받은 multilingual-cased-base 폴더 안에 vocab.txt 형태로 되어 있습니다.
      그 안에서 한글로 된 토큰들을 보시면 될 것 같네요.
      입력된 텍스트를 vocabulary를 이용해서 byte-pair 인코딩하면
      많이 나온 어휘들은 그대로 나오고, 잘 안 나오는 단어들은 [UNK] - Unknown 이란 뜻 - 로 표기됩니다.
      예를 들자면,
      "잭팟이 터졌다." => [UNK] ##이 터 ##졌다 .
      이런 식으로 될 것입니다.

      궁금하신 점이 있을 때 조금 더 정보를 주셨으면 좋겠습니다.

      삭제
  2. 수정해보라고 하신대로 수정해서 돌려보니 말씀하셨던 성능과 비슷하게 나오네요. 감사합니다.

    답글삭제
    답글
    1. 저도 같은 증상으로 헤메고 있는데요. 코드의 어떤 부분을 수정하셨는지 좀 알려주세요? 감사합니다.

      삭제
    2. 윗분이 말씀하신 수정 내용은
      https://github.com/google-research/bert/pull/228
      여기 가 보시면 나와 있습니다.

      삭제
    3. 네, 저도 읽어봤는데 정확히 어떤 부분을 고치라는 건지 잘 이해가 안 가네요. 문맥상 _run_strip_accents() 함수가 문제가 있어 보여 remark 처리했더니 예제와 같은 결과를 얻었습니다. 그리고 어제 run_squad.py 를 재수행하고 오늘 아침 와서 확인해 보니 f1이 그대로네요ㅠ

      삭제
    4. 링크에서 file changed 부분을 보시면 어떤 파일을 어떻게 고쳐야 하는지 나와 있습니다.

      삭제
    5. 나와 있는데로 파일을 수정하여 재 수행하였습니다. 스코어는 아직 f1 = 18.0, exact = 5.611 수준입니다 ㅠㅠ.

      삭제
    6. 한글 같은 경우 NFD 정규화를 수행하게 되면 다음과 같이 tokenization이 됩니다.
      예시) 한글 -> ㅎ ㅏ ㄴ ㄱ ㅡ ㄹ
      영어의 경우 korean 의 문자가 있으면 NFD 정규화를 수행했을때,
      예시) korean -> korean
      이와 같이 됩니다.
      NFD 로 수행할 경우 한국어 처리 모델에서 원하는 결과를 얻기가 어렵습니다. (본래의 단어 형태를 잃어버리는 것으로 인한 것으로 생각합니다.)

      따라서 다음과 같이,

      tokenization.py 파일
      _run_strip_accents 함수의 text = unicodedata.normalize("NFD", text) 부분을
      text = unicodedata.normalize("NFKC", text) # NFKC 유니코드 정규화방식 KC (호환분해 후 정준결합) or NFC
      로 수정하셔서 코드를 다시 실행하는 방안을 적용하시는게 어떠신가요?

      삭제
  3. 저는 exact_match: 2.44, f1: 11.78 이렇게 나옵니다. 물론 완전 똑같이 했구요;;; korQuAD라도 잘 나와야 제 도메인영역으로 데이터를 구성해 볼텐데요;;;

    답글삭제
    답글
    1. 애매한 수치네요;;
      그정도면 epoch이나 step이 적은 느낌이 들긴 하는데, 댓글과 점수만 보고 어디서 문제가 생겼는지 파악해보기는 힘드네요.
      코드 링크나, 명령어나, 실행 환경이나... 그런 것들이 좀 더 필요할 것 같습니다.

      삭제
    2. 작성자가 댓글을 삭제했습니다.

      삭제
    3. 정확도는 아직 해결 못했지만, 어쨌든 한 싸이클 실행하는데에는 성공했습니다. 감사합니다.
      높은 점수로 만들어진 모델파일을 가지고 실제 테스트한 실험에 대한 블로그도 부탁드립니다^^
      즐겨찾기 꾸욱!

      삭제
    4. 실제 테스트는 뉴스 기사에 대한 QA를 해 봤습니다.
      질문들은 명확한 답이 있도록,
      어디에서 촬영했나? 배급사는 어디인가? ~의 배우자 이름은? 접수된 피해 신고는 몇 건인가? ~ 결정을 내린 곳은 어디인가?
      이런 식의 질문들이었고요.

      정확도는 KorQuAD 데이터를 갖고 할 때랑 거의 비슷하게 나왔습니다.
      학습할 때 뉴스 데이터를 같이 넣어서 하면 뉴스에 대해서 좀 더 올라갔고요. 이 때 KorQuAD의 성능은 별로 달라지지 않았습니다.

      실제 테스트에 대한 내용은 위에 쓴 말 빼고 별로 쓸 말이 없어서요... 따로 글을 쓰기엔 내용이 부족한 것 같다고 생각하고 있습니다;;

      삭제
    5. 작성자가 댓글을 삭제했습니다.

      삭제
    6. 온라인 상에서 이것 저것 테스트 해보고 싶은데 QA테스트를 하려면 코드 어느 부분을 수정해야 할까요?

      삭제
    7. 이것 저것이 무엇인지 모르겠지만 일단 이것 저것에 해당하는 데이터를 불러오는 부분을 고쳐야 하겠네요.
      그리고 그 데이터 형식이 KorQuAD랑 같아야 테스트할 수 있을 것 같네요.

      코드를 많이 이해하지는 못한 것 같은데, 그렇다면 제가 '어디'를 수정하라고 말씀드려도 래를 님이 '어떻게' 수정할 지를 모르시지 않을까요?

      삭제
  4. 실제 사용하신 스크립트를 좀 보여주실 수 있을까요?
    저는 shape 에러가 뜨네요.ㅠ

    답글삭제
    답글
    1. 제가 딱히 소스를 크게 건드리지 않았어서 실제 google-research 깃헙에 있는 거랑 별로 다른 게 없습니다.
      에러 메세지를 올려주신다면 좀 더 도움이 되겠지만 저보다는 stackoverflow나 facebook tensorflowKR 같은 데 문의하시는 게 더 좋을 것 같네요.

      삭제
    2. tensorflow.python.framework.errors_impl.InvalidArgumentError: Assign requires shapes of both tensors to match. lhs shape= [119547,768] rhs shape= [30522,768] 에러 코드입니다.

      삭제
    3. 자문자답이 되었네요. squad_base 폴더의 checkpoint를 삭제하고 다시 수행하니 돌아갑니다. 이전 다른 모델을 시험하느라고 형상이 안 맞게 되었나 보네요. 빠른 회신 감사합니다.

      삭제
  5. 사용하신 정확한 모델명이 궁금합니다.

    답글삭제
    답글
    1. 정확한 모델명이라는 게 무슨 뜻인지요?
      "BERT-Base, Multilingual Cased" 말씀하시는 건가요?

      삭제
  6. 네 같은 모델을 사용했는데 위와 같이 에러가 납니다. 에러는 전체가 길어서 일부만 표시하였습니다.

    답글삭제
  7. 작성자가 댓글을 삭제했습니다.

    답글삭제
  8. cased와 uncased의 차이가 궁금합니다. do_lower_case의 옵션의 역할은 무엇인가요?

    답글삭제
    답글
    1. lower case라고 구글에 검색하시면 알 수 있을 거라 생각합니다. https://en.wikipedia.org/wiki/Letter_case
      lower case는 소문자이고, 대문자는 upper case 입니다.
      do_lower_case는 corpus의 대문자를 소문자로 바꿀지를 결정하는 옵션입니다.
      flags.DEFINE_bool(
      "do_lower_case", False,
      "Whether to lower case the input text. Should be True for uncased "
      "models and False for cased models.")
      tokenization.py에 BasicTokenizer의 코드도 참고하시면 좋을 것 같습니다.

      uncased 모델은 corpus의 대문자를 소문자로 바꾸고 난 후 tokenize 한 모델입니다(이 경우 대소문자-case 구분이 안돼서 uncased라고 한 것 같아요).
      cased 모델은 대소문자를 그대로 놔둔 채로 tokenize한 모델이고요.
      이러면 두 모델의 vocabulary가 달라지겠죠. 두 모델을 다운 받은 후에 vocab.txt를 한 번 보세요.
      https://github.com/google-research/bert 의 Pre-trained models 섹션에 가보시면 좀 더 자세한 설명이 나와 있습니다.

      삭제
  9. 작성자가 댓글을 삭제했습니다.

    답글삭제
  10. 한 가지 여쭤 보고 싶어서 댓글 남깁니다.
    KorQuAD 데이터를 사용할 때 따로 어떤 전처리 없이 docQA를 실행하면 문맥, 질문, 대답이 생성이 되는 건가요?
    데이터 전처리 때 쓰이는 Parse Tree, Entity Coreference 같은 레이블링 없이도 QA 모델을 만들 수 있는지 궁금합니다.

    답글삭제
    답글
    1. KorQuAD는 주어진 문맥(context) 속에서 주어진 질문에 대한 답을 찾는 것이 문제입니다.
      문맥이 생성되는게 아니라 주어지고, 질문도 생성되는게 아니라 주어지고, 대답은 문맥 속에 있는 단어입니다.
      예를 들어 문맥에 "2019년 6월 11일에 A가 복권에 당첨되었다." 라고 되어 있고, 질문이 "2019년 6월 11일에 복권에 당첨된 사람의 이름은?" 이렇게 주어지면
      답을 "A" 라고 문맥 속에서 찾아 내야 하는 것입니다.
      현재 거의 대부분의 KorQuAD, SQuAD에 제출된 모델들에서 Parse Tree, Entity Coreference 같은 레이블링은 사용되지 않는 것으로 알고 있습니다.

      삭제
  11. 친절한 설명 잘 보았습니다~!
    구글의 wordpiece 대신 mecab 같은 다른 토크나이저를 사용하고 싶은데, tokenization.py 의 어느 쪽 부분을 바꿔줘야할까요?

    답글삭제
    답글
    1. 일단 corpus에서부터 vocabulary를 만드는게 먼저입니다.
      bert github에 vocabulary를 만드는 소스는 없고, README에 vocabulary에 관한 내용을 참고하셔서 따로 스크립트를 돌려야 합니다.

      형태소 분석기를 사용한다면 두가지 방법이 있는 것 같습니다.
      1. vocabulary가 형태소 그대로인 경우
      2. vocabulary가 형태소 분석 후 wordpiece를 적용한 경우

      1.이면 tokenization.py의 FullTokenizer에 있는 BasicTokenizer, WordpieceTokenizer를 없애고 형태소 기반 tokenizer를 만들면 될 것 같고요,
      2.이면 저는 BasicTokenizer(어절 기반(띄어쓰기 기준)) 안에 형태소 분석을 추가적으로 넣었습니다.

      그런데 mecab은 문장으로 형태소분석을 할 때랑 단어 단위로 형태소 분석을 할 때 결과가 좀 다른 것 같습니다.

      삭제
    2. 작성자가 댓글을 삭제했습니다.

      삭제
    3. 친절한 설명 너무 감사합니다!
      한 가지만 더 여쭤보고 싶은게 있어서요~

      2번 case의 경우 형태소 분석 한거를 WordPiece를 적용하여 vocab을 생성하는되는건지, 형태소 분석 결과 + WordPiece 결과 따로따로 해서 합쳐 vocab을 만들어도 되는건지 궁금합니다~

      삭제
    4. 저는 형태소 분석한 코퍼스로 wordpiece를 적용했습니다.

      삭제
  12. gpu를 사용하기 위해서 따로 설정해주어야 할 부분은 없나요..? 그냥 돌리니 cpu로만 돌아서 너무 오래걸리네요..

    답글삭제
  13. 작성자가 댓글을 삭제했습니다.

    답글삭제
  14. 안녕하세요, BERT 파인튜닝을 공부하다가 보게되어 잘 읽었습니다. 다만 제가 아직 잘 모르겠어서 그런데, 형태소분석기를 사용할 수 있는가요? 어떤 단계에 사용이 가능할지 가늠이 잘안되는것같습니다.

    답글삭제
    답글
    1. 안녕하세요 답변이 늦었습니다.
      기존에 사용되던 subword로 쪼개는 부분을 형태소 분석기로 대체할 수 있습니다.
      그 부분은 vocabulary를 만드는 부분입니다.

      삭제
  15. 안녕하세요? bert 검색하다가 우연히 들어왔는데 너무 정리를 잘해 주셔서 감사히 잘 읽었습니다.
    bert를 활용해 보고 싶은데요.
    질문이 있습니다.
    pretraining data에서 사용한 vocab 리스트와 finetuning에서 사용한 vocab 리스트가 일치해야 하나요?
    finetuning data가 외국인이 쓴 것이라 오류가 많이 포함되어 있어서
    기존 pretraining data의 vocab을 사용하면 unknow이 너무 많이 나올 것 같아서요.
    이 방법이 어렵다면 혹시 다른 좋은 방법은 없을까요?
    아니면 pretraining data에서 사용한 vocab 리스트에
    finetuning data로 만든 vocab 리스트를 추가해서 사용해도 될까요?
    시작 단계가 막연하게 질문드립니다.^^

    답글삭제
    답글
    1. 안녕하세요 답변이 많이 늦어 답을 찾으셨는지 모르겠지만 늦게라도 남깁니다.
      일단 답은, vocab 리스트가 같아야 한다는 것입니다.
      모델이 그 vocab 리스트 상의 단어들에 대해 학습되어 있습니다.
      그렇지만 Multilingual 모델은 한글을 포함한 여러 언어들을 모두 사용할 수 있도록 만들었기 때문에 그냥 finetuning 하셔도 될 것이라 생각합니다.
      하지만 오류를 걱정하신다면 방법은 다시 학습하는 수밖에 없습니다.
      Etri나 SKT에서 공개한 한글 모델이 있기 때문에 그걸 써보시는 것도 방법이 될 것 같습니다.

      삭제
  16. 안녕하세요.. HuggingFace Transformers 패키지를 이용하여 bert-base-multilingual-cased 모델을 이용하여 KorQuAD 파인튜닝을 시도하고 있습니다. 그런데 한글 조사 때문에 정답영역 계산하는 부분에서 오류가 발생하고 있습니다.
    예를 들어 정답이 '976년' 이고 지문 문장에 '... 976년에는 ...' 처럼 나타나서, 두개의 토큰 비교에서 일치가 발생하지 않아서 정답 영역 시작위치와 종료위치 계산식에서 오류가 발생하여, 학습에도 나쁜 영향을 미치는 것 같습니다. 이런 부분을 어떻게 해결해야 하는지 혹은 어떻게 해결하셨는직 궁금합니다. 도움 부탁드립니다.

    답글삭제
    답글
    1. 안녕하세요.
      제 기억으로는(하도 오래 되어서요...) 기본적으로 아무 처리를 추가하지 않아도 꽤 괜찮았었던 것 같고,
      좀 더 정확도를 높이기 위해 형태소 분석을 통해 조사 같은 걸 떼어내는 시도를 해 봤던 것 같아요.

      아니면 etri bert 같은 소스를 참고하시는 것도 좋을 것 같아요.

      삭제
  17. 선생님 안녕하세요! 제가 잘 몰라서 그러는데 저는 KorQuAD 의 기능 (질문이 들어오면 답변 제공)을 쓰고 싶은데, 이 글은 그냥 KorQuAD의 모델을 훈련시키고 평가 받는 것 맞나요? 저처럼 기능만 사용하고 싶으면 모델링은 필요 없을 것 같은데 혹시 어떤 식으로 사용해야 하는지 아시는지 여쭙습니다!

    답글삭제
    답글
    1. 안녕하세요.

      이 글은 pre-training 된 language model을 korquad dataset을 이용해서 fine-tuning하는 과정을 설명하는 글입니다.
      이렇게 학습된 모델을 이용해서 korquad 형식의 질의 응답 기능을 구현할 수 있습니다.

      이런 모델링을 안하고 싶으시다면, 누군가가 korquad처럼 질의응답이 가능한 api 같은 걸 제공해주어야 하는데 있는지는 모르겠습니다.
      (그 api 안에서도 어떤 식으로든 korquad + 그와 비슷한 데이터로 학습한 모델이 사용될 것입니다.)

      삭제