[LLM 기반 문서 점검 자동화 서비스] 오픈소스 모델 소개
안녕하세요. 비투엔입니다!
오늘은 DIX2팀 이정연 전임님께서 사내기고에 작성하신 기고문을 소개해 드리려고 하는데요.
기고 내용이 유익하고 흥미로워 더 많은 사람들과 함께 나누고자 비투엔블로그에도 업로드합니다😊
이 자리를 빌어, 현업으로 바쁘신 중에도 좋은 자료 작성해 주신 이정연 전임님께 감사 인사드립니다!
안녕하세요. DIX2팀 이정연 전임입니다.
이번 기고에서는 ‘예방적품질관리를 위한 문서 점검 자동화 서비스’에서 사용한 오픈소스 모델을 소개하고, 모델을 평가 및 선정할 때 사용했던 성능지표에 대한 설명 드리겠습니다.
<MRC 모델의 데이터셋>
모델 설명에 앞서 저희가 사용한 데이터셋 구조에 대해 간단하게 설명 드리겠습니다.
앞선 기고에서 저희가 데이터를 어떻게 구축했는지 보실 수 있었는데요. 오픈소스 팀은 API 팀과 달리, 오픈소스 모델을 사용해야 하기 때문에 모델의 인풋에 맞는 형식으로 데이터셋을 재구성해야 했습니다.
저희가 해결해야 하는 과제는 RFP 요구사항을 지문(context)으로 보여주고 “A라는 내용이 지문에 포함되어 있는가?”와 같은 질문(question)을 했을 때, A 내용이 지문에 포함되어 있다면 어디에 포함되어 있는지 지문에서 추출해서 답변하고 포함되어 있지 않다면 ""(무응답)을 반환하는 형식의 과제입니다.
이는 MRC(Machine Reading Comprehension, 기계독해) task에 해당하며, 답변 시 새로운 말을 생성해내거나 하지 않고 지문에 있는 내용을 그대로 추출해서 답변하기 때문에 추출형 MRC로 분류됩니다.
따라서, 오픈소스 팀이 사용한 데이터의 구조는 KorQuAD(추출형 MRC의 벤치마크 데이터셋)와 동일하게 변경했는데요. 데이터 구조의 예시는 아래와 같습니다.
{ "version": "v2.0", # 데이터셋 버전 정보 "data": [ { "title": "1", # 출처 문서의 제목 "paragraphs": [ { "context": "정의: ㅇ 데이터 표준 수립\n\n세부 내용 : ㅇ 데이터 표준 개선·정비\n - 구축되는 시스템의 데이터 특성을 고려하여 표준화 관리체계...", # 지문 "qas": [ { "question": "데이터 표준화 지침을 정비하는 내용이 있는가?", # 질문 "id": "2_DAR-001-1_1_1",# 질문id (unique id) "answers": [ { "text": "- 구축되는 시스템의 데이터 특성을 고려하여 표준화 관리체계(데이터베이스 표준화 지침 및 가이드 등)를 개선·정비하여야 함", # 답변 "answer_start": 42 # 지문에서의 답변 시작 위치 인덱스 } ], "is_impossible": false # 정답 있음: false / 정답 없음: true } … } |
오픈소스 팀은 구축한 데이터를 이와 같은 구조로 변경하여 데이터셋 이름을 KorQuAD + Quality(예방적 '품질'관리)를 합친 KorQuALity로 불렀습니다 (이태현 선임님이 지어주셨습니다😊👍)
<BERT>
이번 서비스에서 사용할 모델로는 BERT 기반의 모델을 선택하였습니다.
앞서 설명 드렸듯이 과업을 해결하기 위해 추출형 MRC task로 접근하기로 결정했는데요.
이러한 task는 새로운 텍스트를 생성해야 할 필요가 없으며, 이미 주어진 지문에서 원하는 텍스트를 추출하기만 하면 되기 때문에 encoder 모델인 BERT를 선택했습니다.
1. BERT (Bidirectional Encoder Representations from Transformers)
BERT는 2018년에 구글에서 공개한 NLP pre-trained model입니다.
BERT는 Transformer를 기반으로 하며, Transformer의 Encoder를 여러 개 쌓아서 구성한 모델입니다.
BERT는 self-supervised learning을 통해 학습합니다.
여기서 self-supervised learning이란 unlabeled data를 이용해 데이터의 일부분이 다른 부분을 예측하도록 학습(자기학습) 하는 방법입니다.
supervised learning은 input data를 보고 label이 무엇인지 예측하는데 반해, self-supervised learning은 input data의 일부분을 '예측해야 하는 label'로 생각하고 학습해서 그 데이터 자체의 특성을 학습하는 것입니다.
다음으로 BERT의 내부 동작이 어떻게 되는지 알아보겠습니다.
2. Input representation
BERT의 input은 token embedding, segmentation embedding, position embedding 세 가지 벡터의 합으로 표현됩니다.
세 가지 임베딩을 확인하기 전에 위 이미지의 Input을 보면, [CLS], [SEP]이라고 작성되어 있는 토큰을 볼 수 있습니다.
이는 스페셜 토큰으로 [CLS]는 항상 맨 앞에 입력되며, input의 시작을 알리는 토큰이고 [SEP]은 두 개의 문장을 구분하거나, 마지막 문장임을 알리는 토큰입니다.
1) token embeddings
Token embedding은 일반적으로 알고 있는 word embedding이라고 보시면 되는데요.
BERT는 단어보다 더 작은 단위로 쪼개는 subword tokenizer인 WordPiece tokenizer를 사용합니다.
이렇게 단어를 쪼개서 사용하면 OOV(Out-Of-Vocabulary; 단어 집합에 포함되어 있지 않아 기계가 모르는 단어) 문제를 완화시킬 수 있다는 장점이 있습니다.
ex1) birthplace →birth + ##place
ex2) embeddings → em+ ##bed + ##ding + ##s
위 예시와 같이 하나의 단어를 더 작은 단위로 쪼갬으로써 자주 사용하지 않는 희귀한 단어에 대해서도 임베딩 처리를 할 수 있게 하는 것입니다.
2) segment embeddings
앞에서 [SEP] 토큰을 이용해 문장을 구분한다고 말씀드렸습니다.
이와 더불어 [SEP] 토큰 이전 문장을 sentence A, [SEP] 토큰 이후 문장을 sentence B로 취급해서 문장을 구분해주는 것을 segment embeddings이라고 합니다.
3) position embeddings
RNN과 같은 모델은 이전에 주어진 단어나 정보를 고려해서 현재 time-step의 정보가 인코딩 되는 형태입니다.
하지만, BERT는 RNN 계열의 모델을 사용하지 않았기 때문에 문장의 순서를 파악할 수 없는데요.
이러한 문제점을 보완하기 위해 Transformer에서는 삼각함수 계산을 통해 단어의 위치 정보를 더해주는 positional encoding을 사용했습니다.
BERT에서는 이러한 계산을 이용하지 않고, 위치 정보를 학습하기 위한 position embedding을 사용합니다.
3. Pre-Training
앞서 BERT는 데이터의 일부분이 다른 부분을 예측하도록 하는 self-supervised learning을 통해 학습한다고 말씀드렸습니다. BERT는 이러한 학습을 위해 MLM, NSP 두 가지를 수행합니다.
1) MLM (Masked Language Model)
MLM은 input으로 들어가는 시퀀스 중에서 임의의 token을 지운 후, 이 지워진 단어를 예측하는 방법으로 학습을 진행합니다.
이 때, 인풋 토큰의 15%만 랜덤으로 선택하여 [MASK] 토큰으로 바꿔주는 방식으로 지워주는데요.
이러한 학습 방법은 pre-training 단계에서만 진행하고, fine-tuning 단계에서는 수행하지 않기 때문에 이 [MASK] 토큰에 대한 pre-training과 fine-tuning 간의 미스매치가 발생합니다.
따라서 이 문제를 해결하기 위해 몇몇은 [MASK] 토큰으로 가리는 대신 다른 단어로 바꿔주거나 아예 변경하지 않고 기존 단어를 그대로 사용하기도 합니다.
ex1) My dog is [MASK] : [MASK] 토큰을 이용하여 지우기 (80%)
ex2) My dog is apple : 랜덤으로 다른 단어로 변경 (10%)
ex3) My dog is cute : 미변경 (10%)
이렇게 학습된 문장은 self-attention을 통해 각 단어가 모든 단어를 참고하기 때문에 bidirectional하게 문맥을 반영했다고 볼 수 있습니다.
2) NSP (Next Sentence Prediction)
NSP는 두 개의 문장이 있을 때, 두 번째 문장이 이전문장의 바로 다음으로 오는 문장이 맞는지 예측하는 방법으로 학습을 진행합니다.
학습을 할 때는 50%는 다음 문장에 실제로 이어지는 글을 붙이고, 나머지 50%는 이어지지 않는 랜덤한 문장을 이어 붙인 후 학습시킵니다.
ex1) input = 남자는 상점에 갔다. 그는 우유를 샀다. label = IsNext
ex2) input = 남자는 상점에 갔다. 개는 귀엽다. label = NotNext
이렇게 다음 문장이 문맥상 올바른 문장인지 예측하는 학습을 진행할 때는 앞서 설명 드린 MLM의 형태로 입력이 들어가서 MLM과 NSP가 함께 수행됩니다.
참고로 NSP를 수행하는 이유는 QA(Question Answering)나 자연어 추론과 같은 task를 수행할 때, 문장 간의 관계를 이해해야 하기 때문입니다.
BERT에서는 이렇게 두 개 이상 문장의 관계를 알아야 해결할 수 있는 NLP task에서도 좋은 성능을 내기 위해 NSP를 함께 수행합니다.
4. Fine-tuning
BERT는 앞에서 설명한 MLM, NSP 두 가지의 pre-training 과정을 거친 모델을 가지고 제일 위에 단 하나의 layer를 쌓는 것만으로도 다양한 task에서 좋은 성능을 보입니다.
BERT가 사용될 수 있는 주요 4개의 task를 설명 드리겠습니다.
(a) 문장 두 개를 쌍으로 받아서 분류하는 task
- 두 문장이 같은 뜻을 가지고 있는지 예측하는 task, NSP, …
(b) 하나의 문장을 받아서 분류하는 task
- 감성분석, 스팸메일 분류, …
(c) QA task
- question, paragraph를 쌍으로 받아서 paragraph에서 answer가 입력되어 있는 시작점 토큰부터 답을 반환
d) 하나의 문장을 받아 각각의 토큰마다 답을 예측
- 형태소 분석기, 개체명 인식, …
<RoBERTa>
이번 서비스에서 최종으로 선택한 모델은 RoBERTa(Robustly optimized BERT approach) 모델입니다.
논문 저자는 BERT가 undertrain 되어있기 때문에 이전에 간과했던 부분의 접근을 달리하면, 이후에 발표된 모든 모델의 성능과 일치하거나 초과할 수 있음을 발견했다고 합니다.
따라서 RoBERTa가 BERT와 다른 점은 무엇인지 알아보겠습니다.
1. Dynamic changing the masking pattern applied to the training data
BERT에서는 pre-training 과정에서 랜덤으로 토큰에 mask를 씌우고 예측하는 MLM 방식을 사용했습니다.
이 때, mask를 씌우는 행위는 학습 전에 진행하기 때문에 모든 학습 단계에서 똑같은 mask를 보게 됩니다. 이를 static masking이라고 합니다.
RoBERTa는 BERT와 같이 모든 학습단계에서 같은 mask를 사용하는 것을 피하기 위해 매 epoch마다 mask를 다르게 씌우는 dynamic masking을 사용합니다.
2. Removing the next sentence prediction objective
기존 BERT에서는 두 개의 문장을 이어 붙여 문맥상으로 연결되는 문장인지 예측하는 NSP 과정을 거쳤습니다.
원래 BERT의 pre-training은 MLM을 중점으로 하고 있었기에, RoBERTa는 이 NSP 필요성에 대해 의문을 제기하며 NSP를 제외하고 MLM만을 이용하여 pre-training 했습니다.
3. Bigger batches
RoBERTa는 batch size의 영향력을 확인하기 위한 실험을 진행했습니다.
총 step 수는 유지되도록 batch size와 epoch을 조정하면서 실험을 수행했는데요.
실험 결과로는 같은 step수여도 batch size가 클수록 성능이 좋아지는 경향을 보였다고 합니다.
4. More data
RoBERTa는 총 160GB의 데이터를 이용해 학습되었습니다.
일반적으로 데이터가 많을수록 성능이 좋아지기 때문에 더 많은 데이터를 모았으며, 이는 16GB의 데이터로 학습한 BERT-large보다 실제로 더 좋은 성능을 보였습니다.
벤치마크 데이터셋에 모델을 적용시킨 성능 표를 보면,
결과적으로 RoBERTa가 BERTlarge보다 성능이 더 크게 개선된 것을 확인할 수 있습니다.
또한, 학습을 더 오래했을 때 과적합 되지 않고 성능이 오르는 것을 볼 수 있습니다.
<MRC 모델의 학습 방법>
오픈소스 팀이 사용한 RoBERTa 모델의 아웃풋은 각각의 token이 ① 답변의 시작 token일 확률 ② 답변의 끝 token일 확률을 나타냅니다.
그리고, Cross Entropy loss를 사용하여 두 위치에 대한 확률값과 ground-truth의 차이를 이용해 loss를 구합니다.
이렇게 학습시킨 후, 원하는 형태로 예측값을 추출하기 위해서는 후처리를 조금 거쳐야 하는데요.
Start position과 end position을 독립적으로 예측하기 때문에 답변이 제대로 만들어지지 않는 경우가 있기 때문입니다.
1. 불가능한 답 제거하기
- start position이 end position보다 뒤에 있는 경우 (ex. start=90, end=70)
- 예측한 위치가 context를 벗어나는 경우 (ex. question 위치에 답이 나온 경우)
- 사전에 설정한 하이퍼파라미터 max_answer_length보다 답변 길이가 긴 경우
2. 최적의 답변 찾기
- start position, end position prediction에서 점수(logits)가 가장 높은 토큰 N개를 찾는다.
- 불가능한 start/end 조합을 제거한다.
- 가능한 조합들을 점수 합이 큰 순서대로 정렬한다.
- 점수가 가장 큰 조합을 최종 예측으로 선정한다.
위와 같은 과정을 거치면 최종 예측값이 반환됩니다.
<성능 지표>
추출형 MRC의 성능 지표로는 대표적으로 EM, F1을 사용합니다.
이 때, 질문에 대한 답이 지문에 존재하지 않을 경우, 정답(GT: Ground Truth)은 “”(빈 string)으로 표현하며,
정답과 GT를 비교할 때는 모든 텍스트의 punctuation은 무시한 채 점수를 계산합니다.
1) EM (Exact Match)
- 모델이 예측한 답과 GT가 정확히 일치하는지 확인하는 점수
- 완전히 일치하면 EM=1, 조금도 일치하지 않으면 EM=0
2) F1 score
- 모델이 예측한 답과 GT가 얼마나 overlap되는지 확인하는 점수
- precision: 예측값 중 GT와 일치하는 비율; (예측값과 GT의 겹치는 글자 수)/(예측값 글자 수)
- recall: GT 중 예측값과 일치하는 비율; (예측값과 GT의 겹치는 글자 수)/(GT글자 수)
- F1 = 2 * (precision*recall)/(precision+recall)
정답(GT) | 예측값 | EM | F1 | precision | recall |
“나는 밥을 먹었다.” | “나는 밥을 먹었다” | 1 | 1 | 1 | 1 |
“나는 밥을 먹었다.” | “나는 밥을 먹었” | 0 | 0.9230 | 6/6=1 | 6/7=0.8571 |
“나는 밥을 먹었다.” | “한식집에서 나온 밥 맛있어” | 0 | 0.2222 | 2/11=0.1818 | 2/7=0.2857 |
“나는 밥을 먹었다.” | “” | 0 | 0 | X | X |
“” | “나는 밥을 먹었다” | 0 | 0 | X | X |
“” | “나는 밥을 먹었” | 0 | 0 | X | X |
“” | “한식집에서 나온 밥 맛있어” | 0 | 0 | X | X |
“” | “” | 1 | 1 | X | X |
MRC task의 경우 지문의 또 다른 부분에 정답이 위치해 점수를 높이 받지 못할 수도 있고, 위와 같은 정량지표를 사용했을 때, “나는 밥이 좋다”, “나는 밥이 싫다”와 같이 반대를 의미하는 텍스트가 한 글자 빼고 동일하다는 이유로 높은 점수를 받을 수도 있습니다.
또한 점수를 계산할 때 텍스트의 배치는 고려하지 않기 때문에 EM, F1 지표는 모델을 선택할 때 참고용으로 사용했으며,
최종 모델에 대해서는 정성평가(사람이 직접 평가)도 추가로 진행하였습니다.
<마무리>
이번 기고에서 설명 드린 부분은 기초적이고 개념적인 부분이며, 여기까지 오픈소스 팀에서 사용한 모델과 성능지표에 대한 소개를 마치겠습니다.
감사합니다.