# 2022 인공지능 온라인 경진대회 - 문서 검색 효율화를 위한 기계독해 문제
### 1st Ranked (Team: TUNiB)
- [Soohwan Kim](https://github.com/sooftware)
- [Giseong Lee](https://github.com/gistarrr)
- [Donghyeop Son](https://github.com/sonacer)
- [Bin Jang](https://github.com/binjang)
- [Kijun Kwon](https://github.com/abepy)
## Project Structure
```
/USER/TUNIB_COMPETITION
├── args
│ ├── __init__.py
│ ├── data_args.py
│ ├── logging_args.py
│ ├── model_args.py
│ └── training_args.py
├── classifier_train
│ ├── dataset.py
│ ├── train.py
│ ├── ensemble.py
│ └── trainer.py
├── data
│ ├── __init__.py
│ ├── make_csv.ipynb
│ ├── make_noans.py
│ ├── pg.ipynb
│ ├── postprocess.py
│ ├── processor.py
│ └── ratio_data.ipynb
├── data_collator.py
├── ensemble_lstm_add_noans.py
├── ensemble_lstm.py
├── ensemble_mlm.py
├── ensemble_rl.py
├── inference.sh
├── model.py
├── postprocessing.ipynb
├── requirement.sh
├── run_qa_lstm_add_noans.py
├── run_qa_lstm.py
├── run_qa_rl.py
├── run_qa_with_mlm.py
├── running.sh
├── trainer.py
└── utils.py
```
- `args/`: 학습에 필요한 데이터 및 전처리를 위한 코드가 있는 디렉토리
- `__init__.py` : 하위 모듈을 import
- `data_args.py` : 데이터와 관련된 arguments
- `logging_args.py` : wandb와 관련된 arguments
- `model_args.py` : 모델과 관련된 arguments
- `training_args.py` : 학습과 관련된 arguments
- `classifier_train/`: 분류기를 위한 모델을 학습할 코드와 앙상블을 할 코드가 있는 디렉토리
- `dataset.py`: 학습에 맞게 데이터를 처리하는 코드
- `train.py`: 모델을 학습하기 위한 코드
- `ensemble.py`: 앙상블 인퍼런스 로직이 정의된 파일
- `trainer.py`: 모델을 학습시키기 위한 Trainer 클래스가 정의된 파일
- `data/`: 학습에 필요한 데이터 및 전처리를 위한 코드가 있는 디렉토리
- `__init__.py` : 하위 모듈을 import
- `make_csv.ipynb` : 주어진 데이터를 csv로 변형(train, valid, test)
- `make_noans.py` : 질문을 섞어 No answer Data를 생성하는 파일
- `pg.ipynb` : pororo PG를 활용해 No answer의 질문만 변형하는 파일
- `postprocess.py` : 모델 추론 후 후처리와 관련된 파일
- `processor.py` : 모델 학습을 위한 전처리 함수가 정의된 파일
- `ratio_data.ipynb` : answer와 noanswer의 비율을 맞춘 Data를 생성하는 파일
- `data_collator.py`: MLM loss를 추가한 Model을 위해 정의한 data collator 파일
- `ensemble_*`: 해당 모델들의 checkpoint ensemble을 수행하는 파일
- `inference.sh`: 추론을 위한 script가 정의된 파일
- `model.py`: 모델이 정의되어 있는 파일
- `postprocessing.ipynb`: Reader를 통해 나온 결과에 후처리를 적용하는 파일(조사 제거, 분류기 부착)
- `requirement.sh`: 프로그램 실행을 위해 필요한 패키지를 설치해주는 shell script
- `run_qa_*`: 해당 모델들의 학습을 수행하는 파일
- `running.sh`: 학습을 위한 script가 정의된 파일
- `trainer.py`: 학습에 필요한 Trainer가 정의된 파일
- `utils.py`: config 설정을 위한 함수가 정의되어 있는 파일
## Installation
학습에 필요한 라이브러리를 설치합니다.
재현 서버에 이미 환경설정을 마친 상태입니다.
## Pre-trained Weight를 이용한 최종 제출 파일 재현 방법
- PLM Weight는 `/USER/PLM_WEIGHT` 내부에 있습니다.
- 최종 제출 파일은 `/USER/submission` 에 `최고점.csv` 로 있습니다.
- PLM Weight 파일을 이용하여 아래의 "3. Ensemble" 부분부터 MODEL_PATH만 바꿔가며 진행해 주시면 됩니다
(MODEL_PATHs는 초기에 PLM Weight 기준으로 되어 있습니다)
- output 파일을 덮어씌우지 않으려면 inference.sh에서 output_dir를 변경해 주세요
## 주최측 제공 데이터부터 시작하여 최종 제출 파일 재현 방법
## 1. Data Preparation
- 데이터 오그멘테이션 및 학습을 위한 파일들을 준비합니다.
- train, validation 데이터셋 구성, 데이터 오그멘테이션 등이 포함되어 있습니다.
- 해당 작업은 수 시간이 소요될 수 있습니다.
- 해당 파이썬 파일을 실행한 결과는 `/USER/data/inputs/` 폴더에 저장됩니다.
- `pg.py`를 사용할 때는 "nlp" 라는 이름의 가상환경을 사용하셔야 합니다.
- Run:
```
make_csv.ipynb
make_noans.py
pg.py
ratio_data.ipynb
```
## 2. Training
- 앙상블을 위해 각 모델들을 학습합니다. Out-Of-Memory 에러가 나는 경우 `per_device_train_batch_size`의 인수를 절반, `gradient_accumulation_steps`의 인수를 2배로 늘려주시면 됩니다.
- `output_dir` 인자를 통해 모델 checkpoint가 저장될 path를 지정하면 됩니다.
### Reader Model #1
- full_lstm
- running.sh에 있는 첫번째 스크립트 실행
- Roberta-large에 lstm 레이어를 추가하여 validation 없이 전체 학습한 모델
```
$ cd /USER/TUNIB_COMPETITION
$ python run_qa_lstm.py \
--model_name_or_path klue/roberta-large \
--output_dir /USER/MODEL/reader/full_lstm \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_train \
--learning_rate 3e-5 \
--num_train_epochs 2 \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--per_device_eval_batch_size 32 \
--weight_decay 1e-4 \
--max_seq_length 512 \
--max_answer_length 40 \
--save_strategy epoch \
--lstm_model \
--full_training \
--fp16
```
### Reader Model #2
- lstm_ep2_clean_add_noans
- running.sh에 있는 두번째 스크립트 실행
- Roberta-large에 lstm 레이어를 추가하고 정답 없음 문제들을 paraphrase generation을 통해 추가 증강하여 학습시킨 모델
```
$ cd /root/competition
$ python run_qa_lstm_add_noans.py \
--model_name_or_path klue/roberta-large \
--output_dir /USER/MODEL/reader/lstm_ep2_clean_add_noans \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_train \
--learning_rate 3e-5 \
--num_train_epochs 2 \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--per_device_eval_batch_size 32 \
--weight_decay 1e-4 \
--max_seq_length 512 \
--max_answer_length 40 \
--save_strategy epoch \
--lstm_model \
--fp16
```
### Reader Model #3
- rl_pg_permute_noans
- running.sh에 있는 세번째 스크립트 실행
- Roberta-large에 paraphrase generation로 증강한 데이터와 같은 title 내의 지문을 permute하여 증강한 데이터를 추가하여 학습한 모델
```
$ cd /USER/TUNIB_COMPETITION
$ python run_qa_rl.py \
--model_name_or_path klue/roberta-large \
--output_dir /USER/MODEL/reader/rl_pg_permute_noans \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_train \
--learning_rate 3e-5 \
--num_train_epochs 2 \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--per_device_eval_batch_size 32 \
--weight_decay 1e-4 \
--max_seq_length 512 \
--max_answer_length 40 \
--save_strategy epoch \
--fp16
```
### Reader Model #4
- 4ep_ans_mlm_all_ensem
- running.sh에 있는 네번째 스크립트 실행
- 지문에 masking을 추가하여 해당 mask token을 예측하는 MLM loss를 추가한 모델
```
$ cd /USER/TUNIB_COMPETITION
$ python run_qa_with_mlm.py \
--model_name_or_path klue/roberta-large \
--output_dir /USER/MODEL/reader/4ep_ans_mlm_all_ensem \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_train \
--learning_rate 1e-5 \
--num_train_epochs 3 \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--per_device_eval_batch_size 32 \
--weight_decay 1e-4 \
--max_seq_length 512 \
--max_answer_length 30 \
--save_strategy epoch \
--fp16
```
### Classifier Model #1
- tunib-electra-ko-base
- running.sh에 있는 다섯번째 스크립트 실행
- tunib-electra-ko-base에 정답 비정답 비율이 같은 데이터로 학습
```
$ cd /USER/TUNIB_COMPETITION
$ /classifier_train/train.py \
--save_dir /USER/MODEL/classifier/classific_tunib-electra-ko-base \
--pretrain_model_name_or_path tunib/electra-ko-base \
--batch_size 32 \
--lr 2e-05 \
--num_epochs 1
```
### Classifier Model #2
- klue-roberta-Large
- running.sh에 있는 여섯번째 스크립트 실행
- klue-roberta-Large에 정답 비정답 비율이 같은 데이터로 학습
```
$ cd /USER/TUNIB_COMPETITION
$ python /classifier_train/train.py \
--save_dir /USER/MODEL/classifier/classific_klue-roberta-large \
--pretrain_model_name_or_path klue/roberta-large \
--batch_size 4 \
--lr 2e-05 \
--num_epochs 1
```
## 3. Ensemble
- 학습된 모델들을 이용해서 앙상블 예측을 합니다.
- `output_dir` 인자를 통해 json, csv 파일 등의 저장경로를 설정하시면 됩니다.
### Reader Ensemble #1
- full_nt_lstm
- inferense.sh 파일의 첫번째 스크립트 실행
- 첫번째 Reader Model의 학습 체크포인트 2개 앙상블
- 각 파일별 경로를 설정해주시면 됩니다.
```
$ cd /USER/TUNIB_COMPETITION
$ python ensemble_lstm.py \
--test_file test.json \
--output_dir /root/competition/submission_ensemble/full_nt_lstm \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_predict \
--per_device_eval_batch_size 32 \
--max_seq_length 512 \
--max_answer_length 30
```
### Reader Ensemble #2
- lstm_ep2_clean_add_noans
- inferense.sh 파일의 두번째 스크립트 실행
- 두번째 Reader Model의 학습 체크포인트 2개 앙상블
```
$ cd /USER/TUNIB_COMPETITION
$ python ensemble_lstm_add_noans.py \
--test_file test.json \
--output_dir /root/competition/submission_ensemble/lstm_ep2_clean_add_noans \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_predict \
--per_device_eval_batch_size 32 \
--max_seq_length 512 \
--max_answer_length 30
```
### Reader Ensemble #3
- rl_pg_permute_noans
- inferense.sh 파일의 세번째 스크립트 실행
- 세번째 Reader Model의 학습 체크포인트 2개 앙상블
```
$ cd /USER/TUNIB_COMPETITION
$ python ensemble_rl.py \
--test_file test.json \
--output_dir /root/competition/submission_ensemble/rl_pg_permute_noans \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_predict \
--per_device_eval_batch_size 32 \
--max_seq_length 512 \
--max_answer_length 30
```
### Reader Ensemble #4
- 4ep_ans_mlm_all_ensem
- inferense.sh 파일의 네번째 스크립트 실행
- 네번째 Reader Model의 학습 체크포인트 4개 앙상블
```
$ cd /USER/TUNIB_COMPETITION
$ python ensemble_mlm.py \
--test_file test.json \
--output_dir /root/competition/submission_ensemble/4ep_ans_mlm_all_ensem \
--overwrite_output_dir \
--preprocessing_num_workers 4 \
--do_predict \
--per_device_eval_batch_size 32 \
--max_seq_length 512 \
--max_answer_length 30
```
### Classifier Ensemble #1
- 분류기 앙상블
- klue-roberta-large의 학습 체크포인트 2개와 tunib-electra-ko-base의 학습체크포인트 1개를 앙상블
- /USER/TUNIB_COMPETITION/classifier_train/ensemble.py의 model_path부분에 /USER/MODEL/classific_klue-roberta-large의 EPOCH 하나와 가장 높은 BEST_CKPT /USER/MODEL/classific_tunib-electra-ko-base의 EPOCH 하나의 경로를 입력 한뒤 실행합니다.
- weight 측정시에는 /USER/TUNIB_COMPETITION/classifier_train/ensemble.py의 model_path부분에 /USER/PLM_WEIGHT/classifier/에 있는 BEST_CKPT세개의 경로를 입력 한뒤 실행 하고 no_check.to_csv 경로를 /USER/classifier/plm_weight_classification.csv로 변경
```
$ cd /USER/TUNIB_COMPETITION/
$ python /classifier_train/ensemble.py \
```
## 4. Postprocessing
- 추론 결과에 대해 후처리를 진행합니다.
- Majority voting Ensemble 진행
- 불필요한 조사 제거
- 분류기 예측 결과 적용
```
$ cd /USER/TUNIB_COMPETITION/
```
- `postprocess.ipynb`를 코드블럭 순서에 맞춰 실행해주시면 됩니다.
- Ensemble 시 지정된 `output_dir` 내부에 생성된 파일을 이용해 Majority voting을 진행합니다.
- `/USER/submission/result.csv` 해당 경로에 최종 결과가 생성됩니다.
## Contact
위 내용에 오류가 있거나 문의사항이 있는 경우 kaki.ai@tunib.ai 혹은 tori.ai@tunib.ai 로 연락 부탁드립니다.