2019년 5월 19일 일요일

논문 리뷰 - What do you learn from context? Probing for sentence structure in contextualized word representations

지난 번 포스트에 이어서 Contextualized word representation 모델들에 대해 실험한 논문을 리뷰해보려 합니다. 이번 논문의 제목은 What do you learn from context? Probing for sentence structure in contextualized word representations. 입니다. Google AI Language와 여러 대학에서 공동으로 작업했습니다.

논문 링크 : https://openreview.net/pdf?id=SJzSgnRcKX 

일단 기본적으로 실험을 통해 모델들의 능력을 알아보고, 어떤 task에서 잘 하는지를 분석합니다.  실험에 대한 디테일한 부분보다는 결과에 대한 해석이 중요하다고 생각하여 그 부분을 위주로 정리하겠습니다. 아쉬운 부분이 있다면 성능의 평가가 ELMo를 중심으로 되어 있고, GPT, BERT에 대한 분석은 큰 비중이 없습니다.

실험 task들


POS tagging
Consituent labeling
Dependency labeling
Named entity labeling
Semantic role labeling
Coreference
Sementic proto-role
Relation classification

사용 모델


CoVe (McCann et al., 2017)
Elmo (Peters et al., 2018a)
GPT (Radford et al., 2018)
BERT (Devlin et al. 2018) base, large

실험 


1. Lexical baselines

이 실험 세팅은 contextual encoder를 통해 얻은 embedding vector의 성능을 알아보기 위한 세팅입니다. 각 모델들을 학습시킨 후, input으로 사용되는 벡터만 이용하여 각 task들에 대한 성능을 측정하고, 학습된 모델의 output을 사용하여 측정한 성능과 비교합니다. 모델을 통해 얻은 lexical prior와, 모델이 lexical prior에 context를 결합한 정보를 비교하는 것입니다.

CoVe의 input은 300 dimension의 Glove이며, output은 영어-독일어를 번역하면서 학습한 2층의 biLSTM에 input 벡터를 concatenate한 900차원의 벡터입니다. 이렇게 input과 output의 벡터를 concatenate하여 얻은 결과를 cat라 표기합니다.
ELMo의 input은 character-CNN을 통해 얻은 단어의 벡터이며 output은 2-layer biLSTM에서 얻을 수 있는 각 단어의 벡터를 weight parameter를 이용하여 조합한 1024 dimension의 벡터입니다. 앞으로 이렇게 각 layer의 벡터들을 조합하여 얻은 결과를 mix라 표기합니다.
GPT, BERT base모델의 input은 768 dimension의 subword embedding이며 BERT large 모델의 input은 1024 dimension입니다. 실험에서 cat, mix 방식 통해 얻은 output을 사용합니다.


모델 간의 성능을 비교해보면, ELMo와 GPT가 CoVe보다 6.3정도 F1이 높고, BERT는 이 둘보다 2~3정도 F1이 높았습니다. ELMo와 GPT의 input 벡터는 Glove 벡터보다 훨씬 높은 성능을 보여줍니다. Parsing과 SRL( + Rel)에서 매우 큰 성능 향상을 보였는데, constituent parsing이나 semantic role labeling 같은 경우 character나 subword 정보가 많은 도움이 되기 때문에 그렇다고 합니다.
논문에는 없는 내용이지만 어떤 task에서는 ELMo나 GPT의 input 벡터로 얻은 성능이 CoVe 모델의 output을 이용할 때보다도 좋은 경우도 있네요. 그리고 ELMo의 input 벡터는 BERT의 input 벡터보다도 좋은 성능을 보입니다. BERT 모델의 output을 이용한 성능과도 크게 차이가 나지 않으며 parameter 개수의 차이를 고려하면 꽤 경쟁력이 있는 것 같습니다.

또한 ELMo 스타일의 벡터 조합(mix)을 GPT나 BERT에 사용했을 때 성능을 더 향상시킬 수 있었습니다. 이를 통해 중간 layer들에도 성능 향상에 도움을 주는 정보들이 많이 담겨 있다는 것을 알 수 있으며 이런 내용은 Blevins et al. (2018), ELMo, BERT 논문에서도 확인할 수 있습니다. 그리고 Peters et al. (2018b)에서는, 마지막 layer가 next-word prediction을 위해 overly specialized 되어 있다고 합니다.

BERT 모델은 OntoNotes, Winograd coref에서 ELMo에 비해 많은 성능을 향상시켰는데, 이를 통해 깊은 비지도학습 언어모델이 semantic task의 성능을 높이는데 좋아 보이며, 모델을 더 깊게 하면 더 좋은 결과를 얻지 않을까 예측합니다.

Input 벡터를 사용했을 때와 비교하여 위의 모델들을 사용했을 때 가장 성능 차이가 컸던 task는 syntactic한 정보가 필요한 constituent, dependency labeling이며 성능 차이가 가장 적었던 task는 semantic한 SPR이나 Winograd입니다. POS tagging이나 entity labeling에서도 성능 차이가 작지만 이는 word-level 벡터가 이미 많은 정보를 담고 있기 때문이라고 추측합니다.

