| 분류 | 샘플 수 | 용도 | 공개여부 |
|---|---|---|---|
| Wikipedia | 57k | Retriever 학습용 | 모든 정보 공개(text, corpus_source, url, domain, title, author, html, document_id) |
| Train | 3952 | Reader 학습용 | 모든 정보 공개(id, question, context, answers, document_id, title) |
| Validation | 240 | ||
| Test | 240 (Public) | 제출용 | id, question만 공개 |
| 360 (Private) |
| 개발 환경 | 서버 | AI Stages GPU (Tesla V100-SXM2) * 4EA |
|---|---|---|
| 기술 스택 | Python, Transformers, PyTorch, Pandas, WandB, Hugging Face, Matplotlib | |
| 운영체제 | Linux | |
| 협업도구 | Github | 코드 공유 및 버전 관리, Issue로 진행 중인 Task 공유 |
| Notion | 회의 내용 공유, 프로젝트 일정 관리, 실험 기록 | |
| Slack | Github 및 WandB 봇을 활용한 협업, 의견 공유, 회의 | |
| Zoom | 실시간 소통을 통한 의견 공유 및 회의 |
| 팀원 | 역할 |
|---|---|
| 박준성 | 협업 관리(이슈/PR 템플릿 추가), 코드 리팩토링(베이스라인 코드 재작성, Config 클래스), EDA 서버 개설 및 관리(Page 시스템, 훈련용 데이터셋 EDA 페이지), Cross Encoder 구현, Retrieval 성능 측정 EDA(TF-IDF, BM25), 앙상블 구현(soft voting), 모델 파이프라인 병합(Retriever-Reader 연결), 데이터 증강(KorQuad) |
| 이재백 | Dense Retriever 구현, Negative Sampling 구현(In-batch, random, Hard negative sampling), 모델 조사 및 실험 |
| 강신욱 | 모델 탐색 및 실험, 기능 향상 시도( 가중치를 활용한 sparse와 dense 리트리버 결합, GPT를 활용한 dense retrieval의 네거티브 샘플링 생성) |
| 홍성균 | Sparse Retrieval 구현(BM25), 코드 리팩토링(main.py 병합, CPU병렬처리, Reader/Retrieval Tokenizer 분리 적용), EDA서버 증축(DataEDA, TokenizerEDA), 실험 편의성 개선(WandB 적용), Bug Fix(하이퍼 파라미터 미적용 수정, Argumentparser 미적용 수정), 데이터 증강(KorQuAD와의 병합) |
| 백승우 | Generation-based Reader 모델 구현,PEFT(Parameter Efficient Fine-Tuning) 기법 중 LoRA(Low-Rank Adaptation) 구현, Generation-based 모델 조사 및 실험 |
| 김정석 | 한자 등 각 유니코드 블록에 해당하는 문자를 제거하는 전처리 함수 작성 및 EDA |
project/
│
├── notebooks/ # EDA 결과, 임시 코드를 위한 Jupyter 노트북 디렉토리
│ └── (EDA 결과 또는 임시 코드) # 실제 노트북 파일들
│
├── src/ # 소스 코드의 메인 디렉토리
│ ├── QuestionAnswering/ # 질문 답변 관련 모듈 디렉토리
│ │ ├── tokenizer_wrapper.py # 토크나이저 래퍼 클래스/함수
│ │ ├── trainer.py # 모델 훈련 관련 클래스/함수
│ │ └── utils.py # QA 관련 유틸리티 함수
│ │
│ ├── Retrieval/ # 검색 관련 모듈 디렉토리
│ │ ├── cross_encoder.py # Cross-encoder 모델 관련 코드
│ │ ├── dense_retrieval.py # 밀집 검색 관련 코드
│ │ ├── sparse_retrieval.py # 희소 검색 관련 코드
│ │ ├── hybrid_retrieval.py # 하이브리드 검색 관련 코드
│ │ ├── NegativeSampler.py # 일반 네거티브 샘플링 클래스
│ │ └── SparseNegativeSampler.py # 희소 검색 기반 네거티브 샘플링 클래스
│ │
│ ├── server/ # Streamlit 서버 관련 코드 디렉토리
│ │ ├── page/ # 페이지 구현 클래스 디렉토리
│ │ │ ├── DataEDA.py # 데이터 EDA 페이지 구현
│ │ │ ├── HomePage.py # 홈페이지 구현
│ │ │ ├── TokenizerEDA.py # 토크나이저 분석 페이지 구현
│ │ │ └── trainingDatasetQA.py # QA 데이터셋 분석 페이지 구현
│ │ │
│ │ └── utils/ # 서버 유틸리티 디렉토리
│ │ ├── data_loader.py # 데이터 로딩 관련 함수
│ │ └── Page.py # 페이지 관리 클래스
│ │
│ ├── config.py # 설정 파일 로드 및 파싱
│ ├── index.py # Streamlit 앱의 메인 엔트리 포인트
│ ├── main.py # 전체 모델 훈련 및 검증 관리
│ └── preprocess.py # 텍스트 전처리 함수
│
├── config.yaml # 기본 모델 및 데이터 경로 설정
└── dense_encoder_config.yaml # Dense Encoder 모델 설정




