BackGround
인공지능에게 질문에 대한 답변을 학습시킬 때, 한 가지 문제가 있는데 바로 적합한 질문/답변(Positive)데이터만 존재한다는 것이다.
지도학습을 할때는 정답과 오답을 같이 줘야 학습하는데 오답(Negative)이 없어 직접 만들어야 함.
그래서 오답 샘플을 만들어내는 많은 아이디어가 등장했다.
1. Random Sampling
- 가장 일반적인 방법으로 여러 정답 데이터들 중에서 답변만 랜덤으로 섞어 오답이라고 하는 것이다.
배고프다. | 얼른 밥먹어 ㅠㅠ | Postive |
피곤하다. | 어제 밤샜어?? | Postive |
배고프다. | 어제 밤샜어?? | Negative |
피곤하다. | 얼른 밥먹어 ㅠㅠ | Negative |
실제로 이렇게만 학습해도 어느 정도 잘 된다.
2. Hard Negative Sampling
- 이 방법은 "모델이 헷갈려하는 데이터를 학습시켜야 더 좋다"는 아이디어로 모델에게 풀기 어려운 문제를 주는 것이다.
배고프다. | 얼른 밥먹어 ㅠㅠ | Postive |
배고프다. | 밥 맛있다 | Negative |
피곤하다. | 어제 밤샜어?? | Postive |
피곤하다. | 잠 잘잤다~ | Negative |
- 대표적인 방법으로는 BM25로 키워드 일치하면서 내용이 틀린 데이터를 사용하거나
랜덤 샘플링으로 한 번 학습한 다음 그중에서 헷갈려할 만한 걸 추려서 다시 학습하는 방법이 있다.
+ 시제만 변경하는 방법, 주어를 바꾸는 방법, 동사만 바꾸는 방법 등등...
Hard Negative Sampling 은 성능을 최대로 끌어올릴 수 있는 좋은 방법
하지만 위 방법들은 직접 오답 데이터를 구축해야 한다.
MultipleNegativesRankingLoss를 몰랐을 때 나는 Random Sampling을 하여 정답과 오답의 비율을 1:1로 학습시켰다.
결과는 나쁘지 않았지만 MultipleNegativesRankingLoss 사용하면 더 좋아질 수 있다는 것을 알게 되었다.
MultipleNegativesRankingLoss를 알게 된 경로도 Toss 팀에서 서비스 검색시스템을 구축할 때 사용했다고
콘퍼런스에서 들었기 때문이다.
What is MultipleNegativesRankingLoss?
1. 긍정적인 쌍만 있는 경우에 훌륭한 손실 함수입니다.
- 각 배치 n-1 음성 문서에서 무작위로 샘플링하기 때문입니다.
2. 각 a_i에 대해 다른 모든 p_j를 음의 샘플로 사용합니다.
- a_i에 대해 1개의 양의 예(p_i)와 n-1개의 음의 예(p_j)가 있습니다.
그런 다음 softmax 정규화 점수에 대한 음의 로그 유사도를 최소화합니다.기본적으로 TripletLoss에서 아이디어를 얻은 것 같다.
알기 쉽게 설명하면 Negative Sample 데이터를 넣지 않아도 알아서 생성해서 학습하게 해 준다는 얘기
실제로도 학습할 때 Positive 데이터만 넣어 학습한다
Batch가 32일 경우 정답 1개 오답 31개(랜덤샘플링)를 넣고 학습한다.
단점.
1. 긍정/부정 라벨을 애초에 달지 않기 때문에 중간에 Evaluate 할 수 없음
- 학습이 끝난 후에 직접 검증데이터를 만들어서 검증해야 함.
2. 상대적으로 많은 GPU 메모리 점유 + 많은 학습시간을 사용
How to use train?
Environment
Python3.9
Ubuntu20.04
1. Import
from sentence_transformers import SentenceTransformer, losses
from sentence_transformers.readers import InputExample
from sentence_transformers import datasets, models
import os
import pandas as pd
from pathlib import Path
2. 모델 & 데이터 셋팅하기
embedding_model = models.Transformer('klue/roberta-base')
pooler = models.Pooling(
embedding_model.get_word_embedding_dimension(),
pooling_mode_mean_tokens=True
)
model = SentenceTransformer(modules=[embedding_model, pooler])
model.max_seq_length = 128
data = pd.read_parquet(DATA_PATH)
train_examples = []
for row in data.iterrows():
s1 = row[1]['Q']
s2 = row[1]['answer']
train_examples.append(InputExample(texts=[s1, s2]))
여기서 특징은 위에 말하바와 같이 Label을 넣어주지 않는 게 핵심
3. 학습하기
batch_size = 32
num_epochs = 4
train_dataloader = datasets.NoDuplicatesDataLoader(
train_examples, batch_size=batch_size)
train_loss = losses.MultipleNegativesRankingLoss(model=model)
model.fit(
train_objectives=[(train_dataloader, train_loss)],
epochs=num_epochs,
output_path='./save_path',
show_progress_bar=True
)
여기서 NoDuplicatesDataLoader 가 등장하는데 이는 배치를 구성할 때 중복이 나오지 않도록 구성하는 것
중간에 모델을 저장하지 않고 (보통은 중간에 Evaluate 마다 모델을 저장함) 학습이 전부 끝나야지만 모델을 저장
Conclustion
질문과 답변 데이터셋에서 오답 데이터를 만드는 것은 손이 많이 가고, 그때마다 바꿔서 학습시켜줘야 합니다.
또한 1:1 비율로 정답과 오답을 맞추면 모델이 학습데이터에 없는 답변에 대해서 정답/오답을 가려내기 어렵습니다.
하지만 MultipleNegativesRankingLoss는 Batch 마다 많은 수의 Negative포함시켜 아닌 건 확실하게 아니라고 학습시킵니다.
~~~~
Reference
https://www.pinecone.io/learn/fine-tune-sentence-transformers-mnr/
https://github.com/UKPLab/sentence-transformers/blob/master/examples/training/nli/training_nli_v2.py
https://www.sbert.net/docs/package_reference/losses.html
'Machine learning > NLP' 카테고리의 다른 글
[NLP]. Ray와 Sklearn Pipeline을 사용하여 Pandas 데이터 전처리하기 (0) | 2023.04.05 |
---|---|
[NLP]. HuggingFace Tokenizer에 token 추가(add)하기 (0) | 2023.02.27 |
[NLP]. 인공지능에서 골드 라벨(Gold Label)은 뭘까? (feat. ChatGPT) (0) | 2023.02.21 |
[NLP]. 한국어 존댓말/반말 분류모델 (formal classifier) (0) | 2023.02.03 |
[NLP] KakaoGPT 사용해서 존댓말/반말 변환하기 (0) | 2023.01.04 |