Semantic Role labeling은 모델들을 통해 많은 성능 향상이 있었는데, core-role을 찾아내는 능력이 많은 기여를 했으며 core-role을 찾는 능력은 syntactic 정보를 얻는 능력과 관련이 있다고 합니다(Punyakanok et al. (2008), Gildea & Palmer(2002)). SRL 중에서도 semantic 정보가 중요한 purpose, cause, negation과 관련된 non-core labeling에서는 성능 향상 정도가 비교적 적었습니다. 그리고 semantic encoding 능력이 더 요구되는 SPR에서는 성능이 더 적게 향상되었습니다.

Relation classification task도 semantic reasoning 능력이 많이 요구되는데, 모델을 이용했을 때 성능 향상이 큰 것으로 보입니다. 하지만 그 이유는 input 벡터를 사용했을 때 정확도가 많이 낮기도 하고, 어떤 relation은 문장 내에서 중요한 단어만 보아도 쉽게 분류를 할 수 있기 때문으로 보입니다. 실제로 bag-of-words feature를 사용했을 때 ELMo의 70%에 해당하는 성능을 얻었습니다.(다른 실험들에서는 20~50%정도)


2. Randomized ELMo, non-local context.

이 실험은 훈련된 모델의 효과를 알아보기 위한 실험입니다. ELMo의 네트워크 구조는 그대로 유지한 채 파라미터를 random orthonormal matrices로 대체한 모델과 학습된 ELMo 모델의 성능을 비교합니다. Input은 동일합니다.
여기에 추가적으로 window size 1, 2(앞뒤로 1, 2개의 단어)인 word-level CNN을 사용해 얻은 벡터만 이용하여 성능을 측정합니다. 이를 통해 ELMo가 얼마나 앞뒤로 멀리 떨어진 단어의 정보들도 사용하는지를 비교합니다.


빨간 네모로 표시된 성능과 보라색 원으로 표시된 성능을 비교하면 ELMo의 파라미터가 random하게 분포할 때와 학습된 파라미터를 이용할 때의 성능을 비교할 수 있습니다. Random한 파라미터를 가질 때도 input 벡터만을 이용했을 때보다 성능이 좋지만 학습된 파라미터들은 ELMo가 향상시킨 성능 중 약 70% 정도를 담당합니다.
그런데 Winograd 에서는 random 파라미터의 성능이 학습된 파라미터의 성능보다 좋네요..

CNN을 이용하여 3~5개의 단어 정보를 같이 활용할 경우 ELMo를 사용했을 때에 비해 72, 79% 정도의 성능을 냈습니다. POS, constituent, dependency task같은 경우 80% 이상의 성능을 냈습니다. 이를 통해 이런 syntactic한 task들에서는 멀리 떨어져 있는 단어들은 별로 참조할 필요가 없다는 것을 알 수 있습니다. 반대로 semantic한 task들인 coreference, SRL non-core roles, SPR에서는 ELMo와의 차이가 컸습니다. 이를 통해 ELMo가 semantic한 정보들을 잘 뽑아내지는 못하지만 그래도 성능을 향상시킨 부분들에서는 멀리 떨어진 정보들을 잘 조합했다고 볼 수 있습니다.

ELMo가 멀리 떨어진 단어들의 정보를 잘 참조한다는 것은 아래 그래프를 통해 알 수 있습니다. Dependency labeling task에서 ELMo는 단어가 멀리 떨어져 있어도 단어가 가까울 때보다 성능이 크게 떨어지지 않습니다.



2019년 5월 8일 수요일

형태소 분석기를 이용한 Bert-Multilingual Model 기반 Korquad 결과의 성능 개선

제목이 논문 제목같다...

차례

1. Konlpy가 설치된 도커 만들기
2. 형태소 분석을 통해 마지막 조사를 제거하도록 run_squad.py 수정
3. codalab에 제출하기

공개된 Multilingual Bert 모델을 써보니 vocab 때문인지 몰라도 조사가 답에 포함되는 경우가 많았다.

예 :
이전 업데이트가 내장 메모리에 저장되었던 것과는 달리 이 업데이트는 최초로 무엇을 요구하는가?
prediction:  저장 장치를 truth :  ['저장 장치']

엑스박스 360의 물품 중  별도로 판매되고 있는 것은?
prediction:  액세서리를 truth :  ['액세서리']

이런 조사 한두글자 덕분에 내 EM이 한 10 정도 낮다...
그래서 예전부터 시도해보려고 한 형태소 분석기를 이용하여 조사를 떼어내어 정확도를 높이는 작업을 해보려고 한다.

Konlpy가 설치된 도커 만들기