| 분류 | 순위 | EM | F1 |
|---|---|---|---|
| private (최종 순위) | 5🥉 | 67.22 | 77.88 |
| public (중간 순위) | 12 | 64.17 | 74.89 |
Pages/ 폴더 아래에 띄우고 싶은 페이지 클래스를 모아놓으면 자동으로 import하여 페이지 계층 구조를 만들고 sidebar를 이용하여 다른 페이지로 이동할 수 있도록 하였다. Stream 서버를 활용하여 Context의 어느 부분이 주로 UNK 토큰이 되는지 확인해보았고, 그 결과 한국어가 아닌 다른 국가의 언어(특히, 한자), \n과 같은 이스케이프 문자, 한글로 적혀있는 외국어(ex. 이름: 퓌헤레노르) 등이 UNK 토큰으로 식별하고 있다는 걸 알 수 있었다.
따라서 한자와 같은 문자를 Context에서 제거하는 전처리를 시도하려고 하였으나, test dataset의 예측 결과에 “박준성(朴俊性)”과 같이 한자를 함께 정답으로 인식하는 케이스도 발견하여 최종 예측에 노이즈가 섞이게 될 수도 있다고 판단하에 실행하지 않았다.
한편 기존에 제공된 Trian Dataset 외에 KorQuAD를 활용하여 데이터셋을 증강하였다. 그리고 해당 증강된 데이터에 대하여 특수문자 및 연속 공백 등을 제거하는 등의 전처리를 진행하였으나, 해당 전처리 결과의 효과는 미미하여 본 프로젝트 최종에는 반영하지 않았다.
6.4.1 BERT-base, ELECTRA-base
6.4.2 DeBERTa-base
6.4.3 RoBERTa-base
처음 앙상블을 진행하였을 때에는 Bert-base 및 DeBERTa-base 모델로 여러차례 실험을 진행하였으나, 퍼블릭에서 기대 만큼 좋은 성능을 보이지는 못했다. 그러던 중에 마스터님께서 말씀하셨던 “Public 점수 뿐 아니라, Validation 점수 또한 중시해라”라는 조언이 떠올라서 단순히 Public 점수만을 높히기 위함이 아닌 모델 자체의 일반화 성능을 높히기 위해 RoBERTa-base의 모델들을 앙상블에 추가하여 테스트한 결과 성능이 Private에서 크게 향상 된 것을 확인할 수 있었다.
성능이 향상된 구체적인 사유로는 앙상블의 요지가 좋은 모델을 섞는 것이 아니라, 다양한 관점의 모델들을 섞는 것에 있기 때문이라고 판단된다. 단순히 성능이 좋은 모델들을 나열하여 앙상블 한다면, 이는 결국 같은 base의 모델의 나열일 뿐이고, 해당 결과값들의 앙상블은 강점뿐 아닌 약점 또한 두드러질 것이기 때문이다.
따라서 본 프로젝트의 앙상블에서 단순히 public 스코어에 따른 좋은 모델을 나열해 앙상블 한 것이 아닌, validation에 중점을 두어 다양한 based를 가진 모델들을 섞은 것이 유효하게 작용했다고 판단된다.
1. 프로젝트에 임하며 설정한 목표
‘경쟁’이 아닌 ‘성장’을 목표로 하자
대회 내내 public 스코어가 제공되었지만, 우리 팀은 그것을 상대적 ‘경쟁’지표로 삼는 것이 아닌, ‘절대적’ 성장 지표로 삼기로 하였다. 따라서 각자가 본 기계독해 프로젝트의 강의에서 배운 Retrieval, Reader 파트에 있어서 적용하고 싶었던 것들을 적용해 보기로 하였고 그 외에도 Data-centric한 의사결정을 하기 위해 실시간으로 데이터를 확인할 수 있는 EDA 서버에 관해 논의해보기도 하였다.
또한 단순히 Branch 생성, Issue 및 PR Request 제안 등을 위해 Git을 활용하는 것이 아니라, 프로젝트 자체의 버전 관리를 위해 Git 을 활용해 보자는 제안이 있었으며 따라서 본 프로젝트에서는 Release Tag를 활용하여 프로젝트의 버전관리 까지 신경을 써보기로 하였다
2. 구체적으로 프로젝트에 기여한 점
Retrieval에서의 BM25의 적용
기존에 Sparse Retrieval로 적용되었던 TF-IDF는 ‘문서 길이’에 관한 변수 고려가 전혀 되고 있지 않던 바, 더 정밀한 분석을 위해 BM25(Best Matching 25)를 적용하여 문서 길이와 빈도에 관해 TF-IDF 보다 더 정확하게 모델에 반영할 수 있었다.
실제 프로젝트의 결과에 있어서도 모델 성능에 public Score 기준 10%정도의 향상이 있었다.
실험 편의성을 위한 파일 재구조화
제공된 Base Line 코드에는 inference.py와 train.py가 분리되어 있는 등 실험 편의적인 측면에서 CLI에서 매번 직접 명령어를 수정해줘야 하는 불편이 존재하였기에, 기능이 분리되었던 두 파일에 관해 중첩되는 블록과 재사용성이 있는 부분을 고려하여 main.py라는 통합된 파일로 병합하였고, 해당 파일에서 argumnet parser를 활용하여 train, inference 기능을 수행할 수 있도록 파일을 재구조화 하였다.
해당 변경된 구조는 마지막까지 지속되어 main.py에서 모델의 훈련, 평가, inference 등을 진행하였다.
Data-centric 의사결정을 위한 EDA-server 증축
Streamlit을 활용하여 EDA 결과를 실시간으로 주고 받고 또 확인할 수 있는 서버를 구현하기로 하였으며, 그렇게 구축된 EDA server에서 DataEDA, TokenizerEDA 등의 페이지를 구축하였다.
특히 TokneizerEDA 페이지에서는 모델에 사용되는 토크나이저를 문자로 입력만 해준다면, 해당 토크나이저에 의해 UNK 처리되는 부분이 어떤 것인지 그리고 전체 데이터셋에서 UNK 토큰의 비율이 얼마인지 실시간으로 확인하여 팀원들이 해당 결과를 공유할 수 있도록 처리하여 팀이 데이터 중심의 의사결정을 할 수 있도록 도왔다.
3. 마주한 한계
3.1 PM의 부재: 데드라인, 프로토콜 미정
이전 프로젝트까지 팀 내에서 PM의 필요성에 관해 구체적으로 느끼지 못해서 본 프로젝트에서도 별 다른 의견 없이 PM은 공석인 채로 진행하였는데, 이것이 ‘협업’ 그 자체에 있어서는 좋지 않았던 것 같다. 일정을 조율해 주고 데드라인 및 코드작성에 있어서의 프로토콜 같은 것들을 더 깊게 나눌 수 있도록 PM을 정하여 프로젝트 전반을 관리할 수 있도록 하여야 했는데 그러지 못했다. 해당 문제점은 프로젝트 초반에는 드러나지 않았지만, 후반에 이르러 코드가 더 복잡해질수록 변수명 하나를 설정하더라도 일관되지 못하게 각자의 방식으로 진행하여 개인이 수행한 부분 외의 코드에 관해 이해하는데 시간이 너무 오래 걸렸다.
3.2 버전 관리 미흡: Git Release의 미흡한 활용
Git Release Tag를 활용하여 버전관리를 하고자 하였으나, 실제 구상한 바와 다르게 Semantic Versioning에서 1.0.0, 2.0.0, 3.0.0 과 같은 ‘주’ 버전 관리에만 신경썼고 그 외 세부적인 ‘부’, ‘수’ 버전 관리는 하지 못했다. 아무래도 프로젝트 기간 자체가 그다지 여유롭지 못했어서 그런 면도 있겠지만, Git에서 Merge 되는 순간에 해당 버전을 ‘주’ 단위로 업그레이드 시킬 것인지 ‘부’ 또는 ‘수’ 단위로 업그레이드 시킬 것인지에 관해 구체적으로 합의되지 못한 영향도 큰 것 같다.
4. 한계를 바탕으로 다음 프로젝트에서 시도해볼 것
Tableau를 활용하여 프로젝트 전반을 관리하는 Dash Board 구현
결국 전반적으로 프로젝트 상세(EDA, 모델 분석, 실험 가설 검증)를 기록할 Dash Board가 부재했던 것이 한계점들의 공통점 이라고 생각되었다. 구체적인 코드에 관한 사항들은 Git을 이용하여 버전을 관리할 수 있다고 하더라도, 해당 버전에 관해 가설 검증 및 구체적인 실험들은 ReadMe에 전부 녹여낼 수 없을 것이기에, 이를 따로 관리할 Dash Board가 필요하다고 생각한다. 다음 프로젝트에서는 앞서 언급한 한계점들을 구체적으로 반영할 Dash Board를 구현함과 함께 PM으로서의 역할을 수행해 보고 싶다.