목차
- 프로젝트 개요
- Retriever
- Reader
- Post-Processing
- 결론
프로젝트 개요
ODQA (Open Domain Question Answering)란 domain이 한정되어 있지 않은, 다양한 종류의 질문에 대답하는 task이다. ODQA는 지문이 따로 주어지지 않고, knowledge resource에서 질문에 대답할 때 참고할 수 있는 문서를 스스로 찾아야 하는 과정(retrieval) 단계가 추가되기 때문에 풀기 어려운 task이다. 예를 들어, "광화문이 있는 도시는 어디야?"라는 질문이 입력으로 들어오면, Retriever 모델은 광화문과 관련된 문서를 찾는다. 그 다음, Reader 모델이 해당 문서에서 '서울'이라는 정답을 찾아내야 한다. ODQA는 이처럼 Retriever 모델과 Reader 모델로 구성된 파이프라인 구조로 푸는 것이 일반적이다.
이번 대회에서 내가 맡았던 역할은 PM이었다. 주로 실험 계획 및 일정 조율에 집중하였고, 대회 기간이 짧아서 많은 시도는 못하였지만, 그 중에 내가 시도했던 바 위주로 적어보고자 한다.
Retriever
Retriever는 위키피디아 문서와 같은 데이터베이스로부터, 입력 질문(query)에 대한 정답이 있을 만한 문서를 찾아내어 출력하는 모델이다. Retriever 모델은 database에서 query 문장과 유사도가 가장 높은 문서(passage)를 출력해야 한다. 이때 query 문장과 각 문서 간의 유사도를 구하는 방법으로는 TF-IDF, BM25와 같이 토큰의 출현 빈도를 기반으로 구하는 sparse retrieval 방식과, 신경망을 이용해 query 문장과 각 문서 간의 임베딩을 만들고, 두 임베딩 간의 유사도를 계산하는 dense retrieval 방식이 있다. Dense Passage Retrieval(DPR)을 제안한 Karpukhin et al. (2020)에 의하면 DPR은 기존 sparse embedding에 비해 성능이 뛰어났지만, SQuAD에 대해서는 유독 성능이 sparse embedding에 비해 떨어지는 것으로 나타났다. 해당 논문에서는 SQuAD가 데이터셋을 제작할 때 어노테이터들이 정답을 미리 상정해두고 질문을 만들다 보니, 질문과 문서 간의 어휘적인 겹침이 많이 일어났고, 그 때문에 오히려 전통적인 sparse embedding 방식이 더 효과를 보았을 것이라고 해석한다.
우리의 실험에서도 DPR보다 TF-IDF, BM25가 성능이 더 좋게 나타났다. 특히, ElasticSearch를 이용하여 BM25를 적용하였을 때 retreival 성능이 가장 좋았다. 이에 이번 글에서는 ElasticSearch에 대해 집중적으로 정리해보고자 한다.
ElasticSearch는 Apache Lucene 기반의 오픈소스 검색 엔진으로, 데이터를 저장할 때 역 인덱스(inverted index) 구조를 통해 데이터를 신속하게 검색할 수 있다. 또한, 자체적으로 한국어 토크나이저인 nori tokenizer를 가지고 있고, shingle filter와 같은 기능이 있으며, 유사도를 구하는 함수로 BM25 옵션을 제공하고 있어 사용하기에 매우 편리하다는 장점이 있다. 따라서 우리는 ElasticSearch를 기본 Retriever 모델로 채택하고, ElasticSearch의 옵션을 다양하게 변경해 보며 성능을 향상시키고자 하였다.
ElasticSearch
ElasticSearch에서는 데이터에서 검색어 토큰을 저장하기 위해 사용자가 정의한 token filter와 tokenizer를 적용할 수 있다. 이번 대회에서 우리는 tokenizer로 nori_tokenizer를 사용하였고, filter는 shingle을 적용하여 인덱스를 세팅하였다. 그리고 추가적으로 nori-readingform 토큰 필터( 한자로 된 단어를 한글로 바꾸어 저장하는 필터), staptags(제외할 품사 지정) 여부에 따른 성능 비교 실험을 진행하였다. 각 설정별 구체적인 내용은 아래의 표와 같다.
tokenizer | nori_tokenizer decompound_mode = mixed |
filter | shingle |
similarity | BM25 |
nori_tokenizer는 mecab을 재가공한 토크나이저로, ElasticSearch에서 제공해주는 한글 형태소 분석기이다. decompound mode를 사용자가 설정할 수 있는데, 'mixed' 방식은 어근과 합성어를 모두 인덱스에 저장하는 방식이다.filter로는 'shingle'을 선택하였는데, 이것은 텍스트에서 쪼개져 있는 토큰들을 하나의 토큰으로 묶어서 토큰을 확장시키는 필터이다. 마지막으로 similarity를 구하는 함수로는 BM25를 사용하였다.
실험 가설
조사, 어미, 수사 등의 품사는 문장에서 어휘적 의미를 지니지 않고, 문법적인 역할만 한다. 또한, 등장 빈도가 높기 때문에 쿼리와 유사한 passage를 검색하는 데 방해가 될 수 있다. 따라서 조사, 어미, 수사 등의 품사에 대해 인덱스가 부여되지 않도록 방지한다면 더 정확한 검색이 가능할 것이다. 또한, 한자어를 해당 한자어와 동일한 의미를 갖는 한글 단어로 인덱싱해준다면 더 정확한 검색이 가능할 것이다.
실험 방법
ElasticSearch의 setting을 다음과 같이 설정하였다. ElasticSearch의 stoptags는 기본적으로 조사, 어미 등을 필터링하고 있으나 수사는 그렇지 않다. 따라서 여기에다가 수사(NR)를 추가해주었다. 그리고 한자로만 표기된 단어들도 한글로 인덱싱될 수 있도록 nori_readingform을 적용해주었다. (참고: 품사 태그표 http://kkma.snu.ac.kr/documents/?doc=postag)
# stoptags 디폴트 값
"stoptags": [
"E(어미류)", "IC(감탄사)", "J(조사류)", "MAG(일반부사)", "MAJ(접속부사)",
"MM(관형사)", "SP(쉼표 등)", "SSC(기타부호)", "SSO(기타부호)", "SC(기타부호)",
"SE(줄임표)", "XPN(체언접두사)", "XSA(형파접)", "XSN(명파접)", "XSV(동파접)",
"UNA(?)", "NA(분석불능)", "VSV(?)"
]
# my_pos 인덱스에 수사(NR)을 제거하도록 stoptags를 지정하는 예제
PUT my_pos
{
"settings": {
"index": {
"analysis": {
"filter": {
"my_pos_f": {
"type": "nori_part_of_speech",
"stoptags": [
"NR"
]
}
}
}
}
}
}
GET my_pos/_analyze
{
"tokenizer": "nori_tokenizer",
"filter": [
"my_pos_f"
],
"text": "다섯아이가"
}
실험 결과
stoptags, nori_readingform 적용 여부 | eval_EM | eval_F1 |
stoptags, nori_readingform 미적용 | 57.50 | 65.06 |
stoptags, nori_readingform 적용 | 58.75(+1.25) | 66.31 (+1.25) |
필터(stoptags)와 nori_readingform을 적용하니 EM과 F1 모두 1.25점 상승하였다.
Reader
Reader 모델은 질문(query)과 passage를 입력으로 받아서 질문에 해당하는 답을 passage에서 찾아내어 반환해야 한다.
이번 대회에서 데이터셋은 자유롭게 사용할 수 있었으므로, 우리는 제공된 데이터셋에 더하여 Korquad 1.0을 추가적으로 사용하였다. 또한, 성능을 높이기 위해 PLM 선정, doc_stride 길이 조절, Query 길이 늘이기 등을 실험하였다.
PLM
실험 가설
PLM의 후보로는 huggingface에 공개된 모델 중에서 klue/roberta-large, kobigbird-bert-base, 그리고 koeletra를 korquad에 finetuing시킨 모델인 monologg/koelectra-base-v3-finetuned-korquad를 선정하였다. RoBERTa-large는 파라미터 수가 base모델보다 더 많기 때문에 더 좋은 성능을 기대할 수 있다. BigBird는 길이가 긴 시퀀스에 특화된 모델이므로, 길이가 긴 passage를 읽어서 정답을 추론해야 하는 Question Answering task에 적합하다고 판단하였다. 마지막으로 Korquad에 이미 fine-tuning된 모델도 Question Answering task를 잘 풀 수 있을 것이라고 가정하였다.
실험 방법
후보 PLM들의 성능 비교에 앞서, max_length에 따라서 성능이 어떻게 변하는가를 확인하기 위해 kobigbird의 max_length를 조절해가며 성능을 비교해보았다. 그 결과는 다음과 같다.
실험 결과
Model | Max_length | eval_EM | eval_F1 |
kobigbird | 384 | 60.42 | 69.30 |
kobigbird | 512 | 61.67 | 70.35 |
kobigbird | 1024 | 62.50 | 71.89 |
kobigbird | 2048 | 60.42 | 69.81 |
kobigbird | 4096 | 59.58 | 70.77 |
실험 결과 1024까지는 max_length를 증가시킬수록 성능도 증가하지만, 그 이상으로 max_length를 증가시키면 성능은 하락하는 것으로 나타났다.
이번에는 PLM 간의 성능 비교를 수행하였다. kobigbird는 max_length 1024에서 가장 좋은 성능을 보였고, max_length를 1024 이상으로 설정할 수 있다는 점이 이 모델의 장점이기 때문에, 기본 max_length를 1024로 설정하였다. 나머지 모델들은 max_length를 최대 한계인 512로 설정하였다.
Model | max_length | eval_EM | eval_F1 |
klue/roberta-large | 512 | 69.17 (+6.67) | 76.99 (+5.10) |
monologg/kobigbird-bert-base | 1024 | 62.50 | 71.89 |
monologg/koelectra-base-v3-finetuned-korquad | 512 | 60.83 | 68.64 |
실험 결과, , RoBERTa-large 모델의 EM이 6.67점 더 높은 것으로 나타났다. 즉, BigBird가 긴 길이의 시퀀스를 다룰 수 있다는 장점이 있지만, 그 장점이 파라미터 수가 더 많은 RoBERTa-large를 능가하지는 못한다고 볼 수 있다. 따라서 Reader model에 쓸 최종 모델은 klue/roberta-large로 선정하였다.
doc_stride
실험 가설
doc_stride는 문단을 chunk로 나눈 후, chunk들 간의 겹침을 몇으로 둘 것인가를 설정하는 transformers.tokenizer의 인자이다. 예를 들어, 입력 문단의 총 길이가 500이고, max_len이 300이고 doc_stride가 100이라면, 300 길이의 두 chunk로 나누고, 이 두 개의 chunk 간의 겹치는 구간의 길이를 100으로 둔다는 뜻이다. 만약 문단 전체를 입력으로 받았다면 300자 뒤의 내용은 읽어들일 수 없는 반면, doc_stride를 설정하여 문단을 여러 chunk로 나눈 뒤 일정 부분을 겹쳐가며 읽어들이면 문단 전체를 정보 손실 없이 읽어들일 수 있다.
베이스라인 코드에서 doc_stride의 default값은 128로 되어 있었다. Reader 모델이 정답을 추론할 때 참고하는 문장들의 양이 정답을 추론할 수 있을 정도로 충분해야 하므로, doc_stride의 길이가 길 때 reader 모델이 정답을 더 잘 예측할 것이라고 가정해볼 수 있다.
실험 방법
klue/roberta-large 모델에 대해서 나머지 하이퍼파라미터는 모두 고정하고, doc_stride 옵션을 128, 256, 440으로 넣은 후 성능을 비교하였다.
실험 결과
doc_stride | eval_EM | eval_F1 |
128 (default) | 69.17 | 76.99 |
256 | 68.75 | 75.90 |
440 | 70.00 (+0.83) | 79.82 (+2.83) |
실험 결과, doc_stride를 440으로 늘렸을 때의 성능이 가장 높은 것으로 나타났다. 다만, doc_stride를 그 이상으로 늘릴 경우, query까지 합하였을 때 stride 단위가 max_seq_length(512)를 초과해버려 에러가 발생하여, 최종 제출 시에는 doc_stride를 440으로 설정하였다.
Query 길이 늘이기
실험 가설
Reader 모델은 질문(query)와 문서(passage)를 연결한 입력값을 받아서 정답을 예측해야 한다. 이때 query의 길이가 1문장으로 상당히 짧은 편이기 때문에, 모델이 참고할 수 있는 정보가 부족하다. query에서 중요한 부분(Named entity)을 query 문장 뒤에 이어붙여서 강조를 해준다면 Reader 모델의 성능을 향상시킬 수 있을 것이라고 가정해볼 수 있다. 또한, Elasticsearch로 query와 연관된 문서를 찾을 때도 named entity를 강조해준다면 적절한 문서를 찾는 데 도움이 될 수 있을 것이다.
실험 방법
Pororo library를 이용해 query 문장 내의 named entity를 추출한 뒤, qeury 문장 뒤에 이어붙인 후 Reader 모델의 입력으로 넣어주어 학습 시키고, 추론 단계에서도 이와 같은 형태로 query를 넣어준다.
예: 이순신은 어디에서 태어났어? → 이순신은 어디에서 태어났어? 이순신)
실험 결과
eval_EM | eval_F1 | 제출 EM (public) | 제출 F1 (public) | |
query | 70.42 | 79.81 | 59.17 | 68.94 |
query+NER | 69.78 (-0.64) | 78.40 (-1.41) | 67.92 (+8.75) | 78.10 (+9.16) |
실험 결과, eval set에 대해서는 일반적인 query를 넣은 것이 성능이 근소하게 앞섰으나, 제출 점수는 query + NER이 크게 앞섰다. evaluation에서는 Retrieval 단계를 별도로 거치지 않고, 데이터셋에서 제공되는 gold passage를 입력으로 받는다. 반면, 만들어진 모델로 inference할 때는 위키피디아로부터 passage를 Retrieval해야 한다. 이로 미루어 볼 때, Reader 모델 성능은 떨어진 데 반해 제출 성능(Retriever + Reader)이 크게 오른 것은 Retriever(Elasticsearch) 성능이 오른 것으로 추론해볼 수 있다. 다만, Retriever 성능에 대해서 별도로 대조 실험은 진행하지 못하였다.
Post-processing
후처리 기법으로는 Probability 합산, 모델 앙상블을 실험하였다.
Probability 합산
실험 가설
Reader 모델은 passage에 들어있는 정답 string의 인덱스를 예측하게 된다. 이때, 정답 string은 passage 내에서 여러 번 출현할 수 있다. 즉, 서로 다른 예측값이지만 결국 동일한 출력값을 가질 수 있으며, 자주 예측되는 예측값은 정답 string에 가깝다고 가정해볼 수 있다. 따라서, 예측 string이 동일한 예측값의 확률들을 모두 더하면 성능을 향상시킬 수 있을 것이라고 예상하고 실험을 진행하였다.
실험 방법
예측 확률이 높은 20개의 후보들 중 예측한 string이 동일한 예측값들의 확률을 합산하였다.
실험 결과
eval_EM | eval_F1 | |
prediction.json | 58.33 | 68.05 |
nbest_prediction.json 확률 합산 | 59.58 (+1.25) | 69.15 (+1.1) |
실험 결과, EM이 1.25점, F1이 1.10점 향상한 것을 확인하였다. 또한, 지나치게 길게 예측되는 값을 짧게 정제해주는 효과도 있었다. 예를 들어, "기존 마르크스주의자들과 달랐던 지점이다. 이러한 지점은 여러 학자들에 의해 블랑키주의"와 같은 예시처럼 지나치게 길게 예측한 값을 “블랑키주의”라는 좀 더 그럴싸한 예측값으로 정제해주었다.
앙상블
실험 가설
Reader 모델이 정답 span을 잘 찾기 위해서는 먼저 Retriever 모델이 정답이 들어있는 passage를 올바르게 찾아주는 것이 선행되어야 한다. DPR-Reader 조합이 잘 찾는 정답이 있을 것이고, Elasticsearch-Reader 조합이 잘 찾는 정답이 있을 수 있다. 따라서, 각각의 예측값을 구한 뒤, 이 예측값들의 확률을 합산하여 앙상블을 시도하였다.
실험 방법
각 모델마다 예측 확률 상위 20개를 뽑은 후, 모든 모델의 예측값들에 대하여 예측한 string이 동일한 예측값의 확률을 더하였다.
실험 결과
제출 EM (private) | 제출 F1 (private) | |
DPR + Reader | 46.67 | 59.93 |
ElasticSearch + Reader | 61.67 | 74.71 |
Ensemble | 63.89 (+2.22) | 76.73 (+2.02) |
단일 모델 중에서 가장 성능이 잘 나왔던 모델(ElasticSearch Retriever + klue-RoBERTa-large Reader)의 성능보다 앙상블의 성능이 EM은 2.22점, F1은 2.02점 상승하였다. 앙상블을 통하여 두 Retriever 간 시너지 효과를 만들어낼 수 있었다.
결론
Retriever 실험에서는 ElasticSearch에서 제공하는 stoptags 추가 기능, nori_readingform을 적용하여 성능을 개선하였고, Reader 실험에서는 max_length 실험, doc_stride 실험, query에 named entity를 덧붙이는 실험을 통해 성능을 개선하였다. 마지막으로 후처리 과정에서는 Reader 모델이 정답 span의 인덱스를 예측하고, 그 인덱스로부터 최종 출력 값을 string으로 반환한다는 점, 그리고 정답 string이 passage 내에 여러 군데 있을 수 있다는 점에 착안하여, 모델의 예측값(인덱스) 중에서 동일한 정답 string을 가리키는 것들의 확률을 합산하는 postprocessing 방식을 적용하였다. 그 결과 EM 성능을 1.25점 향상시킬 수 있었다. 그리고 모델 앙상블을 통해 성능을 향상시켰다.
이번 대회는 Retieval 성능과 Reader 성능을 모두 올려야 한다는 점에서 난이도가 상당히 높게 느껴졌다. PM으로서 프로젝트를 최대한 효율적으로 진행하고자 노션 페이지에서 실험 계획, 실험 내용을 한 눈에 파악할 수 있도록 구조를 단순화하고, 진행 중인 작업의 진척도 및 마감 기한까지 한 페이지 내에서 모두 볼 수 있도록 개선하였다. 그리고 노션의 표 기능이 너무 불편하였기 때문에 구글 스프레드시트를 별도로 활용하였다.
여러 가지를 시도하였지만, 한계도 있었다. 첫째, 동일한 정답 string의 확률을 합산하는 작업의 경우 다른 조에서는 정답 string이 나온 횟수도 반영하여 성능이 올랐다고 보고하였는데, 이러한 아이디어를 떠올리지 못하였다.둘째, query에 named entity를 덧붙이는 작업으로 성능을 크게 향상시켰으나, 그 이유가 retriever 성능 향상에 기인한 것인지, reader 성능 향상에 기인한 것인지 명확하게 검증하지 못하였다. 마지막으로, Retriever 실험을 하여금 DPR과 Elasticsearch로 나누어 진행하였는데, PM으로서 두 실험에 대한 이해도와 참여도가 부족하였다.
다음에도 여러 모델을 거치는 파이프라인으로 구성된 프로젝트를 진행하게 된다면, 각 단계별 성능을 분리하여 검증하고, 다른 팀원의 작업에 대한 이해도를 높이기 위해 노력해야겠다.
Reference
- Park, S., Moon, J., Kim, S., Cho, W. I., Han, J., Park, J., ... & Cho, K. (2021). Klue: Korean language understanding evaluation. arXiv preprint arXiv:2105.09680.
- Karpukhin, V., Oguz, B., Min, S., Lewis, P., Wu, L., Edunov, S., Chen, D., & Yih, W. (2020). Dense Passage Retrieval for Open-Domain Question Answering. arXiv preprint arXiv:2004.04906
- Zaheer, M., Guruganesh, G., Dubey, K. A., Ainslie, J., Alberti, C., Ontanon, S., ... & Ahmed, A. (2020). Big bird: Transformers for longer sequences. Advances in Neural Information Processing Systems, 33, 17283-17297.
'NLP' 카테고리의 다른 글
[NLP] Attention의 개념 간단 정리 (0) | 2023.04.17 |
---|---|
Boostcamp AI Tech 4기 최종 프로젝트 후기 (일기 감성 분석 및 코멘트 생성) (1) | 2023.02.19 |
한국어 관계 추출(Relation Extraction) 경진대회 후기 (Naver BoostCamp AI tech 4기) (0) | 2022.12.12 |
[NLP] 한국어의 토큰화(Tokeniziation) - Subword BPE, Sub-character BPE, Morheme-aware-subword BPE (2) | 2022.11.13 |
한국어 STS(Semantic Text Similarity) 경진대회 후기 (Naver Connect BoostCamp AI tech 4기) (0) | 2022.11.12 |