일단, konlpy의 코모란을 사용하려고 하는데, codalab에서 konlpy를 이용하려면 konlpy가 설치된 나만의 도커를 만들어서 그 도커를 codalab에서 불러와 실행하야 한다. 그래서 도커를 우선 만들어야 한다.
참고 url : https://github.com/codalab/codalab-worksheets/wiki/Creating-Docker-Images

(새로운 폴더를 만들고,) " Vim Dockerfile"을 입력해 도커파일을 만든다. 파일 내용은 아래와 같다.

# 기본 이미지는 gpu를 쓰기 위해 tensorflow gpu 지원 오피셜 이미지를 쓴다. 파이썬 버전과 tensorflow 버전에 맞춰 쓴다.
FROM tensorflow/tensorflow:1.12.0-gpu-py3
MAINTAINER "dockerhub id" <이메일>
RUN apt-get -y update
# konlpy의 형태소 분석기에 필요한 자바를 설치한다.
RUN apt-get -y install openjdk-8-jdk
# Language pack을 설치하고 환경변수를 설정해주지 않으면 한글을 파일에 쓰지 못한다. 기본 Decoder가 ascii 코드를 쓴다.
RUN apt-get -y install language-pack-ko
ENV LANG ko_KR.UTF-8
RUN pip3 install konlpy

도커 허브에 회원 가입을 한 후에, repository를 만들고, terminal 상에서 "docker login" 명령어를 통해 로그인한다.

이후, "docker build -t 도커허브id/repository이름:태그이름 "을 입력하여 이미지를 생성한다.
그리고 "docker push 도커허브id/repository이름:태그이름 "을 입력하면 konlpy가 설치된 이미지가 docker hub에 업로드 된다.


run_squad.py 수정

이 부분은 사실 크게 중요하지 않은 듯 하다. 알아서 잘 하실 거라 생각한다...

write_predictions 함수 시작 부분에
from konlpy.tag import Komoran
pos_analyzer = Komoran()
를 추가하고,

all_predictions[example.qas_id] = nbest_json[0]["text"]
이 코드에서 사용되는 nbest_json[0]["text"]를 미리 형태소 분석을 통해 마지막에 붙은 조사를 띄어내도록 수정한다.


Codalab에 제출

이전에 작성한 codalab에 korquad 결과를 제출하는 포스트를 참고하여 결과를 확인한다.
--request-docker-image의 값을 내가 생성한 도커로 설정하면 된다.

아직 리더보드에 반영은 되지 않았지만 EM은 확실히 10 정도 올랐다!
F1은 사실 형태소 분석을 통해서 개선되는 부분이 기껏해야 한두글자이고, 많지는 않지만 형태소 분석기가 조사로 잘못 인식해 없애버리는 글자들도 있고 해서 한 1점 정도 올랐다.(로컬 환경과 codalab 상에서 평가한 점수도 차이가 좀 있고)
이정도 점수면 한 16등 할래나... ㅎㅎ

2019년 5월 6일 월요일

논문 설명 - Learning and Evaluating General Linguistic Intelligence

Intro

작년 한 해 Contextual Embedding을 만들 수 있는 Language Model들이 많이 나왔습니다. 이런 모델들은 자연어 처리의 여러 문제들에서 기존의 모델들보다 좋은 평가 결과를 냈습니다. 이후, 이런 모델들을 분석한 논문들도 나와서 읽어보고 정리해 봤습니다.

논문 1. "Learning and Evaluating General Linguistic Intelligence" by DeepMind, 업로드 날짜 : 2019. 1.
https://arxiv.org/pdf/1901.11373.pdf

논문 2. "What do you Learn from Context? Probing for Sentence Structure in Contextualized Word Representations" by Google AI Language, etc, ICLR 2019.
https://openreview.net/pdf?id=SJzSgnRcKX


요약

두 논문 모두 실험을 통해 모델의 능력들을 평가한다는 공통점이 있습니다. 각 논문을 간단히 소개해보겠습니다.

논문 1의 중요 단어는 "General" 입니다. 특정 task의 특정 dataset으로 훈련시킨 언어 모델이 같은 task의 다른 dataset으로 평가했을 때 얼마나 빨리, 얼마나 좋은 성능을 내는지, 또 다른 task에 적용했을 때는 얼마나 빨리, 좋은 결과를 보여주는지에 관심이 있습니다. 결론적으로 아직 갈 길이 먼 것 같습니다.

논문 2의 중요 단어는 "Context"입니다. 제목에 두 번이나 나오죠. 이 논문의 결론은, "Context를 반영한 word embedding이 주로 syntactic한 정보들을 많이 반영하며, semantic한 정보들은 별로 갖고 있지 않다." 입니다.

이제 본격적으로 두 논문을 하나씩 좀 더 자세히 살펴 보겠습니다.


Learning and Evaluating General Linguistic Intelligence



