SimCSE(Simple Contrastive Sentence Embedding)
: 문장 임베딩 품질을 개선하기 위해 제안된 간단하면서도 강력한 방법론
동일한 문장에 대해 서로 다른 dropout mask를 적용하여 조금씩 다른 임베딩 벡터를 얻고,
이 두 벡터가 서로 가깝도록 다른 문장의 벡터는 멀어지도록 학습하는 contrastive learning 방식을 사용함
"같은 의미는 가깝게, 다른 의미는 멀게" 임베딩 공간을 조정하는 것
BM-K/KoSimCSE-roberta-multitask 모델은 단일 task에만 집중하지 않고, 여러 문장 간 의미 관계를 동시에 학습하는 멀티태스크 학습 방식을 적용
STS (Semantic Textual Similarity)
: 두 문장이 의미적으로 얼마나 유사한지 수치화하는 task
ex) "오늘 날씨가 좋다." vs "맑고 기분 좋은 하루다." → 높은 유사도
"고양이가 야옹거린다." vs "지구는 둥글다." → 낮은 유사도
NLI (Natural Language Inference)
: 두 문장의 관계가 함의(entailment), 모순(contradiction), 또는 중립(neutral) 인지 분류하는 task
ex) "A 남자가 달리고 있다." ⟶ "A 남자가 운동하고 있다." (함의)
"고양이가 잠을 자고 있다." ⟶ "고양이가 깨어 있다." (모순)
▶ 하나의 task로만 학습할 때보다 다양한 문장 관계를 포괄적으로 이해할 수 있음
▶ 모델이 단순히 비슷한 단어 수치만 보는 것이 아니라, 논리적 흐름과 의미적 뉘앙스까지 학습할 수 있게 됨
BM-K/KoSimCSE-roberta-multitask는 다양한 한국어 데이터셋을 활용하여 훈련됨
각 데이터셋은 서로 다른 종류의 문장 간 관계를 학습하는 데 기여함
1) 한국어 STS 데이터셋 ex) KLUE-STS
: 의미적으로 비슷하거나 다른 문장 쌍과 그 유사도 점수를 포함
2) 한국어 NLI 데이터셋 ex) KorNLI
: 문장 간 함의/모순/중립 관계가 라벨링된 데이터
⟶ 모델이 논리적인 문장 관계를 학습할 수 있도록 도움
3) 자체 구축된 다양한 paraphrase & 문장 변형 데이터
: 같은 의미를 가진 다양한 표현 방식을 수집하여 만든 데이터
⟶ 다양한 문장 표현을 폭넓게 이해할 수 있도록 함
4) 문장 랭킹 데이터
: 하나의 기준 문장에 대해 여러 문장 후보를 제시하고, 의미적으로 더 가까운 문장 순서를 학습하는 데이터
▶ 덕분에 단순 유사성뿐만 아니라, 의미적 흐름과 논리적 연결성까지 포착 가능해졌음
기반 모델 : KoRoBERTa Base
RoBERTa 아키텍처를 한국어 대규모 데이터로 학습시킨 버전
한국어 문맥 이해 성능이 매우 뛰어남
Pooler 사용 : CLS 토큰이나 Mean Pooling 기반
CLS Pooling : [CLS] 토큰에 대응하는 임베딩만을 추출
Mean Pooling : 모든 토큰 임베딩의 평균을 내어 문장 임베딩 생성
⟶ 상황에 따라 둘 중 적절한 방법을 선택해서 사용
Loss Function : contrastive loss + cosine similarity loss 조합
Contrastive loss : 같은 의미 문장은 가깝게, 다른 의미 문장은 멀게 만드는 loss
Cosine similarity loss : 코사인 유사도를 이용해 임베딩 방향을 조정하는 loss
⟶ 이 두 가지를 함께 조합하여, 의미적 유사성과 벡터 공간상의 위치 정렬을 동시에 최적화
멀티태스크 학습 방식 : STS / NLI / Ranking 각 Task별 Loss를 계산한 후, 이를 적절한 가중치로 조정하여 최종 Loss를 만듦
⟶ 여러 Task를 통해 얻은 다양한 신호를 조합해, 더 강력하고 일반화된 문장 표현 학습 가능
from sentence_transformers import SentenceTransformer
# 모델 로드
model = SentenceTransformer("BM-K/KoSimCSE-roberta-multitask")
# 문장 리스트
sentences = ["나는 오늘 날씨가 좋다.", "오늘은 맑고 기분이 좋다."]
# 임베딩 추출
embeddings = model.encode(sentences)
print(embeddings.shape) # (batch_size, hidden_size)
# 문장 하나를 768차원 임베딩 벡터로 변환