문장 임베딩
더 많은 작업
- Sentence embedding; 문장 임베딩
- 문장이나 문단 전체의 뜻을 고정 길이 벡터 하나로 나타낸 것, 또는 그 벡터를 만드는 기법
문장 임베딩(sentence embedding)은 문장, 문단, 짧은 문서를 수백~수천 차원의 실수 벡터 하나로 바꾼 것이다. 뜻이 비슷한 문장끼리 벡터 공간에서 가깝게 놓이도록 학습하므로, 두 문장의 유사도를 코사인 유사도 한 번으로 계산할 수 있다. 의미 검색, RAG의 문서 검색, 중복 질문 찾기, 군집화, 분류의 입력 특징으로 쓰인다.
단어 단위 벡터는 워드 임베딩, 텍스트 임베딩 전반의 종류와 활용은 텍스트 임베딩, 임베딩 모델 일반은 임베딩 모델 문서에 있다. 이 문서는 문장 하나를 벡터 하나로 만드는 구조(바이 인코더와 풀링), 학습 목표, 평가 방법, 실제 모델과 코드를 다룬다.
BERT 같은 사전 학습 인코더는 토큰마다 문맥 벡터를 내지만, 문장 벡터를 내도록 학습되지는 않았다. 두 문장의 관계를 판단하려면 두 문장을 한 입력으로 이어 붙여 넣는 교차 인코딩(cross-encoding)을 해야 한다. 이 방식은 정확하지만 문장 n개에서 가장 비슷한 쌍을 찾으려면 n(n-1)/2번 모델을 돌려야 한다. Sentence-BERT 논문은 문장 1만 개에서 가장 비슷한 쌍을 찾는 데 BERT로는 약 5천만 번의 추론, 약 65시간이 걸리지만 문장 임베딩으로는 약 5초면 된다고 계산했다.[1]
그렇다고 BERT 출력을 평균하거나 [CLS] 토큰 벡터를 그대로 쓰면 품질이 낮다. 같은 논문의 STS 평가(코사인 유사도와 정답 점수 사이의 스피어먼 상관, 7개 데이터셋 평균)에서 BERT 평균 벡터는 54.81, [CLS] 벡터는 29.19로, 2014년의 GloVe 단어 벡터 평균(61.32)보다도 낮았다. 미세 조정한 SBERT-NLI-base는 74.89였다.[1] 사전 학습 언어 모델의 표현은 모든 층에서 한쪽 방향으로 쏠려 있어(비등방성, anisotropy) 아무 두 문장이나 코사인 유사도가 높게 나오는 경향이 있다는 분석도 있다.[2]
문장 임베딩 모델은 바이 인코더(bi-encoder) 구조다. 같은 인코더(가중치 공유, 샴 구조)가 두 문장을 따로 인코딩하고, 각 결과를 풀링해 벡터 를 만든 뒤 유사도를 계산한다. 문서 쪽 벡터는 미리 계산해 벡터 데이터베이스에 넣어 둘 수 있으므로, 질의 때는 질의 하나만 인코딩하고 HNSW 같은 근사 최근접 이웃 색인으로 찾는다. 두 문장을 한 입력으로 넣는 크로스 인코더와의 비교는 리랭커 문서 참고.
풀링은 토큰 벡터 을 벡터 하나로 줄이는 단계다.
| 방식 | 계산 | 쓰는 곳 |
|---|---|---|
| 평균 풀링(mean pooling) | 패딩을 뺀 토큰 벡터의 평균 (는 어텐션 마스크) | SBERT 기본값. SBERT 논문의 비교에서 [CLS], 최댓값 풀링보다 대체로 나았다[1] |
| [CLS] 풀링 | 첫 토큰 벡터 | BGE 계열, SimCSE |
| 최댓값 풀링 | 차원별 최댓값 | InferSent |
| 마지막 토큰 풀링 | 마지막(EOS) 토큰 벡터 | 디코더 기반 대형 언어 모델을 임베딩 모델로 바꾼 경우(Qwen3-Embedding 등) |
풀링 뒤에 L2 정규화를 하면 내적과 코사인 유사도가 같아져 색인과 계산이 단순해진다.
SBERT는 문장 쌍 데이터의 형태에 따라 세 가지 목표를 제시했다.[1]
- 분류 목표: 로 자연어 추론(NLI) 레이블(함의, 모순, 중립)을 맞힌다. 논문은 SNLI(57만 쌍)와 MultiNLI(43만 쌍)로 학습했고, 입력 조합 중 가 가장 좋았다.
- 회귀 목표: 와 정답 유사도 점수의 평균 제곱 오차를 줄인다(STS 데이터).
- 트리플렛 목표: , 논문은 유클리드 거리와 을 썼다.
이후 표준이 된 것은 대조 학습이다. 질문-정답, 제목-본문, 함의 문장 쌍처럼 짝이 맞는 쌍을 모으고, 배치 안의 다른 문장을 음성으로 쓰는 InfoNCE 손실(sentence-transformers의 MultipleNegativesRankingLoss)로 학습한다.
- SimCSE(2021): 비지도 판은 같은 문장을 드롭아웃만 바꿔 두 번 인코딩한 것을 양성 쌍으로 썼고, 지도 판은 NLI의 함의 문장을 양성, 모순 문장을 하드 네거티브로 썼다. BERT-base로 STS 평균 스피어먼 상관 76.3%(비지도), 81.6%(지도)를 기록했다.[3]
- 대규모 약지도 사전 학습: E5는 웹에서 모은 대량의 텍스트 쌍(CCPairs)으로 대조 학습을 한 뒤 미세 조정했고, 레이블 데이터 없이 BEIR 검색 벤치마크에서 BM25를 처음 앞선 모델이라고 보고했다.[4] 이후 대부분의 범용 임베딩 모델이 "대규모 약지도 대조 학습 → 하드 네거티브를 넣은 지도 미세 조정" 2단계를 따른다.
- 지시문·접두어: 같은 모델로 질의와 문서를 다르게 인코딩하도록 입력 앞에 표지를 붙인다. multilingual-e5는 모든 입력을 "query: " 또는 "passage: "로 시작하라고 안내하고,[5] Qwen3-Embedding은 과제별 지시문을 넣으면 대부분의 과제에서 1~5% 좋아진다고 밝혔다.[6] 접두어를 빠뜨리면 성능이 떨어지므로 모델 카드를 꼭 확인한다.
- 마트료시카 표현 학습(MRL): 벡터의 앞쪽 일부 차원만 잘라 써도 쓸 만하도록 여러 길이에서 손실을 함께 거는 방법이다.[7] 저장 공간과 검색 속도를 위해 1024차원 벡터를 256차원으로 잘라 쓰는 식으로 활용한다.
- STS: 사람이 0~5점으로 매긴 문장 쌍 유사도와 모델 코사인 유사도의 스피어먼 상관. 한국어는 영어 STS-B와 NLI 학습 데이터를 기계 번역하고 검증·평가 데이터는 사람이 번역한 KorSTS·KorNLI가 2020년 공개되었고,[8] 한국어 이해 벤치마크 KLUE에도 STS 과제가 있다.[9]
- BEIR: 18개 공개 데이터셋으로 검색 모델의 제로샷 일반화를 잰다. 2021년 발표 당시 BM25가 강한 기준선이고, 밀집 검색 모델은 도메인이 바뀌면 자주 BM25보다 못했다.[10]
- MTEB(Massive Text Embedding Benchmark): 분류, 군집화, 쌍 분류, 재순위화, 검색, STS, 요약, 비텍스트 마이닝(bitext mining) 등 8개 과제, 58개 데이터셋, 112개 언어로 33개 모델을 비교하며 2022년 발표되었다. 모든 과제에서 가장 좋은 단일 모델은 없다는 것이 주요 결론이었다.[11] 후속 MMTEB는 품질 관리한 500개 이상의 과제와 250개 이상의 언어로 넓혔다.[12]
리더보드 순위는 평균 점수이므로, 실제로는 자기 데이터(언어, 도메인, 문서 길이)로 검색 재현율을 직접 재 보고 고르는 것이 안전하다. 공개 벤치마크 데이터가 학습에 섞였을 가능성도 고려한다(벤치마크 오염).
아래는 모델 카드로 확인한 예이며 성능 순위가 아니다.
| 모델 | 차원 | 최대 입력 | 특징 |
|---|---|---|---|
| jhgan/ko-sroberta-multitask | 768 | - | KorNLI·KorSTS로 다중 과제 학습한 한국어 SBERT. 모델 카드 기준 KorSTS 평가 코사인 스피어먼 85.60[13] |
| BM-K/KoSimCSE-roberta-multitask | - | - | 한국어 SimCSE. 저장소 표 기준 STS 평가 평균 85.77[14] |
| intfloat/multilingual-e5-large | 1024 | - | 24층 다국어 E5, "query: "/"passage: " 접두어 필요[5] |
| BAAI/bge-m3 | 1024 | 8192 토큰 | 100개 이상 언어, 밀집·희소·다중 벡터 검색을 한 모델에서 지원[15] |
| nlpai-lab/KURE-v1 | 1024 | 8192 토큰 | 고려대 NLP&AI 연구실이 BGE-M3를 한국어 질의-문서-하드 네거티브 데이터로 미세 조정한 검색용 모델[16] |
| Qwen3-Embedding (0.6B/4B/8B) | 1024/2560/4096 | 32K 토큰 | 마지막 토큰 풀링, MRL과 지시문 지원. 8B 모델이 2025년 6월 5일 기준 MTEB 다국어 리더보드 1위(70.58)라고 밝혔다[6] |
OpenAI, 구글, Cohere 등은 API로만 쓰는 임베딩 모델을 제공한다. 같은 벡터 공간을 유지하려면 문서와 질의를 반드시 같은 모델(같은 버전)로 인코딩해야 하며, 모델을 바꾸면 색인 전체를 다시 만들어야 한다.
sentence-transformers 라이브러리(6.1.0 기준)로 인코딩과 유사도 계산을 하는 예다. API는 모델 카드 예제와 같다.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("jhgan/ko-sroberta-multitask")
sentences = [
"치타가 들판을 가로질러 먹이를 쫓는다.",
"치타 한 마리가 먹이 뒤에서 달리고 있다.",
"원숭이 한 마리가 드럼을 연주한다.",
]
emb = model.encode(sentences, normalize_embeddings=True) # (3, 768), L2 정규화
print(model.similarity(emb, emb)) # 3x3 코사인 유사도 행렬
라이브러리 없이 허깅 페이스 Transformers로 평균 풀링을 직접 하면 다음과 같다. 패딩 토큰을 평균에서 빼는 것이 핵심이다.
import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModel
name = "jhgan/ko-sroberta-multitask"
tok, enc = AutoTokenizer.from_pretrained(name), AutoModel.from_pretrained(name)
def embed(texts):
batch = tok(texts, padding=True, truncation=True, return_tensors="pt")
with torch.no_grad():
h = enc(**batch).last_hidden_state # (B, L, H)
mask = batch["attention_mask"].unsqueeze(-1).float() # (B, L, 1)
u = (h * mask).sum(1) / mask.sum(1).clamp(min=1e-9) # 평균 풀링
return F.normalize(u, dim=-1)
q, d = embed(["환불은 며칠 안에 되나요?"]), embed(["결제 취소 후 3~5영업일 안에 환불됩니다."])
print((q @ d.T).item())
자기 데이터로 미세 조정할 때는 (질의, 정답) 쌍을 모아 대조 학습 손실로 학습한다.
from datasets import Dataset
from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses
model = SentenceTransformer("jhgan/ko-sroberta-multitask")
train = Dataset.from_dict({
"anchor": ["비밀번호를 잊어버렸어요", "배송 조회는 어디서 하나요"],
"positive": ["로그인 화면의 '비밀번호 찾기'를 누르세요", "마이페이지 > 주문 내역에서 배송 상태를 볼 수 있습니다"],
})
loss = losses.MultipleNegativesRankingLoss(model) # 배치 내 음성 InfoNCE, scale=20(τ=0.05)
SentenceTransformerTrainer(model=model, train_dataset=train, loss=loss).train()
(anchor, positive, negative) 세 열을 주면 세 번째 열이 하드 네거티브로 분모에 추가된다. 배치 내 음성을 쓰므로 배치를 키울수록 유리하고, 같은 정답이 한 배치에 두 번 들어가지 않게 하는 것이 좋다.
- 정보 압축: 긴 문서를 벡터 하나로 줄이면 세부 내용이 사라진다. 그래서 문서를 청크로 잘라 넣고(RAG (인공지능) 참고), 정밀도가 필요하면 토큰별 벡터를 쓰는 ColBERT나 리랭커를 덧붙인다.
- 정확 일치에 약함: 제품 번호, 오류 코드, 고유명사처럼 철자가 그대로 맞아야 하는 질의는 BM25 같은 어휘 검색이 낫다. 둘을 섞는 것이 하이브리드 검색이다.
- 부정·수치: "환불이 된다"와 "환불이 안 된다"처럼 부정어 하나 차이, 숫자 차이는 벡터가 매우 가깝게 나오기 쉽다.
- 도메인·언어: 일반 영어 데이터로 학습한 모델은 한국어, 법률·의료 문서에서 품질이 떨어질 수 있다. 다국어 모델이나 해당 언어로 미세 조정한 모델을 고르고 직접 평가한다.
- 유사도 값의 의미: 코사인 값의 절대 크기는 모델마다 분포가 달라 "0.8 이상이면 같은 뜻" 같은 고정 임계값은 모델을 바꾸면 맞지 않는다.
- ↑ 1.0 1.1 1.2 1.3 Nils Reimers, Iryna Gurevych, 〈Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks〉, EMNLP 2019. arXiv:1908.10084
- ↑ Kawin Ethayarajh, 〈How Contextual are Contextualized Word Representations?〉, EMNLP 2019. arXiv:1909.00512
- ↑ Tianyu Gao, Xingcheng Yao, Danqi Chen, 〈SimCSE: Simple Contrastive Learning of Sentence Embeddings〉, EMNLP 2021. arXiv:2104.08821
- ↑ Liang Wang 외, 〈Text Embeddings by Weakly-Supervised Contrastive Pre-training〉, 2022. arXiv:2212.03533
- ↑ 5.0 5.1 intfloat/multilingual-e5-large 모델 카드
- ↑ 6.0 6.1 Qwen3-Reranker-0.6B 모델 카드(Qwen3 Embedding 시리즈 소개 포함)
- ↑ Aditya Kusupati 외, 〈Matryoshka Representation Learning〉, NeurIPS 2022. arXiv:2205.13147
- ↑ Jiyeon Ham 외, 〈KorNLI and KorSTS: New Benchmark Datasets for Korean Natural Language Understanding〉, 2020. arXiv:2004.03289
- ↑ Sungjoon Park 외, 〈KLUE: Korean Language Understanding Evaluation〉, 2021. arXiv:2105.09680
- ↑ Nandan Thakur 외, 〈BEIR: A Heterogenous Benchmark for Zero-shot Evaluation of Information Retrieval Models〉, NeurIPS 2021 Datasets and Benchmarks. arXiv:2104.08663
- ↑ Niklas Muennighoff, Nouamane Tazi, Loïc Magne, Nils Reimers, 〈MTEB: Massive Text Embedding Benchmark〉, 2022. arXiv:2210.07316
- ↑ Kenneth Enevoldsen 외, 〈MMTEB: Massive Multilingual Text Embedding Benchmark〉, 2025. arXiv:2502.13595
- ↑ jhgan/ko-sroberta-multitask 모델 카드
- ↑ BM-K/KoSimCSE-roberta-multitask 모델 카드
- ↑ Jianlv Chen 외, 〈M3-Embedding: Multi-Linguality, Multi-Functionality, Multi-Granularity Text Embeddings Through Self-Knowledge Distillation〉, 2024. arXiv:2402.03216
- ↑ nlpai-lab/KURE-v1 모델 카드