사실 DeepMind가 아니면 이런 거창한 제목을 붙이기도 힘들었을 것이라 생각합니다. 아무리 BERT가 대단하다고 해도 말이죠. 아래는 논문 Abstract의 첫 문장입니다.
"We define general linguistic intelligence as the ability to reuse previously acquired knowledge about a language’s lexicon, syntax, semantics, and pragmatic conventions to adapt to new tasks quickly"
논문에서는 General linguistic intelligence를 이미 습득한 단어, 문법적, 의미론적, 실용적(?) 지식을 새로운 task에 맞게 빠르게 재사용하는 능력이라 정의합니다. 그리고 이를 반영할 수 있는 학습 평가 지표도 제시합니다.

Introduction

최근까지의 연구는 특정 데이터에 맞는 모델을 설계하여 좋은 성능을 얻는 것이 목표인 경우가 많았습니다. 이런 경향 때문에 어떤 task에서 좋은 성능을 얻었다고 해서 그만큼 언어에 대한 지능, 이해가 높아졌다라고 보기는 힘들었습니다.

이런 가운데 매우 큰 데이터로 unsupervised pretraining을 거친 후, 각 task, dataset에 맞게 fine-tuning을 하는 language model들이 등장했습니다(ELMo, GPT, BERT 등). 이런 경우 pretrained된 모델은 특정 dataset에 맞게 설계, 학습된 모델보다 상대적으로 일반적인 언어지능을 가졋다고 볼 수 있겠죠.

또한 여러 task들을 동시에 학습하는 multi-task learning도 등장했습니다. 이렇게 학습된 모델도 좀 더 일반적인 언어 지능을 갖고 있겠죠.

논문에서 얘기하는 일반적인 언어 지능의 요소에는
1. 여러 task를 수행할 수 있는(아마 ELMo, GPT, BERT 등이 이런 능력을 갖고 있다고 볼 수 있을 것 같습니다.) 능력
2. 단어 표현을 쉽게 저장하고, 재사용하고 조합(단어 -> phrase, 문장, 문서)하며 이미 습득한 언어 지능을 재활용하는 능력
3. 조금의 학습만으로 새로운 task에 잘 적응할 수 있는 능력
들을 얘기합니다. 이 중에서 이 논문에서 알아보고자 하는 능력은 3. 에서 설명하는 능력입니다.


Tasks

모든 task들에 대해 실험을 하기 어려우니 논문에서는
Reading Comprehension(QA) - (데이터셋 : SQuAD 1.1, TriviaQA, QuAC),
Semantic Role Labeling,
Relation Extraction,
Natural Language Inference - (데이터셋 : MNLI, SNLI)
4가지 task에 대해 실험하고 결과를 분석합니다.

Models

사용한 모델은 ELMo + BiDAF, BERT 입니다.

Evaluating Transfer

위에서 언급했던 것처럼 얼마나 빨리 학습되는가를 평가하기 위한 지표인 "codelength"를 제시합니다. 수식은 아래와 같습니다.
|y| 는 라벨의 개수입니다.
A는 {(x_i, y_i)}, i : 1 ~ N : N개의 데이터 x와 라벨 y의 쌍으로 이루어진 데이터셋입니다.
A_j는 {(x_i, y_i)}, i : 1 ~ j :  j번째까지의 데이터, 라벨로 이루어진 A의 부분 집합입니다.
W^A_i는 A_i를 이용해서(처음 i개의 데이터만 이용해서) 학습한 파라미터입니다.
이를 통해 p(y_i | x_i; W^A_i-1)의 의미는 i-1개의 데이터를 이용해 파라미터 W를 학습한 후, i번째 데이터의 라벨을 예측하는 것이라 알 수 있을 것입니다. 이 값은 학습이 100% 정확하다면 1.0이 될 것이고(x가 주어졌을 때 100% 확률로 y를 예측함) 로그를 씌우면 0이 됩니다.

i를 2부터 N까지 증가시키면서 결과를 모두 더하는데, 학습이 빨리 되면 빨리 될수록 l(A)의 값은 작아지게 됩니다. 예를 들어 설명해보겠습니다.

|y|은 2이고, 데이터의 개수인 N이 4일 때,
예시 1 : (x1, y1)로는 학습이 전혀 되지 않았으나(p(y|x, W)는 1/2), (x2, y2)를 학습한 후 완벽하게 모델이 학습된 경우.
p(y2|x2, W^A1) = 1/2
p(y3|x3, W^A2) = 1
p(y4|x4, W^A3) = 1 이므로 l(A) = 1-(-0.5 + 0 + 0) = 1.5

예시 2 : (x1, y1), (x2, y2)로는 학습이 전혀 되지 않았으나(p(y|x, W)는 1/2), (x3, y3)를 학습한 후 완벽하게 모델이 학습된 경우.
p(y2|x2, W^A1) = 1/2
p(y3|x3, W^A2) = 1/2
p(y4|x4, W^A3) = 1 이므로 l(A) = 1-(-0.5 + -0.5 + 0) = 2

