97. 임베딩 (Embeddings)과 벡터 검색 (Vector Search): 제대로 작동하는 의미론적 검색 (Semantic Search)
요약
키워드 기반 검색의 한계를 극복하는 임베딩과 벡터 검색의 원리를 설명합니다. 텍스트를 벡터로 변환하여 의미론적 유사성을 측정하는 방법과 코사인 유사도, FAISS, ChromaDB 등 실무 도구 활용법을 다룹니다.
핵심 포인트
- 임베딩을 통해 텍스트를 의미를 담은 벡터로 변환
- 코사인 유사도를 활용한 벡터 간 의미론적 거리 측정
- Sentence Transformers를 이용한 문장 인코딩
- FAISS와 ChromaDB를 활용한 효율적인 벡터 검색 구현
전통적인 검색은 키워드 (Keywords)를 기반으로 작동합니다. 사용자가 "cheap hotel"이라고 입력하면, 시스템은 해당 단어들이 정확히 포함된 문서를 찾습니다.
누군가 "affordable accommodation near the beach"라고 질문한다고 가정해 봅시다. 당신의 문서에는 "budget-friendly lodging by the coast"라고 적혀 있습니다. 키워드 중 겹치는 것이 하나도 없습니다. 결과도 없습니다. 검색은 실패합니다.
임베딩 (Embeddings)이 이 문제를 해결합니다. 임베딩은 텍스트를 숫자로 이루어진 벡터 (Vectors)로 변환하며, 유사한 의미를 가진 것들은 기하학적으로 가까운 위치에 놓이게 됩니다. "Cheap"과 "affordable"은 벡터 공간 (Vector space)에서 서로 가까운 곳에 위치합니다. "Hotel"과 "accommodation"도 서로 가까운 곳에 위치합니다. 의미론적 유사성 (Semantic similarity)이 곧 거리가 됩니다.
이 기술은 모든 현대적인 검색 시스템의 동력이 됩니다. ChatGPT의 메모리, Notion AI, GitHub Copilot의 컨텍스트 (Context) 등 모두가 이를 사용합니다.
여기서 배우게 될 내용
- 임베딩 (Embeddings)이란 무엇이며 어떻게 의미를 인코딩 (Encode)하는가
- 코사인 유사도 (Cosine similarity): 두 벡터가 얼마나 가까운지 측정하는 방법
- 문장 트랜스포머 (Sentence transformers): 의미론적 검색을 위한 적절한 모델
- 처음부터 의미론적 검색 엔진 구축하기
- FAISS: 대규모 환경에서의 빠른 근사 최근접 이웃 (Approximate nearest neighbor) 검색
- ChromaDB: 프로덕션 (Production) 환경에서 사용하는 벡터 데이터베이스 (Vector database)
- 문서 검색 (Document retrieval)을 위한 실무 패턴
임베딩 (Embeddings)의 실체
임베딩은 부동 소수점 (Floating point) 숫자로 이루어진 밀집 벡터 (Dense vector)입니다. 모든 텍스트 조각은 하나의 벡터로 매핑됩니다.
핵심 속성: 의미론적으로 유사한 텍스트는 임베딩 공간 (Embedding space)에서 서로 가까운 벡터를 가집니다.
from sentence_transformers import SentenceTransformer
import numpy as np
...
출력 결과:
Embedding shape: (5, 384)
각 문장 → 384차원 벡터
...
384개의 숫자가 문장 전체의 의미를 나타냅니다. 이 숫자들은 사전 학습 (Pretraining) 과정에서 유사한 문장이 유사한 벡터를 생성하도록 학습되었습니다.
코사인 유사도 (Cosine Similarity): 의미론적 거리 측정
가공되지 않은 유클리드 거리 (Euclidean distance)는 텍스트 임베딩에 잘 맞지 않습니다. 두 개의 긴 문서는 동일한 주제를 다루더라도 벡터의 크기가 커서 서로 멀리 떨어져 있을 수 있기 때문입니다.
코사인 유사도 (Cosine similarity)는 벡터의 크기가 아닌 벡터 사이의 각도를 측정합니다. 값의 범위는 -1에서 1 사이입니다. 방향이 같으면 1, 수직이면 0, 반대 방향이면 -1입니다.
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
...
출력 결과:
Cosine similarity between sentences:
Pair Similarity
----------------------------------------------------------------------
...
"Cat on mat"과 "feline on rug"는 0.83의 점수를 기록합니다. 단어는 다르지만 동일한 개념입니다. "ML subset AI"와 "AI includes ML"은 0.89의 점수를 기록합니다. 의미론적으로 동일합니다.
"Cat on mat"과 "ML is AI"는 0.12의 점수를 기록합니다. 완전히 다른 주제입니다.
Sentence Transformers: 적절한 모델
Word2Vec과 같은 단어 수준 (Word-level) 모델은 단어 임베딩 (Word embeddings)의 평균을 구합니다. 이는 문장 구조를 상실하게 만듭니다. Sentence transformers는 문장 수준의 태스크 (Sentence-level tasks)로 학습되어, 문장 전체에 대해 하나의 임베딩을 생성합니다.
from sentence_transformers import SentenceTransformer
# Popular embedding models
...
의미론적 검색 엔진 (Semantic Search Engine) 처음부터 구축하기
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
...
출력 결과:
Query: 'How do transformers work?'
------------------------------------------------------------
1. [0.712] The transformer architecture uses self-attention mechanisms...
...
검색은 정확한 단어가 일치하지 않더라도 의미론적으로 관련 있는 문서를 찾아냅니다. "Make training more efficient"라는 쿼리는 "efficient"라는 단어를 포함하지 않고도 LoRA를 정확하게 검색해냅니다.
FAISS: 대규모 환경에서의 빠른 검색
브루트 포스 (Brute-force, 전수 조사) 방식(쿼리를 모든 문서와 비교)은 수천 개의 문서까지는 작동합니다. 하지만 수백만 개의 문서의 경우, 근사 최근접 이웃 (ANN, Approximate Nearest Neighbor) 검색이 필요합니다. FAISS (Facebook AI Similarity Search)가 표준 도구입니다.
ip install faiss-cpu # 또는 GPU 지원을 위해 faiss-gpu 설치
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
...
매우 큰 데이터셋의 경우: IVF 인덱스 사용 (근사 방식, 더 빠름)
IVF = Inverted File Index, 공간을 클러스터(clusters)로 분할함
...
ChromaDB: 실제 프로젝트를 위한 벡터 데이터베이스 (Vector Database)
FAISS는 강력하지만 저수준 (low-level)입니다. ChromaDB는 영속성 (persistence), 메타데이터 필터링 (metadata filtering), 그리고 깔끔한 API를 제공합니다. 프로덕션 (production) 환경에서 사용하기 좋습니다.
pip install chromadb
import chromadb
from sentence_transformers import SentenceTransformer
...
# 메타데이터로 필터링
results_filtered = collection.query(
query_embeddings=[model.encode("machine learning concepts").tolist()],
...
# 업데이트 및 삭제
collection.update(
ids=['doc1'],
...
배치 인코딩 (Batch Encoding): 대규모 데이터셋을 효율적으로 처리하기
from sentence_transformers import SentenceTransformer
import numpy as np
import time
...
임베딩 품질 평가 (Evaluating Embedding Quality)
모든 임베딩 모델이 모든 작업에서 동일한 성능을 내는 것은 아닙니다. 적용하기 전에 테스트하십시오.
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
...
일반적인 임베딩 패턴 (Common Embedding Patterns)
# 패턴 1: 비대칭 검색 (Asymmetric search) (쿼리와 문서가 서로 다른 모델을 사용함)
# 쿼리가 짧은 질문이고 문서가 긴 구절일 때 유용함
...
# 패턴 2: 주제를 찾기 위한 임베딩 클러스터링 (Clustering embeddings to find topics)
from sklearn.cluster import KMeans
sentences = [
"Python is great for data science.",
"R is used for statistical computing.",
"Machine learning requires lots of data.",
"Deep learning uses neural networks.",
"Java is widely used in enterprise software.",
"JavaScript powers the web frontend.",
"Supervised learning uses labeled data.",
"Unsupervised learning finds hidden patterns.",
]
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
embeddings = model.encode(sentences)
kmeans = KMeans(n_clusters=3, random_state=42,
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기