모델 수정 및 학습
논리 오류를 자동으로 고치는 기능을 구현하기 위해, GraphCodeBERT 모델을 기반으로 학습을 진행했다.
기본적으로 Microsoft가 공개한 refinement task 학습 구조를 활용했고,
여기에 내가 정의한 입력 구조 (문제 설명 + 오답 코드)를 반영하기 위해 모델 입력 포맷을 수정했다.
전체 구조는 그대로 유지하되, 학습 데이터 구성과 전처리 과정에서 필요한 변경을 적용한 형태로 학습을 진행했다.
사용한 모델 구조
기반 모델은 GraphCodeBERT + Seq2Seq 구조로, Microsoft가 공개한 GraphCodeBERT refinement task를 그대로 활용했다.
- 입력 인코더: GraphCodeBERT (CodeBERT에 data flow 정보 추가)
- 출력 디코더: Transformer 기반 seq2seq 디코더
- 기본 목적: 논리적 오류가 포함된 코드를 입력으로 받아 수정된 코드를 생성
입력 포맷 수정
기존 모델의 입력 포맷에서 알고리즘 문제 설명을 추가하였다.
" "[CLS] bug code [SEP] data flow
↓
[CLS] algorithm problem description [SEP] bug code [SEP] data flow
문제 설명 추가 이유
논리 오류는 단순히 코드만 보고 판단하기 어려운 경우가 많고,
문제 조건(예: 입력 범위, 정렬 기준, 특수 조건 등)이 코드 수정의 핵심 단서가 되기 때문이다.
따라서 학습 데이터에 문제 설명을 포함시켜 모델이 문맥을 고려한 수정을 하도록 유도했다.
학습 데이터 수집과 전처리
데이터 수집
algorithm problem description
- CodeNet
IBM Research에서 만든 프로그래밍과 소프트웨어 개발에 관련된 대규모 데이터셋, 다양한 알고리즘 문제와 각 문제에 제출된 다양한 언어의 코드로 이루어져있다.
- CodeNet
bug code - fixed code 쌍 데이터
- FixEval
CodeNet의 데이터셋중에서 파이썬으로 작성한 코드만 뽑아내고 한 유저가 한가지 문제를 풀어내기까지의 코드 변화내역을 1:m으로 엮은 데이터셋
- FixEval
데이터 전처리
학습 데이터는 아래의 과정으로 전처리하였다.
- FixEval 파이썬 데이터셋에서 논리적 오류가 존재하여 TC를 통과하지 못한 WA코드쌍만 추출
- 초보자에게 쉬운, 복잡하지 않는 문제를 선별하기 위해 score가 400이상인 문제 삭제
- 문제별로 같은 의미지만 다른 유니코드의 기호들을 변환
- 코드쌍 추상화 - 변수와 함수의 이름을 var1 … varX, method1 … methodX 로 추상화
- 코드쌍과 알고리즘 문제 설명문을 토큰화 했을 때 최대 토큰 512를 초과하는 코드쌍 삭제
- nl + pl 코드쌍 생성
학습 데이터 샘플
{
"p_name": "p03036",
"code": {
"buggy_code": "(var0, var1, var2) = map(int, input().split())\nfor i in
range(10):\n\tprint(var2)\n\tvar2 = var0 * var2 - var1\nprint(var2)",
"fixed_code": "(var0, var1, var2) = map(int, input().split())\nfor i in
range(10):\n\tvar2 = var0 * var2 - var1\n\tprint(var2)"
}
}
- 알고리즘 문제별로 최소 71쌍, 최대 8989쌍이 존재하며 평균 200개의 nl-pl 쌍이 존재.
- 결과적으로 25만개의 샘플을 가지는 데이터셋을 만들었다.
자세한 전처리 코드는 data_pre_process.py에서 확인할 수 있다.
모델 학습
기본적으로 GraphCodeBERT의 학습 스크립트를 수정하여 사용했다
특별한 구조 수정 없이도 학습이 가능했으며, 입력 포맷만 변경하여 기존 학습 코드와의 호환성을 유지했다.
Google Colab A100 고용량 RAM 을 사용하여 10epoch 씩 총 20epoch 학습 수행
모델 훈련
!python ./COCO_AI/graphcodebert_0718/run.py \\
--do_train \\
--do_eval \\
--model_type roberta \\
--model_name_or_path microsoft/graphcodebert-base \\
--tokenizer_name microsoft/graphcodebert-base \\
--config_name microsoft/graphcodebert-base \\
--train_filename /content/drive/MyDrive/coco_ai/train.json \\
--dev_filename /content/drive/MyDrive/coco_ai/val.json \\
--p_desc_path /content/drive/MyDrive/coco_ai/tmp_p_desc \\
--output_dir /content/drive/MyDrive/coco_ai/graphcodebert_256_0724/output \\
--load_model_path /content/drive/MyDrive/coco_ai/graphcodebert_256_0724/output/checkpoint-last/pytorch_model.bin \\
--max_source_length 512 \\
--max_target_length 256 \\
--beam_size 10 \\
--train_batch_size 32 \\
--eval_batch_size 32 \\
--learning_rate 5e-5 \\
--num_train_epochs 20 2>&1| tee /content/drive/MyDrive/coco_ai/graphcodebert_256_0724/output/train.log
모델 훈련 과정
***** Running evaluation *****
09/10/2023 07:43:18 - INFO - __main__ - Num examples = 25381
09/10/2023 07:43:18 - INFO - __main__ - Batch size = 32
09/10/2023 07:47:45 - INFO - __main__ - eval_ppl = 1.1595
09/10/2023 07:47:45 - INFO - __main__ - global_step = 55981
09/10/2023 07:47:45 - INFO - __main__ - train_loss = 0.029
09/10/2023 07:47:45 - INFO - __main__ - ********************
09/10/2023 08:08:31 - INFO - __main__ - bleu-4 = 73.64
09/10/2023 08:08:31 - INFO - __main__ - xMatch = 37.0
09/10/2023 08:08:31 - INFO - __main__ - ********************
09/10/2023 08:08:31 - INFO - __main__ - Best BLEU+xMatch:110.64
09/10/2023 08:08:31 - INFO - __main__ - ********************
epoch 10 loss 0.0338: 100%|██████████| 5598/5598 [1:38:14<00:00, 1.05s/it]
***** Running evaluation *****
09/10/2023 09:46:47 - INFO - __main__ - Num examples = 25381
09/10/2023 09:46:47 - INFO - __main__ - Batch size = 32
09/10/2023 09:51:14 - INFO - __main__ - eval_ppl = 1.15586
09/10/2023 09:51:14 - INFO - __main__ - global_step = 61579
09/10/2023 09:51:14 - INFO - __main__ - train_loss = 0.0338
09/10/2023 09:51:14 - INFO - __main__ - ********************
09/10/2023 10:11:48 - INFO - __main__ - bleu-4 = 73.29
09/10/2023 10:11:48 - INFO - __main__ - xMatch = 36.8
09/10/2023 10:11:48 - INFO - __main__ - ********************
epoch 11 loss 0.0305: 80%|███████▉ | 4470/5598 [1:18:27<19:47, 1.05s/it]
학습 결과
GraphCodeBERT
최종 train_loss : 0.029 bleu-4 : 73.64, xMatch : 37.0
같은 방식으로 후보 모델이었던 CodeBERT-small 모델도 학습했던 결과이다.
CodeBERT-small
최종 train_loss : 0.0765 bleu-4 : 70.44, xMatch : 29.1
회고
모델을 처음부터 새로 만드는 것이 아니라, 기존 구조를 그대로 유지하면서 입력 포맷만 수정하는 방식은 매우 효과적이었다.
이 방식은 기존 연구를 잘 활용하면서도, 내가 정의한 기능에 맞게 학습 데이터를 구성하는 데 집중할 수 있게 해주었다.
또한 문제 설명을 입력에 추가함으로써, 단순히 코드 패턴을 암기하는 방식이 아니라 문제 조건을 고려한 수정이 가능하도록 유도할 수 있었다.
다음 글에서는 학습 결과를 바탕으로 모델이 어떤 종류의 논리 오류를 잘 고쳤고, 어떤 오류에는 약했는지를 분석할 예정이다.