이렇게 codelength는 학습이 빨리 될수록 작아지는 것을 알 수 있습니다.
실제로 이런 아이디어를 적용해볼 경우 부분집합을 나누는 단위를 데이터 한 개가 아닌 여러 개의 묶음으로 나누어야겠죠.

Experiments


Unsupervised Pretraining

챕터의 제목이 조금 이상하다는 느낌이 들지만 이 챕터는 얼마나 많은 in-domain(training) 데이터가 필요한가에 대해 살펴봅니다. 실험 대상 task는 SQuAD와 MNLI입니다. BERT와 ELMo 모두 4만개 정도의 데이터를 학습해야 최고 성능에 근접했다고 합니다. pretrained 모델 치고는 매우 많다고 하네요. BERT 모델이 ELMo에 비해 최종 성능도 높았고, 학습도 빨리 진행됐습니다. 아래 그림은 차례대로 SQuAD 1.1, MNLI에 대한 실험 그래프 입니다. (+supervised는 뒤에서 설명하겠습니다.)
---

pretraining을 거치치 않고 task의 데이터로 BERT를 학습시킨 BERT_scratch모델은 그리 좋은 성능을 내지 못했습니다. (이 비교를 통해 pretraining의 효과를 알아보려는 것인지도 모르겠습니다)

그리고 추가적으로 codelengh지표를 측정합니다. SQuAD는 만개씩 8개로, MNLI는 4만개씩 10개의 부분집합으로 나누어서 측정했습니다. SQuAD 데이터셋에서는 BERT가 102.42, ELMo가 112.96을 기록했고, MNLI에서는 BERT가 89.22, ELMo가 132.17의 codelength를 가졌습니다. BERT가 성능도 좋았지만 학습도 그만큼 빨리 되었다는 것을 위의 그래프와 함께 codelength로도 알 수 있었습니다.


Beyond Unsupervised Pretraining

이 챕터에서는 다른 데이터셋으로 미리 학습했을 때 성능이 향상되는지를 알아봅니다. 실험에서는 semantic role labeling, relation extraction, SNLI, TriviaQA, QuAC 데이터셋으로 먼저 학습시킨 후 SQuAD 데이터를 학습시킵니다. 이 모델을 각각 BERT+supervised, ELMo+supervised라 부릅니다. 이 때의 성능은 아래의 표와 같습니다.


이런 pretraining을 통해 최종 EM, F1 성능이 향상되었지만 가장 크게 차이나는 것은 codelength입니다. 다른 데이터셋으로 미리 학습시키면 학습이 그만큼 빨리 된다는 것을 알 수 있습니다. 이는 pretrained 모델에 추가되는 각 task에 맞는 final layer가 미리 학습됐기 때문이라고 추정합니다. 좀 더 해석하면 같은 QA task인 TriviaQA, QuAC 데이터셋을 통해 SQuAD도 잘 할 수 있도록 학습된 것이라 할 수 있습니다. 실제로 SQuAD 데이터를 하나도 학습하지 않은 상태에서 BERT+supervised, ELMo+supervised 모델은 각각 62.9, 43.3 F1을 냈다고 합니다.


Generalization

이제 반대로 특정 데이터로 학습한 모델이 그 데이터에 오버피팅 됐는지 아니면 같은 task의 다른 데이터셋에도 잘 적용되는지를 알아봅니다. 실험은 SQuAD를 학습한 모델로 다른 TriviaQA와 QuAC 등에서 얼마나 잘 할까를 평가합니다. 다른 데이터셋으로는 학습하지 않고 평가했습니다.

결론적으로 충분히 좋은 결과를 보여주지 못합니다. TriviaQA의 1등 점수는 68.65입니다.  괄호 밖의 점수는 F1이고 괄호 안의 점수는 EM 점수입니다.


같은 task의 데이터셋이라도 데이터의 분포가 다르기 때문인 것 같습니다. 특정 Task를 배우는 것과 데이터셋에 대해 학습하는 것은 차이가 있어 보입니다.


Curriculum and Catastrophic Forgetting

이 챕터에서는 학습 순서가 성능에 영향을 주는지, 그리고 모델이 이전에 습득한 지식을 얼마나 발리 잊어버리는지 실험합니다.

첫번째는 SQuAD로 학습한 모델을 이어서 MNLI로 학습하면서 성능을 측정합니다. Training iteration은 MNLI의 학습량을 의미합니다.

학습이 진행될 수록 MNLI의 성능은 증가한 후 거의 수렴하지만 SQuAD의 성능은 점점 감소합니다.
MNLI의 final layer는 QA task와는 다른 layer(parameter)를 사용합니다. 그럼에도 불구하고 QA의 성능이 떨어진다는 것은, MNLI를 학습하면서 공통으로 사용하는 pretrained model의 파라미터들도 많이 바뀐다는 뜻이겠죠.

