프로젝트 주제 | Open -Domain Question Answering(ODQA) : Knowledge resource에서 주어진 지문을 찾아 이해하고 , 주어진 질의의 답변을 추론하는 task |
---|---|
프로젝트 구현내용 | 1. Hugging Face의 Pretrained 모델과 Boost camp MRC 데이터 셋을 활용해 질문에 관련된 문서를 찾아주는 Retriever 와 관련된 문서를 읽고 적절한 답변을 찾거나 만들어 주는 Reader 를 구성하여 질의응답을 수행하는 AI 모델 구축 2. 리더 보드 평가지표인 EM과 F1 높은 점수에도 달할 수 있도록 Retriever, Reader 수정 및 데이터 증강 , 모델링 그리고 하이퍼파라미터 튜닝을 진행 |
개발 환경 | GPU: Tesla V100서버 5 개(RAM32G) / GeForce RTX 4090TI 로컬 (RAM24GB) 개발 Tool :PyCharm, Jupyternotebook, VSCode [서버 SSH 연결 ] |
협업 환경 | Github Repository : Baseline 코드 관리 및 CI 테스트 관리 Notion : 대회 Valid Score, Pubilc Score 기록 , 아이디어 브레인 스토밍 , 대회관련 회의 내용 기록 Slack,Zoom : 실시간 대면 / 비대면 회의 |
프로젝트 구조도 | ![]() |
이름 | 역할 |
---|---|
강민재 | Retrieval 리팩토링 (BM25 알고리즘 구현 ), 베이스라인 코드 리팩토링 (eval 단계에서 document retrieval 수행 ), 데이터 증강 (유사도 기반 wikipedia_documents 증강 ) |
김태민 | 베이스라인 코드 리팩토링 , RoBerta 모델 Head 레이어 수정 |
김주원 | 데이터 전처리 (KorQuAD 2.0), 베이스라인 코드 리팩토링 (Seed 고정 ), 모델 실험 (KorQuAD 2.0 Fine tuning, prompt engineering, 모델 하이퍼파라미터 튜닝 ) |
윤상원 | GitHub 프로젝트 관리 (PR, Issue 템플릿 배포 , CI 파이프라인 구축 ), 베이스라인 코드 리팩토링 , 모델 실험 (generation based MRC, TAPT, Prompt Engineering) |
신혁준 | Dense Passage Retrival 구현 |
원활한 협업을 위해서는 GitHub 을 사용할 때 반복적으로 수행되는 내용을 자동화하여 시간을 절약해야 한다고 생각했기 때문에 , 프로젝트 진행 전 Ground Rule 을 설정하여 GitHub 을 사용하는 규칙을 만들고 , Issue와 Pull Request 메세지를 작성하기 편리하도록 템플릿을 생성하였다 . 또한 , main 브랜치에 merge 된 코드에 이상이 없는지 빠르고 , 자동으로 확인하기 위해 CI 파이프라인을 구축함으로써 main 브랜치에 코드가 push 되거나 Pull Request 될 때마다 코드의 테스트를 자동으로 진행했다 .
AI Stage에서는 아래와 같이 3952 개 질의응답 학습 데이터를 제공하지만 문맥을 이해해야 하는 Extraction based MRC Reader 를 학습하기에는 데이터양이 부족하다고 판단했다 . 따라서 외부 데이터를 사용해 리더를 학습시킨다면 성능을 개선할 수 있다고 가설을 세워 외부 데이터를 탐색했다 . 최종적으로 47957 개의 wikipedia article 을 활용한 KorQuAD 2.0[1] 데이터를 MRC Task의 추가 학습 데이터로 활용하기로 결정했다 . 이후 , 서버 메모리 capacity 을 고려하여 길이가 7500 이하의 데이터 27423 개를 학습에 활용했다 .
Level 1~ Level 2의 NLP Task를 진행하면서 학습 데이터와 테스트 데이터의 일관성을 유지하며 학습을 용이하게 해준다는 인사이트를 얻을 수 있었다 . 따라서 , 외부 데이터와 이미 주어진 학습 데이터의 EDA를 진행하여 유사점과 차이점을 분석하였고 이를 전처리에 적용하였다 . 주어진 학습 데이터에 존재하 지 않는 태그 , 유니코드 BOM, 하이퍼링크 , 무분별한 newline 기호 , 불필요하게 모든 문서에서 반복되는 footer 를 제거해 주었다 . 이후 , klue/roberta -large 모델의 special token 을 확인하여 불필요한 데이터를 삭제했는지 확인하였고 , 일부 외국어 데이터는 special token 으로 활용되고 있으므로 삭제하지 않고 학습 데이터로 구축하였다.
베이스라인에서 retrieval 를 통해 문서를 검색하는 데는 TF-IDF 점수가 사용되었다 . TF-IDF 는 단어의 등장 빈도인 Term Frequency 와 단어가 제공하는 정보의 양인 Inverse Document Frequency 를 곱한 값으로 word level에서 문서의 의미를 수치화한 것이다 . 입력된 질의와 문서에 대한 유사도는 아래와 같이 계산한다 .
NLP ODQA 프로젝트 (Open -Domain Question Answering) NLP-08 조 팀 회고
TF-IDF 방식은 희소 행렬을 사용하여 빠르게 문서의 유사도를 계산할 수 있다는 장점이 있지만 , 단어가 제공하는 정보의 양인 IDF를
계산할 때 문서의 길이를 고려하지 않는다 . 실제로 식을 해석해 보면 , ( ) = { / ( )}에서 볼 수 있듯이 , 단순히 전체 문서에서 단어 t 가 등장한 횟수만을 고려하여 단어의 중요성을 판단한다 . 이러한 문제점을 개선한 알고리즘이 BM25 이다 . BM25는 아래 식에서 나타나는 것과 같이 전체 문서의 평균 길이에 대한 현재 문서의 길이를 고려하여 단어의 중요도뿐만 아니라 ‘문장 내에서 의미상으로 얼마나 비중을 갖는지를 수치화한다 .
BM25 알고리즘을 구현하기 위해 rank_bm25 라이브러리를 사용하였고 , BM25Okapi, BM25L, BM25plus 방식을 모두 시도하여 retrieval 의 성능을 비교하였다 .
성능 비교
TF-IDF와 BM25 알고리즘의 성능을 비교하기 위해 klue/roberta -large 모델을 사용하여 학습한 후 inference 결과를 비교하였다 . public EM을 기준으로 TF-IDF를 사용한 기존 모델의 점수 57.92 점에서 BM25 를 사용하여 60 점으로 2.08 점을 향상하였다 . BM25 알고리즘의 계산 방식은 검증 단계에서 비교하였다 . 기존 베이스라인에서는 validation data 에 이미 context 가 포함되어 있어 , retrieval 의 성능을 비교하기 어려웠다 . 이 문제를 해결하기 위하여 eval_as_train 인자를 추가하여 값이 True 일 경우 context 를 삭제하고 , retrieval에서 지정한 알고리즘 (BM25 또는 TF-IDF)에 의해 문서를 검색하여 context 를 새롭게 생성하도록 베이스라인을 변형하였다 . 이 방식으로 검증 단계에서 BM25 Okapi, L, plus 알고리즘의 성능을 비교하였고 , 그 결과는 다음과 같다 .
BM25 Okapi | BM25 L | BM25 plus | |
---|---|---|---|
Exact Match | 54.23 | 33.20 | 57.08 |
입력된 query 와 임베딩 한 후 정답이 있는 passage 를 검색할 passage 임베딩을 만들었다 . klue/bert-base 사전학습 모델로 query 와 passage 를 임베딩으로 만드는 인코더를 학습했다.
데이터 셋 준비
학습
P 인코더와 Q 인코더를 동시에 학습했다 . P 인코더의 output 임베딩과 Q 인코더의 output 임베딩을 행렬 곱 연산과 코사인 유사도를 사용하여 nll loss를 계산하였다 . positive pair인 target 은 1, negative pair는 target 을 0 으로 하여 query 와 passage 의 유사도가 높은 positive pair 는 같은 차원에서 임베딩 거리가 가까워지고 , negative pair는 임베딩 거리가 멀어지도록 contrastive learning 훈련을 진행했다 .
추론
위키피디아 데이터를 P 인코더에 입력으로 넣어 passage 임베딩을 만들고 , inference 할 질문들을 Q 인코더에 입력으로 넣어 query 임베딩을 만들었다 . 이후 질문과 코사인 유사도가 높은 순으로 passage 를 top_k 개 반환했다 .
Baseline MRC 모델의 성능을 향상하기 위해 두 단계로 나눠 fine-tuning 을 진행했다 . 이를 두 단계로 나눈 이유는 먼저 외부 데이터를 활용하여 MRC Task에 최적화된 모델을 만들고 이를 활용하여 성능을 향상하기 위함이었다 . 따라서 첫 번째 단계에서는 전처리된 KorQuAD 2.0 데이터를 이용하여 PLM(Pre-trained Language Model)을 학습시키고 , 그 후 두 번째 단계에서는 원본 학습 데이터에 대해 fine tuning 을 수행하였다 . KLUE 논문 [2]에서 klue/roberta -large 모델의 하이퍼파라미터를 참고하여 learning rate 는 1e-6 ~ 3e-5 사이에서 조정하고 , batch size는 8, 16의 범위에서 실험하여 최적의 성능을 갖는 모델을 구축했다 .
결과 비교
다음 표는 klue/roberta -large 모델과 KorQuAD 2.0 데이터로 학습된 모델을 fine tuning 한 EM과 F1 점수를 비교한 결과이다 . validation 을 기준으로 EM과 F1-score 모두 개선되어 각각 5 점과 3.37 점이 증가하였으며 , public score를 기준으로도 EM과 F1-score 가 각각 3.37 점과 2.73 점 상승하였다.
사용 기법 | validation EM | validation F1 | public EM | public F1 |
---|---|---|---|---|
TF-IDF + klue/roberta -large (Baseline) | 69.1667 | 77.4293 | 54.58 | 65.67 |
TF-IDF + uomnf97/klue-roberta - finetuned-korquad-v2[3] | 74.1667(+5) | 82.6015(+5.17) | 57.92(+3.37) | 68.40 (+2.73) |
모델의 Head 토큰들을 기존에는 단순히 Linear 층 1 개로 구성하여 logit 값들을 뽑아냈지만 , 이를 RoBERTa LM Head를 추가함으로써 아래와 같은 식으로 수정하여 모델의 레이어의 head 부분을 수정하였다 .
변환 전
변환 후
데이터에 대한 이해도를 높이기 위해 task 에 맞는 형식의 데이터 셋을 Masked Language Modeling 방식으로 모델에 학습시켰다 . train 데이터 셋과 validation 데이터 셋 외에 KorQuAD 데이터 셋을 활용하여 학습시켰지만 , 유의미한 성능 차이를 확인하지 못했다 .
generation based MRC를 개발하기 위해 encoder -decoder 구조의 T5 모델을 사용했다 . T5 모델을 학습시키기 위해 기존 베이스라인의 학습 코드에서 모델에 주어지는 입력 값, 모델이 예측한 결과 값을 후처리하는 함수 , 평가 지표 계산 방식을 수정하여 huggingface trainer 를 활용한 학습 코드를 구현했다 . 추론 코드의 경우 , retrieval 모델이 찾아낸 질문과 연관성이 높은 k 개의 본문을 가지고 데이터 셋을 만든 뒤, data loader를 통해 데이터를 모델에 입력하여 예측 값을 생성했다 .
모델에 question 과 context 의 내용을 확실하게 구분해서 입력하기 위해 “질문 : 본문 : ”의 형식으로 데이터를 전처리하여 모델에 입력했다 . prompt engineering 을 하지 않았을 때 26.64 점이었던 public EM 점수를 27.08 점으로 향상할 수 있었다 .
토큰 생성 시, step마다 누적 확률이 가장 높은 n 개의 sequence 를 뽑아 , 다음 step 의 토큰을 생성하는 방식을 사용했다 . n이 커질수록 많은 경우의 수를 고려할 수 있지만 , 실행 시간이 늘어난다는 trade -off 가 존재한다 . greedy search 방식으로 생성했을 때 27.08 점이었던 public EM 점수를 n=4 의 beam search 를 활용하여 28.75 점으로 향상할 수 있었다 .
[1] KorQuAD 2.0 data : https://korquad.github.io/ [2] Sungjoon Park, Jihyung Moon, Sungdong Kim, Won Ik Cho, Jiyoon Han, Jangwon Park, Chisung Song, Junseong Kim, Yongsook Song, Taehwan Oh, Joohong Lee, Juhyun Oh, Sungwon Lyu, Younghoon Jeong, Inkwon Lee, Sangwoo Seo, Dongjun Lee, Hyunwoo Kim, Myeonghwa Lee, Seongbo Jang, Seungwon Do, Sunkyoung Kim, Kyungtae Lim, Jongwon Lee, Kyumin Park, Jamin Shin, Seonghyun Kim, Lucy Park, Alice Oh, Jung-Woo Ha, Kyunghyun Cho. KLUE: Korean Language Understanding Evaluation, NeurlPS, ppl 1~25, May 2023
[3] KorQuAD 2.0으로 학습하여 배포한 huggingface 모델, https://huggingface.co/uomnf97/klue-roberta-finetuned - korquad-v2
1. 나는 내 학습 목표를 달성하기 위해 무엇을 어떻게 했는가 ?
이번 학습 목표로는 크게 NLP 의 꽃인 질의응답 테스크 자체를 이해해보자고 생각하였다 . 이를 위해 성능을 높이는 것보다는 베이스라인을 이해하고 리팩토링하며 테스크에 대해 전반적으로 높은 이해도를 얻어낼 수 있었다 . 또한 개인적으로 모델링에 관심이 조금 더 많아 전에 KLUE-RE 테스트보다 간편하게 모델을 수정하는 코드를 작성하고 GitHub 를 통해 공유하였다 .
2. 마주한 한계는 무엇이며 , 아쉬웠던 점은 무엇인가 ?
마주친 한계로써는 우선으로 KDT해커톤 을 일주일 동안 진행 후 대회에 참가하였지만 몸 관리 실패로 인하여 일주일 가까이 감기에 시달린 게 가장 큰 문제였다 . 몸 관리의 중요성을 깨달았으며 컨디션의 관리 또한 매우 중요하였다 . 대회적인 부분으로써는 많이 시도한 것이 없지만 최근 CLIP 논문을 읽고 있어 이와 비슷한 훈련방식을 가져가는 DPR을 하고 싶었지만 . 맡은 부분이 아니기에 살짝 아쉬운 부분이 존재하였다 .
3. 한계 /교훈을 바탕으로 다음 프로젝트에서 시도해보고 싶은 점은 무엇인가 ?
우선 무엇보다 몸 관리를 최우선 과제로 두고 컨디션을 잘 관리하여야겠다 . 코딩 실력보다 중요한 건 자기관리인 거 같은 생각이 머릿속에 깊게 박힌 프로젝트였다 . 다음 프로젝트의 경우 파이널 프로젝트로써 지금까지 배운 AI 기술들을 활용하며 이제 BERT에서 벗어나 GPT,LLM을 집중적으로 다루어 활용해보고 싶다 . LLM의 코드를 이미 작성해두었지만 4bit Lora tuning등 시도해야 할 것이 아직 많이 보인다 .
4. 나는 어떤 방식으로 모델을 개선했 는가 ?
모델의 개선은 위에 나와 있는 방식으로 개선하였으며 가장 중요한 점은 기존의 이해하기 어려운 베이스라인을 더욱 쉽게 리팩토링을 진행하여 파라미터 튜닝 , 모듈화를 집중적으로 개선한 부분이 큰 것 같다 .
이를 통해 retriver 와 Reader 의 역할을 깨달아 이 둘을 구분하여 개선 방향을 모색하였다 . 기존의 메인코드에 몰려있던 모든 함수를 리팩토링을 진행하여 기존의 데이터셋 코드가 훈련 , 평가에 중복적으로 들어가 코드가 난잡하였지만 데이터는 데이터코 드에 클래스로 정리하여 중복을 제거하고 , 훈련 코드는 Trainer 훈련 코드를 사용하여 파라미터를 쉽게 변경할 수 잇었으며 , korquad 매트릭스나 리트리버에서 몇 개의 문서 집합을 가져오는 코드와 같은 유틸 코드는 유틸에 따로 모아둠으로써 관리를 쉽게 진행하였다 .
5. 내가 해본 시도중 어떠한 실패를 경험 했는가 ? 실패의 과정에서 어떠한 교훈을 얻었는가 ?
모델 레이어 수정 : 레이어를 수정하려고 각종 논문을 찾아보았지만 새로운 모델을 제외한 기존 모델에서 레이어를 수정 한 부분은 적었다 . 확실히 HEAD 의 레이어를 수정하는 것보다는 어떻게 인코더 파트에서 벡터를 잘 만드는 것이 중요한지를 깨달을 수 있었는데 그래도 우선적으로 근거있게 레이어를 수정하였다 . 첫 시도는 트랜스포머의 feed forward layer 을 참고하여 이와 동일하게 구성하였다 . 물론 성능 향상이 있었지만 유의미하지는 않았다 . 아마 RelU 함수의 역할을 내가 아직 0 은 0 으로 그 이상값은 선형적으로 내보는 것 이외의 역전파 단계에서의 무슨 역할을 하는지 잘 모르는 것 같다는 생각이 들어 단순 forward 구문이 아닌 bacward 에서의 역할도 알아야겠다는 생각이 크게 들었다 .
6. 협업 과정에서 잘된점/아쉬웠던점은 어떤것이 있는가?
협업은 이번에 잘된 것 같았다 . 첫 대회는 노션을 집중적으로 사용하였고 그 이후 대회는 두 개를 동시에 사용하였지만 , 깃허브를 많이 사용하지는 않았다 . 하지만 이번에는 깃허브의 비중이 90% 이상일 정도로 노션은 기록용으로만 사용하였다 . 파이널 프로젝트를 대비해 필요한 기능을 이슈로 처리하고 사용하며 이슈에 대한 커밋 그리고 최종 푸시와 풀리퀘를 모두 연관 지어 사용하였으며 모든 부분에 대하여 템플릿을 적용하였다 . CI 또한 사용하여 풀리퀘스트 할시 자동으로 테스트가 수행되도록 진행하였다 . 기존보다 완벽히 깃허브를 사용하여 협업을 사용하였다는 점이 굉장히 잘된 점이라고 생각된다 . 아쉬운 부분은 이처럼 CI등 다른 유틸적인 부분에 집중을 하다 보니 정작 대회의 성적은 낮았다 . 하지만 이보다 중요 한 최종 프로젝트를 대비하기엔 알찬
대회라고 생각된다 .
냐미냐미~ 태민공주 고생했어