이러한 경향은 SQuAD로 학습한 모델을 TriviaQA로 학습할 때도 이어집니다.

MNLI로 학습할 때보다 SQuAD의 성능이 더 떨어지네요.
재밌는(?) 것은 이 모델(pretrain->SQuAD->TriviaQA로 학습된 상태)에 MNLI를 계속해서 학습시키면 SQuAD, TriviaQA의 성능은 거의 그대로이고, MNLI의 성능만 증가합니다.


마지막 실험에서는 random하게 아무 task들의 데이터를 뽑아서 학습시킵니다. 아래는 BERT를 그렇게 학습시킨 실험 결과입니다. 모든 task에서 고루 좋은 성능을 보입니다.


이런 학습 방법의 장점은 모든 task에서 성능이 좋다는 것이지만, 단점으로는 그만큼 여러 종류의 데이터가 필요하고, 새로운 task가 생겼을 경우 처음부터 다시 학습해야 하죠. 학습을 다시하는 것은 매우 비용이 큰 만큼 여러 task를 이어서 학습하는 continual learning을 잘 하기 위한 연구도 필요해 보입니다.

Discussion

이 논문은 제목대로 General Linguistic Intelligence가 있나 평가를 하지 새로운 방법을 제시하지는 않습니다. 실험을 통해 분석한 결과로는 아직 general linguistic intelligence를 달성하기 위한 여러 능력들이 부족하다고 하죠. Pretrained 모델의 성능이 좋다 해도 여전히 fine tuning을 위한 많은 데이터가 필요하고, 하나를 학습시키면 다른 하나는 또 잊어버린다는 것을 알았습니다.

이런 문제들을 해결하기 위한 방법들을 언급하는데, 일반적인 해법들을 제시합니다.
- transfer and continual learning methods
- memory module
- meta learning : 새로운 task에 빨리 적응하는 기술. NLP의 입력은 거의 같기에 많은 도움이 될 것이라 합니다.
- training curriculum
- generative language model

또한 adversarial example들에 대한 robustness, 여러 언어를 동시에 처리하는 능력 등 연구가 필요하다는 등의 언급을 하며 끝을 맺습니다.


결론적으로 아직은 멀었다....... 라는 얘기를 여러 실험을 통해 하는 DeepMind의 논문이었습니다. 다른 사람들의 연구를 약간 까려고 한 실험일 수 있겠다는 생각도 들고요 ㅎㅎ. 아직 이런 General Linguistic Intelligence를 논하기엔 이른 시점이라고 생각합니다.

한 편으로는 실제로 BERT에 Multi-task learning(random으로 task를 선택해서 학습하는)을 적용한 BigBird논문이 BERT보다좋은 성능을 냈는데, 그래도 어느정도 방향성은 제대로 제시했다는 느낌도 주는 논문이었습니다 .

2019년 3월 2일 토요일

20190302 일기

다양한 언어 생성의 이유

여러 나라에서 다른 언어를 쓴다.
동물들은 지역에 따라 다른 언어를 쓸까?ㅋㅋ

만약 인간의 한 종이 한 곳에서 승리하여 퍼져나갔다면 딱히 지역에 따라 언어가 바뀔 일이 없을 것 같다. 물건을 지칭하는 명사나 지역에 좀 특화된 단어들이 생겨날 순 있겠지만 기본적으로 문법과 단어들이 비슷할 것 같다.

그게 아니라면 여러 곳에서 현대 인류가 동시에 생겼다는 건데 그런 것 같지는 않고...
한 번 찾아봐야겠다.

2019년 2월 16일 토요일

Bert-Multilingual model을 이용해 KorQuAD 수행해보기 Part 2 제출

지난 번 글에 Pre-trained Multilingual Bert를 KorQuAD에 맞게 fine tuning시키고 평가하는 방법을 올렸다. 이제 이어서, 실제 KorQuAD leaderboard에 내가 훈련시킨 모델을 등록하는 방법에 대해 정리해 보겠다. Codalab을 처음 쓰는 사람이라면 KorQuAD 홈페이지에 링크되어 있는 제출 튜토리얼만 보고는 알아야 할 사항들이 추가적으로 더 있어서 그런 부분까지 포함하는 제출 방법을 써 보았다.

기본적인 참고 url은 KorQuAD 공식 홈페이지에 링크 되어 있는 KorQuAD codalab 페이지이다.



설명대로 CodaLab에 회원가입(Sign up) 한다.
http://codalab.org 에 가보면 아래와 같은 화면이 나온다. Competetions는 현재 codalab에서 진행되고 있는 대회들을 보여준다.



실제 제출을 위해 필요한 작업들은 Worksheets에서 진행된다. 그리고 Worksheets와 Competetions에서 사용되는 계정이 다른 듯하다...(???) 그러니 Competetions은 둘러보기만 하고 Worksheets 페이지에 가서 Sign up 하자.



로그인하면 위와 같은 화면이 보일 것이다. My Home은 기본으로 만들어진 worksheet을 보여주고, My Dashboard는 나의 모든 worksheets, 기타 여러 정보들을 보여준다. 기본 worksheet을 써도 좋고, 새로운 worksheet을 만들어도 상관없다.

---------------------------------------------------------------------------------------------------

Dev set 평가 단계


이제 KorQuAD codalab 페이지의 Dev set 평가 단계를 진행해보겠다. 이 단계는 그리 어렵지 않고 그냥 써 있는대로 하면 된다.



My Home이나 My Dashboard 아무거나 누르고, 우측에 New Worksheet 버튼을 누르면 새로운 worksheet이 만들어진다
새로 만든 worksheet은 아래와 같다.


None은 worksheet의 타이틀로 클릭하여 수정할 수 있다.

2단계에 써져 있는 것처럼 우측의 Upload 버튼을 눌러 predictions.json(test data에 대해 답을 예측한 파일)을 업로드 한다.


업로드하면 위와 같이 worksheet에 파일이 추가된다.

3단계에 나온 것처럼 화면 위의 web interface에 명령어를 입력하여 업로드한 predictions.json에 대한 평가를 진행한다. 참고로 worksheet에 생성되는 객체(entity, object, instance 등..)을 bundle이라고 부른다.

명령어를 실행하면 아래와 같은 실행 결과에 대한 bundle이 생성된다.



여기서 보이는 점수는 local 환경에서 평가한 점수랑 동일한 점수이다. 실제로 leaderboard에 점수를 등록하기 위해서는 내 컴퓨터에서 dev set에 대해 예측한 predictions.json 파일이 아니라, 훈련시킨 모델을 업로드하고 codalab 상에서 비공개된 test set에 대한 평가를 진행하는 작업이 필요하다.

여기까지 진행하는 데는 사실 이 글을 읽을 필요가 없다. 그냥 KorQuAD codalab 페이지의 Dev set 평가 방법만 보고 하면 된다. 이 글을 쓰게 된 이유는 Test set을 평가하기 위해서는 안내 사항 외에 알아야 할 게 더 있기 때문이다.

---------------------------------------------------------------------------------------------------

Test set 평가 단계



안내 사항에 써 있듯 우리는 "Codalab에서의 파일 업로드 및 명령 실행에 익숙"한 상태가 되어야 한다.



일단, 훈련된 모델의 소스 폴더를 업로드하라고 하는데 웹 페이지 상에 있는 Upload 버튼으로는 폴더 또는 여러 개의 파일을 올리는 것이 불가능하다.

실제로 이 단계를 수행하기 위해서는 Codalab을 사용하기 위한 Command Line Interface가 필요하다. CLI를 설치하기 위해서는 여기에 가서 2. Install the CLI에 나온대로 따라 하면 된다.


이제 cli를 이용해 폴더를 업로드할 수 있다. 위에 링크된 페이지에 3. Uploading files 부분을 참조하면 된다.

편의를 위해 "cl upload 폴더 -n 이름" 처럼 뒤에 -n 옵션을 붙여서 업로드하면 로컬 폴더가 복잡한 이름으로 되어 있을 경우 짧게 바꿀 수 있다. 그 외에도 파일/폴더를 올린 뒤에 클릭하고(화면에서 파란색처럼 선택된 게 보임) 오른쪽 name을 누르면 bundle의 이름을 수정할 수 있다. 위의 이미지에서 src는 로컬 환경의 폴더를 업로드한 bundle 이름이다.



또한 위의 1단계 안내 사항을 보면 실행 명령이 "python 스크립트파일 input-data-file prediction-json-file" 이렇게 되어야 한다고 명시되어 있다. 이에 맞춰 소스를 수정해주어야 한다.

기본적으로 github에서 다운받은 상태의 소스는 모두 "--option 설정값" 이런 식으로 인자를 주어야 한다. 옵션으로 설정하는 input-data-file, prediction-json-file을 args[1], args[2] 이렇게 인자로 받을 수 있도록 수정해준다.


그리고 config_file, vocab_file, checkpoint_file을 한 bundle 안에 묶어서 upload하는 게 편하다. pre-trained만 된 checkpoint_file은 올리지 않아도 된다. 하지만 로컬에서 한 폴더 안에 있지 않다면 따로따로 upload하며 각각 bundle을 만든 후 위의 이미지에서 run-predictions 번들의 summary에 써 있는 것처럼 bundle 이름과 그 안의 파일을 지정하여 실행할 수도 있다.

위의 이미지를 기준으로 소스 파일들은 bert(run_squad.py), pre trained 모델은 multi-cased-base(vocab, config file), fine-tuned 모델은 squad_trained_models bundle(init_checkpoint)에 각각 저장되어 있다. 따라서 옵션으로 파일을 지정할 때 --vocab_file bundle_name/file_name 이런 식으로 지정하면 된다.



이제 마지막으로 필요한 평가 파일(dev set)을 추가하기 위해 위의 명령어를 web interface 상에서 실행한다.

실제 수행해야 할 명령은 아래와 같다.



이 때 -n bundle_name에서 지정한 이름대로(이미지 상에서 run-prediction) bundle이 생성되고 그 안에 predictions.json 파일이 생성된다. 그리고 이미지 상에서 :KorQuAD_v1.0_dev.json, :src은 번들 이름을 말하는데 꼭 안 써도 되는 것 같기도 하다. 명령어 안에 번들이름/필요파일 이렇게만 써도 되는 것 같다.


또하나 중요한 점은, 이 작업을 gpu로 수행시켜야한다는 점이다. 안 그러면 수행 시간이 엄청나게 길어질 것이다. 위의 명령어는 docker 상에서 수행되며 docker의 타입이 gpu를 지원해주는 버젼이어야 한다.

그래서 명령어 뒤에 추가적으로 --request-docker-image tensorflow/tensorflow:1.12.0-gpu-py3(Tensorflow버전과 python 버전에 맞게), --request-gpus 1 --request-memory 8g(기본 세팅인 2g는 너무 작아 프로세스가 죽을 수 있다. 넉넉하게 세팅한다.)을 넣어서 실행한다.


최종 명령어는 cl run "python 소스번들/run_squad.py --필요옵션들 필요번들/필요파일들 dev.json파일 predictions.json" -n 만들어질번들이름 --request-docker-image 도커이미지이름 --request-gpus 1 --request-memory 8g 이다.


이제 -n으로 지정한 번들이 생길 것이다. 번들을 클릭하여 상태를 보면 실행 중인지, 실행이 끝났는지 알 수 있다.  그리고 실행이 끝나면 번들 안에 predictions.json 파일이 생성된 것을 볼 수 있다.

이제 다음 단계를 수행한다.


위 작업은 run_squad.py를 수행한 번들 안의 predictions.json을 따로 빼서 독립된 번들로 만들고, 그 파일에 대해 평가하는 과정을 설명한다. 두 명령어를 다 실행하면 업로드한 predictions.json을 평가할 때처럼 점수를 표시해주는 번들이 새로 생긴다.

첫번째 명령어에서 run-prediction만 -n으로 지정한 번들 이름으로 변경해주면 된다. {MODEL-NAME}은 원하는 대로 지정한다.

이제 마지막 3단계:제출 부분에 써 있는대로 하면 제출이 완료된다.

끄읕!
---------------------------------------------------------------------------------------------------

처음 제출할 때만 해도 baseline 모델과 naver에서 만든 모델 2개 밖에 없었다. 하지만 내가 제출한 모델의 결과가 leaderboard에 반영되었을 때 다른 2개의 모델이 한꺼번에 같이 업데이트 되어 그 때 당시의 naver 모델보다 더 좋은 결과를 냈지만 3등이 됐다... ㅜㅜ
메일이 저렇게 왔다. ㅋㅋㅋ

어짜피 이후 네이버도 더 좋은 모델을 만들고 카카오도 참전하고... 해서 더 순위는 내려갔지만...

1등 언제 찍어보지 ㅜㅜ

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에 등록할 차례이다.
이 부분에 대한 포스트는 다음에 작성하도록 하겠다.

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

2019년 1월 1일 화요일

20190102 일기

새 해 첫 일기다... ㅜㅜ

--------------------------------------------------------------------------------------------------------------
페이스북인가에서 본 글에서, 우리나라 교육의 문제점을 지적하며 천재가 없다는 얘기를 했다.

그 이유야 여러가지가 있겠지만, 우리 나라 사람들이 사람의 단점을 많이 신경쓰기 때문이 아닌가 생각한다.

모든 것을 다 잘 할 수 없는데, 뭔가 하나를 엄청 잘한다고 하면, 다른 건 잘 못하잖아? 라는 식으로 폄하할 수 있다.

이어서, 이런 식으로 사람을 평가하는 이유로 사촌이 땅을 사면 배가 아프다는 말처럼 시기 질투 때문일 수도 있고, 실패에 대한 두려움도 있기 때문이 아닐까 생각한다.

엄청 잘하는 그 분야에서도 꼭 성공하리란 보장이 없는데, 그걸 실패하면 뭐 할거냐 이런 식으로.

물론 인성이 글러먹으면 안 되겠지.


비슷하지만 약간 다른 문제로, 사람을 사귈 때 장점을 볼 것인가 단점을 볼 것인가가 있다.

물론 인간관계는 사바사, 케바케지만, 장점, 또는 배울 점이 명확한 사람과 지내는 것과 큰 장점이 없지만 또 크게 불편하지도 않은 그런 사람과 교류한다고 했을 때 어느 쪽이 더 편한지.

이때까지 나는 나와 맞지 않는 부분에 좀 더 초점을 맞추며 사람을 봐 왔던 것 같다.